dbus: use systemd dbus support instead of out dated dbus-cpp

dbus-cpp was dropped from 18.04 as it is not maintained anymore so we
should migrate to something more stable with a long term perspective.
libsystemd fits in nicely here as it has a nice and clean dbus
implementation which is battle tested and can be easily integrated into
our existing C++ code base.
This commit is contained in:
Simon Fels 2018-05-15 13:47:58 +02:00
commit f1480bfb91
22 changed files with 785 additions and 329 deletions

View file

@ -65,10 +65,10 @@ find_package(Protobuf REQUIRED)
pkg_check_modules(SDL2 sdl2 REQUIRED)
pkg_check_modules(SDL2_IMAGE SDL2_image REQUIRED)
pkg_check_modules(DBUS_CPP dbus-cpp REQUIRED)
pkg_check_modules(DBUS dbus-1 REQUIRED)
pkg_check_modules(LXC lxc REQUIRED)
pkg_check_modules(PROPERTIES_CPP properties-cpp REQUIRED)
pkg_check_modules(LIBSYSTEMD libsystemd REQUIRED)
# FIXME mir support is currently broken due to mir's API
# being broken with recent landings in Ubuntu 16.04
# pkg_check_modules(MIRCLIENT mirclient)

View file

@ -11,6 +11,7 @@ include_directories(
${PROPERTIES_CPP_INCLUDE_DIRS}
${LXC_INCLUDE_DIRS}
${MIRCLIENT_INCLUDE_DIRS}
${LIBSYSTEMD_INCLUDE_DIRS}
${CMAKE_CURRENT_BINARY_DIR}
${CMAKE_SOURCE_DIR}
${CMAKE_SOURCE_DIR}/src
@ -195,11 +196,16 @@ set(SOURCES
anbox/bridge/platform_api_skeleton.cpp
anbox/bridge/android_api_stub.cpp
anbox/dbus/bus.cpp
anbox/dbus/bus.h
anbox/dbus/sd_bus_helpers.h
anbox/dbus/interface.h
anbox/dbus/codecs.h
anbox/dbus/skeleton/service.cpp
anbox/dbus/skeleton/service.h
anbox/dbus/skeleton/application_manager.cpp
anbox/dbus/skeleton/application_manager.h
anbox/dbus/stub/application_manager.cpp
anbox/dbus/stub/application_manager.h
anbox/application/launcher_storage.cpp
anbox/application/database.cpp
@ -210,6 +216,8 @@ set(SOURCES
anbox/cmds/container_manager.cpp
anbox/cmds/launch.cpp
anbox/cmds/system_info.cpp
anbox/cmds/wait_ready.cpp
anbox/cmds/wait_ready.h
anbox/utils/environment_file.cpp
@ -233,6 +241,8 @@ target_link_libraries(anbox-core
${LXC_LIBRARIES}
${MIRCLIENT_LDFLAGS}
${MIRCLIENT_LIBRARIES}
${LIBSYSTEMD_LDFLAGS}
${LIBSYSTEMD_LIBRARIES}
pthread
process-cpp
emugl_common

View file

@ -16,29 +16,30 @@
*/
#include "anbox/cmds/launch.h"
#include "anbox/common/wait_handle.h"
#include "anbox/dbus/stub/application_manager.h"
#include "anbox/common/dispatcher.h"
#include "anbox/dbus/interface.h"
#include "anbox/ui/splash_screen.h"
#include "anbox/system_configuration.h"
#include "anbox/runtime.h"
#include "anbox/logger.h"
#include <core/dbus/asio/executor.h>
#include <boost/filesystem.hpp>
#include "core/posix/exec.h"
#include "core/posix/fork.h"
#include "core/posix/signal.h"
#include <boost/filesystem.hpp>
#include <fcntl.h>
#include <sys/stat.h>
namespace fs = boost::filesystem;
namespace {
const boost::posix_time::seconds max_wait_timeout{240};
const int max_restart_attempts{3};
const std::chrono::seconds restart_interval{5};
constexpr unsigned int max_session_mgr_wait_attempts{10};
const std::chrono::seconds session_mgr_wait_interval{5};
constexpr unsigned int max_dbus_service_wait_attempts{10};
const std::chrono::seconds dbus_service_wait_interval{5};
#if 0
static int redirect_to_null(int flags, int fd) {
int fd2;
if ((fd2 = open("/dev/null", flags)) < 0)
@ -53,6 +54,73 @@ static int redirect_to_null(int flags, int fd) {
close(fd2);
return fd;
}
#endif
} // namespace
bool anbox::cmds::Launch::launch_session_manager() {
std::vector<std::string> args = {"session-manager"};
std::map<std::string,std::string> env;
core::posix::this_process::env::for_each([&](const std::string &name, const std::string &value) {
env.insert({name, value});
});
const auto exe_path = utils::process_get_exe_path(::getpid());
if (!fs::exists(exe_path)) {
ERROR("Can't find correct anbox executable to run. Found %s but does not exist", exe_path);
return false;
}
try {
auto flags = core::posix::StandardStream::empty;
auto child = core::posix::fork([&]() {
#if 0
// We redirect all in/out/err to /dev/null as they can't be seen
// anywhere. All logging output will directly go to syslog as we
// will become a session leader below which will get us rid of a
// controlling terminal.
if (redirect_to_null(O_RDONLY, 0) < 0 ||
redirect_to_null(O_WRONLY, 1) < 0 ||
redirect_to_null(O_WRONLY, 2) < 0) {
ERROR("Failed to redirect stdout/stderr/stdin: %s", strerror(errno));
return core::posix::exit::Status::failure;
}
#endif
// As we forked one time already we're sure that our process is
// not the session leader anymore so we can safely become the
// new one and lead the process group.
if (setsid() < 0) {
ERROR("Failed to become new session leader: %s", strerror(errno));
return core::posix::exit::Status::failure;
}
umask(0077);
if (chdir("/") < 0) {
ERROR("Failed to change current directory: %s", strerror(errno));
return core::posix::exit::Status::failure;
}
auto grandchild = core::posix::exec(exe_path, args, env, flags);
grandchild.dont_kill_on_cleanup();
return core::posix::exit::Status::success;
}, flags);
// We don't wait for the grandchild but the child as we use double forking
// here to break through the process hierarchy and make the grandchild a
// direct child of the init process so it keeps running on its own and
// indepent of our short living process here.
child.wait_for(core::posix::wait::Flags::untraced);
DEBUG("Started session manager, will now try to connect ..");
}
catch (...) {
ERROR("Failed to start session manager instance");
}
return true;
}
bool anbox::cmds::Launch::try_launch_activity(const std::shared_ptr<dbus::stub::ApplicationManager> &stub) {
@ -96,6 +164,7 @@ anbox::cmds::Launch::Launch()
cli::Description{"Use system instead of session DBus"},
use_system_dbus_));
action([this](const cli::Command::Context&) {
if (!intent_.valid()) {
ERROR("The intent you provided is invalid. Please provide a correct launch intent.");
@ -104,144 +173,54 @@ anbox::cmds::Launch::Launch()
return EXIT_FAILURE;
}
auto trap = core::posix::trap_signals_for_process({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();
});
auto rt = Runtime::create();
auto bus_type = core::dbus::WellKnownBus::session;
auto bus_type = anbox::dbus::Bus::Type::Session;
if (use_system_dbus_)
bus_type = core::dbus::WellKnownBus::system;
auto bus = std::make_shared<core::dbus::Bus>(bus_type);
bus->install_executor(core::dbus::asio::make_executor(bus, rt->service()));
bus_type = anbox::dbus::Bus::Type::System;
auto bus = std::make_shared<anbox::dbus::Bus>(bus_type);
std::shared_ptr<ui::SplashScreen> ss;
if (!bus->has_service_with_name(dbus::interface::Service::name())) {
DEBUG("Session manager is not yet running, trying to start it");
// Instead of relying on the user session init system to start our
// session manager process we also attempt to start it on our own
// if not already running. This will help to mitigate problems with
// a crashing or a not yet started session manager instance.
std::shared_ptr<dbus::stub::ApplicationManager> stub;
for (auto n = 0; n < max_restart_attempts; n++) {
try {
stub = dbus::stub::ApplicationManager::create_for_bus(bus);
break;
} catch (std::exception &err) {
WARNING("Anbox session manager service isn't running, trying to start it.");
if (!launch_session_manager())
return EXIT_FAILURE;
// Give us a splash screen as long as we're trying to connect
// with the session manager so the user knows something is
// happening after he started Anbox.
if (!ss)
ss = std::make_shared<ui::SplashScreen>();
std::vector<std::string> args = {"session-manager"};
std::map<std::string,std::string> env;
core::posix::this_process::env::for_each([&](const std::string &name, const std::string &value) {
env.insert({name, value});
});
const auto exe_path = utils::process_get_exe_path(::getpid());
if (!fs::exists(exe_path)) {
ERROR("Can't find correct anbox executable to run. Found %s but does not exist", exe_path);
return EXIT_FAILURE;
}
try {
auto flags = core::posix::StandardStream::empty;
auto child = core::posix::fork([&]() {
// We redirect all in/out/err to /dev/null as they can't be seen
// anywhere. All logging output will directly go to syslog as we
// will become a session leader below which will get us rid of a
// controlling terminal.
if (redirect_to_null(O_RDONLY, 0) < 0 ||
redirect_to_null(O_WRONLY, 1) < 0 ||
redirect_to_null(O_WRONLY, 2) < 0) {
ERROR("Failed to redirect stdout/stderr/stdin: %s", strerror(errno));
return core::posix::exit::Status::failure;
}
// As we forked one time already we're sure that our process is
// not the session leader anymore so we can safely become the
// new one and lead the process group.
if (setsid() < 0) {
ERROR("Failed to become new session leader: %s", strerror(errno));
return core::posix::exit::Status::failure;
}
umask(0077);
if (chdir("/") < 0) {
ERROR("Failed to change current directory: %s", strerror(errno));
return core::posix::exit::Status::failure;
}
auto grandchild = core::posix::exec(exe_path, args, env, flags);
grandchild.dont_kill_on_cleanup();
return core::posix::exit::Status::success;
}, flags);
// We don't wait for the grandchild but the child as we use double forking
// here to break through the process hierarchy and make the grandchild a
// direct child of the init process so it keeps running on its own and
// indepent of our short living process here.
child.wait_for(core::posix::wait::Flags::untraced);
DEBUG("Started session manager, will now try to connect ..");
}
catch (...) {
ERROR("Failed to start session manager instance");
}
std::this_thread::sleep_for(restart_interval);
}
// Give us a splash screen as long as we're trying to connect
// with the session manager so the user knows something is
// happening after he started Anbox.
ss = std::make_shared<ui::SplashScreen>();
}
if (!stub) {
ERROR("Couldn't get a connection with the session manager");
unsigned int n = 0;
while (n < max_dbus_service_wait_attempts) {
if (bus->has_service_with_name(dbus::interface::Service::name()))
break;
std::this_thread::sleep_for(dbus_service_wait_interval);
n++;
}
auto app_mgr = dbus::stub::ApplicationManager::create_for_bus(bus);
n = 0;
while (n < max_session_mgr_wait_attempts) {
app_mgr->update_properties();
if (app_mgr->ready().get())
break;
std::this_thread::sleep_for(session_mgr_wait_interval);
n++;
}
if (!app_mgr->ready()) {
ERROR("Session manager failed to become ready");
return EXIT_FAILURE;
}
bool success = false;
auto dispatcher = anbox::common::create_dispatcher_for_runtime(rt);
dispatcher->dispatch([&]() {
if (stub->ready()) {
ss.reset();
success = try_launch_activity(stub);
trap->stop();
return;
}
DEBUG("Android hasn't fully booted yet. Waiting a bit ..");
stub->ready().changed().connect([&](bool ready) {
if (!ready)
return;
ss.reset();
success = try_launch_activity(stub);
trap->stop();
});
});
boost::asio::deadline_timer timer(rt->service());
timer.expires_from_now(max_wait_timeout);
timer.async_wait([&](const boost::system::error_code&) {
WARNING("Stopped waiting as we've already waited for too long. Something");
WARNING("is wrong with your setup or the container has failed to boot.");
WARNING("If you think you found a bug please don't hesitate to file on");
WARNING("at https://github.com/anbox/anbox/issues/new");
trap->stop();
});
rt->start();
trap->run();
rt->stop();
// If we have a splash screen now is the time to drop it as we're
// going to launch the real application now.
ss.reset();
const auto success = try_launch_activity(app_mgr);
return success ? EXIT_SUCCESS : EXIT_FAILURE;
});
}

View file

@ -34,6 +34,7 @@ class Launch : public cli::CommandWithFlagsAndAction {
Launch();
private:
bool launch_session_manager();
bool try_launch_activity(const std::shared_ptr<dbus::stub::ApplicationManager> &stub);
android::Intent intent_;

View file

@ -33,6 +33,7 @@
#include "anbox/common/dispatcher.h"
#include "anbox/system_configuration.h"
#include "anbox/container/client.h"
#include "anbox/dbus/bus.h"
#include "anbox/dbus/skeleton/service.h"
#include "anbox/input/manager.h"
#include "anbox/logger.h"
@ -49,8 +50,6 @@
#include <sys/prctl.h>
#include <core/dbus/asio/executor.h>
#include <core/dbus/bus.h>
#pragma GCC diagnostic pop
namespace fs = boost::filesystem;
@ -140,13 +139,12 @@ anbox::cmds::SessionManager::SessionManager()
if (!standalone_) {
container_ = std::make_shared<container::Client>(rt);
container_->register_terminate_handler([&]() {
WARNING("Lost connection to container manager, terminating.");
trap->stop();
});
WARNING("Lost connection to container manager, terminating.");
trap->stop();
});
}
auto input_manager = std::make_shared<input::Manager>(rt);
auto android_api_stub = std::make_shared<bridge::AndroidApiStub>();
auto display_frame = graphics::Rect::Invalid;
@ -244,18 +242,21 @@ anbox::cmds::SessionManager::SessionManager()
{"/dev/fuse", "/dev/fuse"},
};
dispatcher->dispatch([&]() { container_->start(container_configuration); });
dispatcher->dispatch([&]() {
DEBUG("Starting container");
container_->start(container_configuration);
});
}
auto bus_type = core::dbus::WellKnownBus::session;
auto bus_type = anbox::dbus::Bus::Type::Session;
if (use_system_dbus_)
bus_type = core::dbus::WellKnownBus::system;
auto bus = std::make_shared<core::dbus::Bus>(bus_type);
bus->install_executor(core::dbus::asio::make_executor(bus, rt->service()));
bus_type = anbox::dbus::Bus::Type::System;
auto bus = std::make_shared<anbox::dbus::Bus>(bus_type);
auto skeleton = anbox::dbus::skeleton::Service::create_for_bus(bus, app_manager);
bus->run_async();
rt->start();
trap->run();

View file

@ -24,8 +24,6 @@
#include <iostream>
#include <memory>
#include <core/dbus/bus.h>
#include "anbox/graphics/gl_renderer_server.h"
#include "anbox/graphics/rect.h"

View file

@ -148,7 +148,7 @@ class SystemInformation {
EGLConfig config;
int n;
if (s_egl.eglChooseConfig(display, config_attribs, &config, 1, &n) && n > 0) {
GLint attribs[] = { EGL_CONTEXT_CLIENT_VERSION, 1, EGL_NONE};
GLint attribs[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE};
auto context = s_egl.eglCreateContext(display, config, nullptr, attribs);
if (context != EGL_NO_CONTEXT) {
// We require surfaceless-context support here for now. If eglMakeCurrent fails

View file

@ -0,0 +1,56 @@
/*
* Copyright (C) 2016 Canonical, Ltd.
*
* 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/>.
*
* Authored by: Thomas Voß <thomas.voss@canonical.com>
*
*/
#include "anbox/cmds/wait_ready.h"
#include "anbox/dbus/stub/application_manager.h"
namespace {
constexpr const unsigned int max_wait_attempts{30};
}
anbox::cmds::WaitReady::WaitReady()
: CommandWithFlagsAndAction{
cli::Name{"wait-ready"}, cli::Usage{"wait-ready"},
cli::Description{"Wait until the Android system has successfully booted"}} {
flag(cli::make_flag(cli::Name{"use-system-dbus"},
cli::Description{"Use system instead of session DBus"},
use_system_dbus_));
action([this](const cli::Command::Context&) {
auto bus_type = anbox::dbus::Bus::Type::Session;
if (use_system_dbus_)
bus_type = anbox::dbus::Bus::Type::System;
auto bus = std::make_shared<anbox::dbus::Bus>(bus_type);
auto stub = dbus::stub::ApplicationManager::create_for_bus(bus);
unsigned int n = 0;
while (n < max_wait_attempts) {
stub->update_properties();
if (stub->ready().get())
return EXIT_SUCCESS;
std::this_thread::sleep_for(std::chrono::seconds{1});
n++;
}
return EXIT_FAILURE;
});
}

View file

@ -0,0 +1,39 @@
/*
* Copyright (C) 2018 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_CMDS_WAIT_READY_H_
#define ANBOX_CMDS_WAIT_READY_H_
#include <functional>
#include <iostream>
#include <memory>
#include "anbox/cli.h"
namespace anbox {
namespace cmds {
class WaitReady : public cli::CommandWithFlagsAndAction {
public:
WaitReady();
private:
bool use_system_dbus_ = false;
};
} // namespace cmds
} // namespace anbox
#endif

View file

@ -37,6 +37,8 @@ ManagementApiSkeleton::~ManagementApiSkeleton() {}
void ManagementApiSkeleton::start_container(
anbox::protobuf::container::StartContainer const *request,
anbox::protobuf::rpc::Void *response, google::protobuf::Closure *done) {
DEBUG("");
if (container_->state() == Container::State::running) {
response->set_error("Container is already running");
done->Run();

View file

@ -27,6 +27,7 @@
#include "anbox/cmds/system_info.h"
#include "anbox/cmds/launch.h"
#include "anbox/cmds/version.h"
#include "anbox/cmds/wait_ready.h"
#include <boost/filesystem.hpp>
@ -40,8 +41,8 @@ Daemon::Daemon()
.command(std::make_shared<cmds::SessionManager>())
.command(std::make_shared<cmds::Launch>())
.command(std::make_shared<cmds::ContainerManager>())
.command(std::make_shared<cmds::SystemInfo>());
.command(std::make_shared<cmds::SystemInfo>())
.command(std::make_shared<cmds::WaitReady>());
Log().Init(anbox::Logger::Severity::kWarning);

86
src/anbox/dbus/bus.cpp Normal file
View file

@ -0,0 +1,86 @@
/*
* 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/>.
*
*/
#include "anbox/dbus/bus.h"
#include "anbox/logger.h"
namespace anbox {
namespace dbus {
Bus::Bus(Type type) :
type_{type} {
int ret = 0;
switch (type) {
case Type::Session:
ret = sd_bus_open_user(&bus_);
break;
case Type::System:
ret = sd_bus_open_system(&bus_);
break;
default:
throw std::invalid_argument("Invalid bus type");
}
if (ret < 0 || !bus_)
throw std::runtime_error("Failed to connect to DBus");
}
Bus::~Bus() {
stop();
if (bus_)
sd_bus_unref(bus_);
}
bool Bus::has_service_with_name(const std::string &name) {
auto r = sd_bus_get_name_creds(bus_,
name.c_str(),
0,
nullptr);
return r >= 0;
}
sd_bus* Bus::raw() {
return bus_;
}
void Bus::run_async() {
running_ = true;
worker_thread_ = std::thread(&Bus::worker_main, this);
}
void Bus::stop() {
running_ = false;
if (worker_thread_.joinable())
worker_thread_.join();
}
void Bus::worker_main() {
while (running_) {
auto ret = sd_bus_process(bus_, nullptr);
if (ret < 0)
break;
if (ret > 0)
continue;
ret = sd_bus_wait(bus_, 1000 * 500);
if (ret < 0)
break;
}
}
} // namespace dbus
} // namespace anbox

60
src/anbox/dbus/bus.h Normal file
View file

@ -0,0 +1,60 @@
/*
* 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_DBUS_BUS_H_
#define ANBOX_DBUS_BUS_H_
#include "anbox/do_not_copy_or_move.h"
#include <atomic>
#include <memory>
#include <mutex>
#include <thread>
#include <systemd/sd-bus.h>
namespace anbox {
namespace dbus {
class Bus : public DoNotCopyOrMove {
public:
enum class Type {
System,
Session
};
Bus(Type type);
~Bus();
sd_bus* raw();
bool has_service_with_name(const std::string& name);
void run_async();
void stop();
private:
void worker_main();
Type type_;
sd_bus *bus_ = nullptr;
std::thread worker_thread_;
std::atomic_bool running_{false};
};
using BusPtr = std::shared_ptr<Bus>;
} // namespace dbus
} // namespace anbox
#endif

View file

@ -18,51 +18,28 @@
#ifndef ANBOX_DBUS_INTERFACE_H_
#define ANBOX_DBUS_INTERFACE_H_
#include <core/dbus/macros.h>
#include <core/dbus/property.h>
#include <chrono>
#include <string>
namespace anbox {
namespace dbus {
namespace interface {
struct Service {
static inline std::string name() { return "org.anbox"; }
static inline std::string path() { return "/"; }
static inline const char* name() { return "org.anbox"; }
static inline const char* path() { return "/org/anbox"; }
};
struct ApplicationManager {
static inline std::string name() { return "org.anbox.ApplicationManager"; }
static inline const char* name() { return "org.anbox.ApplicationManager"; }
struct Methods {
struct Launch {
static inline std::string name() { return "Launch"; }
typedef anbox::dbus::interface::ApplicationManager Interface;
typedef void ResultType;
static inline std::chrono::milliseconds default_timeout() {
return std::chrono::seconds{60};
}
};
struct Launch {
static inline const char* name() { return "Launch"; }
};
};
struct Properties {
DBUS_CPP_READABLE_PROPERTY_DEF(Ready, ApplicationManager, bool)
struct Ready {
static inline const char* name() { return "Ready"; }
};
};
};
} // namespace interface
} // namespace dbus
} // namespace anbox
namespace core {
namespace dbus {
namespace traits {
template <>
struct Service<anbox::dbus::interface::ApplicationManager> {
static inline const std::string& interface_name() {
static const std::string s{"org.anbox.ApplicationManager"};
return s;
}
};
} // namespace traits
} // namespace dbus
} // namespace core
#endif

View file

@ -0,0 +1,121 @@
/*
* Copyright (C) 2018 OpenBMC
*
* Taken from https://github.com/openbmc/sdbusplus/blob/master/sdbusplus/vtable.hpp
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef ANBOX_DBUS_SD_BUS_HELPERS_H_
#define ANBOX_DBUS_SD_BUS_HELPERS_H_
#include <systemd/sd-bus-vtable.h>
namespace anbox {
namespace dbus {
namespace sdbus {
namespace vtable {
constexpr sd_bus_vtable start(uint64_t flags)
{
sd_bus_vtable v{};
v.type = _SD_BUS_VTABLE_START;
v.flags = flags;
v.x.start = decltype(v.x.start){sizeof(sd_bus_vtable)};
return v;
}
constexpr sd_bus_vtable end() {
sd_bus_vtable v{};
v.type = _SD_BUS_VTABLE_END;
return v;
}
constexpr sd_bus_vtable method_o(const char* member, const char* signature,
const char* result,
sd_bus_message_handler_t handler, size_t offset,
uint64_t flags) {
sd_bus_vtable v{};
v.type = _SD_BUS_VTABLE_METHOD;
v.flags = flags;
v.x.method = decltype(v.x.method){member, signature, result, handler, offset};
return v;
}
constexpr sd_bus_vtable method(const char* member, const char* signature,
const char* result, sd_bus_message_handler_t handler,
uint64_t flags) {
return method_o(member, signature, result, handler, 0, flags);
}
constexpr sd_bus_vtable signal(const char* member, const char* signature, uint64_t flags) {
sd_bus_vtable v{};
v.type = _SD_BUS_VTABLE_SIGNAL;
v.flags = flags;
v.x.signal = decltype(v.x.signal){member, signature};
return v;
}
constexpr sd_bus_vtable property(const char* member, const char* signature,
sd_bus_property_get_t get,
uint64_t flags) {
sd_bus_vtable v{};
v.type = _SD_BUS_VTABLE_PROPERTY;
v.flags = flags;
v.x.property = decltype(v.x.property){member, signature, get, nullptr, 0};
return v;
}
constexpr sd_bus_vtable property(const char* member, const char* signature,
sd_bus_property_get_t get,
sd_bus_property_set_t set,
uint64_t flags) {
sd_bus_vtable v{};
v.type = _SD_BUS_VTABLE_WRITABLE_PROPERTY;
v.flags = flags;
v.x.property = decltype(v.x.property){member, signature, get, set, 0};
return v;
}
constexpr sd_bus_vtable property_o(const char* member, const char* signature,
size_t offset, uint64_t flags) {
sd_bus_vtable v{};
v.type = _SD_BUS_VTABLE_PROPERTY;
v.flags = flags;
v.x.property = decltype(v.x.property){member, signature, nullptr, nullptr, offset};
return v;
}
constexpr sd_bus_vtable property_o(const char* member, const char* signature,
sd_bus_property_set_t set, size_t offset,
uint64_t flags)
{
sd_bus_vtable v{};
v.type = _SD_BUS_VTABLE_WRITABLE_PROPERTY;
v.flags = flags;
v.x.property = decltype(v.x.property){member, signature, nullptr, set, offset};
return v;
}
} // namespace vtable
} // namespace sd_bus
} // namespace dbus
} // namespace anbox
#endif

View file

@ -16,85 +16,164 @@
*/
#include "anbox/dbus/skeleton/application_manager.h"
#include "anbox/android/intent.h"
#include "anbox/dbus/interface.h"
#include "anbox/dbus/codecs.h"
#include "anbox/dbus/sd_bus_helpers.h"
#include "anbox/android/intent.h"
#include "anbox/logger.h"
#include <sstream>
#include <core/property.h>
namespace {
int parse_string_from_message(sd_bus_message *m, std::string &str) {
const char *contents = nullptr;
auto r = sd_bus_message_enter_container(m, SD_BUS_TYPE_VARIANT, contents);
if (r < 0)
return r;
const char *value;
r = sd_bus_message_read(m, "s", &value);
if (r < 0)
return r;
str = value;
r = sd_bus_message_exit_container(m);
if (r < 0)
return r;
return 0;
}
} // namespace
namespace anbox {
namespace dbus {
namespace skeleton {
ApplicationManager::ApplicationManager(
const core::dbus::Bus::Ptr &bus, const core::dbus::Object::Ptr &object,
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>() } {
const sd_bus_vtable ApplicationManager::vtable[] = {
sdbus::vtable::start(0),
sdbus::vtable::method("Launch", "a{sv}s", "", ApplicationManager::method_launch, SD_BUS_VTABLE_UNPRIVILEGED),
sdbus::vtable::property("Ready", "b", ApplicationManager::property_ready_get, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
sdbus::vtable::end()
};
object_->install_method_handler<anbox::dbus::interface::ApplicationManager::Methods::Launch>(
[this](const core::dbus::Message::Ptr &msg) {
auto reader = msg->reader();
int ApplicationManager::method_launch(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
auto r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "{sv}");
if (r < 0)
return r;
android::Intent intent;
reader >> intent.action;
reader >> intent.uri;
reader >> intent.type;
reader >> intent.flags;
reader >> intent.package;
reader >> intent.component;
android::Intent intent;
std::int32_t left, top, right, bottom;
reader >> left;
reader >> top;
reader >> right;
reader >> bottom;
graphics::Rect launch_bounds{left, top, right, bottom};
while ((r = sd_bus_message_enter_container(m, SD_BUS_TYPE_DICT_ENTRY, "sv")) > 0) {
const char *key = nullptr;
wm::Stack::Id stack = wm::Stack::Id::Default;
reader >> stack;
r = sd_bus_message_read(m, "s", &key);
if (r < 0)
return r;
core::dbus::Message::Ptr reply;
if (strcmp(key, "package") == 0) {
r = parse_string_from_message(m, intent.package);
if (r < 0)
return r;
} else if (strcmp(key, "component") == 0) {
r = parse_string_from_message(m, intent.component);
if (r < 0)
return r;
} else if (strcmp(key, "action") == 0) {
r = parse_string_from_message(m, intent.action);
if (r < 0)
return r;
} else if (strcmp(key, "type") == 0) {
r = parse_string_from_message(m, intent.type);
if (r < 0)
return r;
} else if (strcmp(key, "uri") == 0) {
r = parse_string_from_message(m, intent.uri);
if (r < 0)
return r;
}
}
try {
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",
err.what());
}
r = sd_bus_message_exit_container(m);
if (r < 0)
return r;
bus_->send(reply);
});
const char *stack = nullptr;
r = sd_bus_message_read(m, "s", &stack);
if (r < 0)
return r;
wm::Stack::Id launch_stack = wm::Stack::Id::Default;
if (stack && strlen(stack) > 0) {
auto s = std::string(stack);
std::istringstream i(s);
i >> launch_stack;
}
if (intent.package.length() == 0) {
sd_bus_error_set_const(ret_error, "org.anbox.InvalidArgument", "No package specified");
return -EINVAL;
}
auto thiz = static_cast<ApplicationManager*>(userdata);
try {
thiz->launch(intent, graphics::Rect::Invalid, launch_stack);
} catch (std::exception &err) {
ERROR("Failed to launch application: %s", err.what());
sd_bus_error_set_const(ret_error, "org.anbox.InternalError", err.what());
return -EIO;
}
return sd_bus_reply_method_return(m, "");
}
int ApplicationManager::property_ready_get(sd_bus *bus, const char *path, const char *interface,
const char *property, sd_bus_message *reply,
void *userdata, sd_bus_error *ret_error) {
(void) bus;
(void) path;
(void) interface;
(void) property;
(void) ret_error;
auto thiz = static_cast<ApplicationManager*>(userdata);
return sd_bus_message_append(reply, "b", thiz->impl_->ready().get());
}
ApplicationManager::ApplicationManager(const BusPtr& bus, const std::shared_ptr<anbox::application::Manager> &impl)
: bus_(bus), impl_(impl) {
const auto r = sd_bus_add_object_vtable(bus_->raw(),
&obj_slot_,
interface::Service::path(),
interface::ApplicationManager::name(),
vtable,
this);
if (r < 0)
std::runtime_error("Failed to setup application manager DBus service");
// Forward AndroidApi status to our dbus property
properties_.ready->install([&]() { return impl_->ready().get(); });
impl_->ready().changed().connect([&](bool value) {
properties_.ready->set(value);
on_property_value_changed<anbox::dbus::interface::ApplicationManager::Properties::Ready>(value);
(void) value;
sd_bus_emit_properties_changed(bus_->raw(),
interface::Service::path(),
interface::ApplicationManager::name(),
interface::ApplicationManager::Properties::Ready::name(),
nullptr);
});
}
ApplicationManager::~ApplicationManager() {}
template<typename Property>
void ApplicationManager::on_property_value_changed(const typename Property::ValueType& value)
{
typedef std::map<std::string, core::dbus::types::Variant> Dictionary;
static const std::vector<std::string> the_empty_list_of_invalidated_properties;
Dictionary dict; dict[Property::name()] = core::dbus::types::Variant::encode(value);
signals_.properties_changed->emit(
std::make_tuple(core::dbus::traits::Service<anbox::dbus::interface::ApplicationManager>::interface_name(),
dict, the_empty_list_of_invalidated_properties));
}
void ApplicationManager::launch(const android::Intent &intent,
const graphics::Rect &launch_bounds,
const wm::Stack::Id &stack) {
if (!impl_->ready())
throw std::runtime_error("Anbox not yet ready to launch applications");
DEBUG("Launching %s", intent);
impl_->launch(intent, launch_bounds, stack);
}

View file

@ -19,22 +19,16 @@
#define ANBOX_DBUS_SKELETON_APPLICATION_MANAGER_H_
#include "anbox/application/manager.h"
#include "anbox/dbus/bus.h"
#include <core/dbus/bus.h>
#include <core/dbus/object.h>
#include <core/dbus/service.h>
#include <core/dbus/property.h>
#include "anbox/dbus/interface.h"
#include <memory>
namespace anbox {
namespace dbus {
namespace skeleton {
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::application::Manager> &impl);
ApplicationManager(const BusPtr& bus, const std::shared_ptr<anbox::application::Manager> &impl);
~ApplicationManager();
void launch(const android::Intent &intent,
@ -44,20 +38,15 @@ class ApplicationManager : public anbox::application::Manager {
core::Property<bool>& ready() override;
private:
template<typename Property>
void on_property_value_changed(const typename Property::ValueType& value);
static const sd_bus_vtable vtable[];
static int method_launch(sd_bus_message *m, void *userdata, sd_bus_error *ret_error);
static int property_ready_get(sd_bus *bus, const char *path, const char *interface,
const char *property, sd_bus_message *reply, void *userdata,
sd_bus_error *ret_error);
core::dbus::Bus::Ptr bus_;
core::dbus::Service::Ptr service_;
core::dbus::Object::Ptr object_;
BusPtr bus_;
std::shared_ptr<anbox::application::Manager> impl_;
struct {
std::shared_ptr<core::dbus::Property<anbox::dbus::interface::ApplicationManager::Properties::Ready>> ready;
} properties_;
struct {
core::dbus::Signal<core::dbus::interfaces::Properties::Signals::PropertiesChanged,
core::dbus::interfaces::Properties::Signals::PropertiesChanged::ArgumentType>::Ptr properties_changed;
} signals_;
sd_bus_slot *obj_slot_ = nullptr;
};
} // namespace skeleton
} // namespace dbus

View file

@ -15,29 +15,33 @@
*
*/
#include "anbox/dbus/skeleton/service.h"
#include "anbox/dbus/interface.h"
#include "anbox/dbus/skeleton/service.h"
#include "anbox/dbus/skeleton/application_manager.h"
#include "anbox/logger.h"
namespace anbox {
namespace dbus {
namespace skeleton {
std::shared_ptr<Service> Service::create_for_bus(
const core::dbus::Bus::Ptr &bus,
const std::shared_ptr<anbox::application::Manager> &application_manager) {
auto service = core::dbus::Service::add_service(bus, anbox::dbus::interface::Service::name());
auto object = service->add_object_for_path(anbox::dbus::interface::Service::path());
return std::make_shared<Service>(bus, service, object, application_manager);
std::shared_ptr<Service> Service::create_for_bus(const BusPtr& bus, const std::shared_ptr<anbox::application::Manager> &impl) {
return std::shared_ptr<Service>(new Service(bus, impl));
}
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::application::Manager> &application_manager)
: bus_(bus),
service_(service),
object_(object),
application_manager_(std::make_shared<ApplicationManager>(bus_, object_, application_manager)) {}
Service::Service(const BusPtr& bus, const std::shared_ptr<anbox::application::Manager> &impl)
: bus_{bus} {
if (!bus_)
throw std::invalid_argument("Missing bus object");
const auto r = sd_bus_request_name(bus_->raw(),
interface::Service::name(),
0);
if (r < 0)
throw std::runtime_error("Failed to request DBus service name");
DEBUG("Successfully acquired DBus service name");
application_manager_ = std::make_shared<ApplicationManager>(bus, impl);
}
Service::~Service() {}
} // namespace skeleton

View file

@ -19,11 +19,14 @@
#define ANBOX_DBUS_SKELETON_SERVICE_H_
#include "anbox/application/manager.h"
#include "anbox/dbus/bus.h"
#include "anbox/do_not_copy_or_move.h"
#include <core/dbus/bus.h>
#include <core/dbus/object.h>
#include <core/dbus/service.h>
#include <atomic>
#include <memory>
#include <thread>
#include <systemd/sd-bus.h>
namespace anbox {
namespace dbus {
@ -31,20 +34,14 @@ namespace skeleton {
class ApplicationManager;
class Service : public DoNotCopyOrMove {
public:
static std::shared_ptr<Service> create_for_bus(
const core::dbus::Bus::Ptr &bus,
const std::shared_ptr<anbox::application::Manager> &application_manager);
static std::shared_ptr<Service> create_for_bus(const BusPtr& bus, const std::shared_ptr<anbox::application::Manager> &impl);
Service(
const core::dbus::Bus::Ptr &bus, const core::dbus::Service::Ptr &service,
const core::dbus::Object::Ptr &object,
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_;
Service(const BusPtr& bus, const std::shared_ptr<anbox::application::Manager> &impl);
BusPtr bus_;
std::shared_ptr<application::Manager> application_manager_;
};
} // namespace skeleton

View file

@ -15,44 +15,108 @@
*
*/
#include "anbox/dbus/stub/application_manager.h"
#include "anbox/dbus/interface.h"
#include "anbox/dbus/codecs.h"
#include "anbox/dbus/stub/application_manager.h"
#include "anbox/logger.h"
#include <sstream>
namespace anbox {
namespace dbus {
namespace stub {
std::shared_ptr<ApplicationManager> ApplicationManager::create_for_bus(const core::dbus::Bus::Ptr &bus) {
auto service = core::dbus::Service::use_service_or_throw_if_not_available(bus, anbox::dbus::interface::Service::name());
auto object = service->object_for_path(anbox::dbus::interface::Service::path());
return std::make_shared<ApplicationManager>(bus, service, object);
std::shared_ptr<ApplicationManager> ApplicationManager::create_for_bus(const BusPtr& bus) {
return std::shared_ptr<ApplicationManager>(new ApplicationManager(bus));
}
ApplicationManager::ApplicationManager(const core::dbus::Bus::Ptr &bus,
const core::dbus::Service::Ptr &service,
const core::dbus::Object::Ptr &object)
: bus_(bus), service_(service), object_(object),
properties_{ object_->get_property<anbox::dbus::interface::ApplicationManager::Properties::Ready>() } {
ApplicationManager::ApplicationManager(const BusPtr& bus)
: bus_(bus) {
// Forward changes on the dbus property to our users
ready_.install([&]() { return properties_.ready->get(); });
properties_.ready->changed().connect([&](bool value) { ready_.set(value); });
if (!bus_->has_service_with_name(interface::Service::name()))
throw std::runtime_error("Application manager service is not running yet");
update_properties();
}
ApplicationManager::~ApplicationManager() {}
void ApplicationManager::update_properties() {
bool ready = false;
const auto r = sd_bus_get_property_trivial(bus_->raw(),
interface::Service::name(),
interface::Service::path(),
interface::ApplicationManager::name(),
interface::ApplicationManager::Properties::Ready::name(),
nullptr,
'b',
&ready);
if (r < 0)
throw std::runtime_error("Failed to retrieve ready property from application manager");
ready_.set(ready);
}
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(), stack);
(void) launch_bounds;
if (result.is_error()) throw std::runtime_error(result.error().print());
sd_bus_message *m = nullptr;
auto r = sd_bus_message_new_method_call(bus_->raw(),
&m,
interface::Service::name(),
interface::Service::path(),
interface::ApplicationManager::name(),
interface::ApplicationManager::Methods::Launch::name());
if (r < 0)
throw std::runtime_error("Failed to construct DBus message");
r = sd_bus_message_open_container(m, 'a', "{sv}");
if (r < 0)
throw std::runtime_error("Failed to construct DBus message");
if (intent.package.length() > 0) {
r = sd_bus_message_append(m, "{sv}", "package", "s", intent.package.c_str());
if (r < 0)
throw std::runtime_error("Failed to construct DBus message");
}
if (intent.component.length() > 0) {
r = sd_bus_message_append(m, "{sv}", "component", "s", intent.component.c_str());
if (r < 0)
throw std::runtime_error("Failed to construct DBus message");
}
if (intent.action.length() > 0) {
r = sd_bus_message_append(m, "{sv}", "action", "s", intent.action.c_str());
if (r < 0)
throw std::runtime_error("Failed to construct DBus message");
}
if (intent.type.length() > 0) {
r = sd_bus_message_append(m, "{sv}", "type", "s", intent.type.c_str());
if (r < 0)
throw std::runtime_error("Failed to construct DBus message");
}
if (intent.uri.length() > 0) {
r = sd_bus_message_append(m, "{sv}", "uri", "s", intent.uri.c_str());
if (r < 0)
throw std::runtime_error("Failed to construct DBus message");
}
r = sd_bus_message_close_container(m);
if (r < 0)
throw std::runtime_error("Failed to construct DBus message");
std::ostringstream launch_stack;
launch_stack << stack;
r = sd_bus_message_append(m, "s", launch_stack.str().c_str());
if (r < 0)
throw std::runtime_error("Failed to construct DBus message");
r = sd_bus_call(bus_->raw(), m, 0, nullptr, nullptr);
if (r < 0)
throw std::runtime_error("Failed to call Anbox application manager");
}
core::Property<bool>& ApplicationManager::ready() {

View file

@ -19,24 +19,17 @@
#define ANBOX_DBUS_SKELETON_APPLICATION_MANAGER_H_
#include "anbox/application/manager.h"
#include "anbox/dbus/bus.h"
#include <core/dbus/bus.h>
#include <core/dbus/object.h>
#include <core/dbus/service.h>
#include "anbox/dbus/interface.h"
#include <memory>
namespace anbox {
namespace dbus {
namespace stub {
class ApplicationManager : public anbox::application::Manager {
public:
static std::shared_ptr<ApplicationManager> create_for_bus(
const core::dbus::Bus::Ptr &bus);
static std::shared_ptr<ApplicationManager> create_for_bus(const BusPtr& bus);
ApplicationManager(const core::dbus::Bus::Ptr &bus,
const core::dbus::Service::Ptr &service,
const core::dbus::Object::Ptr &object);
~ApplicationManager();
void launch(const android::Intent &intent,
@ -45,14 +38,13 @@ class ApplicationManager : public anbox::application::Manager {
core::Property<bool>& ready() override;
void update_properties();
private:
core::dbus::Bus::Ptr bus_;
core::dbus::Service::Ptr service_;
core::dbus::Object::Ptr object_;
ApplicationManager(const BusPtr& bus);
BusPtr bus_;
core::Property<bool> ready_;
struct {
std::shared_ptr<core::dbus::Property<anbox::dbus::interface::ApplicationManager::Properties::Ready>> ready;
} properties_;
};
} // namespace stub
} // namespace dbus

View file

@ -49,7 +49,7 @@ bool AudioSink::connect_audio() {
spec_.freq = 44100;
spec_.format = AUDIO_S16;
spec_.channels = 2;
spec_.samples = 4096;
spec_.samples = 1024;
spec_.callback = &AudioSink::on_data_requested;
spec_.userdata = this;