TYPE3
[iec.git] / src / type3_AndroidCloud / anbox-master / src / anbox / cmds / session_manager.cpp
1 /*
2  * Copyright (C) 2016 Simon Fels <morphis@gravedo.de>
3  *
4  * This program is free software: you can redistribute it and/or modify it
5  * under the terms of the GNU General Public License version 3, as published
6  * by the Free Software Foundation.
7  *
8  * This program is distributed in the hope that it will be useful, but
9  * WITHOUT ANY WARRANTY; without even the implied warranties of
10  * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
11  * PURPOSE.  See the GNU General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License along
14  * with this program.  If not, see <http://www.gnu.org/licenses/>.
15  *
16  */
17
18 #pragma GCC diagnostic push
19 #pragma GCC diagnostic ignored "-Wswitch-default"
20 #include <boost/filesystem.hpp>
21
22 #include "core/posix/signal.h"
23
24 #include "anbox/application/launcher_storage.h"
25 #include "anbox/application/database.h"
26 #include "anbox/audio/server.h"
27 #include "anbox/bridge/android_api_stub.h"
28 #include "anbox/bridge/platform_api_skeleton.h"
29 #include "anbox/bridge/platform_message_processor.h"
30 #include "anbox/graphics/gl_renderer_server.h"
31
32 #include "anbox/cmds/session_manager.h"
33 #include "anbox/common/dispatcher.h"
34 #include "anbox/system_configuration.h"
35 #include "anbox/container/client.h"
36 #include "anbox/dbus/bus.h"
37 #include "anbox/dbus/skeleton/service.h"
38 #include "anbox/input/manager.h"
39 #include "anbox/logger.h"
40 #include "anbox/network/published_socket_connector.h"
41 #include "anbox/qemu/pipe_connection_creator.h"
42 #include "anbox/rpc/channel.h"
43 #include "anbox/rpc/connection_creator.h"
44 #include "anbox/runtime.h"
45 #include "anbox/platform/base_platform.h"
46 #include "anbox/wm/multi_window_manager.h"
47 #include "anbox/wm/single_window_manager.h"
48
49 #include "external/xdg/xdg.h"
50
51 #include <sys/prctl.h>
52
53 #pragma GCC diagnostic pop
54
55 namespace fs = boost::filesystem;
56
57 namespace {
58 constexpr const char *default_appmgr_package{"org.anbox.appmgr"};
59 constexpr const char *default_appmgr_component{"org.anbox.appmgr.AppViewActivity"};
60 const boost::posix_time::milliseconds default_appmgr_startup_delay{50};
61 const anbox::graphics::Rect default_single_window_size{0, 0, 1024, 768};
62
63 class NullConnectionCreator : public anbox::network::ConnectionCreator<
64                                   boost::asio::local::stream_protocol> {
65  public:
66   void create_connection_for(
67       std::shared_ptr<boost::asio::local::stream_protocol::socket> const
68           &socket) override {
69     WARNING("Not implemented");
70     socket->close();
71   }
72 };
73 }
74
75 void anbox::cmds::SessionManager::launch_appmgr_if_needed(const std::shared_ptr<bridge::AndroidApiStub> &android_api_stub) {
76   if (!single_window_)
77     return;
78
79   android::Intent launch_intent;
80   launch_intent.package = default_appmgr_package;
81   launch_intent.component = default_appmgr_component;
82   // As this will only be executed in single window mode we don't have
83   // to specify and launch bounds.
84   android_api_stub->launch(launch_intent, graphics::Rect::Invalid, wm::Stack::Id::Default);
85 }
86
87 anbox::cmds::SessionManager::SessionManager()
88     : CommandWithFlagsAndAction{cli::Name{"session-manager"}, cli::Usage{"session-manager"},
89                                 cli::Description{"Run the the anbox session manager"}},
90       window_size_(default_single_window_size) {
91   // Just for the purpose to allow QtMir (or unity8) to find this on our
92   // /proc/*/cmdline
93   // for proper confinement etc.
94   flag(cli::make_flag(cli::Name{"desktop_file_hint"},
95                       cli::Description{"Desktop file hint for QtMir/Unity8"},
96                       desktop_file_hint_));
97   flag(cli::make_flag(cli::Name{"single-window"},
98                       cli::Description{"Start in single window mode."},
99                       single_window_));
100   flag(cli::make_flag(cli::Name{"window-size"},
101                       cli::Description{"Size of the window in single window mode, e.g. --window-size=1024,768"},
102                       window_size_));
103   flag(cli::make_flag(cli::Name{"standalone"},
104                       cli::Description{"Prevents the Container Manager from starting the default container (Experimental)"},
105                       standalone_));
106   flag(cli::make_flag(cli::Name{"experimental"},
107                       cli::Description{"Allows users to use experimental features"},
108                       experimental_));
109   flag(cli::make_flag(cli::Name{"use-system-dbus"},
110                       cli::Description{"Use system instead of session DBus"},
111                       use_system_dbus_));
112   flag(cli::make_flag(cli::Name{"software-rendering"},
113                       cli::Description{"Use software rendering instead of hardware accelerated GL rendering"},
114                       use_software_rendering_));
115   flag(cli::make_flag(cli::Name{"no-touch-emulation"},
116                       cli::Description{"Disable touch emulation applied on mouse inputs"},
117                       no_touch_emulation_));
118   flag(cli::make_flag(cli::Name{"server-side-decoration"},
119                       cli::Description{"Prefer to use server-side decoration instead of client-side decoration"},
120                       server_side_decoration_));
121
122   action([this](const cli::Command::Context &) {
123     auto trap = core::posix::trap_signals_for_process(
124         {core::posix::Signal::sig_term, core::posix::Signal::sig_int});
125     trap->signal_raised().connect([trap](const core::posix::Signal &signal) {
126       INFO("Signal %i received. Good night.", static_cast<int>(signal));
127       trap->stop();
128     });
129
130     if (standalone_ && !experimental_) {
131       ERROR("Experimental features selected, but --experimental flag not set");
132       return EXIT_FAILURE;
133     }
134
135     if ((!fs::exists("/dev/binder") && !fs::exists(BINDERFS_PATH)) || !fs::exists("/dev/ashmem")) {
136       ERROR("Failed to start as either binder or ashmem kernel drivers are not loaded");
137       return EXIT_FAILURE;
138     }
139
140     utils::ensure_paths({
141         SystemConfiguration::instance().socket_dir(),
142         SystemConfiguration::instance().input_device_dir(),
143     });
144
145     auto rt = Runtime::create();
146     auto dispatcher = anbox::common::create_dispatcher_for_runtime(rt);
147
148     if (!standalone_) {
149       container_ = std::make_shared<container::Client>(rt);
150       container_->register_terminate_handler([&]() {
151         WARNING("Lost connection to container manager, terminating.");
152         trap->stop();
153       });
154     }
155
156     auto input_manager = std::make_shared<input::Manager>(rt);
157     auto android_api_stub = std::make_shared<bridge::AndroidApiStub>();
158
159     auto display_frame = graphics::Rect::Invalid;
160     if (single_window_)
161       display_frame = window_size_;
162
163     const auto should_enable_touch_emulation = utils::get_env_value("ANBOX_ENABLE_TOUCH_EMULATION", "true");
164     if (should_enable_touch_emulation == "false" || no_touch_emulation_)
165       no_touch_emulation_ = true;
166
167     const auto should_force_server_side_decoration = utils::get_env_value("ANBOX_FORCE_SERVER_SIDE_DECORATION", "false");
168     if (should_force_server_side_decoration == "true")
169       server_side_decoration_ = true;
170
171     platform::Configuration platform_config;
172     platform_config.single_window = single_window_;
173     platform_config.no_touch_emulation = no_touch_emulation_;
174     platform_config.display_frame = display_frame;
175     platform_config.server_side_decoration = server_side_decoration_;
176
177     auto platform = platform::create(utils::get_env_value("ANBOX_PLATFORM", "sdl"),
178                                      input_manager,
179                                      platform_config);
180     if (!platform)
181       return EXIT_FAILURE;
182
183     auto app_db = std::make_shared<application::Database>();
184
185     std::shared_ptr<wm::Manager> window_manager;
186     bool using_single_window = false;
187     if (platform->supports_multi_window() && !single_window_)
188       window_manager = std::make_shared<wm::MultiWindowManager>(platform, android_api_stub, app_db);
189     else {
190       window_manager = std::make_shared<wm::SingleWindowManager>(platform, display_frame, app_db);
191       using_single_window = true;
192     }
193
194     const auto should_force_software_rendering = utils::get_env_value("ANBOX_FORCE_SOFTWARE_RENDERING", "false");
195     auto gl_driver = graphics::GLRendererServer::Config::Driver::Host;
196     if (should_force_software_rendering == "true" || use_software_rendering_)
197      gl_driver = graphics::GLRendererServer::Config::Driver::Software;
198
199     graphics::GLRendererServer::Config renderer_config {
200       gl_driver,
201       single_window_
202     };
203     auto gl_server = std::make_shared<graphics::GLRendererServer>(renderer_config, window_manager);
204
205     platform->set_window_manager(window_manager);
206     platform->set_renderer(gl_server->renderer());
207     window_manager->setup();
208
209     auto app_manager = std::static_pointer_cast<application::Manager>(android_api_stub);
210     if (!using_single_window) {
211       // When we're not running single window mode we need to restrict ourself to
212       // only launch applications in freeform mode as otherwise the window tracking
213       // doesn't work.
214       app_manager = std::make_shared<application::RestrictedManager>(
215             android_api_stub, wm::Stack::Id::Freeform);
216     }
217
218     auto audio_server = std::make_shared<audio::Server>(rt, platform);
219
220     const auto socket_path = SystemConfiguration::instance().socket_dir();
221
222     // The qemu pipe is used as a very fast communication channel between guest
223     // and host for things like the GLES emulation/translation, the RIL or ADB.
224     auto qemu_pipe_connector =
225         std::make_shared<network::PublishedSocketConnector>(
226             utils::string_format("%s/qemu_pipe", socket_path), rt,
227             std::make_shared<qemu::PipeConnectionCreator>(gl_server->renderer(), rt));
228
229     boost::asio::deadline_timer appmgr_start_timer(rt->service());
230
231     auto bridge_connector = std::make_shared<network::PublishedSocketConnector>(
232         utils::string_format("%s/anbox_bridge", socket_path), rt,
233         std::make_shared<rpc::ConnectionCreator>(
234             rt, [&](const std::shared_ptr<network::MessageSender> &sender) {
235               auto pending_calls = std::make_shared<rpc::PendingCallCache>();
236               auto rpc_channel =
237                   std::make_shared<rpc::Channel>(pending_calls, sender);
238               // This is safe as long as we only support a single client. If we
239               // support
240               // more than one one day we need proper dispatching to the right
241               // one.
242               android_api_stub->set_rpc_channel(rpc_channel);
243
244               auto server = std::make_shared<bridge::PlatformApiSkeleton>(
245                   pending_calls, platform, window_manager, app_db);
246               server->register_boot_finished_handler([&]() {
247                 DEBUG("Android successfully booted");
248                 android_api_stub->ready().set(true);
249                 appmgr_start_timer.expires_from_now(default_appmgr_startup_delay);
250                 appmgr_start_timer.async_wait([&](const boost::system::error_code &err) {
251                   if (err != boost::system::errc::success)
252                     return;
253                   launch_appmgr_if_needed(android_api_stub);
254                 });
255               });
256               return std::make_shared<bridge::PlatformMessageProcessor>(
257                   sender, server, pending_calls);
258             }));
259
260     container::Configuration container_configuration;
261
262     // Instruct healthd to fake battery level as it may take it from other connected
263     // devices like mouse or keyboard and will incorrectly show a system popup to
264     // shutdown the Android system because of low battery. This prevents any kind of
265     // input as focus is bound to the system popup exclusively.
266     //
267     // See https://github.com/anbox/anbox/issues/780 for further details.
268     container_configuration.extra_properties.push_back("ro.boot.fake_battery=1");
269
270     if (server_side_decoration_)
271         container_configuration.extra_properties.push_back("ro.anbox.no_decorations=1");
272
273     if (!standalone_) {
274       container_configuration.bind_mounts = {
275         {qemu_pipe_connector->socket_file(), "/dev/qemu_pipe"},
276         {bridge_connector->socket_file(), "/dev/anbox_bridge"},
277         {audio_server->socket_file(), "/dev/anbox_audio"},
278         {SystemConfiguration::instance().input_device_dir(), "/dev/input"},
279
280       };
281
282       container_configuration.devices = {
283         {"/dev/fuse", {0666}},
284       };
285
286       dispatcher->dispatch([&]() {
287         container_->start(container_configuration);
288       });
289     }
290
291     auto bus_type = anbox::dbus::Bus::Type::Session;
292     if (use_system_dbus_)
293       bus_type = anbox::dbus::Bus::Type::System;
294     auto bus = std::make_shared<anbox::dbus::Bus>(bus_type);
295
296     auto skeleton = anbox::dbus::skeleton::Service::create_for_bus(bus, app_manager);
297
298     bus->run_async();
299
300     rt->start();
301     trap->run();
302
303     if (!standalone_) {
304       // Stop the container which should close all open connections we have on
305       // our side and should terminate all services.
306       container_->stop();
307     }
308
309     rt->stop();
310
311     return EXIT_SUCCESS;
312   });
313 }