Merge branch 'master' into basic-touch
This commit is contained in:
commit
f2030cc894
1022 changed files with 184437 additions and 18469 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
56
src/anbox/cmds/wait_ready.cpp
Normal file
56
src/anbox/cmds/wait_ready.cpp
Normal 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;
|
||||
});
|
||||
}
|
||||
39
src/anbox/cmds/wait_ready.h
Normal file
39
src/anbox/cmds/wait_ready.h
Normal 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
|
||||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
86
src/anbox/dbus/bus.cpp
Normal 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
60
src/anbox/dbus/bus.h
Normal 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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
121
src/anbox/dbus/sd_bus_helpers.h
Normal file
121
src/anbox/dbus/sd_bus_helpers.h
Normal 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
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
30
src/anbox/graphics/density.cpp
Normal file
30
src/anbox/graphics/density.cpp
Normal 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
|
||||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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_;
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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_;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue