session_manager.cpp 12.2 KB
Newer Older
S
Simon Fels 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
/*
 * 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/>.
 *
 */

T
Thomas Voß 已提交
18 19
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wswitch-default"
S
Simon Fels 已提交
20 21 22 23
#include <boost/filesystem.hpp>

#include "core/posix/signal.h"

24
#include "anbox/application/launcher_storage.h"
25
#include "anbox/application/database.h"
S
Simon Fels 已提交
26
#include "anbox/audio/server.h"
27 28 29
#include "anbox/bridge/android_api_stub.h"
#include "anbox/bridge/platform_api_skeleton.h"
#include "anbox/bridge/platform_message_processor.h"
B
Bernhard Rosenkränzer 已提交
30 31
#include "anbox/graphics/gl_renderer_server.h"

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"
S
Simon Fels 已提交
38
#include "anbox/input/manager.h"
39 40 41
#include "anbox/logger.h"
#include "anbox/network/published_socket_connector.h"
#include "anbox/qemu/pipe_connection_creator.h"
42
#include "anbox/rpc/channel.h"
43 44
#include "anbox/rpc/connection_creator.h"
#include "anbox/runtime.h"
45
#include "anbox/platform/base_platform.h"
46 47
#include "anbox/wm/multi_window_manager.h"
#include "anbox/wm/single_window_manager.h"
48 49

#include "external/xdg/xdg.h"
S
Simon Fels 已提交
50 51 52

#include <sys/prctl.h>

T
Thomas Voß 已提交
53
#pragma GCC diagnostic pop
54

S
Simon Fels 已提交
55 56 57
namespace fs = boost::filesystem;

namespace {
58 59 60
constexpr const char *default_appmgr_package{"org.anbox.appmgr"};
constexpr const char *default_appmgr_component{"org.anbox.appmgr.AppViewActivity"};
const boost::posix_time::milliseconds default_appmgr_startup_delay{50};
61 62
const anbox::graphics::Rect default_single_window_size{0, 0, 1024, 768};

63 64 65 66 67 68 69 70 71
class NullConnectionCreator : public anbox::network::ConnectionCreator<
                                  boost::asio::local::stream_protocol> {
 public:
  void create_connection_for(
      std::shared_ptr<boost::asio::local::stream_protocol::socket> const
          &socket) override {
    WARNING("Not implemented");
    socket->close();
  }
S
Simon Fels 已提交
72 73 74
};
}

75 76 77 78 79 80 81 82 83 84 85 86
void anbox::cmds::SessionManager::launch_appmgr_if_needed(const std::shared_ptr<bridge::AndroidApiStub> &android_api_stub) {
  if (!single_window_)
    return;

  android::Intent launch_intent;
  launch_intent.package = default_appmgr_package;
  launch_intent.component = default_appmgr_component;
  // As this will only be executed in single window mode we don't have
  // to specify and launch bounds.
  android_api_stub->launch(launch_intent, graphics::Rect::Invalid, wm::Stack::Id::Default);
}

87
anbox::cmds::SessionManager::SessionManager()
88 89
    : CommandWithFlagsAndAction{cli::Name{"session-manager"}, cli::Usage{"session-manager"},
                                cli::Description{"Run the the anbox session manager"}},
90
      window_size_(default_single_window_size) {
91 92 93 94 95 96
  // Just for the purpose to allow QtMir (or unity8) to find this on our
  // /proc/*/cmdline
  // for proper confinement etc.
  flag(cli::make_flag(cli::Name{"desktop_file_hint"},
                      cli::Description{"Desktop file hint for QtMir/Unity8"},
                      desktop_file_hint_));
97 98 99
  flag(cli::make_flag(cli::Name{"single-window"},
                      cli::Description{"Start in single window mode."},
                      single_window_));
100 101 102
  flag(cli::make_flag(cli::Name{"window-size"},
                      cli::Description{"Size of the window in single window mode, e.g. --window-size=1024,768"},
                      window_size_));
103 104 105
  flag(cli::make_flag(cli::Name{"standalone"},
                      cli::Description{"Prevents the Container Manager from starting the default container (Experimental)"},
                      standalone_));
106 107 108
  flag(cli::make_flag(cli::Name{"experimental"},
                      cli::Description{"Allows users to use experimental features"},
                      experimental_));
109 110 111
  flag(cli::make_flag(cli::Name{"use-system-dbus"},
                      cli::Description{"Use system instead of session DBus"},
                      use_system_dbus_));
112 113 114
  flag(cli::make_flag(cli::Name{"software-rendering"},
                      cli::Description{"Use software rendering instead of hardware accelerated GL rendering"},
                      use_software_rendering_));
115 116 117
  flag(cli::make_flag(cli::Name{"no-touch-emulation"},
                      cli::Description{"Disable touch emulation applied on mouse inputs"},
                      no_touch_emulation_));
118

T
Thomas Voß 已提交
119
  action([this](const cli::Command::Context &) {
120
    auto trap = core::posix::trap_signals_for_process(
121 122 123 124 125
        {core::posix::Signal::sig_term, core::posix::Signal::sig_int});
    trap->signal_raised().connect([trap](const core::posix::Signal &signal) {
      INFO("Signal %i received. Good night.", static_cast<int>(signal));
      trap->stop();
    });
S
Simon Fels 已提交
126

127 128 129 130 131
    if (standalone_ && !experimental_) {
      ERROR("Experimental features selected, but --experimental flag not set");
      return EXIT_FAILURE;
    }

S
Simon Fels 已提交
132
    if ((!fs::exists("/dev/binder") && !fs::exists(BINDERFS_PATH)) || !fs::exists("/dev/ashmem")) {
133 134 135 136
      ERROR("Failed to start as either binder or ashmem kernel drivers are not loaded");
      return EXIT_FAILURE;
    }

137
    utils::ensure_paths({
138 139
        SystemConfiguration::instance().socket_dir(),
        SystemConfiguration::instance().input_device_dir(),
140
    });
S
Simon Fels 已提交
141

142 143
    auto rt = Runtime::create();
    auto dispatcher = anbox::common::create_dispatcher_for_runtime(rt);
S
Simon Fels 已提交
144

145
    if (!standalone_) {
146 147
      container_ = std::make_shared<container::Client>(rt);
      container_->register_terminate_handler([&]() {
148 149 150
        WARNING("Lost connection to container manager, terminating.");
        trap->stop();
      });
151
    }
152

153 154
    auto input_manager = std::make_shared<input::Manager>(rt);
    auto android_api_stub = std::make_shared<bridge::AndroidApiStub>();
S
Simon Fels 已提交
155

156 157
    auto display_frame = graphics::Rect::Invalid;
    if (single_window_)
158
      display_frame = window_size_;
159

160 161 162 163
    const auto should_enable_touch_emulation = utils::get_env_value("ANBOX_ENABLE_TOUCH_EMULATION", "true");
    if (should_enable_touch_emulation == "false" || no_touch_emulation_)
      no_touch_emulation_ = true;

164 165 166 167 168
    platform::Configuration platform_config;
    platform_config.single_window = single_window_;
    platform_config.no_touch_emulation = no_touch_emulation_;
    platform_config.display_frame = display_frame;

169 170
    auto platform = platform::create(utils::get_env_value("ANBOX_PLATFORM", "sdl"),
                                     input_manager,
171 172
                                     platform_config);
    if (!platform)
173
      return EXIT_FAILURE;
174

175
    auto app_db = std::make_shared<application::Database>();
176 177

    std::shared_ptr<wm::Manager> window_manager;
178
    bool using_single_window = false;
179
    if (platform->supports_multi_window() && !single_window_)
180
      window_manager = std::make_shared<wm::MultiWindowManager>(platform, android_api_stub, app_db);
181
    else {
182
      window_manager = std::make_shared<wm::SingleWindowManager>(platform, display_frame, app_db);
183 184
      using_single_window = true;
    }
185

186
    const auto should_force_software_rendering = utils::get_env_value("ANBOX_FORCE_SOFTWARE_RENDERING", "false");
187
    auto gl_driver = graphics::GLRendererServer::Config::Driver::Host;
188
    if (should_force_software_rendering == "true" || use_software_rendering_)
189 190
     gl_driver = graphics::GLRendererServer::Config::Driver::Software;

S
Simon Fels 已提交
191
    graphics::GLRendererServer::Config renderer_config {
192
      gl_driver,
S
Simon Fels 已提交
193 194 195
      single_window_
    };
    auto gl_server = std::make_shared<graphics::GLRendererServer>(renderer_config, window_manager);
S
Simon Fels 已提交
196

197 198
    platform->set_window_manager(window_manager);
    platform->set_renderer(gl_server->renderer());
199
    window_manager->setup();
200
    android_api_stub->set_platform(platform);
201

202 203 204 205 206 207 208 209 210
    auto app_manager = std::static_pointer_cast<application::Manager>(android_api_stub);
    if (!using_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);
    }

211
    auto audio_server = std::make_shared<audio::Server>(rt, platform);
S
Simon Fels 已提交
212

213 214
    const auto socket_path = SystemConfiguration::instance().socket_dir();

215 216 217 218
    // The qemu pipe is used as a very fast communication channel between guest
    // and host for things like the GLES emulation/translation, the RIL or ADB.
    auto qemu_pipe_connector =
        std::make_shared<network::PublishedSocketConnector>(
219
            utils::string_format("%s/qemu_pipe", socket_path), rt,
S
Simon Fels 已提交
220
            std::make_shared<qemu::PipeConnectionCreator>(gl_server->renderer(), rt));
221

222 223
    boost::asio::deadline_timer appmgr_start_timer(rt->service());

224
    auto bridge_connector = std::make_shared<network::PublishedSocketConnector>(
225
        utils::string_format("%s/anbox_bridge", socket_path), rt,
226 227 228 229 230 231 232 233 234 235 236 237
        std::make_shared<rpc::ConnectionCreator>(
            rt, [&](const std::shared_ptr<network::MessageSender> &sender) {
              auto pending_calls = std::make_shared<rpc::PendingCallCache>();
              auto rpc_channel =
                  std::make_shared<rpc::Channel>(pending_calls, sender);
              // This is safe as long as we only support a single client. If we
              // support
              // more than one one day we need proper dispatching to the right
              // one.
              android_api_stub->set_rpc_channel(rpc_channel);

              auto server = std::make_shared<bridge::PlatformApiSkeleton>(
238
                  pending_calls, platform, window_manager, app_db);
239 240 241
              server->register_boot_finished_handler([&]() {
                DEBUG("Android successfully booted");
                android_api_stub->ready().set(true);
242 243 244 245 246 247
                appmgr_start_timer.expires_from_now(default_appmgr_startup_delay);
                appmgr_start_timer.async_wait([&](const boost::system::error_code &err) {
                  if (err != boost::system::errc::success)
                    return;
                  launch_appmgr_if_needed(android_api_stub);
                });
248
              });
249 250 251 252 253
              return std::make_shared<bridge::PlatformMessageProcessor>(
                  sender, server, pending_calls);
            }));

    container::Configuration container_configuration;
254 255 256 257 258 259 260 261 262

    // Instruct healthd to fake battery level as it may take it from other connected
    // devices like mouse or keyboard and will incorrectly show a system popup to
    // shutdown the Android system because of low battery. This prevents any kind of
    // input as focus is bound to the system popup exclusively.
    //
    // See https://github.com/anbox/anbox/issues/780 for further details.
    container_configuration.extra_properties.push_back("ro.boot.fake_battery=1");

263 264
    if (!standalone_) {
      container_configuration.bind_mounts = {
265 266
        {qemu_pipe_connector->socket_file(), "/dev/qemu_pipe"},
        {bridge_connector->socket_file(), "/dev/anbox_bridge"},
S
Simon Fels 已提交
267
        {audio_server->socket_file(), "/dev/anbox_audio"},
268
        {SystemConfiguration::instance().input_device_dir(), "/dev/input"},
269
        {platform->ime_socket_file(), "/dev/ime"},
270 271 272
      };

      container_configuration.devices = {
273
        {"/dev/fuse", {0666}},
274
      };
S
Simon Fels 已提交
275

276 277 278
      dispatcher->dispatch([&]() {
        container_->start(container_configuration);
      });
279
    }
280

281
    auto bus_type = anbox::dbus::Bus::Type::Session;
282
    if (use_system_dbus_)
283 284
      bus_type = anbox::dbus::Bus::Type::System;
    auto bus = std::make_shared<anbox::dbus::Bus>(bus_type);
285

286
    auto skeleton = anbox::dbus::skeleton::Service::create_for_bus(bus, app_manager);
287

288 289
    bus->run_async();

290 291
    rt->start();
    trap->run();
292

293 294 295
    if (!standalone_) {
      // Stop the container which should close all open connections we have on
      // our side and should terminate all services.
296
      container_->stop();
297
    }
298

299 300 301 302
    rt->stop();

    return EXIT_SUCCESS;
  });
S
Simon Fels 已提交
303
}