Allow user to specifiy which stack an application is launched on

This commit is contained in:
Simon Fels 2017-03-15 17:53:28 +01:00
commit 3fededabd0
23 changed files with 257 additions and 89 deletions

View file

@ -73,10 +73,13 @@ void AndroidApiSkeleton::launch_application(anbox::protobuf::bridge::LaunchAppli
std::vector<std::string> argv = {
"/system/bin/am",
"start",
// Launch any application always in the freeform stack
"--stack", "2",
};
if (request->has_stack()) {
argv.push_back("--stack");
argv.push_back(std::to_string(request->stack()));
}
if (request->has_launch_bounds()) {
argv.push_back("--launch-bounds");
std::stringstream launch_bounds;

View file

@ -185,12 +185,14 @@ set(SOURCES
anbox/ubuntu/audio_sink.cpp
anbox/dbus/interface.h
anbox/dbus/codecs.h
anbox/dbus/skeleton/service.cpp
anbox/dbus/skeleton/application_manager.cpp
anbox/dbus/stub/application_manager.cpp
anbox/application/launcher_storage.cpp
anbox/application/database.cpp
anbox/application/manager.h
anbox/cmds/version.cpp
anbox/cmds/session_manager.cpp

View file

@ -0,0 +1,71 @@
/*
* Copyright (C) 2016 Simon Fels <morphis@gravedo.de>
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 3, as published
* by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranties of
* MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef ANBOX_APPLICATION_MANAGER_H_
#define ANBOX_APPLICATION_MANAGER_H_
#include "anbox/android/intent.h"
#include "anbox/do_not_copy_or_move.h"
#include "anbox/graphics/rect.h"
#include "anbox/wm/stack.h"
#include <string>
#include <core/property.h>
namespace anbox {
namespace application {
class Manager : public DoNotCopyOrMove {
public:
virtual void launch(const android::Intent &intent,
const graphics::Rect &launch_bounds = graphics::Rect::Invalid,
const wm::Stack::Id &stack = wm::Stack::Id::Default) = 0;
virtual core::Property<bool>& ready() = 0;
};
class RestrictedManager : public Manager {
public:
RestrictedManager(
const std::shared_ptr<Manager> &other,
const wm::Stack::Id &launch_stack = wm::Stack::Id::Invalid) :
other_(other),
launch_stack_(launch_stack) {}
virtual ~RestrictedManager() {}
void launch(const android::Intent &intent,
const graphics::Rect &launch_bounds = graphics::Rect::Invalid,
const wm::Stack::Id &stack = wm::Stack::Id::Default) override {
auto selected_stack = stack;
// If we have a static launch stack set use that one instead of
// the one the caller gave us.
if (launch_stack_ != wm::Stack::Id::Invalid)
selected_stack = launch_stack_;
other_->launch(intent, launch_bounds, selected_stack);
}
core::Property<bool>& ready() override { return other_->ready(); }
private:
std::shared_ptr<Manager> other_;
wm::Stack::Id launch_stack_;
};
} // namespace application
} // namespace anbox
#endif

View file

@ -1,37 +0,0 @@
/*
* Copyright (C) 2016 Simon Fels <morphis@gravedo.de>
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 3, as published
* by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranties of
* MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef ANBOX_APPLICATION_MANAGER_H_
#define ANBOX_APPLICATION_MANAGER_H_
#include "anbox/android/intent.h"
#include "anbox/do_not_copy_or_move.h"
#include "anbox/graphics/rect.h"
#include <string>
#include <core/property.h>
namespace anbox {
class ApplicationManager : public DoNotCopyOrMove {
public:
virtual void launch(const android::Intent &intent, const graphics::Rect &launch_bounds = graphics::Rect::Invalid) = 0;
virtual core::Property<bool>& ready() = 0;
};
} // namespace anbox
#endif

View file

@ -20,6 +20,7 @@
#include "anbox/logger.h"
#include "anbox/rpc/channel.h"
#include "anbox/utils.h"
#include "anbox/wm/stack.h"
#include "anbox_bridge.pb.h"
#include "anbox_rpc.pb.h"
@ -46,7 +47,8 @@ void AndroidApiStub::ensure_rpc_channel() {
}
void AndroidApiStub::launch(const android::Intent &intent,
const graphics::Rect &launch_bounds) {
const graphics::Rect &launch_bounds,
const wm::Stack::Id &stack) {
ensure_rpc_channel();
auto c = std::make_shared<Request<protobuf::rpc::Void>>();
@ -57,6 +59,20 @@ void AndroidApiStub::launch(const android::Intent &intent,
launch_wait_handle_.expect_result();
}
switch (stack) {
case wm::Stack::Id::Default:
message.set_stack(::anbox::protobuf::bridge::LaunchApplication_Stack_DEFAULT);
break;
case wm::Stack::Id::Fullscreen:
message.set_stack(::anbox::protobuf::bridge::LaunchApplication_Stack_FULLSCREEN);
break;
case wm::Stack::Id::Freeform:
message.set_stack(::anbox::protobuf::bridge::LaunchApplication_Stack_FREEFORM);
break;
default:
break;
}
if (launch_bounds != graphics::Rect::Invalid) {
auto rect = message.mutable_launch_bounds();
rect->set_left(launch_bounds_.left());

View file

@ -18,7 +18,7 @@
#ifndef ANBOX_BRIDGE_ANDROID_API_STUB_H_
#define ANBOX_BRIDGE_ANDROID_API_STUB_H_
#include "anbox/application_manager.h"
#include "anbox/application/manager.h"
#include "anbox/common/wait_handle.h"
#include "anbox/graphics/rect.h"
@ -35,7 +35,7 @@ namespace rpc {
class Channel;
} // namespace rpc
namespace bridge {
class AndroidApiStub : public anbox::ApplicationManager {
class AndroidApiStub : public anbox::application::Manager {
public:
AndroidApiStub();
~AndroidApiStub();
@ -48,7 +48,10 @@ class AndroidApiStub : public anbox::ApplicationManager {
void resize_task(const std::int32_t &id, const anbox::graphics::Rect &rect,
const std::int32_t &resize_mode);
void launch(const android::Intent &intent, const graphics::Rect &launch_bounds = graphics::Rect::Invalid) override;
void launch(const android::Intent &intent,
const graphics::Rect &launch_bounds = graphics::Rect::Invalid,
const wm::Stack::Id &stack = wm::Stack::Id::Default) override;
core::Property<bool>& ready() override;
private:

View file

@ -73,7 +73,7 @@ std::ostream& operator<<(std::ostream& out,
// We are imposing size constraints to ensure a consistent CLI layout.
typedef SizeConstrainedString<20> Name;
typedef SizeConstrainedString<60> Usage;
typedef SizeConstrainedString<80> Description;
typedef SizeConstrainedString<100> Description;
/// @brief Flag models an input parameter to a command.
class Flag : public DoNotCopyOrMove {

View file

@ -50,10 +50,12 @@ anbox::cmds::Launch::Launch()
flag(cli::make_flag(cli::Name{"package"},
cli::Description{"Package the intent should go to"},
intent_.package));
flag(cli::make_flag(
cli::Name{"component"},
cli::Description{"Component of a package the intent should go"},
intent_.component));
flag(cli::make_flag(cli::Name{"component"},
cli::Description{"Component of a package the intent should go"},
intent_.component));
flag(cli::make_flag(cli::Name{"stack"},
cli::Description{"Which window stack the activity should be started on. Possible: default, fullscreen, freeform"},
stack_));
action([this](const cli::Command::Context&) {
auto trap = core::posix::trap_signals_for_process({core::posix::Signal::sig_term, core::posix::Signal::sig_int});
@ -82,7 +84,7 @@ anbox::cmds::Launch::Launch()
dispatcher->dispatch([&]() {
if (stub->ready()) {
try {
stub->launch(intent_);
stub->launch(intent_, graphics::Rect::Invalid, stack_);
success = true;
} catch (std::exception &err) {
ERROR("err %s", err.what());
@ -97,7 +99,7 @@ anbox::cmds::Launch::Launch()
if (!ready)
return;
try {
stub->launch(intent_);
stub->launch(intent_, graphics::Rect::Invalid, stack_);
success = true;
} catch (std::exception &err) {
ERROR("Failed to launch activity: %s", err.what());

View file

@ -23,6 +23,7 @@
#include <memory>
#include "anbox/android/intent.h"
#include "anbox/wm/stack.h"
#include "anbox/cli.h"
namespace anbox {
@ -33,6 +34,7 @@ class Launch : public cli::CommandWithFlagsAndAction {
private:
android::Intent intent_;
wm::Stack::Id stack_;
};
} // namespace cmds
} // namespace anbox

View file

@ -145,6 +145,15 @@ anbox::cmds::SessionManager::SessionManager(const BusFactory &bus_factory)
auto android_api_stub = std::make_shared<bridge::AndroidApiStub>();
auto app_manager = std::static_pointer_cast<application::Manager>(android_api_stub);
if (!single_window_) {
// When we're not running single window mode we need to restrict ourself to
// only launch applications in freeform mode as otherwise the window tracking
// doesn't work.
app_manager = std::make_shared<application::RestrictedManager>(
android_api_stub, wm::Stack::Id::Freeform);
}
auto display_frame = graphics::Rect::Invalid;
if (single_window_)
display_frame = window_size_;
@ -219,7 +228,7 @@ anbox::cmds::SessionManager::SessionManager(const BusFactory &bus_factory)
auto bus = bus_factory_();
bus->install_executor(core::dbus::asio::make_executor(bus, rt->service()));
auto skeleton = anbox::dbus::skeleton::Service::create_for_bus(bus, android_api_stub);
auto skeleton = anbox::dbus::skeleton::Service::create_for_bus(bus, app_manager);
rt->start();
trap->run();

47
src/anbox/dbus/codecs.h Normal file
View file

@ -0,0 +1,47 @@
/*
* Copyright (C) 2017 Simon Fels <morphis@gravedo.de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef ANBOX_DBUS_CODECS_H_
#define ANBOX_DBUS_CODECS_H_
#include "anbox/wm/stack.h"
#include <core/dbus/codec.h>
#include <sstream>
namespace core {
namespace dbus {
template<>
struct Codec<anbox::wm::Stack::Id> {
inline static void encode_argument(Message::Writer &out, const anbox::wm::Stack::Id &stack) {
std::stringstream ss;
ss << stack;
auto s = ss.str();
out.push_stringn(s.c_str(), s.length());
}
inline static void decode_argument(Message::Reader &in, anbox::wm::Stack::Id &stack) {
std::stringstream ss;
ss << in.pop_string();
ss >> stack;
}
};
} // namespace dbus
} // namespace core
#endif

View file

@ -18,6 +18,7 @@
#include "anbox/dbus/skeleton/application_manager.h"
#include "anbox/android/intent.h"
#include "anbox/dbus/interface.h"
#include "anbox/dbus/codecs.h"
#include "anbox/logger.h"
#include <core/property.h>
@ -27,7 +28,7 @@ namespace dbus {
namespace skeleton {
ApplicationManager::ApplicationManager(
const core::dbus::Bus::Ptr &bus, const core::dbus::Object::Ptr &object,
const std::shared_ptr<anbox::ApplicationManager> &impl)
const std::shared_ptr<anbox::application::Manager> &impl)
: bus_(bus), object_(object), impl_(impl),
properties_{ object_->get_property<anbox::dbus::interface::ApplicationManager::Properties::Ready>() },
signals_{ object_->get_signal<core::dbus::interfaces::Properties::Signals::PropertiesChanged>() } {
@ -51,10 +52,13 @@ ApplicationManager::ApplicationManager(
reader >> bottom;
graphics::Rect launch_bounds{left, top, right, bottom};
wm::Stack::Id stack = wm::Stack::Id::Default;
reader >> stack;
core::dbus::Message::Ptr reply;
try {
launch(intent, launch_bounds);
launch(intent, launch_bounds, stack);
reply = core::dbus::Message::make_method_return(msg);
} catch (std::exception const &err) {
reply = core::dbus::Message::make_error(msg, "org.anbox.Error.Failed",
@ -88,8 +92,10 @@ void ApplicationManager::on_property_value_changed(const typename Property::Valu
dict, the_empty_list_of_invalidated_properties));
}
void ApplicationManager::launch(const android::Intent &intent, const graphics::Rect &launch_bounds) {
impl_->launch(intent, launch_bounds);
void ApplicationManager::launch(const android::Intent &intent,
const graphics::Rect &launch_bounds,
const wm::Stack::Id &stack) {
impl_->launch(intent, launch_bounds, stack);
}
core::Property<bool>& ApplicationManager::ready() {

View file

@ -18,7 +18,7 @@
#ifndef ANBOX_DBUS_SKELETON_APPLICATION_MANAGER_H_
#define ANBOX_DBUS_SKELETON_APPLICATION_MANAGER_H_
#include "anbox/application_manager.h"
#include "anbox/application/manager.h"
#include <core/dbus/bus.h>
#include <core/dbus/object.h>
@ -30,14 +30,17 @@
namespace anbox {
namespace dbus {
namespace skeleton {
class ApplicationManager : public anbox::ApplicationManager {
class ApplicationManager : public anbox::application::Manager {
public:
ApplicationManager(const core::dbus::Bus::Ptr &bus,
const core::dbus::Object::Ptr &object,
const std::shared_ptr<anbox::ApplicationManager> &impl);
const std::shared_ptr<anbox::application::Manager> &impl);
~ApplicationManager();
void launch(const android::Intent &intent, const graphics::Rect &launch_bounds = graphics::Rect::Invalid) override;
void launch(const android::Intent &intent,
const graphics::Rect &launch_bounds = graphics::Rect::Invalid,
const wm::Stack::Id &stack = wm::Stack::Id::Default) override;
core::Property<bool>& ready() override;
private:
@ -47,7 +50,7 @@ class ApplicationManager : public anbox::ApplicationManager {
core::dbus::Bus::Ptr bus_;
core::dbus::Service::Ptr service_;
core::dbus::Object::Ptr object_;
std::shared_ptr<anbox::ApplicationManager> impl_;
std::shared_ptr<anbox::application::Manager> impl_;
struct {
std::shared_ptr<core::dbus::Property<anbox::dbus::interface::ApplicationManager::Properties::Ready>> ready;
} properties_;

View file

@ -24,7 +24,7 @@ namespace dbus {
namespace skeleton {
std::shared_ptr<Service> Service::create_for_bus(
const core::dbus::Bus::Ptr &bus,
const std::shared_ptr<anbox::ApplicationManager> &application_manager) {
const std::shared_ptr<anbox::application::Manager> &application_manager) {
auto service = core::dbus::Service::add_service(
bus, anbox::dbus::interface::Service::name());
auto object =
@ -35,7 +35,7 @@ std::shared_ptr<Service> Service::create_for_bus(
Service::Service(
const core::dbus::Bus::Ptr &bus, const core::dbus::Service::Ptr &service,
const core::dbus::Object::Ptr &object,
const std::shared_ptr<anbox::ApplicationManager> &application_manager)
const std::shared_ptr<anbox::application::Manager> &application_manager)
: bus_(bus),
service_(service),
object_(object),

View file

@ -18,7 +18,7 @@
#ifndef ANBOX_DBUS_SKELETON_SERVICE_H_
#define ANBOX_DBUS_SKELETON_SERVICE_H_
#include "anbox/application_manager.h"
#include "anbox/application/manager.h"
#include "anbox/do_not_copy_or_move.h"
#include <core/dbus/bus.h>
@ -33,19 +33,19 @@ class Service : public DoNotCopyOrMove {
public:
static std::shared_ptr<Service> create_for_bus(
const core::dbus::Bus::Ptr &bus,
const std::shared_ptr<anbox::ApplicationManager> &application_manager);
const std::shared_ptr<anbox::application::Manager> &application_manager);
Service(
const core::dbus::Bus::Ptr &bus, const core::dbus::Service::Ptr &service,
const core::dbus::Object::Ptr &object,
const std::shared_ptr<anbox::ApplicationManager> &application_manager);
const std::shared_ptr<anbox::application::Manager> &application_manager);
~Service();
private:
core::dbus::Bus::Ptr bus_;
core::dbus::Service::Ptr service_;
core::dbus::Object::Ptr object_;
std::shared_ptr<ApplicationManager> application_manager_;
std::shared_ptr<application::Manager> application_manager_;
};
} // namespace skeleton
} // namespace dbus

View file

@ -17,6 +17,7 @@
#include "anbox/dbus/stub/application_manager.h"
#include "anbox/dbus/interface.h"
#include "anbox/dbus/codecs.h"
#include "anbox/logger.h"
namespace anbox {
@ -41,13 +42,15 @@ ApplicationManager::ApplicationManager(const core::dbus::Bus::Ptr &bus,
ApplicationManager::~ApplicationManager() {}
void ApplicationManager::launch(const android::Intent &intent, const graphics::Rect &launch_bounds) {
void ApplicationManager::launch(const android::Intent &intent,
const graphics::Rect &launch_bounds,
const wm::Stack::Id &stack) {
auto result = object_->invoke_method_synchronously<
anbox::dbus::interface::ApplicationManager::Methods::Launch,
anbox::dbus::interface::ApplicationManager::Methods::Launch::ResultType>(
intent.action, intent.uri, intent.type, intent.flags, intent.package,
intent.component, launch_bounds.left(), launch_bounds.top(),
launch_bounds.right(), launch_bounds.bottom());
launch_bounds.right(), launch_bounds.bottom(), stack);
if (result.is_error()) throw std::runtime_error(result.error().print());
}

View file

@ -18,7 +18,7 @@
#ifndef ANBOX_DBUS_SKELETON_APPLICATION_MANAGER_H_
#define ANBOX_DBUS_SKELETON_APPLICATION_MANAGER_H_
#include "anbox/application_manager.h"
#include "anbox/application/manager.h"
#include <core/dbus/bus.h>
#include <core/dbus/object.h>
@ -29,7 +29,7 @@
namespace anbox {
namespace dbus {
namespace stub {
class ApplicationManager : public anbox::ApplicationManager {
class ApplicationManager : public anbox::application::Manager {
public:
static std::shared_ptr<ApplicationManager> create_for_bus(
const core::dbus::Bus::Ptr &bus);
@ -39,7 +39,10 @@ class ApplicationManager : public anbox::ApplicationManager {
const core::dbus::Object::Ptr &object);
~ApplicationManager();
void launch(const android::Intent &intent, const graphics::Rect &launch_bounds = graphics::Rect::Invalid) override;
void launch(const android::Intent &intent,
const graphics::Rect &launch_bounds = graphics::Rect::Invalid,
const wm::Stack::Id &stack = wm::Stack::Id::Default) override;
core::Property<bool>& ready() override;
private:

View file

@ -34,6 +34,12 @@ message Notification {
message LaunchApplication {
required Intent intent = 1;
optional Rect launch_bounds = 2;
enum Stack {
DEFAULT = 0;
FULLSCREEN = 1;
FREEFORM = 2;
}
optional Stack stack = 3 [default = DEFAULT];
}
message SetFocusedTask {

View file

@ -44,7 +44,7 @@ void MultiWindowManager::apply_window_state_update(const WindowState::List &upda
for (const auto &window : updated) {
// Ignore all windows which are not part of the freeform task stack
if (window.stack() != Stack::Freeform) continue;
if (window.stack() != Stack::Id::Freeform) continue;
// And also those which don't have a surface mapped at the moment
if (!window.has_surface()) continue;

View file

@ -19,9 +19,33 @@
namespace anbox {
namespace wm {
Stack::Id Stack::Invalid = -1;
Stack::Id Stack::Default = 0;
Stack::Id Stack::Fullscreen = 1;
Stack::Id Stack::Freeform = 2;
std::ostream &operator<<(std::ostream &out, const Stack::Id &stack) {
switch (stack) {
case anbox::wm::Stack::Id::Default:
out << "default";
break;
case anbox::wm::Stack::Id::Fullscreen:
out << "fullscreen";
break;
case anbox::wm::Stack::Id::Freeform:
out << "freeform";
break;
default:
break;
}
return out;
}
std::istream& operator>>(std::istream& in, Stack::Id &stack) {
std::string s;
in >> s;
if (s == "default")
stack = anbox::wm::Stack::Id::Default;
else if (s == "fullscreen")
stack = anbox::wm::Stack::Id::Fullscreen;
else if (s == "freeform")
stack = anbox::wm::Stack::Id::Freeform;
return in;
}
} // namespace wm
} // namespace anbox

View file

@ -18,23 +18,28 @@
#ifndef ANBOX_WM_STACK_H_
#define ANBOX_WM_STACK_H_
#include <cstdint>
#include <ostream>
namespace anbox {
namespace wm {
class Stack {
public:
typedef std::int32_t Id;
static Id Invalid;
static Id Default;
static Id Fullscreen;
static Id Freeform;
enum class Id {
Invalid = -1,
Default = 0,
Fullscreen = 1,
Freeform = 2,
};
Stack() = delete;
Stack(const Stack&) = delete;
};
std::ostream& operator<<(std::ostream &out, Stack::Id const &stack);
std::istream& operator>>(std::istream &in, Stack::Id &stack);
} // namespace wm
} // namespace anbox
#endif

View file

@ -25,7 +25,7 @@ WindowState::WindowState()
frame_(graphics::Rect::Invalid),
package_name_(""),
task_(Task::Invalid),
stack_(Stack::Invalid) {}
stack_(Stack::Id::Invalid) {}
WindowState::WindowState(const Display::Id &display, bool has_surface,
const graphics::Rect &frame,

View file

@ -55,7 +55,7 @@ TEST(LayerComposer, FindsNoSuitableWindowForLayer) {
graphics::Rect{0, 0, 1024, 768},
"org.anbox.test.1",
wm::Task::Id{1},
wm::Stack::Freeform,
wm::Stack::Id::Freeform,
};
wm->apply_window_state_update({single_window}, {});
@ -89,7 +89,7 @@ TEST(LayerComposer, MapsLayersToWindows) {
graphics::Rect{0, 0, 1024, 768},
"org.anbox.foo",
wm::Task::Id{1},
wm::Stack::Freeform,
wm::Stack::Id::Freeform,
};
auto second_window = wm::WindowState{
@ -98,7 +98,7 @@ TEST(LayerComposer, MapsLayersToWindows) {
graphics::Rect{300, 400, 1324, 1168},
"org.anbox.bar",
wm::Task::Id{2},
wm::Stack::Freeform,
wm::Stack::Id::Freeform,
};
wm->apply_window_state_update({first_window, second_window}, {});
@ -149,7 +149,7 @@ TEST(LayerComposer, WindowPartiallyOffscreen) {
graphics::Rect{-100, -100, 924, 668},
"org.anbox.foo",
wm::Task::Id{1},
wm::Stack::Freeform,
wm::Stack::Id::Freeform,
};
wm->apply_window_state_update({window}, {});
@ -194,7 +194,7 @@ TEST(LayerComposer, PopupShouldNotCauseWindowLayerOffset) {
graphics::Rect{1120, 270, 2144, 1038},
"org.anbox.foo",
wm::Task::Id{3},
wm::Stack::Freeform,
wm::Stack::Id::Freeform,
};
wm->apply_window_state_update({window}, {});