Merge branch 'master' into basic-touch

This commit is contained in:
Simon Fels 2018-05-25 09:11:02 +02:00 • committed by GitHub
commit f2030cc894
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
1022 changed files with 184437 additions and 18469 deletions

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
@ -28,6 +29,7 @@ include_directories(
${CMAKE_BINARY_DIR}/external/android-emugl/host/libs/GLESv2_dec
${CMAKE_SOURCE_DIR}/external/android-emugl/host/libs/renderControl_dec
${CMAKE_BINARY_DIR}/external/android-emugl/host/libs/renderControl_dec
${CMAKE_SOURCE_DIR}/external/glm
)
protobuf_generate_cpp(
@ -124,6 +126,7 @@ set(SOURCES
anbox/graphics/buffer_queue.cpp
anbox/graphics/buffered_io_stream.cpp
anbox/graphics/gl_renderer_server.cpp
anbox/graphics/density.cpp
anbox/graphics/density.h
anbox/graphics/rect.cpp
anbox/graphics/layer_composer.cpp
@ -194,11 +197,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
@ -209,6 +217,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
@ -232,6 +242,8 @@ target_link_libraries(anbox-core
${LXC_LIBRARIES}
${MIRCLIENT_LDFLAGS}
${MIRCLIENT_LIBRARIES}
${LIBSYSTEMD_LDFLAGS}
${LIBSYSTEMD_LIBRARIES}
pthread
process-cpp
emugl_common
@ -243,9 +255,10 @@ target_link_libraries(anbox-core
anbox-protobuf
xdg)
add_executable(anbox main.cpp)
add_executable(anbox main.cpp ${BACKWARD_ENABLE})
target_link_libraries(anbox
anbox-core)
add_backward(anbox)
install(
TARGETS anbox

View file

@ -30,7 +30,7 @@
namespace fs = boost::filesystem;
namespace {
constexpr const std::chrono::milliseconds default_rpc_call_timeout{5000};
constexpr const std::chrono::milliseconds default_rpc_call_timeout{30000};
} // namespace
namespace anbox {

View file

@ -16,28 +16,28 @@
*/
#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};
static int redirect_to_null(int flags, int fd) {
int fd2;
@ -53,6 +53,69 @@ static int redirect_to_null(int flags, int fd) {
close(fd2);
return fd;
}
} // 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([&]() {
// 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");
}
return true;
}
bool anbox::cmds::Launch::try_launch_activity(const std::shared_ptr<dbus::stub::ApplicationManager> &stub) {
@ -96,6 +159,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 +168,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,10 +34,11 @@ 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_;
wm::Stack::Id stack_;
wm::Stack::Id stack_ = wm::Stack::Id::Default;
bool use_system_dbus_ = false;
};
} // namespace cmds

View file

@ -29,14 +29,11 @@
#include "anbox/bridge/platform_message_processor.h"
#include "anbox/graphics/gl_renderer_server.h"
namespace {
std::istream& operator>>(std::istream& in, anbox::graphics::GLRendererServer::Config::Driver& driver);
}
#include "anbox/cmds/session_manager.h"
#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"
@ -53,8 +50,6 @@ std::istream& operator>>(std::istream& in, anbox::graphics::GLRendererServer::Co
#include <sys/prctl.h>
#include <core/dbus/asio/executor.h>
#include <core/dbus/bus.h>
#pragma GCC diagnostic pop
namespace fs = boost::filesystem;
@ -75,17 +70,6 @@ class NullConnectionCreator : public anbox::network::ConnectionCreator<
socket->close();
}
};
std::istream& operator>>(std::istream& in, anbox::graphics::GLRendererServer::Config::Driver& driver) {
std::string str(std::istreambuf_iterator<char>(in), {});
if (str.empty() || str == "translator")
driver = anbox::graphics::GLRendererServer::Config::Driver::Translator;
else if (str == "host")
driver = anbox::graphics::GLRendererServer::Config::Driver::Host;
else
BOOST_THROW_EXCEPTION(std::runtime_error("Invalid GLES driver value provided"));
return in;
}
}
void anbox::cmds::SessionManager::launch_appmgr_if_needed(const std::shared_ptr<bridge::AndroidApiStub> &android_api_stub) {
@ -103,7 +87,6 @@ void anbox::cmds::SessionManager::launch_appmgr_if_needed(const std::shared_ptr<
anbox::cmds::SessionManager::SessionManager()
: CommandWithFlagsAndAction{cli::Name{"session-manager"}, cli::Usage{"session-manager"},
cli::Description{"Run the the anbox session manager"}},
gles_driver_(graphics::GLRendererServer::Config::Driver::Host),
window_size_(default_single_window_size) {
// Just for the purpose to allow QtMir (or unity8) to find this on our
// /proc/*/cmdline
@ -111,9 +94,6 @@ anbox::cmds::SessionManager::SessionManager()
flag(cli::make_flag(cli::Name{"desktop_file_hint"},
cli::Description{"Desktop file hint for QtMir/Unity8"},
desktop_file_hint_));
flag(cli::make_flag(cli::Name{"gles-driver"},
cli::Description{"Which GLES driver to use. Possible values are 'host' or'translator'"},
gles_driver_));
flag(cli::make_flag(cli::Name{"single-window"},
cli::Description{"Start in single window mode."},
single_window_));
@ -129,6 +109,9 @@ anbox::cmds::SessionManager::SessionManager()
flag(cli::make_flag(cli::Name{"use-system-dbus"},
cli::Description{"Use system instead of session DBus"},
use_system_dbus_));
flag(cli::make_flag(cli::Name{"software-rendering"},
cli::Description{"Use software rendering instead of hardware accelerated GL rendering"},
use_software_rendering_));
action([this](const cli::Command::Context &) {
auto trap = core::posix::trap_signals_for_process(
@ -159,13 +142,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;
@ -190,8 +172,15 @@ anbox::cmds::SessionManager::SessionManager()
using_single_window = true;
}
auto gl_server = std::make_shared<graphics::GLRendererServer>(
graphics::GLRendererServer::Config{gles_driver_, single_window_}, window_manager);
auto gl_driver = graphics::GLRendererServer::Config::Driver::Host;
if (use_software_rendering_)
gl_driver = graphics::GLRendererServer::Config::Driver::Software;
graphics::GLRendererServer::Config renderer_config {
gl_driver,
single_window_
};
auto gl_server = std::make_shared<graphics::GLRendererServer>(renderer_config, window_manager);
platform->set_window_manager(window_manager);
platform->set_renderer(gl_server->renderer());
@ -260,18 +249,20 @@ anbox::cmds::SessionManager::SessionManager()
{"/dev/fuse", "/dev/fuse"},
};
dispatcher->dispatch([&]() { container_->start(container_configuration); });
dispatcher->dispatch([&]() {
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"
@ -46,12 +44,12 @@ class SessionManager : public cli::CommandWithFlagsAndAction {
std::shared_ptr<container::Client> container_;
std::string desktop_file_hint_;
graphics::GLRendererServer::Config::Driver gles_driver_;
bool single_window_ = false;
graphics::Rect window_size_;
bool standalone_ = false;
bool experimental_ = false;
bool use_system_dbus_ = false;
bool use_software_rendering_ = false;
};
} // namespace cmds
} // namespace anbox

View file

@ -129,7 +129,7 @@ class SystemInformation {
}
void collect_graphics_info() {
auto gl_libs = anbox::graphics::emugl::default_gl_libraries(true);
auto gl_libs = anbox::graphics::emugl::default_gl_libraries();
if (!anbox::graphics::emugl::initialize(gl_libs, nullptr, nullptr)) {
return;
}
@ -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

@ -44,7 +44,7 @@ constexpr const char *default_container_ip_address{"192.168.250.2"};
constexpr const std::uint32_t default_container_ip_prefix_length{24};
constexpr const char *default_host_ip_address{"192.168.250.1"};
constexpr const char *default_dns_server{"8.8.8.8"};
}
} // namespace
namespace anbox {
namespace container {
@ -178,9 +178,9 @@ void LxcContainer::start(const Configuration &configuration) {
BOOST_THROW_EXCEPTION(std::runtime_error("Failed to create LXC container instance"));
// If container is still running (for example after a crash) we stop it here
// to ensure
// its configuration is synchronized.
if (container_->is_running(container_)) container_->stop(container_);
// to ensure its configuration is synchronized.
if (container_->is_running(container_))
container_->stop(container_);
}
// We can mount proc/sys as rw here as we will run the container unprivileged
@ -214,17 +214,7 @@ void LxcContainer::start(const Configuration &configuration) {
setup_network();
#if 0
// Android uses namespaces as well so we have to allow nested namespaces for LXC
// which are otherwise forbidden by AppArmor.
set_config_item("lxc.aa_profile", "lxc-container-default-with-nesting");
#else
// FIXME: when using the nested profile we still get various denials from
// things Android tries to do but isn't allowed to. We need to look into
// those and see how we can switch back to a confined way of running the
// container.
set_config_item("lxc.aa_profile", "unconfined");
#endif
set_config_item("lxc.aa_profile", "anbox-container");
if (!privileged_)
setup_id_maps();

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

@ -28,6 +28,10 @@
#include "anbox/rpc/channel.h"
#include "anbox/rpc/pending_call_cache.h"
#include <boost/filesystem.hpp>
namespace fs = boost::filesystem;
namespace anbox {
namespace container {
std::shared_ptr<Service> Service::create(const std::shared_ptr<Runtime> &rt, bool privileged) {
@ -41,6 +45,10 @@ std::shared_ptr<Service> Service::create(const std::shared_ptr<Runtime> &rt, boo
});
const auto container_socket_path = SystemConfiguration::instance().container_socket_path();
const auto socket_parent_path = fs::path(container_socket_path).parent_path();
if (!fs::exists(socket_parent_path))
fs::create_directories(socket_parent_path);
sp->connector_ = std::make_shared<network::PublishedSocketConnector>(container_socket_path, rt, delegate_connector);
// Make sure others can connect to our socket

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,168 @@
*/
#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);
});
r = sd_bus_message_exit_container(m);
if (r < 0)
return r;
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,112 @@
*
*/
#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");
sd_bus_error error = SD_BUS_ERROR_NULL;
r = sd_bus_call(bus_->raw(), m, 0, &error, nullptr);
if (r < 0) {
const auto msg = utils::string_format("%s", error.message);
sd_bus_error_free(&error);
throw std::runtime_error(msg);
}
}
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

@ -0,0 +1,30 @@
/*
* 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/graphics/density.h"
namespace anbox {
namespace graphics {
DensityType current_density() {
return DensityType::medium;
}
int dp_to_pixel(unsigned int dp) {
return dp * static_cast<unsigned int>(current_density()) / static_cast<unsigned int>(DensityType::medium);
}
} // namespace graphics
} // namespace anbox

View file

@ -35,6 +35,9 @@ enum class DensityType {
xhigh = 360,
xxhigh = 480,
};
DensityType current_density();
int dp_to_pixel(unsigned int dp);
} // namespace graphics
} // namespace anbox

View file

@ -37,13 +37,9 @@ constexpr const char *default_glesv2_lib{"libGLESv2.so.2"};
namespace anbox {
namespace graphics {
namespace emugl {
std::vector<GLLibrary> default_gl_libraries(bool no_glesv1) {
std::vector<GLLibrary> default_gl_libraries() {
return std::vector<GLLibrary>{
{GLLibrary::Type::EGL, default_egl_lib},
// If environment doesn't provide a GLESv1 .so file we use the GLESv2
// implementation instead. If our stack allows it we can try to get
// rid of GLESv1 completely at a later point.
{GLLibrary::Type::GLESv1, no_glesv1 ? default_glesv2_lib : default_glesv1_lib},
{GLLibrary::Type::GLESv2, default_glesv2_lib},
};
}
@ -75,7 +71,15 @@ bool initialize(const std::vector<GLLibrary> &libs, emugl_logger_struct *log_fun
}
}
if (!s_egl.initialized || !s_gles1.initialized || !s_gles2.initialized)
// If we are not provided with a link to a OpenGL ES v1 implementation
// we assign dummy functions to all of the functions we would call.
// This allows us to still manage the major chunk of Android applications
// which are all >= GLESv2 until we have a proper GLESv1->GLESv2
// translation mechanism in place.
if (!s_gles1.initialized)
gles1_dispatch_init(nullptr, &s_gles1);
if (!s_egl.initialized || !s_gles2.initialized)
return false;
return true;

View file

@ -37,7 +37,7 @@ struct GLLibrary {
boost::filesystem::path path;
};
std::vector<GLLibrary> default_gl_libraries(bool no_glesv1 = false);
std::vector<GLLibrary> default_gl_libraries();
bool initialize(const std::vector<GLLibrary> &libs, emugl_logger_struct *log_funcs, logger_t crash_func);
} // namespace emugl

View file

@ -414,7 +414,7 @@ bool is_layer_blacklisted(const std::string &name) {
return std::find(blacklist.begin(), blacklist.end(), name) != blacklist.end();
}
void rcPostLayer(const char *name, uint32_t color_buffer,
void rcPostLayer(const char *name, uint32_t color_buffer, float alpha,
int32_t sourceCropLeft, int32_t sourceCropTop,
int32_t sourceCropRight, int32_t sourceCropBottom,
int32_t displayFrameLeft, int32_t displayFrameTop,
@ -422,6 +422,7 @@ void rcPostLayer(const char *name, uint32_t color_buffer,
Renderable r{
name,
color_buffer,
alpha,
{displayFrameLeft, displayFrameTop, displayFrameRight, displayFrameBottom},
{sourceCropLeft, sourceCropTop, sourceCropRight, sourceCropBottom}};
frame_layers.push_back(r);

View file

@ -16,10 +16,10 @@
#include "anbox/graphics/emugl/Renderable.h"
Renderable::Renderable(const std::string &name, const std::uint32_t &buffer,
Renderable::Renderable(const std::string &name, const std::uint32_t &buffer, float alpha,
const anbox::graphics::Rect &screen_position,
const anbox::graphics::Rect &crop,
const glm::mat4 &transformation, const float &alpha)
const glm::mat4 &transformation)
: name_(name),
buffer_(buffer),
screen_position_(screen_position),

View file

@ -24,14 +24,17 @@
#include <cstdint>
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wold-style-cast"
#include <glm/glm.hpp>
#pragma GCC diagnostic pop
class Renderable {
public:
Renderable(const std::string &name, const std::uint32_t &buffer,
Renderable(const std::string &name, const std::uint32_t &buffer, float alpha,
const anbox::graphics::Rect &screen_position,
const anbox::graphics::Rect &crop = {},
const glm::mat4 &transformation = {}, const float &alpha = 1.0f);
const glm::mat4 &transformation = {});
~Renderable();
std::string name() const;

View file

@ -28,9 +28,12 @@
#include <stdio.h>
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wold-style-cast"
#include <glm/glm.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <glm/gtx/transform.hpp>
#pragma GCC diagnostic pop
namespace {
@ -115,9 +118,9 @@ bool Renderer::initialize(EGLNativeDisplayType nativeDisplay) {
// Create EGL context for framebuffer post rendering.
GLint surfaceType = EGL_WINDOW_BIT | EGL_PBUFFER_BIT;
const GLint configAttribs[] = {EGL_RED_SIZE, 1,
EGL_GREEN_SIZE, 1,
EGL_BLUE_SIZE, 1,
const GLint configAttribs[] = {EGL_RED_SIZE, 8,
EGL_GREEN_SIZE, 8,
EGL_BLUE_SIZE, 8,
EGL_SURFACE_TYPE, surfaceType,
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
EGL_NONE};
@ -255,7 +258,7 @@ Renderer::Program::Program(GLuint program_id) {
position_attr = s_gles2.glGetAttribLocation(id, "position");
texcoord_attr = s_gles2.glGetAttribLocation(id, "texcoord");
tex_uniform = s_gles2.glGetUniformLocation(id, "tex");
center_uniform = s_gles2.glGetUniformLocation(id, "centre");
center_uniform = s_gles2.glGetUniformLocation(id, "center");
display_transform_uniform =
s_gles2.glGetUniformLocation(id, "display_transform");
transform_uniform = s_gles2.glGetUniformLocation(id, "transform");

View file

@ -30,6 +30,8 @@
#include <cstdarg>
#include <stdexcept>
namespace fs = boost::filesystem;
namespace {
void logger_write(const emugl::LogLevel &level, const char *format, ...) {
(void)level;
@ -76,14 +78,18 @@ GLRendererServer::GLRendererServer(const Config &config, const std::shared_ptr<w
composer_ = std::make_shared<LayerComposer>(renderer_, composer_strategy);
auto gl_libs = emugl::default_gl_libraries(true);
auto gl_libs = emugl::default_gl_libraries();
if (config.driver == Config::Driver::Software) {
const auto snap_path = utils::get_env_value("SNAP");
if (snap_path.empty())
throw std::runtime_error("Software rendering is not available outside of a snap build");
if (config.driver == Config::Driver::Translator) {
DEBUG("Using GLES-to-GL translator for rendering");
boost::filesystem::path translator_dir = utils::prefix_dir_from_env(TRANSLATOR_INSTALL_DIR, "SNAP");
gl_libs.push_back(emugl::GLLibrary{emugl::GLLibrary::Type::EGL, (translator_dir / "libEGL_translator.so")});
gl_libs.push_back(emugl::GLLibrary{emugl::GLLibrary::Type::GLESv1, (translator_dir / "libGLES_CM_translator.so")});
gl_libs.push_back(emugl::GLLibrary{emugl::GLLibrary::Type::GLESv2, (translator_dir / "libGLES_V2_translator.so")});
auto swiftshader_path = fs::path(snap_path) / "lib" / "anbox" / "swiftshader";
gl_libs = std::vector<emugl::GLLibrary>{
{emugl::GLLibrary::Type::EGL, (swiftshader_path / "libEGL.so").string()},
{emugl::GLLibrary::Type::GLESv1, (swiftshader_path / "libGLES_CM.so").string()},
{emugl::GLLibrary::Type::GLESv2, (swiftshader_path / "libGLESv2.so").string()},
};
}
emugl_logger_struct log_funcs;

View file

@ -35,7 +35,14 @@ class LayerComposer;
class GLRendererServer {
public:
struct Config {
enum class Driver { Translator, Host };
enum class Driver {
// Use the GL driver provided by the host operating system
Host,
// Use a builtin software based GL driver implementation without any support
// for hardware acceleration.
Software,
};
Driver driver;
bool single_window;
};

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;

View file

@ -16,8 +16,9 @@
*/
#include "anbox/platform/sdl/window.h"
#include "anbox/logger.h"
#include "anbox/wm/window_state.h"
#include "anbox/graphics/density.h"
#include "anbox/logger.h"
#include <boost/throw_exception.hpp>
@ -25,6 +26,14 @@
#include <mir_toolkit/mir_client_library.h>
#endif
namespace {
constexpr const int window_resize_border{30};
constexpr const int top_drag_area{50};
constexpr const int button_size{32};
constexpr const int button_margin{5};
constexpr const int button_padding{4};
}
namespace anbox {
namespace platform {
namespace sdl {
@ -50,7 +59,7 @@ Window::Window(const std::shared_ptr<Renderer> &renderer,
// initializing GL here will cause a surface to be created and the
// renderer will attempt to create one too which will not work as
// only a single surface per EGLNativeWindowType is supported.
std::uint32_t flags = 0;
std::uint32_t flags = SDL_WINDOW_BORDERLESS;
if (resizable)
flags |= SDL_WINDOW_RESIZABLE;
@ -63,6 +72,9 @@ Window::Window(const std::shared_ptr<Renderer> &renderer,
BOOST_THROW_EXCEPTION(std::runtime_error(message));
}
if (SDL_SetWindowHitTest(window_, &Window::on_window_hit, this) < 0)
BOOST_THROW_EXCEPTION(std::runtime_error("Failed to register for window hit test"));
SDL_SysWMinfo info;
SDL_VERSION(&info.version);
SDL_GetWindowWMInfo(window_, &info);
@ -91,6 +103,58 @@ Window::~Window() {
if (window_) SDL_DestroyWindow(window_);
}
SDL_HitTestResult Window::on_window_hit(SDL_Window *window, const SDL_Point *pt, void *data) {
auto platform_window = reinterpret_cast<Window*>(data);
int w = 0, h = 0;
SDL_GetWindowSize(window, &w, &h);
const auto border_size = graphics::dp_to_pixel(window_resize_border);
const auto top_drag_area_height = graphics::dp_to_pixel(top_drag_area);
const auto button_area_width = graphics::dp_to_pixel(button_size + button_padding * 2 + button_margin * 2);
if (pt->y < top_drag_area_height) {
if (pt->x > w - button_area_width && pt->x < w) {
platform_window->close();
return SDL_HITTEST_NORMAL;
} else if (pt->x > w - button_area_width * 2 && pt->x < w - button_area_width) {
platform_window->switch_window_state();
return SDL_HITTEST_NORMAL;
}
return SDL_HITTEST_DRAGGABLE;
} else if (pt->x < border_size && pt->y < border_size)
return SDL_HITTEST_RESIZE_TOPLEFT;
else if (pt->x > window_resize_border && pt->x < w - border_size && pt->y < border_size)
return SDL_HITTEST_RESIZE_TOP;
else if (pt->x > w - border_size && pt->y < border_size)
return SDL_HITTEST_RESIZE_TOPRIGHT;
else if (pt->x > w - border_size && pt->y > border_size && pt->y < h - border_size)
return SDL_HITTEST_RESIZE_RIGHT;
else if (pt->x > w - border_size && pt->y > h - border_size)
return SDL_HITTEST_RESIZE_BOTTOMRIGHT;
else if (pt->x < w - border_size && pt->x > border_size && pt->y > h - border_size)
return SDL_HITTEST_RESIZE_BOTTOM;
else if (pt->x < border_size && pt->y > h - border_size)
return SDL_HITTEST_RESIZE_BOTTOMLEFT;
else if (pt->x < border_size && pt->y < h - border_size && pt->y > border_size)
return SDL_HITTEST_RESIZE_LEFT;
return SDL_HITTEST_NORMAL;
}
void Window::close() {
if (observer_)
observer_->window_deleted(id_);
}
void Window::switch_window_state() {
const auto flags = SDL_GetWindowFlags(window_);
if (flags & SDL_WINDOW_MAXIMIZED)
SDL_RestoreWindow(window_);
else
SDL_MaximizeWindow(window_);
}
void Window::process_event(const SDL_Event &event) {
switch (event.window.event) {
case SDL_WINDOWEVENT_FOCUS_GAINED:
@ -113,7 +177,10 @@ void Window::process_event(const SDL_Event &event) {
case SDL_WINDOWEVENT_HIDDEN:
break;
case SDL_WINDOWEVENT_CLOSE:
if (observer_) observer_->window_deleted(id_);
if (observer_)
observer_->window_deleted(id_);
close();
break;
default:
break;

View file

@ -62,6 +62,11 @@ class Window : public std::enable_shared_from_this<Window>, public wm::Window {
std::uint32_t window_id() const;
private:
static SDL_HitTestResult on_window_hit(SDL_Window *window, const SDL_Point *pt, void *data);
void close();
void switch_window_state();
Id id_;
std::shared_ptr<Observer> observer_;
EGLNativeDisplayType native_display_;

View file

@ -35,10 +35,8 @@ void BootPropertiesMessageProcessor::handle_command(
void BootPropertiesMessageProcessor::list_properties() {
std::vector<std::string> properties = {
// TODO(morphis): Using HDPI here for now but should be adjusted to the
// device
// we're running on.
utils::string_format("ro.sf.lcd_density=%d",
static_cast<int>(graphics::DensityType::medium)),
// device we're running on
utils::string_format("ro.sf.lcd_density=%d", static_cast<int>(graphics::current_density())),
};
for (const auto &prop : properties) {

View file

@ -23,7 +23,7 @@
namespace {
static constexpr const long header_size{4};
}
} // namespace
namespace anbox {
namespace qemu {
@ -33,18 +33,17 @@ QemudMessageProcessor::QemudMessageProcessor(
QemudMessageProcessor::~QemudMessageProcessor() {}
bool QemudMessageProcessor::process_data(
const std::vector<std::uint8_t> &data) {
for (const auto &byte : data) buffer_.push_back(byte);
bool QemudMessageProcessor::process_data(const std::vector<std::uint8_t> &data) {
for (const auto &byte : data)
buffer_.push_back(byte);
process_commands();
return true;
return process_commands();
}
void QemudMessageProcessor::process_commands() {
bool QemudMessageProcessor::process_commands() {
while (true) {
if (buffer_.size() < header_size) break;
if (buffer_.size() < header_size)
break;
char header[header_size] = {0};
::memcpy(header, buffer_.data(), header_size);
@ -52,6 +51,12 @@ void QemudMessageProcessor::process_commands() {
unsigned int body_size = 0;
::sscanf(header, "%04x", &body_size);
// Double check that we have enough data to ready the whole body. If
// not we have to wait until we have everything.
size_t total_size = header_size + body_size;
if (buffer_.size() < total_size)
break;
std::string command;
// Make sure we only copy as much bytes as we have to and not more
command.insert(0,
@ -64,8 +69,11 @@ void QemudMessageProcessor::process_commands() {
buffer_.erase(buffer_.begin(), buffer_.begin() + consumed);
const auto remaining = buffer_.size() - consumed;
if (remaining <= 0) break;
if (remaining <= 0)
break;
}
return true;
}
void QemudMessageProcessor::send_header(const size_t &size) {

View file

@ -40,7 +40,7 @@ class QemudMessageProcessor : public network::MessageProcessor {
std::shared_ptr<network::SocketMessenger> messenger_;
private:
void process_commands();
bool process_commands();
std::vector<std::uint8_t> buffer_;
};

View file

@ -30,9 +30,14 @@ namespace {
static std::string runtime_dir() {
static std::string path;
if (path.empty()) {
path = anbox::utils::get_env_value("XDG_RUNTIME_DIR", "");
if (path.empty())
BOOST_THROW_EXCEPTION(std::runtime_error("No runtime directory specified"));
const auto snap_user_common = anbox::utils::get_env_value("SNAP_USER_COMMON");
if (!snap_user_common.empty())
path = (fs::path(snap_user_common) / "runtime").string();
else {
path = anbox::utils::get_env_value("XDG_RUNTIME_DIR");
if (path.empty())
BOOST_THROW_EXCEPTION(std::runtime_error("No runtime directory specified"));
}
}
return path;
}
@ -59,7 +64,15 @@ std::string anbox::SystemConfiguration::container_config_dir() const {
}
std::string anbox::SystemConfiguration::container_socket_path() const {
return "/run/anbox-container.socket";
static std::string path;
if (path.empty()) {
const auto snap_common = anbox::utils::get_env_value("SNAP_COMMON");
if (!snap_common.empty())
path = (fs::path(snap_common) / "sockets" / "anbox-container.socket").string();
else
path = "/run/anbox-container.socket";
}
return path;
}
std::string anbox::SystemConfiguration::socket_dir() const {