diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index 6c04d72..1881bb6 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -1,15 +1,16 @@ Make sure you are running the latest version of Anbox before reporting an issue. -Please also check that no similar bug is already reported. Have a look on the -list of open bugs at https://github.com/anbox/anbox/issues +Please also check that no similar bug is already reported. Have a look on the list of open bugs at https://github.com/anbox/anbox/issues -**Anbox release (`anbox version`) and system info (`anbox system-info`):** +** Please paste the result of `anbox system-info` below:** +``` +[please paste printout of `anbox system-info` here] +``` + +**Please describe your problem:** -**Description of the problem:** - - -**Expected:** +**What were you expecting?:** **Additional info:** diff --git a/CMakeLists.txt b/CMakeLists.txt index 957664b..c6432a1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -64,9 +64,11 @@ find_package(GLESv2 REQUIRED) find_package(Protobuf REQUIRED) pkg_check_modules(SDL2 sdl2 REQUIRED) +pkg_check_modules(SDL2_IMAGE SDL2_image REQUIRED) pkg_check_modules(DBUS_CPP dbus-cpp REQUIRED) pkg_check_modules(DBUS dbus-1 REQUIRED) pkg_check_modules(LXC lxc REQUIRED) +pkg_check_modules(PROPERTIES_CPP properties-cpp REQUIRED) pkg_check_modules(MIRCLIENT mirclient) if (MIRCLIENT_FOUND) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DMIR_SUPPORT") @@ -94,6 +96,16 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -fPIC") set(ANBOX_TRANSLATOR_INSTALL_DIR ${CMAKE_INSTALL_LIBDIR}/anbox/translators) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DTRANSLATOR_INSTALL_DIR=\\\"${CMAKE_INSTALL_PREFIX}/${ANBOX_TRANSLATOR_INSTALL_DIR}\\\"") +if (NOT ANBOX_VERSION) + exec_program("git" ${CMAKE_SOURCE_DIR} ARGS "rev-parse --short HEAD" OUTPUT_VARIABLE GIT_COMMIT_HASH) + set(ANBOX_VERSION "local-${GIT_COMMIT_HASH}") +endif() +if (ANBOX_VERSION_SUFFIX) + set(ANBOX_VERSION "${ANBOX_VERSION}-${ANBOX_VERSION_SUFFIX}") +endif() +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/src/anbox/build/version.h.in + ${CMAKE_CURRENT_SOURCE_DIR}/src/anbox/build/version.h) + add_subdirectory(external) add_subdirectory(src) add_subdirectory(tests) @@ -103,3 +115,6 @@ if (NOT "${HOST_CMAKE_C_COMPILER}" STREQUAL "") message(STATUS "Host C compiler: ${HOST_CMAKE_C_COMPILER}") message(STATUS "Host C compiler: ${HOST_CMAKE_CXX_COMPILER}") endif() + +install(FILES data/ui/loading-screen.png DESTINATION share/anbox/ui) + diff --git a/README.md b/README.md index 4d28005..eeeb5d9 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,23 @@ +[![Snap Status](https://build.snapcraft.io/badge/anbox/anbox.svg)](https://build.snapcraft.io/user/anbox/anbox) +[![Build Status](https://travis-ci.org/anbox/anbox.svg?branch=master)](https://travis-ci.org/anbox/anbox) + # Anbox -Anbox is container based approach to boot a full Android system on a -regular GNU Linux system like Ubuntu. +Anbox is a container-based approach to boot a full Android system on a +regular GNU/Linux system like Ubuntu. In other words: Anbox will let +you run Android on your Linux system without the slowness of +virtualization. ## Overview Anbox uses Linux namespaces (user, pid, uts, net, mount, ipc) to run a full Android system in a container and provide Android applications on -any GNU Linux based platform. +any GNU/Linux-based platform. The Android inside the container has no direct access to any hardware. All hardware access is going through the anbox daemon on the host. We're -reusing what Android implemented within the QEMU based emulator for Open -GL ES accelerated rendering. The Android system inside the container uses +reusing what Android implemented within the QEMU-based emulator for OpenGL +ES accelerated rendering. The Android system inside the container uses different pipes to communicate with the host system and sends all hardware access commands through these. @@ -128,8 +133,8 @@ $ sudo apt install build-essential cmake cmake-data debhelper dbus google-mock \ libboost-program-options-dev libboost-system-dev libboost-test-dev \ libboost-thread-dev libcap-dev libdbus-1-dev libdbus-cpp-dev libegl1-mesa-dev \ libgles2-mesa-dev libglib2.0-dev libglm-dev libgtest-dev liblxc1 \ - libproperties-cpp-dev libprotobuf-dev libsdl2-dev lxc-dev pkg-config \ - protobuf-compiler + libproperties-cpp-dev libprotobuf-dev libsdl2-dev libsdl2-image-dev lxc-dev \ + pkg-config protobuf-compiler ``` Afterwards you can build Anbox with @@ -172,14 +177,11 @@ Running Anbox from a local build requires a few more things you need to know about. Please have a look at the ["Runtime Setup"](docs/runtime-setup.md) documentation. -An alias will save typing later. ``` -$ alias android='anbox launch --package=org.anbox.appmgr --component=org.anbox.appmgr.AppViewActivity' -$ alias android >> ~/.bashrc -$ android +$ anbox launch --package=org.anbox.appmgr --component=org.anbox.appmgr.AppViewActivity' ``` -## documentation +## Documentation You will find additional documentation for Anbox in the *docs* subdirectory of the project source. @@ -196,7 +198,7 @@ If you have found an issue with Anbox, please [file a bug](https://github.com/an ## Get in Touch If you want to get in contact with the developers please feel free to join the -*#anbox* IRC channel on [FreeNode](https://freenode.net/). +*#anbox* IRC channel on [Freenode](https://freenode.net/). ## Copyright and Licensing @@ -204,5 +206,5 @@ Anbox reuses code from other projects like the Android QEMU emulator. These projects are available in the external/ subdirectory with the licensing terms included. -The anbox source itself, if not stated differently in the relevant source files, +The Anbox source itself, if not stated differently in the relevant source files, is licensed under the terms of the GPLv3 license. diff --git a/cmake/FindGMock.cmake b/cmake/FindGMock.cmake new file mode 100644 index 0000000..2078030 --- /dev/null +++ b/cmake/FindGMock.cmake @@ -0,0 +1,92 @@ +# Copyright (C) 2014 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 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 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 . + +# Build with system gmock and embedded gtest +# +# Usage: +# +# find_package(GMock) +# +# ... +# +# target_link_libraries( +# my-target +# ${GTEST_BOTH_LIBRARIES} +# ) +# +# NOTE: Due to the way this package finder is implemented, do not attempt +# to find the GMock package more than once. + +find_package(Threads) + +if (EXISTS "/usr/src/googletest") + # As of version 1.8.0 + set(GMOCK_SOURCE_DIR "/usr/src/googletest/googlemock" CACHE PATH "gmock source directory") + set(GMOCK_INCLUDE_DIRS "${GMOCK_SOURCE_DIR}/include" CACHE PATH "gmock source include directory") + set(GTEST_INCLUDE_DIRS "/usr/src/googletest/googletest/include" CACHE PATH "gtest source include directory") +else() + set(GMOCK_SOURCE_DIR "/usr/src/gmock" CACHE PATH "gmock source directory") + set(GMOCK_INCLUDE_DIRS "/usr/include" CACHE PATH "gmock source include directory") + set(GTEST_INCLUDE_DIRS "/usr/include" CACHE PATH "gtest source include directory") +endif() + +# We add -g so we get debug info for the gtest stack frames with gdb. +# The warnings are suppressed so we get a noise-free build for gtest and gmock if the caller +# has these warnings enabled. +set(findgmock_cxx_flags "${CMAKE_CXX_FLAGS} -g -Wno-old-style-cast -Wno-missing-field-initializers -Wno-ctor-dtor-privacy -Wno-switch-default") + +set(findgmock_bin_dir "${CMAKE_CURRENT_BINARY_DIR}/gmock") +set(findgmock_gtest_lib "${findgmock_bin_dir}/gtest/libgtest.a") +set(findgmock_gtest_main_lib "${findgmock_bin_dir}/gtest/libgtest_main.a") +set(findgmock_gmock_lib "${findgmock_bin_dir}/libgmock.a") +set(findgmock_gmock_main_lib "${findgmock_bin_dir}/libgmock_main.a") + +include(ExternalProject) +ExternalProject_Add(GMock SOURCE_DIR "${GMOCK_SOURCE_DIR}" + BINARY_DIR "${findgmock_bin_dir}" + BUILD_BYPRODUCTS "${findgmock_gtest_lib}" + "${findgmock_gtest_main_lib}" + "${findgmock_gmock_lib}" + "${findgmock_gmock_main_lib}" + INSTALL_COMMAND "" + CMAKE_ARGS "-DCMAKE_CXX_FLAGS=${findgmock_cxx_flags}") + +add_library(gtest INTERFACE) +target_include_directories(gtest INTERFACE ${GTEST_INCLUDE_DIRS}) +target_link_libraries(gtest INTERFACE ${findgmock_gtest_lib} ${CMAKE_THREAD_LIBS_INIT}) +add_dependencies(gtest GMock) + +add_library(gtest_main INTERFACE) +target_include_directories(gtest_main INTERFACE ${GTEST_INCLUDE_DIRS}) +target_link_libraries(gtest_main INTERFACE ${findgmock_gtest_main_lib} gtest) + +add_library(gmock INTERFACE) +target_include_directories(gmock INTERFACE ${GMOCK_INCLUDE_DIRS}) +target_link_libraries(gmock INTERFACE ${findgmock_gmock_lib} gtest) + +add_library(gmock_main INTERFACE) +target_include_directories(gmock_main INTERFACE ${GMOCK_INCLUDE_DIRS}) +target_link_libraries(gmock_main INTERFACE ${findgmock_gmock_main_lib} gmock) + +set(GTEST_LIBRARIES gtest) +set(GTEST_MAIN_LIBRARIES gtest_main) +set(GMOCK_LIBRARIES gmock gmock_main) +set(GTEST_BOTH_LIBRARIES ${GTEST_LIBRARIES} ${GTEST_MAIN_LIBRARIES}) + +unset(findgmock_cxx_flags) +unset(findgmock_bin_dir) +unset(findgmock_gtest_lib) +unset(findgmock_gtest_main_lib) +unset(findgmock_gmock_lib) +unset(findgmock_gmock_main_lib) diff --git a/data/anbox.service.in b/data/anbox.service.in index 933b4d1..7cdd14f 100644 --- a/data/anbox.service.in +++ b/data/anbox.service.in @@ -2,7 +2,11 @@ Description=Anbox session manager [Service] +Type=simple ExecStart=@SNAP_MOUNT_DIR@/anbox session-manager +Restart=always +StartLimitIntervalSec=3 +StartLimitBurst=1 [Install] WantedBy=default.target diff --git a/data/ui/loading-screen.png b/data/ui/loading-screen.png new file mode 100644 index 0000000..2678361 Binary files /dev/null and b/data/ui/loading-screen.png differ diff --git a/debian/anbox-common.install b/debian/anbox-common.install index 4700a8e..bf67a97 100644 --- a/debian/anbox-common.install +++ b/debian/anbox-common.install @@ -1,3 +1,4 @@ etc/X11/Xsession.d/68anbox usr/share/upstart/sessions/anbox.conf usr/lib/systemd/user/anbox.service +usr/lib/systemd/user/default.target.wants/anbox.service diff --git a/debian/changelog b/debian/changelog index eccc558..a7152a9 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,10 @@ +anbox (6) artful; urgency=medium + + * Add anbox-common package which ships any additional files needed for + anbox but can't be installed with a snap as of today. + + -- Simon Fels Sat, 29 Apr 2017 12:06:56 +0200 + anbox (5) zesty; urgency=medium * Rework packaging to also ship things we installed through the snap diff --git a/debian/control b/debian/control index 8696f8e..9dfb528 100644 --- a/debian/control +++ b/debian/control @@ -10,7 +10,7 @@ Vcs-Git: https://github.com/anbox/anbox.git Package: anbox-common Architecture: all -Depends: ${misc:Depends} +Depends: ${misc:Depends}, dbus-user-session Description: Common files necessary for Anbox . This package contains necessary things which can't be shipped diff --git a/debian/rules b/debian/rules index b141034..85b38d1 100755 --- a/debian/rules +++ b/debian/rules @@ -28,8 +28,9 @@ override_dh_install: install -d $(CURDIR)/debian/tmp/etc/modules-load.d install -m 0644 kernel/anbox.conf $(CURDIR)/debian/tmp/etc/modules-load.d - install -d $(CURDIR)/debian/tmp/usr/lib/systemd/user/ + install -d $(CURDIR)/debian/tmp/usr/lib/systemd/user/default.target.wants install -m 0644 data/anbox.service $(CURDIR)/debian/tmp/usr/lib/systemd/user/ + ln -sf /usr/lib/systemd/user/anbox.service $(CURDIR)/debian/tmp/usr/lib/systemd/user/default.target.wants/anbox.service install -d $(CURDIR)/debian/tmp/usr/share/upstart/sessions/ install -m 0644 data/anbox.conf $(CURDIR)/debian/tmp/usr/share/upstart/sessions/ diff --git a/docs/build-android.md b/docs/build-android.md index 7f4b76e..cfd0a56 100644 --- a/docs/build-android.md +++ b/docs/build-android.md @@ -33,8 +33,15 @@ This will take quite some time depending on the speed of your internet connectio ## Build Android -When all sources are successfully downloaded you can start building Android -itself. Initialize the build by +When all sources are successfully downloaded you can start building Android itself. + +Firstly initialize the environment with the ```envsetup.sh``` script. + +``` +$ . build/envsetup.sh +``` + +Then initialize the build using ```lunch```. ``` $ lunch anbox_desktop_x86_64-userdebug @@ -43,7 +50,7 @@ $ lunch anbox_desktop_x86_64-userdebug The complete list of supported build targets: * anbox_desktop_x86_64-userdebug - * anbox_desktop_armv7a_arm-userdebug + * anbox_desktop_armv7a_neon-userdebug * anbox_desktop_arm64-userdebug Now build everything with diff --git a/external/android-emugl/host/libs/GLESv1_dec/CMakeLists.txt b/external/android-emugl/host/libs/GLESv1_dec/CMakeLists.txt index e8d83e2..c0b79af 100644 --- a/external/android-emugl/host/libs/GLESv1_dec/CMakeLists.txt +++ b/external/android-emugl/host/libs/GLESv1_dec/CMakeLists.txt @@ -20,5 +20,5 @@ if ("${cmake_build_type_lower}" STREQUAL "trace") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OPENGL_DEBUG}") endif() -add_library(GLESv1_dec ${SOURCES} ${GENERATED_SOURCES}) +add_library(GLESv1_dec STATIC ${SOURCES} ${GENERATED_SOURCES}) target_link_libraries(GLESv1_dec OpenglCodecCommon) diff --git a/external/android-emugl/host/libs/GLESv2_dec/CMakeLists.txt b/external/android-emugl/host/libs/GLESv2_dec/CMakeLists.txt index 140d061..6bd78c5 100644 --- a/external/android-emugl/host/libs/GLESv2_dec/CMakeLists.txt +++ b/external/android-emugl/host/libs/GLESv2_dec/CMakeLists.txt @@ -20,5 +20,5 @@ if ("${cmake_build_type_lower}" STREQUAL "trace") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OPENGL_DEBUG}") endif() -add_library(GLESv2_dec ${SOURCES} ${GENERATED_SOURCES}) +add_library(GLESv2_dec STATIC ${SOURCES} ${GENERATED_SOURCES}) target_link_libraries(GLESv2_dec OpenglCodecCommon) diff --git a/external/android-emugl/host/libs/Translator/GLcommon/CMakeLists.txt b/external/android-emugl/host/libs/Translator/GLcommon/CMakeLists.txt index 79339f6..9f553f9 100644 --- a/external/android-emugl/host/libs/Translator/GLcommon/CMakeLists.txt +++ b/external/android-emugl/host/libs/Translator/GLcommon/CMakeLists.txt @@ -12,6 +12,6 @@ set(SOURCES objectNameManager.cpp FramebufferData.cpp) -add_library(GLcommon ${SOURCES}) +add_library(GLcommon STATIC ${SOURCES}) target_link_libraries(GLcommon emugl_common) diff --git a/external/android-emugl/host/libs/libOpenGLESDispatch/CMakeLists.txt b/external/android-emugl/host/libs/libOpenGLESDispatch/CMakeLists.txt index 715fa8b..c4bd232 100644 --- a/external/android-emugl/host/libs/libOpenGLESDispatch/CMakeLists.txt +++ b/external/android-emugl/host/libs/libOpenGLESDispatch/CMakeLists.txt @@ -3,7 +3,7 @@ set(SOURCES GLESv2Dispatch.cpp GLESv1Dispatch.cpp) -add_library(OpenGLESDispatch ${SOURCES} ${GENERATED_SOURCES}) +add_library(OpenGLESDispatch STATIC ${SOURCES} ${GENERATED_SOURCES}) target_link_libraries(OpenGLESDispatch emugl_common GLESv2_dec diff --git a/external/android-emugl/host/libs/renderControl_dec/CMakeLists.txt b/external/android-emugl/host/libs/renderControl_dec/CMakeLists.txt index cb67c5a..4b0786f 100644 --- a/external/android-emugl/host/libs/renderControl_dec/CMakeLists.txt +++ b/external/android-emugl/host/libs/renderControl_dec/CMakeLists.txt @@ -16,5 +16,5 @@ if ("${cmake_build_type_lower}" STREQUAL "trace") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OPENGL_DEBUG}") endif() -add_library(renderControl_dec ${GENERATED_SOURCES}) +add_library(renderControl_dec STATIC ${GENERATED_SOURCES}) target_link_libraries(renderControl_dec OpenglCodecCommon) diff --git a/external/android-emugl/shared/OpenglCodecCommon/CMakeLists.txt b/external/android-emugl/shared/OpenglCodecCommon/CMakeLists.txt index 67606d7..fe3db64 100644 --- a/external/android-emugl/shared/OpenglCodecCommon/CMakeLists.txt +++ b/external/android-emugl/shared/OpenglCodecCommon/CMakeLists.txt @@ -12,4 +12,4 @@ set(SOURCES Makefile ProtocolUtils.h) -add_library(OpenglCodecCommon ${SOURCES}) +add_library(OpenglCodecCommon STATIC ${SOURCES}) diff --git a/external/android-emugl/shared/emugl/common/CMakeLists.txt b/external/android-emugl/shared/emugl/common/CMakeLists.txt index ef8b6d2..09388c6 100644 --- a/external/android-emugl/shared/emugl/common/CMakeLists.txt +++ b/external/android-emugl/shared/emugl/common/CMakeLists.txt @@ -28,6 +28,6 @@ set(COMMON_SOURCES thread_unittest.cpp unique_integer_map.h) -add_library(emugl_common ${COMMON_SOURCES}) +add_library(emugl_common STATIC ${COMMON_SOURCES}) target_link_libraries(emugl_common dl pthread) diff --git a/external/process-cpp-minimal/include/core/posix/child_process.h b/external/process-cpp-minimal/include/core/posix/child_process.h index d6b3b31..fde7977 100644 --- a/external/process-cpp-minimal/include/core/posix/child_process.h +++ b/external/process-cpp-minimal/include/core/posix/child_process.h @@ -114,6 +114,12 @@ public: */ wait::Result wait_for(const wait::Flags& flags); + /** + * @brief Mark the child process to not to be killed when the ChildProcess + * instance goes away. + */ + void dont_kill_on_cleanup(); + #ifndef ANDROID /** * @brief Access this process's stderr. diff --git a/external/process-cpp-minimal/src/CMakeLists.txt b/external/process-cpp-minimal/src/CMakeLists.txt index 8daf938..dec4659 100644 --- a/external/process-cpp-minimal/src/CMakeLists.txt +++ b/external/process-cpp-minimal/src/CMakeLists.txt @@ -15,6 +15,8 @@ # Authored by: Thomas Voss add_library( process-cpp + + STATIC core/posix/backtrace.h core/posix/backtrace.cpp diff --git a/external/process-cpp-minimal/src/core/posix/child_process.cpp b/external/process-cpp-minimal/src/core/posix/child_process.cpp index b121b89..ad16571 100644 --- a/external/process-cpp-minimal/src/core/posix/child_process.cpp +++ b/external/process-cpp-minimal/src/core/posix/child_process.cpp @@ -303,7 +303,7 @@ struct ChildProcess::Private ~Private() { // Check if we are in the original parent process. - if (original_parent_pid == getpid()) + if (original_parent_pid == getpid() && !dont_kill_on_cleanup) { // If so, check if we are considering a valid pid here. // If so, we kill the original child. @@ -333,6 +333,8 @@ struct ChildProcess::Private // is called from the child process. pid_t original_parent_pid; pid_t original_child_pid; + + bool dont_kill_on_cleanup = false; }; ChildProcess ChildProcess::invalid() @@ -395,6 +397,11 @@ wait::Result ChildProcess::wait_for(const wait::Flags& flags) return result; } +void ChildProcess::dont_kill_on_cleanup() +{ + d->dont_kill_on_cleanup = true; +} + #ifndef ANDROID std::istream& ChildProcess::cerr() { diff --git a/external/xdg/CMakeLists.txt b/external/xdg/CMakeLists.txt index f19f69d..249da48 100644 --- a/external/xdg/CMakeLists.txt +++ b/external/xdg/CMakeLists.txt @@ -11,7 +11,7 @@ include_directories( ${Boost_INCLUDE_DIRS} ) -add_library(xdg xdg.cpp) +add_library(xdg STATIC xdg.cpp) set_property(TARGET xdg PROPERTY CXX_STANDARD 11) target_link_libraries(xdg ${Boost_LIBRARIES}) diff --git a/external/xdg/xdg.cpp b/external/xdg/xdg.cpp index de9b043..d54fdfe 100644 --- a/external/xdg/xdg.cpp +++ b/external/xdg/xdg.cpp @@ -57,7 +57,6 @@ constexpr const char* xdg_data_dirs{"XDG_DATA_DIRS"}; constexpr const char* xdg_config_home{"XDG_CONFIG_HOME"}; constexpr const char* xdg_config_dirs{"XDG_CONFIG_DIRS"}; constexpr const char* xdg_cache_home{"XDG_CACHE_HOME"}; -constexpr const char* xdg_runtime_dir{"XDG_RUNTIME_DIR"}; } namespace impl diff --git a/kernel/ashmem/dkms.conf b/kernel/ashmem/dkms.conf index bde1557..715fa57 100644 --- a/kernel/ashmem/dkms.conf +++ b/kernel/ashmem/dkms.conf @@ -1,7 +1,7 @@ PACKAGE_NAME="anbox-ashmem" PACKAGE_VERSION="1" CLEAN="make clean" -MAKE[0]="make all KVERSION=$kernelver" +MAKE[0]="make all KERNEL_SRC=/lib/modules/$kernelver/build" BUILT_MODULE_NAME[0]="ashmem_linux" DEST_MODULE_LOCATION[0]="/updates" AUTOINSTALL="yes" diff --git a/kernel/binder/dkms.conf b/kernel/binder/dkms.conf index 8484321..1b5fd7a 100644 --- a/kernel/binder/dkms.conf +++ b/kernel/binder/dkms.conf @@ -1,7 +1,7 @@ PACKAGE_NAME="anbox-binder" PACKAGE_VERSION="1" CLEAN="make clean" -MAKE[0]="make all KVERSION=$kernelver" +MAKE[0]="make all KERNEL_SRC=/lib/modules/$kernelver/build" BUILT_MODULE_NAME[0]="binder_linux" DEST_MODULE_LOCATION[0]="/updates" AUTOINSTALL="yes" diff --git a/kernel/patches/0001-ipc-namespace-a-generic-per-ipc-pointer-and-peripc_o.patch b/kernel/patches/0001-ipc-namespace-a-generic-per-ipc-pointer-and-peripc_o.patch new file mode 100644 index 0000000..0acd5e9 --- /dev/null +++ b/kernel/patches/0001-ipc-namespace-a-generic-per-ipc-pointer-and-peripc_o.patch @@ -0,0 +1,185 @@ +From 8f8ac2552c4a411cf1b8c6328409f861248e8d0d Mon Sep 17 00:00:00 2001 +From: Oren Laadan +Date: Sun, 22 Dec 2013 10:07:39 +0000 +Subject: [PATCH 1/2] ipc namespace: a generic per-ipc pointer and peripc_ops + +Add a void * pointer to struct ipc_namespace. The access rules are: +1. (un)register ops with (un)register_peripc_operations() +2. call ipc_assign_generic() to put private data on the ipc_namespace +3. call ipc_access_generic() to access the private data +4. do not change the pointer during the lifetime of ipc_namespace + +Modeled after generic net-ns pointers (commit dec827d), but simplified +to accommodate a single user for now (reduce code churn): +5. only one caller can register at a time +6. caller must register at boot time (not to be used by modules) + +Signed-off-by: Oren Laadan +Signed-off-by: Amir Goldstein +--- + include/linux/ipc_namespace.h | 29 +++++++++++++++++++++ + ipc/namespace.c | 9 +++++++ + ipc/util.c | 60 +++++++++++++++++++++++++++++++++++++++++++ + ipc/util.h | 3 +++ + 4 files changed, 101 insertions(+) + +diff --git a/include/linux/ipc_namespace.h b/include/linux/ipc_namespace.h +index d6ad91f..535061a 100644 +--- a/include/linux/ipc_namespace.h ++++ b/include/linux/ipc_namespace.h +@@ -70,8 +70,37 @@ struct ipc_namespace { + struct user_namespace *user_ns; + + unsigned int proc_inum; ++ ++ /* allow others to piggyback on ipc_namesspaces */ ++ void *gen; /* for others' private stuff */ + }; + ++/* ++ * To access to the per-ipc generic data: ++ * 1. (un)register ops with (un)register_peripc_operations() ++ * 2. call ipc_assign_generic() to put private data on the ipc_namespace ++ * 3. call ipc_access_generic() to access the private data ++ * 4. do not change the pointer during the lifetime of ipc_namespace ++ * ++ * Modeled after generic net-ns pointers (commit dec827d), simplified for ++ * a single user case for now: ++ * 5. only one caller can register at a time ++ * 6. caller must register at boot time (not to be used by modules) ++ */ ++struct peripc_operations { ++ int (*init)(struct ipc_namespace *); ++ void (*exit)(struct ipc_namespace *); ++}; ++ ++static inline void ipc_assign_generic(struct ipc_namespace *ns, void *data) ++{ ns->gen = data; } ++ ++static inline void *ipc_access_generic(struct ipc_namespace *ns) ++{ return ns->gen; } ++ ++extern int register_peripc_ops(struct peripc_operations *ops); ++extern void unregister_peripc_ops(struct peripc_operations *ops); ++ + extern struct ipc_namespace init_ipc_ns; + extern atomic_t nr_ipc_ns; + +diff --git a/ipc/namespace.c b/ipc/namespace.c +index aba9a58..575aeae 100644 +--- a/ipc/namespace.c ++++ b/ipc/namespace.c +@@ -33,9 +33,17 @@ static struct ipc_namespace *create_ipc_ns(struct user_namespace *user_ns, + } + + atomic_set(&ns->count, 1); ++ ++ err = init_peripc_ns(ns); ++ if (err) { ++ kfree(ns); ++ return ERR_PTR(err); ++ } ++ + err = mq_init_ns(ns); + if (err) { + proc_free_inum(ns->proc_inum); ++ exit_peripc_ns(ns); + kfree(ns); + return ERR_PTR(err); + } +@@ -111,6 +119,7 @@ static void free_ipc_ns(struct ipc_namespace *ns) + sem_exit_ns(ns); + msg_exit_ns(ns); + shm_exit_ns(ns); ++ exit_peripc_ns(ns); + atomic_dec(&nr_ipc_ns); + + /* +diff --git a/ipc/util.c b/ipc/util.c +index 7353425..533f8f9 100644 +--- a/ipc/util.c ++++ b/ipc/util.c +@@ -71,6 +71,66 @@ struct ipc_proc_iface { + int (*show)(struct seq_file *, void *); + }; + ++/* allow others to piggyback on ipc_namespace */ ++static DEFINE_MUTEX(peripc_mutex); ++static struct peripc_operations *peripc_ops; ++ ++/* ++ * peripc_operations is a simplified pernet_operations: ++ * - allow only one entity to register ++ * - allow to register only at boot time (no modules) ++ * (these assumptions make the code much simpler) ++ */ ++ ++static int init_peripc_count; ++ ++/* caller hold peripc_mutex */ ++int init_peripc_ns(struct ipc_namespace *ns) ++{ ++ int ret = 0; ++ ++ if (peripc_ops && peripc_ops->init) ++ ret = peripc_ops->init(ns); ++ if (ret == 0) ++ init_peripc_count++; ++ return ret; ++} ++ ++/* caller hold peripc_mutex */ ++void exit_peripc_ns(struct ipc_namespace *ns) ++{ ++ if (peripc_ops && peripc_ops->exit) ++ peripc_ops->exit(ns); ++ init_peripc_count--; ++} ++ ++int register_peripc_ops(struct peripc_operations *ops) ++{ ++ int ret = -EBUSY; ++ ++ mutex_lock(&peripc_mutex); ++ /* must be first register, and only init ipc_namespace exists */ ++ if (peripc_ops == NULL && init_peripc_count == 0) { ++ peripc_ops = ops; ++ ret = init_peripc_ns(&init_ipc_ns); ++ if (ret < 0) ++ peripc_ops = NULL; ++ } ++ mutex_unlock(&peripc_mutex); ++ return ret; ++} ++ ++void unregister_peripc_ops(struct peripc_operations *ops) ++{ ++ mutex_lock(&peripc_mutex); ++ /* sanity: be same as registered, and no other ipc ns (beyond init) */ ++ BUG_ON(peripc_ops != ops); ++ BUG_ON(init_peripc_count != 1); ++ if (ops->exit) ++ exit_peripc_ns(&init_ipc_ns); ++ peripc_ops = NULL; ++ mutex_unlock(&peripc_mutex); ++} + static void ipc_memory_notifier(struct work_struct *work) + { + ipcns_notify(IPCNS_MEMCHANGED); +diff --git a/ipc/util.h b/ipc/util.h +index 59d78aa..daee0be 100644 +--- a/ipc/util.h ++++ b/ipc/util.h +@@ -47,6 +47,9 @@ static inline void msg_exit_ns(struct ipc_namespace *ns) { } + static inline void shm_exit_ns(struct ipc_namespace *ns) { } + #endif + ++int init_peripc_ns(struct ipc_namespace *ns); ++void exit_peripc_ns(struct ipc_namespace *ns); ++ + struct ipc_rcu { + struct rcu_head rcu; + atomic_t refcount; +-- +2.7.4 + diff --git a/kernel/patches/0002-binder-implement-namepsace-support-for-Android-binde.patch b/kernel/patches/0002-binder-implement-namepsace-support-for-Android-binde.patch new file mode 100644 index 0000000..74595a0 --- /dev/null +++ b/kernel/patches/0002-binder-implement-namepsace-support-for-Android-binde.patch @@ -0,0 +1,428 @@ +From fcd9d70190dd7e6536878a6379122f06e3b90919 Mon Sep 17 00:00:00 2001 +From: Oren Laadan +Date: Sun, 22 Dec 2013 10:07:40 +0000 +Subject: [PATCH 2/2] binder: implement namepsace support for Android binder + driver + +Add namespaces support for the Android binder driver. +As binder is an IPC mechanism, tie its namespace to IPC_NS. + +In binder, the first process to call BINDER_SET_CONTEXT_MGR ioctl +becomes the manager with context 0, and thereafter IPC is realized +through binder handles obtained from this manager. + +For namespaces, associate a separate binder state with each namespace +so each namespace has its own context manager. Binder users within a +namespace interact only with the context manager there. This suffices +because binder does not allow IPC not via the context manager. + +Currently, statistics remain global, except for the list of processes +that hold an open binder device (reported per namespace). + +Signed-off-by: Oren Laadan +Acked-by: Amir Goldstein +--- + drivers/android/Kconfig | 1 + + drivers/android/binder.c | 172 ++++++++++++++++++++++++++++++++++++++--------- + 2 files changed, 143 insertions(+), 30 deletions(-) + +diff --git a/drivers/android/Kconfig b/drivers/android/Kconfig +index bdfc6c6..739063d 100644 +--- a/drivers/android/Kconfig ++++ b/drivers/android/Kconfig +@@ -10,6 +10,7 @@ if ANDROID + config ANDROID_BINDER_IPC + bool "Android Binder IPC Driver" + depends on MMU ++ select SYSVIPC + default n + ---help--- + Binder is used in Android for both communication between processes, +diff --git a/drivers/android/binder.c b/drivers/android/binder.c +index fee479d..2a63e9b 100644 +--- a/drivers/android/binder.c ++++ b/drivers/android/binder.c +@@ -37,6 +37,7 @@ + #include + #include + #include ++#include + #include + #include + +@@ -47,19 +48,98 @@ + #include + #include "binder_trace.h" + ++/* ++ * Using a private context manager for each binder namespace is sufficient ++ * to isolate between namespaces, because in binder all IPC must be realized ++ * via hanldes obtained from the context manager. ++ * ++ * TODO: currently, most debugfs data is not tracked per binder namespaces. ++ * Except for "procs" which are properly virtualized, everything else is ++ * global, including stats, logs, and dead nodes. ++ */ ++struct binder_namespace { ++ struct kref kref; ++ ++ struct binder_node *context_mgr_node; ++ kuid_t context_mgr_uid; ++ int last_id; ++ ++ struct hlist_head procs; ++}; ++ ++static struct binder_namespace *create_binder_ns(void) ++{ ++ struct binder_namespace *binder_ns; ++ ++ binder_ns = kzalloc(sizeof(struct binder_namespace), GFP_KERNEL); ++ if (binder_ns) { ++ kref_init(&binder_ns->kref); ++ binder_ns->context_mgr_uid = INVALID_UID; ++ INIT_HLIST_HEAD(&binder_ns->procs); ++ } ++ return binder_ns; ++} ++ ++static void free_binder_ns(struct kref *kref) ++{ ++ kfree(container_of(kref, struct binder_namespace, kref)); ++} ++ ++static void get_binder_ns(struct binder_namespace *binder_ns) ++{ ++ kref_get(&binder_ns->kref); ++} ++ ++static void put_binder_ns(struct binder_namespace *binder_ns) ++{ ++ kref_put(&binder_ns->kref, free_binder_ns); ++} ++ ++/* ++ * Binder is an IPC mechanism, so tie its namespace to IPC_NS ++ * using the generic data pointer and per-ipc operations. ++ */ ++static struct binder_namespace *current_binder_ns(void) ++{ ++ return ipc_access_generic(current->nsproxy->ipc_ns); ++} ++ ++int binder_init_ns(struct ipc_namespace *ipcns) ++{ ++ struct binder_namespace *binder_ns; ++ int ret = -ENOMEM; ++ ++ binder_ns = create_binder_ns(); ++ if (binder_ns) { ++ ipc_assign_generic(ipcns, binder_ns); ++ ret = 0; ++ } ++ return ret; ++} ++ ++void binder_exit_ns(struct ipc_namespace *ipcns) ++{ ++ struct binder_namespace *binder_ns; ++ ++ binder_ns = ipc_access_generic(ipcns); ++ if (binder_ns) ++ put_binder_ns(binder_ns); ++} ++ ++struct peripc_operations binder_peripc_ops = { ++ .init = binder_init_ns, ++ .exit = binder_exit_ns, ++}; ++ + static DEFINE_RT_MUTEX(binder_main_lock); + static DEFINE_MUTEX(binder_deferred_lock); + static DEFINE_MUTEX(binder_mmap_lock); + +-static HLIST_HEAD(binder_procs); + static HLIST_HEAD(binder_deferred_list); + static HLIST_HEAD(binder_dead_nodes); + + static struct dentry *binder_debugfs_dir_entry_root; + static struct dentry *binder_debugfs_dir_entry_proc; +-static struct binder_node *binder_context_mgr_node; +-static kuid_t binder_context_mgr_uid = INVALID_UID; +-static int binder_last_id; + static struct workqueue_struct *binder_deferred_workqueue; + + #define BINDER_DEBUG_ENTRY(name) \ +@@ -327,6 +407,8 @@ struct binder_proc { + int ready_threads; + long default_priority; + struct dentry *debugfs_entry; ++ ++ struct binder_namespace *binder_ns; + }; + + enum { +@@ -910,7 +992,7 @@ static struct binder_node *binder_new_node(struct binder_proc *proc, + binder_stats_created(BINDER_STAT_NODE); + rb_link_node(&node->rb_node, parent, p); + rb_insert_color(&node->rb_node, &proc->nodes); +- node->debug_id = ++binder_last_id; ++ node->debug_id = ++proc->binder_ns->last_id; + node->proc = proc; + node->ptr = ptr; + node->cookie = cookie; +@@ -931,7 +1013,7 @@ static int binder_inc_node(struct binder_node *node, int strong, int internal, + if (internal) { + if (target_list == NULL && + node->internal_strong_refs == 0 && +- !(node == binder_context_mgr_node && ++ !(node == node->proc->binder_ns->context_mgr_node && + node->has_strong_ref)) { + pr_err("invalid inc strong node for %d\n", + node->debug_id); +@@ -1045,13 +1127,13 @@ static struct binder_ref *binder_get_ref_for_node(struct binder_proc *proc, + if (new_ref == NULL) + return NULL; + binder_stats_created(BINDER_STAT_REF); +- new_ref->debug_id = ++binder_last_id; ++ new_ref->debug_id = ++proc->binder_ns->last_id; + new_ref->proc = proc; + new_ref->node = node; + rb_link_node(&new_ref->rb_node_node, parent, p); + rb_insert_color(&new_ref->rb_node_node, &proc->refs_by_node); + +- new_ref->desc = (node == binder_context_mgr_node) ? 0 : 1; ++ new_ref->desc = (node == proc->binder_ns->context_mgr_node) ? 0 : 1; + for (n = rb_first(&proc->refs_by_desc); n != NULL; n = rb_next(n)) { + ref = rb_entry(n, struct binder_ref, rb_node_desc); + if (ref->desc > new_ref->desc) +@@ -1391,7 +1473,7 @@ static void binder_transaction(struct binder_proc *proc, + } + target_node = ref->node; + } else { +- target_node = binder_context_mgr_node; ++ target_node = proc->binder_ns->context_mgr_node; + if (target_node == NULL) { + return_error = BR_DEAD_REPLY; + goto err_no_context_mgr_node; +@@ -1452,7 +1534,7 @@ static void binder_transaction(struct binder_proc *proc, + } + binder_stats_created(BINDER_STAT_TRANSACTION_COMPLETE); + +- t->debug_id = ++binder_last_id; ++ t->debug_id = ++proc->binder_ns->last_id; + e->debug_id = t->debug_id; + + if (reply) +@@ -1787,10 +1869,10 @@ static int binder_thread_write(struct binder_proc *proc, + if (get_user(target, (uint32_t __user *)ptr)) + return -EFAULT; + ptr += sizeof(uint32_t); +- if (target == 0 && binder_context_mgr_node && ++ if (target == 0 && proc->binder_ns->context_mgr_node && + (cmd == BC_INCREFS || cmd == BC_ACQUIRE)) { + ref = binder_get_ref_for_node(proc, +- binder_context_mgr_node); ++ proc->binder_ns->context_mgr_node); + if (ref->desc != target) { + binder_user_error("%d:%d tried to acquire reference to desc 0, got %d instead\n", + proc->pid, thread->pid, +@@ -2696,9 +2778,10 @@ static int binder_ioctl_set_ctx_mgr(struct file *filp) + { + int ret = 0; + struct binder_proc *proc = filp->private_data; ++ struct binder_namespace *binder_ns = proc->binder_ns; + kuid_t curr_euid = current_euid(); + +- if (binder_context_mgr_node != NULL) { ++ if (binder_ns->context_mgr_node != NULL) { + pr_err("BINDER_SET_CONTEXT_MGR already set\n"); + ret = -EBUSY; + goto out; +@@ -2706,27 +2789,27 @@ static int binder_ioctl_set_ctx_mgr(struct file *filp) + ret = security_binder_set_context_mgr(proc->tsk); + if (ret < 0) + goto out; +- if (uid_valid(binder_context_mgr_uid)) { +- if (!uid_eq(binder_context_mgr_uid, curr_euid)) { ++ if (uid_valid(binder_ns->context_mgr_uid)) { ++ if (!uid_eq(binder_ns->context_mgr_uid, curr_euid)) { + pr_err("BINDER_SET_CONTEXT_MGR bad uid %d != %d\n", + from_kuid(&init_user_ns, curr_euid), + from_kuid(&init_user_ns, +- binder_context_mgr_uid)); ++ binder_ns->context_mgr_uid)); + ret = -EPERM; + goto out; + } + } else { +- binder_context_mgr_uid = curr_euid; ++ binder_ns->context_mgr_uid = curr_euid; + } +- binder_context_mgr_node = binder_new_node(proc, 0, 0); +- if (binder_context_mgr_node == NULL) { ++ binder_ns->context_mgr_node = binder_new_node(proc, 0, 0); ++ if (binder_ns->context_mgr_node == NULL) { + ret = -ENOMEM; + goto out; + } +- binder_context_mgr_node->local_weak_refs++; +- binder_context_mgr_node->local_strong_refs++; +- binder_context_mgr_node->has_strong_ref = 1; +- binder_context_mgr_node->has_weak_ref = 1; ++ binder_ns->context_mgr_node->local_weak_refs++; ++ binder_ns->context_mgr_node->local_strong_refs++; ++ binder_ns->context_mgr_node->has_strong_ref = 1; ++ binder_ns->context_mgr_node->has_weak_ref = 1; + out: + return ret; + } +@@ -2947,10 +3030,15 @@ err_bad_arg: + static int binder_open(struct inode *nodp, struct file *filp) + { + struct binder_proc *proc; ++ struct binder_namespace *binder_ns; + + binder_debug(BINDER_DEBUG_OPEN_CLOSE, "binder_open: %d:%d\n", + current->group_leader->pid, current->pid); + ++ binder_ns = current_binder_ns(); ++ if (binder_ns == NULL) ++ return -ENOMEM; ++ + proc = kzalloc(sizeof(*proc), GFP_KERNEL); + if (proc == NULL) + return -ENOMEM; +@@ -2960,10 +3048,13 @@ static int binder_open(struct inode *nodp, struct file *filp) + init_waitqueue_head(&proc->wait); + proc->default_priority = task_nice(current); + ++ proc->binder_ns = binder_ns; ++ get_binder_ns(binder_ns); ++ + binder_lock(__func__); + + binder_stats_created(BINDER_STAT_PROC); +- hlist_add_head(&proc->proc_node, &binder_procs); ++ hlist_add_head(&proc->proc_node, &binder_ns->procs); + proc->pid = current->group_leader->pid; + INIT_LIST_HEAD(&proc->delivered_death); + filp->private_data = proc; +@@ -3067,6 +3158,7 @@ static int binder_node_release(struct binder_node *node, int refs) + + static void binder_deferred_release(struct binder_proc *proc) + { ++ struct binder_namespace *binder_ns = proc->binder_ns; + struct binder_transaction *t; + struct rb_node *n; + int threads, nodes, incoming_refs, outgoing_refs, buffers, +@@ -3077,11 +3169,12 @@ static void binder_deferred_release(struct binder_proc *proc) + + hlist_del(&proc->proc_node); + +- if (binder_context_mgr_node && binder_context_mgr_node->proc == proc) { ++ if (binder_ns->context_mgr_node && ++ binder_ns->context_mgr_node->proc == proc) { + binder_debug(BINDER_DEBUG_DEAD_BINDER, + "%s: %d context_mgr_node gone\n", + __func__, proc->pid); +- binder_context_mgr_node = NULL; ++ binder_ns->context_mgr_node = NULL; + } + + threads = 0; +@@ -3160,6 +3253,7 @@ static void binder_deferred_release(struct binder_proc *proc) + vfree(proc->buffer); + } + ++ put_binder_ns(proc->binder_ns); + put_task_struct(proc->tsk); + + binder_debug(BINDER_DEBUG_OPEN_CLOSE, +@@ -3540,10 +3634,14 @@ static void print_binder_proc_stats(struct seq_file *m, + + static int binder_state_show(struct seq_file *m, void *unused) + { ++ struct binder_namespace *binder_ns = current_binder_ns(); + struct binder_proc *proc; + struct binder_node *node; + int do_lock = !binder_debug_no_lock; + ++ if (binder_ns == NULL) ++ return 0; ++ + if (do_lock) + binder_lock(__func__); + +@@ -3554,7 +3652,7 @@ static int binder_state_show(struct seq_file *m, void *unused) + hlist_for_each_entry(node, &binder_dead_nodes, dead_node) + print_binder_node(m, node); + +- hlist_for_each_entry(proc, &binder_procs, proc_node) ++ hlist_for_each_entry(proc, &binder_ns->procs, proc_node) + print_binder_proc(m, proc, 1); + if (do_lock) + binder_unlock(__func__); +@@ -3563,9 +3661,13 @@ static int binder_state_show(struct seq_file *m, void *unused) + + static int binder_stats_show(struct seq_file *m, void *unused) + { ++ struct binder_namespace *binder_ns = current_binder_ns(); + struct binder_proc *proc; + int do_lock = !binder_debug_no_lock; + ++ if (binder_ns == NULL) ++ return 0; ++ + if (do_lock) + binder_lock(__func__); + +@@ -3573,7 +3675,7 @@ static int binder_stats_show(struct seq_file *m, void *unused) + + print_binder_stats(m, "", &binder_stats); + +- hlist_for_each_entry(proc, &binder_procs, proc_node) ++ hlist_for_each_entry(proc, &binder_ns->procs, proc_node) + print_binder_proc_stats(m, proc); + if (do_lock) + binder_unlock(__func__); +@@ -3582,14 +3684,18 @@ static int binder_stats_show(struct seq_file *m, void *unused) + + static int binder_transactions_show(struct seq_file *m, void *unused) + { ++ struct binder_namespace *binder_ns = current_binder_ns(); + struct binder_proc *proc; + int do_lock = !binder_debug_no_lock; + ++ if (binder_ns == NULL) ++ return 0; ++ + if (do_lock) + binder_lock(__func__); + + seq_puts(m, "binder transactions:\n"); +- hlist_for_each_entry(proc, &binder_procs, proc_node) ++ hlist_for_each_entry(proc, &binder_ns->procs, proc_node) + print_binder_proc(m, proc, 0); + if (do_lock) + binder_unlock(__func__); +@@ -3661,9 +3767,15 @@ static int __init binder_init(void) + { + int ret; + ++ ret = register_peripc_ops(&binder_peripc_ops); ++ if (ret < 0) ++ return ret; ++ + binder_deferred_workqueue = create_singlethread_workqueue("binder"); +- if (!binder_deferred_workqueue) ++ if (!binder_deferred_workqueue) { ++ unregister_peripc_ops(&binder_peripc_ops); + return -ENOMEM; ++ } + + binder_debugfs_dir_entry_root = debugfs_create_dir("binder", NULL); + if (binder_debugfs_dir_entry_root) +-- +2.7.4 + diff --git a/scripts/anbox-bridge.sh b/scripts/anbox-bridge.sh index e6793b5..46f34b7 100755 --- a/scripts/anbox-bridge.sh +++ b/scripts/anbox-bridge.sh @@ -14,34 +14,21 @@ # limitations under the License. # Taken from https://github.com/lxc/lxd-pkg-ubuntu/blob/dpm-xenial/lxd-bridge/lxd-bridge +# but modified for the use within anbox. varrun="/run/anbox" -varlib="/var/lib/anbox" -BRIDGE="anboxbr0" +BRIDGE="anbox0" # IPv4 -IPV4_ADDR="10.0.6.1" +IPV4_ADDR="192.168.250.1" IPV4_NETMASK="255.255.255.0" -IPV4_NETWORK="10.0.6.1/24" -IPV4_DHCP_RANGE="10.0.6.2,10.0.6.254" -IPV4_DHCP_MAX="252" +IPV4_NETWORK="192.168.250.1/24" IPV4_NAT="true" -# IPv6 -IPV6_ADDR="fd9d:e4dc:4e00:9e98::1" -IPV6_MASK="64" -IPV6_NETWORK="fd9d:e4dc:4e00:9e98::1/64" -IPV6_NAT="true" -IPV6_PROXY="false" - use_iptables_lock="-w" iptables -w -L -n > /dev/null 2>&1 || use_iptables_lock="" -HAS_IPV6=false -[ -e "/proc/sys/net/ipv6/conf/default/disable_ipv6" ] && \ - [ "$(cat /proc/sys/net/ipv6/conf/default/disable_ipv6)" = "0" ] && HAS_IPV6=true - _netmask2cidr () { # Assumes there's no "255." after a non-255 byte in the mask local x=${1##*255.} @@ -88,11 +75,6 @@ start() { # set up the anbox network [ ! -d "/sys/class/net/${BRIDGE}" ] && ip link add dev "${BRIDGE}" type bridge - if [ "${HAS_IPV6}" = "true" ]; then - echo 0 > "/proc/sys/net/ipv6/conf/${BRIDGE}/autoconf" || true - echo 0 > "/proc/sys/net/ipv6/conf/${BRIDGE}/accept_dad" || true - fi - # if we are run from systemd on a system with selinux enabled, # the mkdir will create /run/anbox as init_var_run_t which dnsmasq # can't write its pid into, so we restorecon it (to var_run_t) @@ -103,13 +85,6 @@ start() { fi fi - if [ ! -d "${varlib}" ]; then - mkdir -p "${varlib}" - if which restorecon >/dev/null 2>&1; then - restorecon "${varlib}" - fi - fi - ifup "${BRIDGE}" "${IPV4_ADDR}" "${IPV4_NETMASK}" IPV4_ARG="" @@ -118,25 +93,6 @@ start() { if [ "${IPV4_NAT}" = "true" ]; then iptables "${use_iptables_lock}" -t nat -A POSTROUTING -s "${IPV4_NETWORK}" ! -d "${IPV4_NETWORK}" -j MASQUERADE -m comment --comment "managed by anbox-bridge" fi - IPV4_ARG="--listen-address ${IPV4_ADDR} --dhcp-range ${IPV4_DHCP_RANGE} --dhcp-lease-max=${IPV4_DHCP_MAX}" - fi - - IPV6_ARG="" - if [ "${HAS_IPV6}" = "true" ] && [ -n "${IPV6_ADDR}" ] && [ -n "${IPV6_MASK}" ] && [ -n "${IPV6_NETWORK}" ]; then - # IPv6 sysctls don't respect the "all" path... - for interface in /proc/sys/net/ipv6/conf/*; do - echo 2 > "${interface}/accept_ra" - done - - for interface in /proc/sys/net/ipv6/conf/*; do - echo 1 > "${interface}/forwarding" - done - - ip -6 addr add dev "${BRIDGE}" "${IPV6_ADDR}/${IPV6_MASK}" - if [ "${IPV6_NAT}" = "true" ]; then - ip6tables "${use_iptables_lock}" -t nat -A POSTROUTING -s "${IPV6_NETWORK}" ! -d "${IPV6_NETWORK}" -j MASQUERADE -m comment --comment "managed by anbox-bridge" - fi - IPV6_ARG="--dhcp-range=${IPV6_ADDR},ra-stateless,ra-names --listen-address ${IPV6_ADDR}" fi iptables "${use_iptables_lock}" -I INPUT -i "${BRIDGE}" -p udp --dport 67 -j ACCEPT -m comment --comment "managed by anbox-bridge" @@ -147,35 +103,6 @@ start() { iptables "${use_iptables_lock}" -I FORWARD -o "${BRIDGE}" -j ACCEPT -m comment --comment "managed by anbox-bridge" iptables "${use_iptables_lock}" -t mangle -A POSTROUTING -o "${BRIDGE}" -p udp -m udp --dport 68 -j CHECKSUM --checksum-fill -m comment --comment "managed by anbox-bridge" - DOMAIN_ARG="" - if [ -n "${DOMAIN}" ]; then - DOMAIN_ARG="-s ${DOMAIN} -S /${DOMAIN}/" - fi - - CONFILE_ARG="" - if [ -n "${CONFILE}" ]; then - CONFILE_ARG="--conf-file=${CONFILE}" - fi - - # https://lists.linuxcontainers.org/pipermail/lxc-devel/2014-October/010561.html - for DNSMASQ_USER in anbox dnsmasq nobody - do - if getent passwd "${DNSMASQ_USER}" >/dev/null; then - break - fi - done - - if [ -n "${IPV4_ADDR}" ] || [ -n "${IPV6_ADDR}" ]; then - # shellcheck disable=SC2086 - dnsmasq ${CONFILE_ARG} ${DOMAIN_ARG} -u "${DNSMASQ_USER}" --strict-order --bind-interfaces --pid-file="${varrun}/dnsmasq.pid" --dhcp-no-override --except-interface=lo --interface="${BRIDGE}" --dhcp-leasefile="${varlib}/dnsmasq.${BRIDGE}.leases" --dhcp-authoritative ${IPV4_ARG} ${IPV6_ARG} || cleanup - fi - - if [ "${HAS_IPV6}" = "true" ] && [ "${IPV6_PROXY}" = "true" ]; then - PATH="${PATH}:$(dirname "${0}")" anbox-bridge-proxy --addr="[fe80::1%${BRIDGE}]:13128" & - PID=$! - echo "${PID}" > "${varrun}/proxy.pid" - fi - touch "${varrun}/network_up" FAILED=0 } @@ -197,20 +124,6 @@ stop() { iptables ${use_iptables_lock} -t nat -D POSTROUTING -s ${IPV4_NETWORK} ! -d ${IPV4_NETWORK} -j MASQUERADE -m comment --comment "managed by anbox-bridge" fi - if [ "${HAS_IPV6}" = "true" ] && [ -n "${IPV6_NETWORK}" ] && [ "${IPV6_NAT}" = "true" ]; then - ip6tables ${use_iptables_lock} -t nat -D POSTROUTING -s ${IPV6_NETWORK} ! -d ${IPV6_NETWORK} -j MASQUERADE -m comment --comment "managed by anbox-bridge" - fi - - if [ -e "${varrun}/dnsmasq.pid" ]; then - pid=$(cat "${varrun}/dnsmasq.pid" 2>/dev/null) && kill -9 "${pid}" - rm -f "${varrun}/dnsmasq.pid" - fi - - if [ -e "${varrun}/proxy.pid" ]; then - pid=$(cat "${varrun}/proxy.pid" 2>/dev/null) && kill -9 "${pid}" - rm -f "${varrun}/proxy.pid" - fi - # if ${BRIDGE} has attached interfaces, don't destroy the bridge ls /sys/class/net/${BRIDGE}/brif/* > /dev/null 2>&1 || ip link delete "${BRIDGE}" fi diff --git a/scripts/clean-build.sh b/scripts/clean-build.sh index b2de311..ab659bb 100755 --- a/scripts/clean-build.sh +++ b/scripts/clean-build.sh @@ -32,6 +32,7 @@ apt-get install -qq -y \ libproperties-cpp-dev \ libprotobuf-dev \ libsdl2-dev \ + libsdl2-image-dev \ lxc-dev \ pkg-config \ protobuf-compiler @@ -40,14 +41,26 @@ apt-get clean cd /anbox -# In cases where anbox comes directly from a checked out Android -# build environment we miss some symlinks which are present on -# the host and don't have a valid git repository in that case. -git clean -fdx . || true -git reset --hard || true +cleanup() { + # In cases where anbox comes directly from a checked out Android + # build environment we miss some symlinks which are present on + # the host and don't have a valid git repository in that case. + if [ -d .git ] ; then + git clean -fdx . + git reset --hard + fi +} + +cleanup mkdir build || rm -rf build/* cd build cmake .. make -j10 make test + +cleanup + +apt-get install -y build-essential curl devscripts gdebi-core dkms dh-systemd +apt-get install -y $(gdebi --quiet --apt-line ./debian/control) +debuild -us -uc diff --git a/scripts/container-manager.sh b/scripts/container-manager.sh index 993a150..440f7ea 100755 --- a/scripts/container-manager.sh +++ b/scripts/container-manager.sh @@ -37,7 +37,8 @@ start() { exec $AA_EXEC $SNAP/bin/anbox-wrapper.sh container-manager \ --data-path=$DATA_PATH \ - --android-image=$ANDROID_IMG + --android-image=$ANDROID_IMG \ + --daemon } stop() { diff --git a/scripts/snap-wrapper.sh b/scripts/snap-wrapper.sh index 0122ad3..0ca2d83 100755 --- a/scripts/snap-wrapper.sh +++ b/scripts/snap-wrapper.sh @@ -29,6 +29,12 @@ export LIBGL_DRIVERS_PATH=$SNAP/usr/lib/$ARCH/dri # ensure the snappy gl libs win export LD_LIBRARY_PATH="$SNAP_LIBRARY_PATH:$LD_LIBRARY_PATH" +# Workaround in snapd for proprietary nVidia drivers mounts the drivers in +# /var/lib/snapd/lib/gl that needs to be in LD_LIBRARY_PATH +# Without that OpenGL using apps do not work with the nVidia drivers. +# Ref.: https://bugs.launchpad.net/snappy/+bug/1588192 +export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/var/lib/snapd/lib/gl + # We set XDG_DATA_HOME to SNAP_USER_COMMON here as this will be the location we will # create all our application launchers in. The system application launcher will # be configured by our installer to look into this directory for available diff --git a/snapcraft.yaml b/snapcraft.yaml index 0318ead..3ed6b2b 100644 --- a/snapcraft.yaml +++ b/snapcraft.yaml @@ -1,5 +1,11 @@ name: anbox -version: 1-dev +version: 3 +version-script: | + if [ "$SNAPCRAFT_GRADE" = "stable" ]; then + echo $SNAPCRAFT_PROJECT_VERSION + else + echo $SNAPCRAFT_PROJECT_VERSION-$(git rev-parse --short HEAD) + fi summary: Android in a Box description: | Runtime for Android applications which runs a full Android system @@ -84,6 +90,7 @@ parts: # FIXME: Anbox currently has some paths with hard coded prefixes. Once # that is fixed we can avoid using a prefix here. - -DCMAKE_INSTALL_PREFIX:PATH=/usr + - -DANBOX_VERSION=$SNAPCRAFT_PROJECT_VERSION build-packages: - build-essential - cmake @@ -111,6 +118,7 @@ parts: - libprotobuf-dev - libproperties-cpp-dev - libsdl2-dev + - libsdl2-image-dev - pkg-config - protobuf-compiler stage-packages: @@ -123,4 +131,5 @@ parts: make test prime: - usr/bin/anbox + - usr/share/anbox - usr/lib/*-linux-*/ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 5675ccb..3fb6a7e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -7,6 +7,8 @@ include_directories( ${DBUS_CPP_INCLUDE_DIRS} ${DBUS_INCLUDE_DIRS} ${SDL2_INCLUDE_DIRS} + ${SDL2_IMAGE_INCLUDE_DIRS} + ${PROPERTIES_CPP_INCLUDE_DIRS} ${LXC_INCLUDE_DIRS} ${MIRCLIENT_INCLUDE_DIRS} ${CMAKE_CURRENT_BINARY_DIR} @@ -41,6 +43,7 @@ protobuf_generate_cpp( add_library(anbox-protobuf + STATIC ${GENERATED_PROTOBUF_BRIDGE_SRCS} ${GENERATED_PROTOBUF_BRIDGE_HDRS} ${GENERATED_PROTOBUF_RPC_SRCS} @@ -56,12 +59,15 @@ set(SOURCES anbox/utils.cpp anbox/cli.cpp anbox/runtime.cpp - anbox/version.cpp anbox/daemon.cpp anbox/config.cpp anbox/not_reachable.cpp + anbox/build/version.cpp + anbox/build/version.h.in + anbox/android/intent.cpp + anbox/android/ip_config_builder.cpp anbox/common/fd.cpp anbox/common/fd_sets.h @@ -75,6 +81,7 @@ set(SOURCES anbox/common/loop_device.cpp anbox/common/loop_device_allocator.cpp anbox/common/mount_entry.cpp + anbox/common/binary_writer.cpp anbox/testing/gtest_utils.h @@ -203,11 +210,13 @@ set(SOURCES anbox/utils/environment_file.cpp + anbox/ui/splash_screen.cpp + anbox/do_not_copy_or_move.h anbox/optional.h anbox/defer_action.h) -add_library(anbox-core ${SOURCES}) +add_library(anbox-core STATIC ${SOURCES}) target_link_libraries(anbox-core ${Boost_LDFLAGS} ${Boost_LIBRARIES} @@ -215,6 +224,8 @@ target_link_libraries(anbox-core ${DBUS_CPP_LIBRARIES} ${SDL2_LDFLAGS} ${SDL2_LIBRARIES} + ${SDL2_IMAGE_LDFLAGS} + ${SDL2_IMAGE_LIBRARIES} ${LXC_LDFLAGS} ${LXC_LIBRARIES} ${MIRCLIENT_LDFLAGS} diff --git a/src/anbox/android/intent.cpp b/src/anbox/android/intent.cpp index aa40f2c..1b7b0e3 100644 --- a/src/anbox/android/intent.cpp +++ b/src/anbox/android/intent.cpp @@ -21,17 +21,32 @@ namespace anbox { namespace android { +bool Intent::valid() const { + // At the moment we only support component+package for intents + // (see android/service/android_api_skeleton.cpp for more details) + return !(component.empty() && package.empty()); +} + std::ostream &operator<<(std::ostream &out, const Intent &intent) { - out << "[" - << "action=" << intent.action << " " - << "uri=" << intent.uri << " " - << "type=" << intent.type << " " - << "flags=" << intent.flags << " " - << "package=" << intent.package << " " - << "component=" << intent.component << " " - << "categories=[ "; - for (const auto &category : intent.categories) out << category << " "; - out << "]]"; + out << "["; + if (!intent.action.empty()) + out << " " << "action=" << intent.action << " "; + if (!intent.uri.empty()) + out << " " << "uri=" << intent.uri << " "; + if (!intent.type.empty()) + out << " " << "type=" << intent.type << " "; + if (intent.flags > 0) + out << " " << "flags=" << intent.flags << " "; + if (!intent.package.empty()) + out << " " << "package=" << intent.package << " "; + if (!intent.component.empty()) + out << "component=" << intent.component << " "; + if (intent.categories.size() > 0) { + out << "categories=[ "; + for (const auto &category : intent.categories) out << category << " "; + out << "] "; + } + out << "]"; return out; } } // namespace android diff --git a/src/anbox/android/intent.h b/src/anbox/android/intent.h index a628f8f..57f2add 100644 --- a/src/anbox/android/intent.h +++ b/src/anbox/android/intent.h @@ -31,6 +31,8 @@ struct Intent { std::string package; std::string component; std::vector categories; + + bool valid() const; }; std::ostream &operator<<(std::ostream &out, const Intent &intent); diff --git a/src/anbox/android/ip_config_builder.cpp b/src/anbox/android/ip_config_builder.cpp new file mode 100644 index 0000000..8ba13ce --- /dev/null +++ b/src/anbox/android/ip_config_builder.cpp @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2017 Simon Fels + * + * 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 . + * + */ + +#include "anbox/android/ip_config_builder.h" + +#include + +#include +#include + +namespace { +constexpr const char *assignment_key{"ipAssignment"}; +constexpr const char *link_address_key{"linkAddress"}; +constexpr const char *gateway_key{"gateway"}; +constexpr const char *dns_key{"dns"}; +constexpr const char *id_key{"id"}; +constexpr const char *eos_key{"eos"}; +constexpr const char *assignment_static{"STATIC"}; +constexpr const char *assignment_dhcp{"DHCP"}; +constexpr const char *assignment_unknown{"UNKNOWN"}; + +constexpr const std::uint32_t is_default_gateway{0}; +constexpr const std::uint32_t gateway_is_present{1}; + +namespace aa = anbox::android; +std::string assignment_to_string(const aa::IpConfigBuilder::Assignment &value) { + switch (value) { + case anbox::android::IpConfigBuilder::Assignment::Static: + return assignment_static; + break; + case anbox::android::IpConfigBuilder::Assignment::DHCP: + return assignment_dhcp; + break; + default: + break; + } + return assignment_unknown; +} +} + +namespace anbox { +namespace android { +std::size_t IpConfigBuilder::write(common::BinaryWriter &writer) { + writer.set_byte_order(common::BinaryWriter::Order::Big); + + // See http://androidxref.com/7.1.1_r6/xref/frameworks/base/services/core/java/com/android/server/net/IpConfigStore.java + // for more details on the binary file format used here. + + writer.write_uint32(static_cast(version_)); + + writer.write_string_with_size(assignment_key); + writer.write_string_with_size(assignment_to_string(assignment_)); + + writer.write_string_with_size(link_address_key); + writer.write_string_with_size(link_.address); + writer.write_uint32(link_.prefix_length); + + writer.write_string_with_size(gateway_key); + writer.write_uint32(is_default_gateway); + writer.write_uint32(gateway_is_present); + writer.write_string_with_size(gateway_); + + writer.write_string_with_size(dns_key); + for (const auto &server : dns_servers_) + writer.write_string_with_size(server); + + writer.write_string_with_size(id_key); + writer.write_uint32(id_); + + writer.write_string_with_size(eos_key); + + return writer.bytes_written(); +} + +void IpConfigBuilder::set_version(const Version &version) { + version_ = version; +} + +void IpConfigBuilder::set_assignment(const Assignment &assignment) { + assignment_ = assignment; +} + +void IpConfigBuilder::set_link_address(const std::string &address, uint32_t prefix_length) { + link_.address = address; + link_.prefix_length = prefix_length; +} + +void IpConfigBuilder::set_gateway(const std::string &gateway) { + gateway_ = gateway; +} + +void IpConfigBuilder::set_dns_servers(const std::vector &dns_servers) { + dns_servers_ = dns_servers; +} + +void IpConfigBuilder::set_id(uint32_t id) { + id_ = id; +} +} // namespace android +} // namespace anbox diff --git a/src/anbox/android/ip_config_builder.h b/src/anbox/android/ip_config_builder.h new file mode 100644 index 0000000..812da7c --- /dev/null +++ b/src/anbox/android/ip_config_builder.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2017 Simon Fels + * + * 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 . + * + */ + +#ifndef ANBOX_ANDROID_IPCONFIGBUILDER_H_ +#define ANBOX_ANDROID_IPCONFIGBUILDER_H_ + +#include "anbox/common/binary_writer.h" + +#include +#include +#include + +namespace anbox { +namespace android { +class IpConfigBuilder { + public: + enum class Version : std::uint32_t { + Version1 = 1, + Version2 = 2, + }; + + enum class Assignment { + Static, + DHCP, + }; + + IpConfigBuilder() = default; + + std::size_t write(common::BinaryWriter &writer); + + void set_version(const Version &version); + void set_assignment(const Assignment &assignment); + void set_link_address(const std::string &address, std::uint32_t prefix_length); + void set_gateway(const std::string &gateway); + void set_dns_servers(const std::vector &dns_servers); + void set_id(std::uint32_t id); + + private: + Version version_; + Assignment assignment_; + struct { + std::string address; + std::uint32_t prefix_length; + } link_; + std::string gateway_; + std::vector dns_servers_; + std::uint32_t id_; +}; +} // namespace android +} // namespace anbox + +#endif diff --git a/src/anbox/bridge/platform_api_skeleton.h b/src/anbox/bridge/platform_api_skeleton.h index 7a9891b..843416e 100644 --- a/src/anbox/bridge/platform_api_skeleton.h +++ b/src/anbox/bridge/platform_api_skeleton.h @@ -18,6 +18,7 @@ #ifndef ANBOX_BRIDGE_PLATFORM_SERVER_H_ #define ANBOX_BRIDGE_PLATFORM_SERVER_H_ +#include #include namespace google { diff --git a/src/anbox/build/version.cpp b/src/anbox/build/version.cpp new file mode 100644 index 0000000..b63b804 --- /dev/null +++ b/src/anbox/build/version.cpp @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2017 Simon Fels + * + * 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 . + * + */ + +#include "anbox/build/version.h" +#include "anbox/utils.h" + +#include + +namespace anbox { +namespace build { +static std::string init_version_string() { + std::string v; + if (!version_suffix || std::strlen(version_suffix) == 0) + v = utils::string_format("%s", version); + else + v = utils::string_format("%s-%s", version, version_suffix); + return v; +} + +std::string print_version() { + static const std::string v{init_version_string()}; + return v; +} +} // namespace build +} // namespace anbox diff --git a/src/anbox/version.cpp b/src/anbox/build/version.h.in similarity index 55% rename from src/anbox/version.cpp rename to src/anbox/build/version.h.in index 8759db7..fb7983a 100644 --- a/src/anbox/version.cpp +++ b/src/anbox/build/version.h.in @@ -17,11 +17,22 @@ * */ -#include "anbox/version.h" +#ifndef ANBOX_VERSION_H_ +#define ANBOX_VERSION_H_ -void anbox::version(std::uint32_t& major, std::uint32_t& minor, - std::uint32_t& patch) { - major = anbox::build::version_major; - minor = anbox::build::version_minor; - patch = anbox::build::version_patch; -} +#include +#include + +namespace anbox { +namespace build { +/// @brief version marks the version +static constexpr const char *version{"@ANBOX_VERSION@"}; +/// @brief version_suffix is an additional suffix which can be amended to +/// the major version number to indicate a dev build for example. +static constexpr const char *version_suffix{"@ANBOX_VERSION_SUFFIX@"}; +/// @brief version queries the version of Anbox +std::string print_version(); +} // namespace build +} // namespace anbox + +#endif // ANBOX_VERSION_H_ diff --git a/src/anbox/cmds/container_manager.cpp b/src/anbox/cmds/container_manager.cpp index 4716303..a71f07b 100644 --- a/src/anbox/cmds/container_manager.cpp +++ b/src/anbox/cmds/container_manager.cpp @@ -48,11 +48,22 @@ anbox::cmds::ContainerManager::ContainerManager() flag(cli::make_flag(cli::Name{"privileged"}, cli::Description{"Run Android container in privileged mode"}, privileged_)); + flag(cli::make_flag(cli::Name{"daemon"}, + cli::Description{"Mark service as being started as systemd daemon"}, + daemon_)); action([&](const cli::Command::Context&) { try { - if (getuid() != 0) { - ERROR("You're not running the container-manager as root. Generally you don't"); + if (!daemon_) { + WARNING("You are running the container manager manually which is most likely not"); + WARNING("what you want. The container manager is normally started by systemd or"); + WARNING("another init system. If you still want to run the container-manager"); + WARNING("you can get rid of this warning by starting with the --daemon option."); + WARNING(""); + } + + if (geteuid() != 0) { + ERROR("You are not running the container-manager as root. Generally you don't"); ERROR("want to run the container-manager manually unless you're a developer"); ERROR("as it is started by the init system of your operating system."); return EXIT_FAILURE; @@ -68,6 +79,9 @@ anbox::cmds::ContainerManager::ContainerManager() if (!data_path_.empty()) SystemConfiguration::instance().set_data_path(data_path_); + if (!fs::exists(data_path_)) + fs::create_directories(data_path_); + if (!setup_mounts()) return EXIT_FAILURE; diff --git a/src/anbox/cmds/container_manager.h b/src/anbox/cmds/container_manager.h index 4525756..679c802 100644 --- a/src/anbox/cmds/container_manager.h +++ b/src/anbox/cmds/container_manager.h @@ -42,6 +42,7 @@ class ContainerManager : public cli::CommandWithFlagsAndAction { std::shared_ptr android_img_loop_dev_; std::vector> mounts_; bool privileged_ = false; + bool daemon_ = false; }; } // namespace cmds } // namespace anbox diff --git a/src/anbox/cmds/launch.cpp b/src/anbox/cmds/launch.cpp index a73e2b6..3e0f885 100644 --- a/src/anbox/cmds/launch.cpp +++ b/src/anbox/cmds/launch.cpp @@ -19,6 +19,8 @@ #include "anbox/common/wait_handle.h" #include "anbox/dbus/stub/application_manager.h" #include "anbox/common/dispatcher.h" +#include "anbox/ui/splash_screen.h" +#include "anbox/config.h" #include "anbox/runtime.h" #include "anbox/logger.h" @@ -26,12 +28,31 @@ #include +#include "core/posix/exec.h" +#include "core/posix/fork.h" #include "core/posix/signal.h" namespace fs = boost::filesystem; namespace { -const boost::posix_time::seconds max_wait_timeout{30}; +const boost::posix_time::seconds max_wait_timeout{240}; +const int max_restart_attempts{3}; +const std::chrono::seconds restart_interval{5}; +} + +bool anbox::cmds::Launch::try_launch_activity(const std::shared_ptr &stub) { + try { + DEBUG("Sending launch intent %s to Android ..", intent_); + stub->launch(intent_, graphics::Rect::Invalid, stack_); + } catch (const std::exception &err) { + ERROR("Failed to launch activity: %s", err.what()); + return false; + } catch (...) { + ERROR("Failed to launch activity"); + return false; + } + + return true; } anbox::cmds::Launch::Launch() @@ -58,6 +79,11 @@ anbox::cmds::Launch::Launch() stack_)); action([this](const cli::Command::Context&) { + if (!intent_.valid()) { + ERROR("The intent you provided is invalid. Please provide a correct launch intent."); + 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(signal)); @@ -69,42 +95,95 @@ anbox::cmds::Launch::Launch() auto bus = std::make_shared(core::dbus::WellKnownBus::session); bus->install_executor(core::dbus::asio::make_executor(bus, rt->service())); + const auto snap_path = utils::get_env_value("SNAP"); + if (!snap_path.empty()) { + const auto resource_path = fs::path(snap_path) / "usr" / "share" / "anbox"; + SystemConfiguration::instance().set_resource_path(resource_path); + } + + std::shared_ptr ss; + + // 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 stub; - try { - stub = dbus::stub::ApplicationManager::create_for_bus(bus); - } catch (...) { - ERROR("Anbox session manager service isn't running!"); + 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."); + + // 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(); + + std::vector args = {"session-manager"}; + + std::map 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::stdout | core::posix::StandardStream::stderr; + // If we have logging enable in debug mode then we allow the child process + // to print to stdout/stderr too. + if (Log().GetSeverity() == Logger::Severity::kDebug) + flags = core::posix::StandardStream::empty; + auto child = core::posix::fork([&]() { + 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); + } + } + + if (!stub) { + ERROR("Couldn't get a connection with the session manager"); return EXIT_FAILURE; } - auto dispatcher = anbox::common::create_dispatcher_for_runtime(rt); - bool success = false; - + auto dispatcher = anbox::common::create_dispatcher_for_runtime(rt); dispatcher->dispatch([&]() { if (stub->ready()) { - try { - stub->launch(intent_, graphics::Rect::Invalid, stack_); - success = true; - } catch (std::exception &err) { - ERROR("err %s", err.what()); - } + ss.reset(); + success = try_launch_activity(stub); trap->stop(); return; } - DEBUG("Android hasn't fully booted yet. Waiting a bit.."); + DEBUG("Android hasn't fully booted yet. Waiting a bit .."); stub->ready().changed().connect([&](bool ready) { if (!ready) return; - try { - stub->launch(intent_, graphics::Rect::Invalid, stack_); - success = true; - } catch (std::exception &err) { - ERROR("Failed to launch activity: %s", err.what()); - success = false; - } + ss.reset(); + success = try_launch_activity(stub); trap->stop(); }); }); @@ -112,8 +191,10 @@ anbox::cmds::Launch::Launch() boost::asio::deadline_timer timer(rt->service()); timer.expires_from_now(max_wait_timeout); timer.async_wait([&](const boost::system::error_code&) { - WARNING("Stop waiting as we're already waiting for too long. Something is wrong"); - WARNING("with your setup and the container may have failed to boot."); + 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(); }); diff --git a/src/anbox/cmds/launch.h b/src/anbox/cmds/launch.h index b7a6dfe..7fc71c9 100644 --- a/src/anbox/cmds/launch.h +++ b/src/anbox/cmds/launch.h @@ -23,6 +23,7 @@ #include #include "anbox/android/intent.h" +#include "anbox/dbus/stub/application_manager.h" #include "anbox/wm/stack.h" #include "anbox/cli.h" @@ -33,6 +34,8 @@ class Launch : public cli::CommandWithFlagsAndAction { Launch(); private: + bool try_launch_activity(const std::shared_ptr &stub); + android::Intent intent_; wm::Stack::Id stack_; }; diff --git a/src/anbox/cmds/session_manager.cpp b/src/anbox/cmds/session_manager.cpp index 5c294d0..731de0a 100644 --- a/src/anbox/cmds/session_manager.cpp +++ b/src/anbox/cmds/session_manager.cpp @@ -27,12 +27,17 @@ #include "anbox/bridge/android_api_stub.h" #include "anbox/bridge/platform_api_skeleton.h" #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/config.h" #include "anbox/container/client.h" #include "anbox/dbus/skeleton/service.h" -#include "anbox/graphics/gl_renderer_server.h" #include "anbox/input/manager.h" #include "anbox/logger.h" #include "anbox/network/published_socket_connector.h" @@ -106,6 +111,12 @@ anbox::cmds::SessionManager::SessionManager(const BusFactory &bus_factory) flag(cli::make_flag(cli::Name{"window-size"}, cli::Description{"Size of the window in single window mode, e.g. --window-size=1024,768"}, window_size_)); + flag(cli::make_flag(cli::Name{"standalone"}, + cli::Description{"Prevents the Container Manager from starting the default container (Experimental)"}, + standalone_)); + flag(cli::make_flag(cli::Name{"experimental"}, + cli::Description{"Allows users to use experimental features"}, + experimental_)); action([this](const cli::Command::Context &) { auto trap = core::posix::trap_signals_for_process( @@ -115,6 +126,11 @@ anbox::cmds::SessionManager::SessionManager(const BusFactory &bus_factory) trap->stop(); }); + if (standalone_ && !experimental_) { + ERROR("Experimental features selected, but --experimental flag not set"); + return EXIT_FAILURE; + } + if (!fs::exists("/dev/binder") || !fs::exists("/dev/ashmem")) { ERROR("Failed to start as either binder or ashmem kernel drivers are not loaded"); return EXIT_FAILURE; @@ -135,11 +151,13 @@ anbox::cmds::SessionManager::SessionManager(const BusFactory &bus_factory) auto rt = Runtime::create(); auto dispatcher = anbox::common::create_dispatcher_for_runtime(rt); - container::Client container(rt); - container.register_terminate_handler([&]() { - WARNING("Lost connection to container manager, terminating."); - trap->stop(); - }); + if (!standalone_) { + container_ = std::make_shared(rt); + container_->register_terminate_handler([&]() { + WARNING("Lost connection to container manager, terminating."); + trap->stop(); + }); + } auto input_manager = std::make_shared(rt); @@ -159,8 +177,6 @@ anbox::cmds::SessionManager::SessionManager(const BusFactory &bus_factory) display_frame = window_size_; auto policy = std::make_shared(input_manager, display_frame, single_window_); - // FIXME this needs to be removed and solved differently behind the scenes - registerDisplayManager(policy); auto app_db = std::make_shared(); @@ -213,7 +229,8 @@ anbox::cmds::SessionManager::SessionManager(const BusFactory &bus_factory) })); container::Configuration container_configuration; - container_configuration.bind_mounts = { + if (!standalone_) { + container_configuration.bind_mounts = { {qemu_pipe_connector->socket_file(), "/dev/qemu_pipe"}, {bridge_connector->socket_file(), "/dev/anbox_bridge"}, {audio_server->socket_file(), "/dev/anbox_audio"}, @@ -221,9 +238,10 @@ anbox::cmds::SessionManager::SessionManager(const BusFactory &bus_factory) {"/dev/binder", "/dev/binder"}, {"/dev/ashmem", "/dev/ashmem"}, {"/dev/fuse", "/dev/fuse"}, - }; + }; - dispatcher->dispatch([&]() { container.start(container_configuration); }); + dispatcher->dispatch([&]() { container_->start(container_configuration); }); + } auto bus = bus_factory_(); bus->install_executor(core::dbus::asio::make_executor(bus, rt->service())); @@ -233,9 +251,11 @@ anbox::cmds::SessionManager::SessionManager(const BusFactory &bus_factory) rt->start(); trap->run(); - // Stop the container which should close all open connections we have on - // our side and should terminate all services. - container.stop(); + if (!standalone_) { + // Stop the container which should close all open connections we have on + // our side and should terminate all services. + container_->stop(); + } rt->stop(); diff --git a/src/anbox/cmds/session_manager.h b/src/anbox/cmds/session_manager.h index 9ffb419..25185ac 100644 --- a/src/anbox/cmds/session_manager.h +++ b/src/anbox/cmds/session_manager.h @@ -30,6 +30,9 @@ #include "anbox/graphics/rect.h" namespace anbox { +namespace container { +class Client; +} // namespace container namespace cmds { class SessionManager : public cli::CommandWithFlagsAndAction { public: @@ -40,11 +43,14 @@ class SessionManager : public cli::CommandWithFlagsAndAction { SessionManager(const BusFactory& bus_factory = session_bus_factory()); private: + std::shared_ptr container_; BusFactory bus_factory_; 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; }; } // namespace cmds } // namespace anbox diff --git a/src/anbox/cmds/system_info.cpp b/src/anbox/cmds/system_info.cpp index 421f11c..e8f7443 100644 --- a/src/anbox/cmds/system_info.cpp +++ b/src/anbox/cmds/system_info.cpp @@ -19,9 +19,9 @@ #include "anbox/graphics/emugl/RenderApi.h" #include "anbox/graphics/emugl/DispatchTables.h" #include "anbox/utils/environment_file.h" -#include "anbox/version.h" #include "anbox/logger.h" -#include "anbox/version.h" + +#include "anbox/build/version.h" #include #include @@ -34,9 +34,12 @@ namespace fs = boost::filesystem; namespace { constexpr const char *os_release_path{"/etc/os-release"}; +constexpr const char *host_os_release_path{"/var/lib/snapd/hostfs/etc/os-release"}; constexpr const char *proc_version_path{"/proc/version"}; constexpr const char *binder_path{"/dev/binder"}; constexpr const char *ashmem_path{"/dev/ashmem"}; +constexpr const char *os_release_name{"NAME"}; +constexpr const char *os_release_version{"VERSION"}; class SystemInformation { public: @@ -50,12 +53,15 @@ class SystemInformation { std::stringstream s; s << "version: " - << anbox::utils::string_format("%d.%d.%d", - anbox::build::version_major, - anbox::build::version_minor, - anbox::build::version_patch) + << anbox::build::print_version() << std::endl; + if (anbox::utils::is_env_set("SNAP_REVISION")) { + s << "snap-revision: " + << anbox::utils::get_env_value("SNAP_REVISION") + << std::endl; + } + s << "os:" << std::endl << " name: " << os_info_.name << std::endl << " version: " << os_info_.version << std::endl @@ -96,15 +102,19 @@ class SystemInformation { private: void collect_os_info() { - os_info_.snap_based = (getenv("SNAP") != nullptr); - if (fs::exists(os_release_path)) { - anbox::utils::EnvironmentFile os_release(os_release_path); - os_info_.name = os_release.value("NAME"); - os_info_.version = os_release.value("VERSION"); - } else if (os_info_.snap_based) { - // As we can't read /etc/os-release and we're snap-based this is the best we can guess - os_info_.name = "Ubuntu"; - os_info_.version = "16"; + os_info_.snap_based = !anbox::utils::get_env_value("SNAP").empty(); + fs::path path = os_release_path; + // If we're running from within a snap the best we can do is to + // access the hostfs and read the os-release file from there. + if (os_info_.snap_based && fs::exists(host_os_release_path)) + path = host_os_release_path; + + // Double check that there aren't any permission errors when trying + // to access the file (e.g. because of snap confinement) + if (fs::exists(path)) { + anbox::utils::EnvironmentFile os_release(path); + os_info_.name = os_release.value(os_release_name); + os_info_.version = os_release.value(os_release_version); } } diff --git a/src/anbox/cmds/version.cpp b/src/anbox/cmds/version.cpp index c405e48..d8aef39 100644 --- a/src/anbox/cmds/version.cpp +++ b/src/anbox/cmds/version.cpp @@ -18,16 +18,15 @@ */ #include "anbox/cmds/version.h" -#include "anbox/version.h" +#include "anbox/build/version.h" +#include "anbox/utils.h" anbox::cmds::Version::Version() : CommandWithFlagsAndAction{ cli::Name{"version"}, cli::Usage{"version"}, cli::Description{"print the version of the daemon"}} { action([](const cli::Command::Context& ctxt) { - std::uint32_t major, minor, patch; - anbox::version(major, minor, patch); - ctxt.cout << "anbox " << major << "." << minor << "." << patch << std::endl; + ctxt.cout << "anbox " << build::print_version() << std::endl; return 0; }); } diff --git a/src/anbox/common/binary_writer.cpp b/src/anbox/common/binary_writer.cpp new file mode 100644 index 0000000..3202199 --- /dev/null +++ b/src/anbox/common/binary_writer.cpp @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2016 Thomas Voss + * Simon Fels + * + * 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 . + * + */ + +#include "anbox/common/binary_writer.h" + +#include +#include + +#include + +namespace { +bool is_little_endian() { + static const std::uint32_t v = 1; + return (*reinterpret_cast(&v) == 1); +} +} + +namespace anbox { +namespace common { + +BinaryWriter::BinaryWriter(std::vector::iterator begin, + std::vector::iterator end) : + begin_{begin}, current_{begin}, end_{end}, + byte_order_{is_little_endian() ? Order::Little : Order::Big} {} + +void BinaryWriter::set_byte_order(Order order) { + byte_order_ = order; +} + +void BinaryWriter::write_uint16(std::uint16_t value) { + if (current_ + sizeof(value) > end_) + throw std::out_of_range{"Write buffer exhausted"}; + + std::uint16_t v = value; + switch (byte_order_) { + case Order::Big: + v = boost::endian::native_to_big(value); + break; + case Order::Little: + v = boost::endian::native_to_little(value); + break; + default: + break; + } + + *reinterpret_cast(&(*current_)) = v; + current_ += sizeof(v); +} + +void BinaryWriter::write_uint32(std::uint32_t value) { + if (current_ + sizeof(value) > end_) + throw std::out_of_range{"Write buffer exhausted"}; + + std::uint32_t v = value; + switch (byte_order_) { + case Order::Big: + v = boost::endian::native_to_big(value); + break; + case Order::Little: + v = boost::endian::native_to_little(value); + break; + default: + break; + } + + *reinterpret_cast(&(*current_)) = v; + current_ += sizeof(v); +} + +void BinaryWriter::write_string(const char *s, std::size_t size) { + if (current_ + size > end_) + throw std::out_of_range{"Write buffer exhausted"}; + + memcpy(&(*current_), s, size); + current_ += size; +} + +void BinaryWriter::write_string_with_size(const std::string &str) { + write_string_with_size(str.c_str(), str.length()); +} + +void BinaryWriter::write_string_with_size(const char *s, std::size_t size) { + write_uint16(size); + write_string(s, size); +} + +std::size_t BinaryWriter::bytes_written() const { + return current_ - begin_; +} +} // namespace common +} // namespace anbox diff --git a/src/anbox/common/binary_writer.h b/src/anbox/common/binary_writer.h new file mode 100644 index 0000000..23708c6 --- /dev/null +++ b/src/anbox/common/binary_writer.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2016 Thomas Voss + * Simon Fels + * + * 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 . + * + */ + +#ifndef ANBOX_COMMON_BINARY_WRITER_H_ +#define ANBOX_COMMON_BINARY_WRITER_H_ + +#include +#include +#include + +namespace anbox { +namespace common { +class BinaryWriter { + public: + enum class Order { + Big, + Little, + }; + + explicit BinaryWriter(std::vector::iterator begin_, + std::vector::iterator end_); + + void set_byte_order(Order order); + + void write_uint16(std::uint16_t value); + void write_uint32(std::uint32_t value); + void write_string(const char *s, std::size_t size); + void write_string_with_size(const std::string &str); + void write_string_with_size(const char *s, std::size_t size); + + std::size_t bytes_written() const; + + private: + std::vector::iterator begin_; + std::vector::iterator current_; + std::vector::iterator end_; + Order byte_order_; +}; +} // namespace common +} // namespace anbox + +#endif diff --git a/src/anbox/config.cpp b/src/anbox/config.cpp index 7c0961e..a13aa43 100644 --- a/src/anbox/config.cpp +++ b/src/anbox/config.cpp @@ -41,6 +41,10 @@ void anbox::SystemConfiguration::set_data_path(const std::string &path) { data_path = path; } +void anbox::SystemConfiguration::set_resource_path(const fs::path &path) { + resource_path = path; +} + fs::path anbox::SystemConfiguration::data_dir() const { return data_path; } @@ -76,6 +80,10 @@ std::string anbox::SystemConfiguration::application_item_dir() const { return dir.string(); } +std::string anbox::SystemConfiguration::resource_dir() const { + return resource_path.string(); +} + anbox::SystemConfiguration& anbox::SystemConfiguration::instance() { static SystemConfiguration config; return config; diff --git a/src/anbox/config.h b/src/anbox/config.h index 91f72c6..0fce571 100644 --- a/src/anbox/config.h +++ b/src/anbox/config.h @@ -31,6 +31,7 @@ class SystemConfiguration { virtual ~SystemConfiguration() = default; void set_data_path(const std::string &path); + void set_resource_path(const boost::filesystem::path &path); boost::filesystem::path data_dir() const; std::string rootfs_dir() const; @@ -40,11 +41,13 @@ class SystemConfiguration { std::string container_socket_path() const; std::string input_device_dir() const; std::string application_item_dir() const; + std::string resource_dir() const; protected: SystemConfiguration() = default; boost::filesystem::path data_path = "/var/lib/anbox"; + boost::filesystem::path resource_path = "/usr/share/anbox"; }; } // namespace anbox diff --git a/src/anbox/container/lxc_container.cpp b/src/anbox/container/lxc_container.cpp index 3faf842..a2e0705 100644 --- a/src/anbox/container/lxc_container.cpp +++ b/src/anbox/container/lxc_container.cpp @@ -15,6 +15,7 @@ * */ +#include "anbox/android/ip_config_builder.h" #include "anbox/container/lxc_container.h" #include "anbox/config.h" #include "anbox/logger.h" @@ -22,6 +23,7 @@ #include #include +#include #include #include @@ -30,9 +32,20 @@ #include #include #include +#include + +#include namespace fs = boost::filesystem; +namespace { +constexpr unsigned int unprivileged_user_id{100000}; +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 anbox { namespace container { LxcContainer::LxcContainer(bool privileged, const network::Credentials &creds) @@ -49,8 +62,7 @@ LxcContainer::~LxcContainer() { } void LxcContainer::setup_id_maps() { - // FIXME make these id sets configurable - const auto base_id = 100000; + const auto base_id = unprivileged_user_id; const auto max_id = 65536; set_config_item("lxc.id_map", @@ -76,6 +88,75 @@ void LxcContainer::setup_id_maps() { max_id - creds_.gid() - 1)); } +void LxcContainer::setup_network() { + if (!fs::exists("/sys/class/net/anbox0")) { + WARNING("Anbox bridge interface 'anbox0' doesn't exist. Network functionality will not be available"); + return; + } + + set_config_item("lxc.network.type", "veth"); + set_config_item("lxc.network.flags", "up"); + set_config_item("lxc.network.link", "anbox0"); + + // Instead of relying on DHCP we will give Android a static IP configuration + // for the virtual ethernet interface LXC creates for us. This will be bridged + // to the host and will allows us to have reliable network connectivity and + // not depend on any other system service. + + android::IpConfigBuilder ip_conf; + ip_conf.set_version(android::IpConfigBuilder::Version::Version2); + ip_conf.set_assignment(android::IpConfigBuilder::Assignment::Static); + ip_conf.set_link_address(default_container_ip_address, default_container_ip_prefix_length); + ip_conf.set_gateway(default_host_ip_address); + ip_conf.set_dns_servers({default_dns_server}); + ip_conf.set_id(0); + + std::vector buffer(512); + common::BinaryWriter writer(buffer.begin(), buffer.end()); + const auto size = ip_conf.write(writer); + + const auto data_ethernet_path = fs::path("data") / "misc" / "ethernet"; + const auto ip_conf_dir = SystemConfiguration::instance().data_dir() / data_ethernet_path; + if (!fs::exists(ip_conf_dir)) + fs::create_directories(ip_conf_dir); + + // We have to walk through the created directory hierachy now and + // ensure the permissions are set correctly. Otherwise the Android + // system will fail to boot as it isn't allowed to write anything + // into these directories. As previous versions of Anbox which were + // published to our users did this incorrectly we need to check on + // every startup if those directories are still owned by root and + // if they are we move them over to the unprivileged user. + auto path = SystemConfiguration::instance().data_dir(); + for (auto iter = data_ethernet_path.begin(); iter != data_ethernet_path.end(); iter++) { + path /= *iter; + + struct stat st; + if (stat(path.c_str(), &st) < 0) { + WARNING("Cannot retrieve permissions of path %s", path); + continue; + } + + if (st.st_uid != 0 && st.st_gid != 0) + continue; + + if (::chown(path.c_str(), unprivileged_user_id, unprivileged_user_id) < 0) + WARNING("Failed to set owner for path '%s'", path); + } + + const auto ip_conf_path = ip_conf_dir / "ipconfig.txt"; + if (fs::exists(ip_conf_path)) + fs::remove(ip_conf_path); + + std::ofstream f(ip_conf_path.string(), std::ofstream::binary); + if (f.is_open()) { + f.write(reinterpret_cast(buffer.data()), size); + f.close(); + } else { + ERROR("Failed to write IP configuration. Network functionality will not be available."); + } +} + void LxcContainer::start(const Configuration &configuration) { if (getuid() != 0) BOOST_THROW_EXCEPTION(std::runtime_error("You have to start the container as root")); @@ -131,11 +212,7 @@ void LxcContainer::start(const Configuration &configuration) { const auto log_path = SystemConfiguration::instance().log_dir(); set_config_item("lxc.logfile", utils::string_format("%s/container.log", log_path).c_str()); - if (fs::exists("/sys/class/net/anboxbr0")) { - set_config_item("lxc.network.type", "veth"); - set_config_item("lxc.network.flags", "up"); - set_config_item("lxc.network.link", "anboxbr0"); - } + setup_network(); #if 0 // Android uses namespaces as well so we have to allow nested namespaces for LXC diff --git a/src/anbox/container/lxc_container.h b/src/anbox/container/lxc_container.h index 886a9d4..bc87760 100644 --- a/src/anbox/container/lxc_container.h +++ b/src/anbox/container/lxc_container.h @@ -39,6 +39,7 @@ class LxcContainer : public Container { private: void set_config_item(const std::string &key, const std::string &value); void setup_id_maps(); + void setup_network(); State state_; lxc_container *container_; diff --git a/src/anbox/dbus/skeleton/service.cpp b/src/anbox/dbus/skeleton/service.cpp index 40910c1..8f309c2 100644 --- a/src/anbox/dbus/skeleton/service.cpp +++ b/src/anbox/dbus/skeleton/service.cpp @@ -25,10 +25,8 @@ namespace skeleton { std::shared_ptr Service::create_for_bus( const core::dbus::Bus::Ptr &bus, const std::shared_ptr &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()); + 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(bus, service, object, application_manager); } diff --git a/src/anbox/dbus/stub/application_manager.cpp b/src/anbox/dbus/stub/application_manager.cpp index e11176f..743d73e 100644 --- a/src/anbox/dbus/stub/application_manager.cpp +++ b/src/anbox/dbus/stub/application_manager.cpp @@ -25,7 +25,7 @@ namespace dbus { namespace stub { std::shared_ptr 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->add_object_for_path(anbox::dbus::interface::Service::path()); + auto object = service->object_for_path(anbox::dbus::interface::Service::path()); return std::make_shared(bus, service, object); } diff --git a/src/anbox/graphics/emugl/ColorBuffer.cpp b/src/anbox/graphics/emugl/ColorBuffer.cpp index c1efb6e..f336c83 100644 --- a/src/anbox/graphics/emugl/ColorBuffer.cpp +++ b/src/anbox/graphics/emugl/ColorBuffer.cpp @@ -13,17 +13,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#include "ColorBuffer.h" - -#include "DispatchTables.h" -#include "RenderThreadInfo.h" -#include "TextureDraw.h" -#include "TextureResize.h" - -#include "OpenGLESDispatch/EGLDispatch.h" +#include "anbox/graphics/emugl/ColorBuffer.h" +#include "anbox/graphics/emugl/DispatchTables.h" +#include "anbox/graphics/emugl/RenderThreadInfo.h" +#include "anbox/graphics/emugl/TextureDraw.h" +#include "anbox/graphics/emugl/TextureResize.h" #include "anbox/logger.h" +#include "external/android-emugl/host/include/OpenGLESDispatch/EGLDispatch.h" + #include namespace { @@ -35,22 +34,6 @@ inline void* SafePointerFromUInt(unsigned int handle) { return reinterpret_cast(static_cast(handle)); } -inline unsigned int SafeUIntFromPointer(const void* ptr) { -#if 1 - // Ignore the assert below to avoid crashing when running older - // system images, which might have buggy encoder libraries. Print - // an error message though. - if (reinterpret_cast(ptr) != static_cast(reinterpret_cast(ptr))) { - WARNING("Bad generic pointer %p", ptr); - } -#else - // Assertion error if the pointer contains a value that does not fit - // in an unsigned integer! - assert((uintptr_t)(ptr) == (unsigned int)(uintptr_t)(ptr)); -#endif - return static_cast(reinterpret_cast(ptr)); -} - // Lazily create and bind a framebuffer object to the current host context. // |fbo| is the address of the framebuffer object name. // |tex| is the name of a texture that is attached to the framebuffer object @@ -251,7 +234,7 @@ void ColorBuffer::subUpdate(int x, int y, int width, int height, bool ColorBuffer::blitFromCurrentReadBuffer() { RenderThreadInfo* tInfo = RenderThreadInfo::get(); - if (!tInfo->currContext.Ptr()) { + if (!tInfo->currContext) { // no Current context return false; } @@ -312,7 +295,7 @@ bool ColorBuffer::bindToTexture() { return false; } RenderThreadInfo* tInfo = RenderThreadInfo::get(); - if (!tInfo->currContext.Ptr()) { + if (!tInfo->currContext) { return false; } if (tInfo->currContext->isGL2()) { @@ -328,7 +311,7 @@ bool ColorBuffer::bindToRenderbuffer() { return false; } RenderThreadInfo* tInfo = RenderThreadInfo::get(); - if (!tInfo->currContext.Ptr()) { + if (!tInfo->currContext) { return false; } if (tInfo->currContext->isGL2()) { diff --git a/src/anbox/graphics/emugl/ColorBuffer.h b/src/anbox/graphics/emugl/ColorBuffer.h index 793a671..19bf44c 100644 --- a/src/anbox/graphics/emugl/ColorBuffer.h +++ b/src/anbox/graphics/emugl/ColorBuffer.h @@ -19,7 +19,6 @@ #include #include #include -#include "emugl/common/smart_ptr.h" #include @@ -135,6 +134,6 @@ class ColorBuffer { TextureResize* m_resizer; }; -typedef emugl::SmartPtr ColorBufferPtr; +typedef std::shared_ptr ColorBufferPtr; #endif diff --git a/src/anbox/graphics/emugl/DisplayManager.cpp b/src/anbox/graphics/emugl/DisplayManager.cpp index 0a624cd..91b842d 100644 --- a/src/anbox/graphics/emugl/DisplayManager.cpp +++ b/src/anbox/graphics/emugl/DisplayManager.cpp @@ -17,22 +17,22 @@ #include "DisplayManager.h" -namespace { -std::shared_ptr display_mgr; - -class NullDisplayManager : public DisplayManager { - public: - DisplayInfo display_info() const override { return {1280, 720}; } -}; +namespace anbox { +namespace graphics { +namespace emugl { +std::shared_ptr DisplayInfo::get() { + static auto info = std::make_shared(); + return info; } -DisplayManager::~DisplayManager() {} - -std::shared_ptr DisplayManager::get() { - if (!display_mgr) display_mgr = std::make_shared(); - return display_mgr; +void DisplayInfo::set_resolution(const std::uint32_t &vertical, const std::uint32_t horizontal) { + vertical_resolution_ = vertical; + horizontal_resolution_ = horizontal; } -void registerDisplayManager(const std::shared_ptr &mgr) { - display_mgr = mgr; -} +std::uint32_t DisplayInfo::vertical_resolution() const { return vertical_resolution_; } + +std::uint32_t DisplayInfo::horizontal_resolution() const { return horizontal_resolution_; } +} // namespace emugl +} // namespace graphics +} // namespace anbox diff --git a/src/anbox/graphics/emugl/DisplayManager.h b/src/anbox/graphics/emugl/DisplayManager.h index 857742f..fdba3cd 100644 --- a/src/anbox/graphics/emugl/DisplayManager.h +++ b/src/anbox/graphics/emugl/DisplayManager.h @@ -15,25 +15,32 @@ * */ -#ifndef DISPLAY_MANAGER_H_ -#define DISPLAY_MANAGER_H_ +#ifndef ANBOX_GRAPHICS_EMUGL_DISPLAY_INFO_H_ +#define ANBOX_GRAPHICS_EMUGL_DISPLAY_INFO_H_ +#include #include -class DisplayManager { +namespace anbox { +namespace graphics { +namespace emugl { +class DisplayInfo { public: - virtual ~DisplayManager(); + DisplayInfo() = default; - struct DisplayInfo { - int horizontal_resolution; - int vertical_resolution; - }; + static std::shared_ptr get(); - virtual DisplayInfo display_info() const = 0; + void set_resolution(const std::uint32_t &vertical, const std::uint32_t horizontal); - static std::shared_ptr get(); + std::uint32_t vertical_resolution() const; + std::uint32_t horizontal_resolution() const; + + private: + std::uint32_t vertical_resolution_ = 1280; + std::uint32_t horizontal_resolution_ = 720; }; - -void registerDisplayManager(const std::shared_ptr &mgr); +} // namespace emugl +} // namespace graphics +} // namespace anbox #endif diff --git a/src/anbox/graphics/emugl/ReadBuffer.cpp b/src/anbox/graphics/emugl/ReadBuffer.cpp index 3ff151d..d7bff7b 100644 --- a/src/anbox/graphics/emugl/ReadBuffer.cpp +++ b/src/anbox/graphics/emugl/ReadBuffer.cpp @@ -13,13 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#include "ReadBuffer.h" + +#include "anbox/graphics/emugl/ReadBuffer.h" +#include "anbox/logger.h" + #include #include #include -#include "anbox/logger.h" - ReadBuffer::ReadBuffer(size_t bufsize) { m_size = bufsize; m_buf = static_cast(malloc(m_size * sizeof(unsigned char))); diff --git a/src/anbox/graphics/emugl/ReadBuffer.h b/src/anbox/graphics/emugl/ReadBuffer.h index 86a486f..68c7545 100644 --- a/src/anbox/graphics/emugl/ReadBuffer.h +++ b/src/anbox/graphics/emugl/ReadBuffer.h @@ -16,7 +16,7 @@ #ifndef _READ_BUFFER_H #define _READ_BUFFER_H -#include "IOStream.h" +#include "external/android-emugl/host/include/libOpenglRender/IOStream.h" class ReadBuffer { public: @@ -34,4 +34,5 @@ class ReadBuffer { size_t m_size; size_t m_validData; }; + #endif diff --git a/src/anbox/graphics/emugl/RenderApi.cpp b/src/anbox/graphics/emugl/RenderApi.cpp index dc07604..69fce23 100644 --- a/src/anbox/graphics/emugl/RenderApi.cpp +++ b/src/anbox/graphics/emugl/RenderApi.cpp @@ -14,15 +14,14 @@ * limitations under the License. */ -#include "RenderApi.h" +#include "anbox/graphics/emugl/RenderApi.h" +#include "anbox/graphics/emugl/DispatchTables.h" -#include "DispatchTables.h" +#include "external/android-emugl/host/include/OpenGLESDispatch/EGLDispatch.h" +#include "external/android-emugl/host/include/OpenGLESDispatch/GLESv1Dispatch.h" +#include "external/android-emugl/host/include/OpenGLESDispatch/GLESv2Dispatch.h" -#include "OpenGLESDispatch/EGLDispatch.h" -#include "OpenGLESDispatch/GLESv1Dispatch.h" -#include "OpenGLESDispatch/GLESv2Dispatch.h" - -#include "emugl/common/crash_reporter.h" +#include "external/android-emugl/shared/emugl/common/crash_reporter.h" #include diff --git a/src/anbox/graphics/emugl/RenderApi.h b/src/anbox/graphics/emugl/RenderApi.h index 65cab84..150fdd6 100644 --- a/src/anbox/graphics/emugl/RenderApi.h +++ b/src/anbox/graphics/emugl/RenderApi.h @@ -21,7 +21,7 @@ #include -#include "emugl/common/logging.h" +#include "external/android-emugl/shared/emugl/common/logging.h" typedef struct { logger_t coarse; diff --git a/src/anbox/graphics/emugl/RenderContext.cpp b/src/anbox/graphics/emugl/RenderContext.cpp index ef47143..2909a49 100644 --- a/src/anbox/graphics/emugl/RenderContext.cpp +++ b/src/anbox/graphics/emugl/RenderContext.cpp @@ -13,7 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#include "RenderContext.h" + +#include "anbox/graphics/emugl/RenderContext.h" #include "OpenGLESDispatch/EGLDispatch.h" diff --git a/src/anbox/graphics/emugl/RenderContext.h b/src/anbox/graphics/emugl/RenderContext.h index fd2dccf..6e18a9b 100644 --- a/src/anbox/graphics/emugl/RenderContext.h +++ b/src/anbox/graphics/emugl/RenderContext.h @@ -13,14 +13,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + #ifndef _LIBRENDER_RENDER_CONTEXT_H #define _LIBRENDER_RENDER_CONTEXT_H -#include "GLDecoderContextData.h" -#include "emugl/common/smart_ptr.h" +#include "external/android-emugl/shared/OpenglCodecCommon/GLDecoderContextData.h" #include +#include + // A class used to model a guest EGLContext. This simply wraps a host // EGLContext, associated with an GLDecoderContextData instance that is // used to store copies of guest-side arrays. @@ -60,6 +62,6 @@ class RenderContext { GLDecoderContextData mContextData; }; -typedef emugl::SmartPtr RenderContextPtr; +typedef std::shared_ptr RenderContextPtr; #endif // _LIBRENDER_RENDER_CONTEXT_H diff --git a/src/anbox/graphics/emugl/RenderControl.cpp b/src/anbox/graphics/emugl/RenderControl.cpp index b479be0..26e9559 100644 --- a/src/anbox/graphics/emugl/RenderControl.cpp +++ b/src/anbox/graphics/emugl/RenderControl.cpp @@ -13,20 +13,20 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#include "RenderControl.h" - -#include "ChecksumCalculatorThreadInfo.h" -#include "DispatchTables.h" -#include "DisplayManager.h" -#include "RenderThreadInfo.h" -#include "Renderer.h" -#include "RendererConfig.h" - -#include "OpenGLESDispatch/EGLDispatch.h" +#include "anbox/graphics/emugl/RenderControl.h" +#include "anbox/graphics/emugl/DispatchTables.h" +#include "anbox/graphics/emugl/DisplayManager.h" +#include "anbox/graphics/emugl/RenderThreadInfo.h" +#include "anbox/graphics/emugl/Renderer.h" +#include "anbox/graphics/emugl/RendererConfig.h" #include "anbox/graphics/layer_composer.h" #include "anbox/logger.h" +#include "external/android-emugl/shared/OpenglCodecCommon/ChecksumCalculatorThreadInfo.h" +#include "external/android-emugl/host/include/OpenGLESDispatch/EGLDispatch.h" + + #include #include @@ -169,10 +169,10 @@ static EGLint rcGetFBParam(EGLint param) { switch (param) { case FB_WIDTH: - ret = DisplayManager::get()->display_info().horizontal_resolution; + ret = static_cast(anbox::graphics::emugl::DisplayInfo::get()->vertical_resolution()); break; case FB_HEIGHT: - ret = DisplayManager::get()->display_info().vertical_resolution; + ret = static_cast(anbox::graphics::emugl::DisplayInfo::get()->horizontal_resolution()); break; case FB_XDPI: ret = 72; // XXX: should be implemented @@ -360,12 +360,12 @@ int rcGetNumDisplays() { int rcGetDisplayWidth(uint32_t display_id) { (void)display_id; - return DisplayManager::get()->display_info().horizontal_resolution; + return static_cast(anbox::graphics::emugl::DisplayInfo::get()->vertical_resolution()); } int rcGetDisplayHeight(uint32_t display_id) { (void)display_id; - return DisplayManager::get()->display_info().vertical_resolution; + return static_cast(anbox::graphics::emugl::DisplayInfo::get()->horizontal_resolution()); } int rcGetDisplayDpiX(uint32_t display_id) { diff --git a/src/anbox/graphics/emugl/RenderControl.h b/src/anbox/graphics/emugl/RenderControl.h index 82f1635..5495560 100644 --- a/src/anbox/graphics/emugl/RenderControl.h +++ b/src/anbox/graphics/emugl/RenderControl.h @@ -13,9 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + #ifndef _RENDER_CONTROL_H #define _RENDER_CONTROL_H +// Generated with emugl at build time #include "renderControl_dec.h" #include diff --git a/src/anbox/graphics/emugl/RenderThread.cpp b/src/anbox/graphics/emugl/RenderThread.cpp index 9dbc569..7e6dff7 100644 --- a/src/anbox/graphics/emugl/RenderThread.cpp +++ b/src/anbox/graphics/emugl/RenderThread.cpp @@ -13,32 +13,31 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#include "RenderThread.h" - -#include "ReadBuffer.h" -#include "RenderControl.h" -#include "RenderThreadInfo.h" -#include "Renderer.h" -#include "TimeUtils.h" - -#include "../../../shared/OpenglCodecCommon/ChecksumCalculatorThreadInfo.h" -#include "OpenGLESDispatch/EGLDispatch.h" -#include "OpenGLESDispatch/GLESv1Dispatch.h" -#include "OpenGLESDispatch/GLESv2Dispatch.h" +#include "anbox/graphics/emugl/RenderThread.h" +#include "anbox/graphics/emugl/ReadBuffer.h" +#include "anbox/graphics/emugl/RenderControl.h" +#include "anbox/graphics/emugl/RenderThreadInfo.h" +#include "anbox/graphics/emugl/Renderer.h" +#include "anbox/graphics/emugl/TimeUtils.h" #include "anbox/logger.h" +#include "external/android-emugl/shared/OpenglCodecCommon/ChecksumCalculatorThreadInfo.h" +#include "external/android-emugl/host/include/OpenGLESDispatch/EGLDispatch.h" +#include "external/android-emugl/host/include/OpenGLESDispatch/GLESv1Dispatch.h" +#include "external/android-emugl/host/include/OpenGLESDispatch/GLESv2Dispatch.h" + #define STREAM_BUFFER_SIZE 4 * 1024 * 1024 -RenderThread::RenderThread(const std::shared_ptr &renderer, IOStream *stream, emugl::Mutex *lock) - : emugl::Thread(), renderer_(renderer), m_lock(lock), m_stream(stream) {} +RenderThread::RenderThread(const std::shared_ptr &renderer, IOStream *stream, std::mutex &m) + : emugl::Thread(), renderer_(renderer), m_lock(m), m_stream(stream) {} RenderThread::~RenderThread() { forceStop(); } -RenderThread *RenderThread::create(const std::shared_ptr &renderer, IOStream *stream, emugl::Mutex *lock) { - return new RenderThread(renderer, stream, lock); +RenderThread *RenderThread::create(const std::shared_ptr &renderer, IOStream *stream, std::mutex &m) { + return new RenderThread(renderer, stream, m); } void RenderThread::forceStop() { m_stream->forceStop(); } @@ -62,7 +61,8 @@ intptr_t RenderThread::main() { do { progress = false; - m_lock->lock(); + std::unique_lock l(m_lock); + size_t last = threadInfo.m_glDec.decode(readBuf.buf(), readBuf.validData(), m_stream); if (last > 0) { @@ -83,8 +83,6 @@ intptr_t RenderThread::main() { progress = true; } - m_lock->unlock(); - } while (progress); } diff --git a/src/anbox/graphics/emugl/RenderThread.h b/src/anbox/graphics/emugl/RenderThread.h index 5e6ae11..74cde6d 100644 --- a/src/anbox/graphics/emugl/RenderThread.h +++ b/src/anbox/graphics/emugl/RenderThread.h @@ -13,15 +13,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + #ifndef _LIB_OPENGL_RENDER_RENDER_THREAD_H #define _LIB_OPENGL_RENDER_RENDER_THREAD_H -#include "IOStream.h" +#include "external/android-emugl/host/include/libOpenglRender/IOStream.h" -#include "emugl/common/mutex.h" #include "emugl/common/thread.h" #include +#include class Renderer; @@ -36,7 +37,7 @@ class RenderThread : public emugl::Thread { // decoding operations between all threads. // TODO(digit): Why is this needed here? Shouldn't this be handled // by the decoders themselves or at a lower-level? - static RenderThread* create(const std::shared_ptr& renderer, IOStream* stream, emugl::Mutex* mutex); + static RenderThread* create(const std::shared_ptr& renderer, IOStream* stream, std::mutex &m); // Destructor. virtual ~RenderThread(); @@ -51,12 +52,12 @@ class RenderThread : public emugl::Thread { private: RenderThread(); // No default constructor - RenderThread(const std::shared_ptr& renderer, IOStream* stream, emugl::Mutex* mutex); + RenderThread(const std::shared_ptr& renderer, IOStream* stream, std::mutex &m); virtual intptr_t main(); std::shared_ptr renderer_; - emugl::Mutex* m_lock; + std::mutex &m_lock; IOStream* m_stream; }; diff --git a/src/anbox/graphics/emugl/RenderThreadInfo.cpp b/src/anbox/graphics/emugl/RenderThreadInfo.cpp index 500a0b4..bc87705 100644 --- a/src/anbox/graphics/emugl/RenderThreadInfo.cpp +++ b/src/anbox/graphics/emugl/RenderThreadInfo.cpp @@ -14,10 +14,10 @@ * limitations under the License. */ -#include "RenderThreadInfo.h" +#include "anbox/graphics/emugl/RenderThreadInfo.h" -#include "emugl/common/lazy_instance.h" -#include "emugl/common/thread_store.h" +#include "external/android-emugl/shared/emugl/common/lazy_instance.h" +#include "external/android-emugl/shared/emugl/common/thread_store.h" namespace { class ThreadInfoStore : public ::emugl::ThreadStore { diff --git a/src/anbox/graphics/emugl/RenderThreadInfo.h b/src/anbox/graphics/emugl/RenderThreadInfo.h index f246ae5..9440d61 100644 --- a/src/anbox/graphics/emugl/RenderThreadInfo.h +++ b/src/anbox/graphics/emugl/RenderThreadInfo.h @@ -13,13 +13,17 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + #ifndef _LIB_OPENGL_RENDER_THREAD_INFO_H #define _LIB_OPENGL_RENDER_THREAD_INFO_H -#include "GLESv1Decoder.h" -#include "GLESv2Decoder.h" -#include "RenderContext.h" -#include "WindowSurface.h" +#include "anbox/graphics/emugl/RenderContext.h" +#include "anbox/graphics/emugl/WindowSurface.h" + +#include "external/android-emugl/host/libs/GLESv1_dec/GLESv1Decoder.h" +#include "external/android-emugl/host/libs/GLESv2_dec/GLESv2Decoder.h" + +// Generated with emugl at build time #include "renderControl_dec.h" #include diff --git a/src/anbox/graphics/emugl/Renderable.cpp b/src/anbox/graphics/emugl/Renderable.cpp index e75d1a2..06e4273 100644 --- a/src/anbox/graphics/emugl/Renderable.cpp +++ b/src/anbox/graphics/emugl/Renderable.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "Renderable.h" +#include "anbox/graphics/emugl/Renderable.h" Renderable::Renderable(const std::string &name, const std::uint32_t &buffer, const anbox::graphics::Rect &screen_position, diff --git a/src/anbox/graphics/emugl/Renderer.cpp b/src/anbox/graphics/emugl/Renderer.cpp index f4e5a76..776de49 100644 --- a/src/anbox/graphics/emugl/Renderer.cpp +++ b/src/anbox/graphics/emugl/Renderer.cpp @@ -14,19 +14,18 @@ * limitations under the License. */ -#include "Renderer.h" - -#include "DispatchTables.h" -#include "RenderThreadInfo.h" -#include "TimeUtils.h" -#include "gles2_dec.h" - -#include "OpenGLESDispatch/EGLDispatch.h" - +#include "anbox/graphics/emugl/Renderer.h" +#include "anbox/graphics/emugl/DispatchTables.h" +#include "anbox/graphics/emugl/RenderThreadInfo.h" +#include "anbox/graphics/emugl/TimeUtils.h" #include "anbox/graphics/gl_extensions.h" - #include "anbox/logger.h" +#include "external/android-emugl/host/include/OpenGLESDispatch/EGLDispatch.h" + +// Generated with emugl at build time +#include "gles2_dec.h" + #include #include @@ -361,13 +360,14 @@ HandleType Renderer::genHandle() { HandleType Renderer::createColorBuffer(int p_width, int p_height, GLenum p_internalFormat) { - emugl::Mutex::AutoLock mutex(m_lock); + std::unique_lock l(m_lock); + HandleType ret = 0; ColorBufferPtr cb(ColorBuffer::create( getDisplay(), p_width, p_height, p_internalFormat, getCaps().has_eglimage_texture_2d, m_colorBufferHelper)); - if (cb.Ptr() != NULL) { + if (cb) { ret = genHandle(); m_colorbuffers[ret].cb = cb; m_colorbuffers[ret].refcount = 1; @@ -377,7 +377,8 @@ HandleType Renderer::createColorBuffer(int p_width, int p_height, HandleType Renderer::createRenderContext(int p_config, HandleType p_share, bool p_isGL2) { - emugl::Mutex::AutoLock mutex(m_lock); + std::unique_lock l(m_lock); + HandleType ret = 0; const RendererConfig *config = getConfigs()->get(p_config); @@ -394,11 +395,11 @@ HandleType Renderer::createRenderContext(int p_config, HandleType p_share, share = (*s).second; } EGLContext sharedContext = - share.Ptr() ? share->getEGLContext() : EGL_NO_CONTEXT; + share ? share->getEGLContext() : EGL_NO_CONTEXT; RenderContextPtr rctx(RenderContext::create( m_eglDisplay, config->getEglConfig(), sharedContext, p_isGL2)); - if (rctx.Ptr() != NULL) { + if (rctx) { ret = genHandle(); m_contexts[ret] = rctx; RenderThreadInfo *tinfo = RenderThreadInfo::get(); @@ -409,7 +410,7 @@ HandleType Renderer::createRenderContext(int p_config, HandleType p_share, HandleType Renderer::createWindowSurface(int p_config, int p_width, int p_height) { - emugl::Mutex::AutoLock mutex(m_lock); + std::unique_lock l(m_lock); HandleType ret = 0; @@ -420,7 +421,7 @@ HandleType Renderer::createWindowSurface(int p_config, int p_width, WindowSurfacePtr win(WindowSurface::create( getDisplay(), config->getEglConfig(), p_width, p_height)); - if (win.Ptr() != NULL) { + if (win) { ret = genHandle(); m_windows[ret] = std::pair(win, 0); RenderThreadInfo *tinfo = RenderThreadInfo::get(); @@ -431,7 +432,8 @@ HandleType Renderer::createWindowSurface(int p_config, int p_width, } void Renderer::drainRenderContext() { - emugl::Mutex::AutoLock mutex(m_lock); + std::unique_lock l(m_lock); + RenderThreadInfo *tinfo = RenderThreadInfo::get(); if (tinfo->m_contextSet.empty()) return; for (std::set::iterator it = tinfo->m_contextSet.begin(); @@ -443,7 +445,8 @@ void Renderer::drainRenderContext() { } void Renderer::drainWindowSurface() { - emugl::Mutex::AutoLock mutex(m_lock); + std::unique_lock l(m_lock); + RenderThreadInfo *tinfo = RenderThreadInfo::get(); if (tinfo->m_windowSet.empty()) return; for (std::set::iterator it = tinfo->m_windowSet.begin(); @@ -466,7 +469,8 @@ void Renderer::drainWindowSurface() { } void Renderer::DestroyRenderContext(HandleType p_context) { - emugl::Mutex::AutoLock mutex(m_lock); + std::unique_lock l(m_lock); + m_contexts.erase(p_context); RenderThreadInfo *tinfo = RenderThreadInfo::get(); if (tinfo->m_contextSet.empty()) return; @@ -474,7 +478,8 @@ void Renderer::DestroyRenderContext(HandleType p_context) { } void Renderer::DestroyWindowSurface(HandleType p_surface) { - emugl::Mutex::AutoLock mutex(m_lock); + std::unique_lock l(m_lock); + if (m_windows.find(p_surface) != m_windows.end()) { m_windows.erase(p_surface); RenderThreadInfo *tinfo = RenderThreadInfo::get(); @@ -484,7 +489,8 @@ void Renderer::DestroyWindowSurface(HandleType p_surface) { } int Renderer::openColorBuffer(HandleType p_colorbuffer) { - emugl::Mutex::AutoLock mutex(m_lock); + std::unique_lock l(m_lock); + ColorBufferMap::iterator c(m_colorbuffers.find(p_colorbuffer)); if (c == m_colorbuffers.end()) { // bad colorbuffer handle @@ -496,7 +502,8 @@ int Renderer::openColorBuffer(HandleType p_colorbuffer) { } void Renderer::closeColorBuffer(HandleType p_colorbuffer) { - emugl::Mutex::AutoLock mutex(m_lock); + std::unique_lock l(m_lock); + ColorBufferMap::iterator c(m_colorbuffers.find(p_colorbuffer)); if (c == m_colorbuffers.end()) { // This is harmless: it is normal for guest system to issue @@ -511,7 +518,7 @@ void Renderer::closeColorBuffer(HandleType p_colorbuffer) { } bool Renderer::flushWindowSurfaceColorBuffer(HandleType p_surface) { - emugl::Mutex::AutoLock mutex(m_lock); + std::unique_lock l(m_lock); WindowSurfaceMap::iterator w(m_windows.find(p_surface)); if (w == m_windows.end()) { @@ -521,7 +528,10 @@ bool Renderer::flushWindowSurfaceColorBuffer(HandleType p_surface) { return false; } - WindowSurface *surface = (*w).second.first.Ptr(); + auto surface = (*w).second.first; + if (!surface) + return false; + surface->flushColorBuffer(); return true; @@ -529,7 +539,7 @@ bool Renderer::flushWindowSurfaceColorBuffer(HandleType p_surface) { bool Renderer::setWindowSurfaceColorBuffer(HandleType p_surface, HandleType p_colorbuffer) { - emugl::Mutex::AutoLock mutex(m_lock); + std::unique_lock l(m_lock); WindowSurfaceMap::iterator w(m_windows.find(p_surface)); if (w == m_windows.end()) { @@ -553,7 +563,7 @@ bool Renderer::setWindowSurfaceColorBuffer(HandleType p_surface, void Renderer::readColorBuffer(HandleType p_colorbuffer, int x, int y, int width, int height, GLenum format, GLenum type, void *pixels) { - emugl::Mutex::AutoLock mutex(m_lock); + std::unique_lock l(m_lock); ColorBufferMap::iterator c(m_colorbuffers.find(p_colorbuffer)); if (c == m_colorbuffers.end()) { @@ -567,7 +577,7 @@ void Renderer::readColorBuffer(HandleType p_colorbuffer, int x, int y, bool Renderer::updateColorBuffer(HandleType p_colorbuffer, int x, int y, int width, int height, GLenum format, GLenum type, void *pixels) { - emugl::Mutex::AutoLock mutex(m_lock); + std::unique_lock l(m_lock); ColorBufferMap::iterator c(m_colorbuffers.find(p_colorbuffer)); if (c == m_colorbuffers.end()) { @@ -581,7 +591,7 @@ bool Renderer::updateColorBuffer(HandleType p_colorbuffer, int x, int y, } bool Renderer::bindColorBufferToTexture(HandleType p_colorbuffer) { - emugl::Mutex::AutoLock mutex(m_lock); + std::unique_lock l(m_lock); ColorBufferMap::iterator c(m_colorbuffers.find(p_colorbuffer)); if (c == m_colorbuffers.end()) { @@ -593,7 +603,7 @@ bool Renderer::bindColorBufferToTexture(HandleType p_colorbuffer) { } bool Renderer::bindColorBufferToRenderbuffer(HandleType p_colorbuffer) { - emugl::Mutex::AutoLock mutex(m_lock); + std::unique_lock l(m_lock); ColorBufferMap::iterator c(m_colorbuffers.find(p_colorbuffer)); if (c == m_colorbuffers.end()) { @@ -606,7 +616,7 @@ bool Renderer::bindColorBufferToRenderbuffer(HandleType p_colorbuffer) { bool Renderer::bindContext(HandleType p_context, HandleType p_drawSurface, HandleType p_readSurface) { - emugl::Mutex::AutoLock mutex(m_lock); + std::unique_lock l(m_lock); WindowSurfacePtr draw(NULL), read(NULL); RenderContextPtr ctx(NULL); @@ -654,7 +664,7 @@ bool Renderer::bindContext(HandleType p_context, HandleType p_drawSurface, // RenderThreadInfo *tinfo = RenderThreadInfo::get(); WindowSurfacePtr bindDraw, bindRead; - if (draw.Ptr() == NULL && read.Ptr() == NULL) { + if (!draw && !read) { // Unbind the current read and draw surfaces from the context bindDraw = tinfo->currDrawSurf; bindRead = tinfo->currReadSurf; @@ -663,8 +673,8 @@ bool Renderer::bindContext(HandleType p_context, HandleType p_drawSurface, bindRead = read; } - if (bindDraw.Ptr() != NULL && bindRead.Ptr() != NULL) { - if (bindDraw.Ptr() != bindRead.Ptr()) { + if (bindDraw && bindRead) { + if (bindDraw != bindRead) { bindDraw->bind(ctx, WindowSurface::BIND_DRAW); bindRead->bind(ctx, WindowSurface::BIND_READ); } else { diff --git a/src/anbox/graphics/emugl/Renderer.h b/src/anbox/graphics/emugl/Renderer.h index 5258639..cfcc0b2 100644 --- a/src/anbox/graphics/emugl/Renderer.h +++ b/src/anbox/graphics/emugl/Renderer.h @@ -16,14 +16,12 @@ #ifndef _LIBRENDER_FRAMEBUFFER_H #define _LIBRENDER_FRAMEBUFFER_H -#include "ColorBuffer.h" -#include "RenderContext.h" -#include "RendererConfig.h" -#include "TextureDraw.h" -#include "WindowSurface.h" -#include "emugl/common/mutex.h" - -#include "Renderable.h" +#include "anbox/graphics/emugl/ColorBuffer.h" +#include "anbox/graphics/emugl/RenderContext.h" +#include "anbox/graphics/emugl/RendererConfig.h" +#include "anbox/graphics/emugl/TextureDraw.h" +#include "anbox/graphics/emugl/WindowSurface.h" +#include "anbox/graphics/emugl/Renderable.h" #include "anbox/graphics/primitives.h" #include "anbox/graphics/program_family.h" @@ -32,6 +30,7 @@ #include #include +#include #include @@ -257,7 +256,7 @@ class Renderer : public anbox::graphics::Renderer { private: static Renderer* s_renderer; static HandleType s_nextHandle; - emugl::Mutex m_lock; + std::mutex m_lock; RendererConfigList* m_configs; RendererCaps m_caps; EGLDisplay m_eglDisplay; diff --git a/src/anbox/graphics/emugl/RendererConfig.cpp b/src/anbox/graphics/emugl/RendererConfig.cpp index 83b044f..02b6b78 100644 --- a/src/anbox/graphics/emugl/RendererConfig.cpp +++ b/src/anbox/graphics/emugl/RendererConfig.cpp @@ -12,15 +12,14 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "RendererConfig.h" +#include "anbox/graphics/emugl/RendererConfig.h" +#include "anbox/logger.h" -#include "OpenGLESDispatch/EGLDispatch.h" +#include "external/android-emugl/host/include/OpenGLESDispatch/EGLDispatch.h" #include #include -#include "anbox/logger.h" - namespace { const GLuint kConfigAttributes[] = { EGL_DEPTH_SIZE, // must be first - see getDepthSize() diff --git a/src/anbox/graphics/emugl/TextureDraw.cpp b/src/anbox/graphics/emugl/TextureDraw.cpp index bb421bd..8bb1e59 100644 --- a/src/anbox/graphics/emugl/TextureDraw.cpp +++ b/src/anbox/graphics/emugl/TextureDraw.cpp @@ -12,9 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "TextureDraw.h" - -#include "DispatchTables.h" +#include "anbox/graphics/emugl/TextureDraw.h" +#include "anbox/graphics/emugl/DispatchTables.h" +#include "anbox/logger.h" #include #include @@ -22,8 +22,6 @@ #include -#include "anbox/logger.h" - // M_PI isn't defined in C++ (when strict ISO compliance is enabled) #ifndef M_PI #define M_PI 3.14159265358979323846264338327 @@ -97,16 +95,13 @@ const GLint kIndicesLen = sizeof(kIndices) / sizeof(kIndices[0]); } // namespace -TextureDraw::TextureDraw(EGLDisplay display) - : mDisplay(display), - mVertexShader(0), +TextureDraw::TextureDraw(EGLDisplay) + : mVertexShader(0), mFragmentShader(0), mProgram(0), mPositionSlot(-1), mInCoordSlot(-1), - mTextureSlot(-1), - mRotationSlot(-1), - mTranslationSlot(-1) { + mTextureSlot(-1) { // Create shaders and program. mVertexShader = createShader(GL_VERTEX_SHADER, kVertexShaderSource); mFragmentShader = createShader(GL_FRAGMENT_SHADER, kFragmentShaderSource); diff --git a/src/anbox/graphics/emugl/TextureDraw.h b/src/anbox/graphics/emugl/TextureDraw.h index 5f6ef96..10f796c 100644 --- a/src/anbox/graphics/emugl/TextureDraw.h +++ b/src/anbox/graphics/emugl/TextureDraw.h @@ -42,15 +42,12 @@ class TextureDraw { bool draw(GLuint texture); private: - EGLDisplay mDisplay; GLuint mVertexShader; GLuint mFragmentShader; GLuint mProgram; GLint mPositionSlot; GLint mInCoordSlot; GLint mTextureSlot; - GLint mRotationSlot; - GLint mTranslationSlot; GLuint mVertexBuffer; GLuint mIndexBuffer; }; diff --git a/src/anbox/graphics/emugl/TextureResize.cpp b/src/anbox/graphics/emugl/TextureResize.cpp index 131be30..49407b1 100644 --- a/src/anbox/graphics/emugl/TextureResize.cpp +++ b/src/anbox/graphics/emugl/TextureResize.cpp @@ -13,17 +13,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#include "TextureResize.h" -#include "DispatchTables.h" +#include "anbox/graphics/emugl/TextureResize.h" +#include "anbox/graphics/emugl/DispatchTables.h" +#include "anbox/logger.h" #include #include #include #include -#include "anbox/logger.h" - #define MAX_FACTOR_POWER 4 static const char kCommonShaderSource[] = diff --git a/src/anbox/graphics/emugl/TextureResize.h b/src/anbox/graphics/emugl/TextureResize.h index 8024761..fece99d 100644 --- a/src/anbox/graphics/emugl/TextureResize.h +++ b/src/anbox/graphics/emugl/TextureResize.h @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + #ifndef _LIBRENDER_TEXTURERESIZE_H #define _LIBRENDER_TEXTURERESIZE_H diff --git a/src/anbox/graphics/emugl/TimeUtils.cpp b/src/anbox/graphics/emugl/TimeUtils.cpp index 854bb79..8e80cd4 100644 --- a/src/anbox/graphics/emugl/TimeUtils.cpp +++ b/src/anbox/graphics/emugl/TimeUtils.cpp @@ -13,7 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#include "TimeUtils.h" + +#include "anbox/graphics/emugl/TimeUtils.h" #include #include diff --git a/src/anbox/graphics/emugl/TimeUtils.h b/src/anbox/graphics/emugl/TimeUtils.h index bc4fd1c..116e37a 100644 --- a/src/anbox/graphics/emugl/TimeUtils.h +++ b/src/anbox/graphics/emugl/TimeUtils.h @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + #ifndef _TIME_UTILS_H #define _TIME_UTILS_H diff --git a/src/anbox/graphics/emugl/WindowSurface.cpp b/src/anbox/graphics/emugl/WindowSurface.cpp index 669ce6d..fd60aba 100644 --- a/src/anbox/graphics/emugl/WindowSurface.cpp +++ b/src/anbox/graphics/emugl/WindowSurface.cpp @@ -13,17 +13,18 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#include "WindowSurface.h" -#include "RendererConfig.h" -#include "OpenGLESDispatch/EGLDispatch.h" +#include "anbox/graphics/emugl/WindowSurface.h" +#include "anbox/graphics/emugl/RendererConfig.h" +#include "anbox/logger.h" + +#include "external/android-emugl/host/include/OpenGLESDispatch/EGLDispatch.h" #include #include #include -#include "anbox/logger.h" WindowSurface::WindowSurface(EGLDisplay display, EGLConfig config) : mSurface(NULL), @@ -84,7 +85,7 @@ void WindowSurface::bind(RenderContextPtr p_ctx, BindType p_bindType) { } bool WindowSurface::flushColorBuffer() { - if (!mAttachedColorBuffer.Ptr()) { + if (!mAttachedColorBuffer) { return true; } if (!mWidth || !mHeight) { @@ -98,7 +99,7 @@ bool WindowSurface::flushColorBuffer() { return false; } - if (!mDrawContext.Ptr()) { + if (!mDrawContext) { ERROR("Draw context is NULL"); return false; } diff --git a/src/anbox/graphics/emugl/WindowSurface.h b/src/anbox/graphics/emugl/WindowSurface.h index 62c497d..4b35ddb 100644 --- a/src/anbox/graphics/emugl/WindowSurface.h +++ b/src/anbox/graphics/emugl/WindowSurface.h @@ -16,10 +16,8 @@ #ifndef _LIBRENDER_WINDOW_SURFACE_H #define _LIBRENDER_WINDOW_SURFACE_H -#include "ColorBuffer.h" -#include "RenderContext.h" - -#include "emugl/common/smart_ptr.h" +#include "anbox/graphics/emugl/ColorBuffer.h" +#include "anbox/graphics/emugl/RenderContext.h" #include #include @@ -94,6 +92,6 @@ class WindowSurface { EGLDisplay mDisplay; }; -typedef emugl::SmartPtr WindowSurfacePtr; +typedef std::shared_ptr WindowSurfacePtr; #endif // _LIBRENDER_WINDOW_SURFACE_H diff --git a/src/anbox/graphics/opengles_message_processor.cpp b/src/anbox/graphics/opengles_message_processor.cpp index bd06793..e54c205 100644 --- a/src/anbox/graphics/opengles_message_processor.cpp +++ b/src/anbox/graphics/opengles_message_processor.cpp @@ -29,7 +29,7 @@ namespace anbox { namespace graphics { -emugl::Mutex OpenGlesMessageProcessor::global_lock{}; +std::mutex OpenGlesMessageProcessor::global_lock{}; OpenGlesMessageProcessor::OpenGlesMessageProcessor( const std::shared_ptr &renderer, @@ -43,7 +43,7 @@ OpenGlesMessageProcessor::OpenGlesMessageProcessor( boost::asio::buffer(&client_flags, sizeof(unsigned int))); if (err) ERROR("%s", err.message()); - render_thread_.reset(RenderThread::create(renderer, stream_.get(), &global_lock)); + render_thread_.reset(RenderThread::create(renderer, stream_.get(), std::ref(global_lock))); if (!render_thread_->start()) BOOST_THROW_EXCEPTION( std::runtime_error("Failed to start renderer thread")); diff --git a/src/anbox/graphics/opengles_message_processor.h b/src/anbox/graphics/opengles_message_processor.h index 45f4c01..8f3bd7a 100644 --- a/src/anbox/graphics/opengles_message_processor.h +++ b/src/anbox/graphics/opengles_message_processor.h @@ -18,16 +18,15 @@ #ifndef ANBOX_GRAPHICS_OPENGLES_MESSAGE_PROCESSOR_H_ #define ANBOX_GRAPHICS_OPENGLES_MESSAGE_PROCESSOR_H_ -#include - -#include - #include "anbox/network/message_processor.h" #include "anbox/network/socket_connection.h" #include "anbox/network/socket_messenger.h" #include "anbox/runtime.h" -#include "external/android-emugl/shared/emugl/common/mutex.h" +#include + +#include +#include class IOStream; class RenderThread; @@ -45,7 +44,7 @@ class OpenGlesMessageProcessor : public network::MessageProcessor { bool process_data(const std::vector &data) override; private: - static emugl::Mutex global_lock; + static std::mutex global_lock; std::shared_ptr messenger_; std::shared_ptr stream_; diff --git a/src/anbox/logger.cpp b/src/anbox/logger.cpp index f59803d..2e184d8 100644 --- a/src/anbox/logger.cpp +++ b/src/anbox/logger.cpp @@ -64,6 +64,10 @@ struct BoostLogLogger : public anbox::Logger { severity_ = severity; } + Severity GetSeverity() override { + return severity_; + } + void Log(Severity severity, const std::string& message, const boost::optional& loc) override { if (!initialized_) Init(); diff --git a/src/anbox/logger.h b/src/anbox/logger.h index 6e797b6..e9b06e4 100644 --- a/src/anbox/logger.h +++ b/src/anbox/logger.h @@ -51,6 +51,7 @@ class Logger : public DoNotCopyOrMove { bool SetSeverityFromString(const std::string &severity); virtual void SetSeverity(const Severity& severity) = 0; + virtual Severity GetSeverity() = 0; virtual void Log(Severity severity, const std::string& message, const boost::optional& location) = 0; diff --git a/src/anbox/network/published_socket_connector.cpp b/src/anbox/network/published_socket_connector.cpp index 5e1a9b6..50e8fc5 100644 --- a/src/anbox/network/published_socket_connector.cpp +++ b/src/anbox/network/published_socket_connector.cpp @@ -33,7 +33,7 @@ PublishedSocketConnector::PublishedSocketConnector( start_accept(); } -PublishedSocketConnector::~PublishedSocketConnector() {} +PublishedSocketConnector::~PublishedSocketConnector() noexcept {} void PublishedSocketConnector::start_accept() { auto socket = std::make_shared(runtime_->service()); diff --git a/src/anbox/network/tcp_socket_connector.cpp b/src/anbox/network/tcp_socket_connector.cpp index 00d33c2..63e8325 100644 --- a/src/anbox/network/tcp_socket_connector.cpp +++ b/src/anbox/network/tcp_socket_connector.cpp @@ -34,7 +34,7 @@ TcpSocketConnector::TcpSocketConnector( start_accept(); } -TcpSocketConnector::~TcpSocketConnector() { acceptor_.cancel(); } +TcpSocketConnector::~TcpSocketConnector() noexcept { acceptor_.cancel(); } void TcpSocketConnector::start_accept() { auto socket = diff --git a/src/anbox/qemu/adb_message_processor.cpp b/src/anbox/qemu/adb_message_processor.cpp index f41b9de..97135cb 100644 --- a/src/anbox/qemu/adb_message_processor.cpp +++ b/src/anbox/qemu/adb_message_processor.cpp @@ -16,10 +16,10 @@ */ #include "anbox/qemu/adb_message_processor.h" -#include "anbox/logger.h" #include "anbox/network/delegate_connection_creator.h" #include "anbox/network/delegate_message_processor.h" #include "anbox/network/tcp_socket_messenger.h" +#include "anbox/utils.h" #include #include @@ -27,18 +27,23 @@ namespace { const unsigned short default_adb_client_port{5037}; const unsigned short default_host_listen_port{6664}; +constexpr const char *loopback_address{"127.0.0.1"}; const std::string accept_command{"accept"}; const std::string ok_command{"ok"}; const std::string ko_command{"ko"}; const std::string start_command{"start"}; +// This timeount should be too high to not cause a too long wait time for the +// user until we connect to the adb host instance after it appeared and not +// too short to not put unnecessary burden on the CPU. const boost::posix_time::seconds default_adb_wait_time{1}; -static std::mutex active_instance; } using namespace std::placeholders; namespace anbox { namespace qemu { +std::mutex AdbMessageProcessor::active_instance_{}; + AdbMessageProcessor::AdbMessageProcessor( const std::shared_ptr &rt, const std::shared_ptr &messenger) @@ -46,12 +51,14 @@ AdbMessageProcessor::AdbMessageProcessor( state_(waiting_for_guest_accept_command), expected_command_(accept_command), messenger_(messenger), - host_notify_timer_(rt->service()) {} + host_notify_timer_(rt->service()), + lock_(active_instance_, std::defer_lock) {} AdbMessageProcessor::~AdbMessageProcessor() { state_ = closed_by_host; + + host_notify_timer_.cancel(); host_connector_.reset(); - active_instance.unlock(); } void AdbMessageProcessor::advance_state() { @@ -61,9 +68,10 @@ void AdbMessageProcessor::advance_state() { // running we don't have to do anything here until that one is done. // The container directly starts a second connection once the first // one is established but will not use it until the active one is closed. - active_instance.lock(); + lock_.lock(); if (state_ == closed_by_host) { + host_notify_timer_.cancel(); host_connector_.reset(); return; } @@ -100,9 +108,12 @@ void AdbMessageProcessor::advance_state() { } void AdbMessageProcessor::wait_for_host_connection() { + if (state_ == closed_by_host || state_ == closed_by_container) + return; + if (!host_connector_) { host_connector_ = std::make_shared( - boost::asio::ip::address_v4::from_string("127.0.0.1"), + boost::asio::ip::address_v4::from_string(loopback_address), default_host_listen_port, runtime_, std::make_shared< network::DelegateConnectionCreator>( @@ -113,18 +124,19 @@ void AdbMessageProcessor::wait_for_host_connection() { // Notify the adb host instance so that it knows on which port our // proxy is waiting for incoming connections. auto messenger = std::make_shared( - boost::asio::ip::address_v4::from_string("127.0.0.1"), - default_adb_client_port, runtime_); - auto message = - utils::string_format("host:emulator:%d", default_host_listen_port); - auto handshake = - utils::string_format("%04x%s", message.size(), message.c_str()); + boost::asio::ip::address_v4::from_string(loopback_address), default_adb_client_port, runtime_); + auto message = utils::string_format("host:emulator:%d", default_host_listen_port); + auto handshake = utils::string_format("%04x%s", message.size(), message.c_str()); messenger->send(handshake.data(), handshake.size()); - } catch (std::exception &) { + } catch (...) { // Try again later when the host adb service is maybe available + host_notify_timer_.cancel(); host_notify_timer_.expires_from_now(default_adb_wait_time); - host_notify_timer_.async_wait( - [&](const boost::system::error_code &) { wait_for_host_connection(); }); + host_notify_timer_.async_wait([this](const boost::system::error_code &err) { + if (err) + return; + wait_for_host_connection(); + }); } } @@ -148,15 +160,13 @@ void AdbMessageProcessor::on_host_connection(std::shared_ptrasync_receive_msg(callback, - boost::asio::buffer(host_buffer_)); + host_messenger_->async_receive_msg(callback, boost::asio::buffer(host_buffer_)); } -void AdbMessageProcessor::on_host_read_size( - const boost::system::error_code &error, std::size_t bytes_read) { +void AdbMessageProcessor::on_host_read_size(const boost::system::error_code &error, std::size_t bytes_read) { if (error) { state_ = closed_by_host; - BOOST_THROW_EXCEPTION(std::runtime_error(error.message())); + return; } messenger_->send(reinterpret_cast(host_buffer_.data()), bytes_read); diff --git a/src/anbox/qemu/adb_message_processor.h b/src/anbox/qemu/adb_message_processor.h index b98fa7a..a58925e 100644 --- a/src/anbox/qemu/adb_message_processor.h +++ b/src/anbox/qemu/adb_message_processor.h @@ -27,6 +27,8 @@ #include +#include + namespace anbox { namespace qemu { class AdbMessageProcessor : public network::MessageProcessor { @@ -66,6 +68,9 @@ class AdbMessageProcessor : public network::MessageProcessor { std::shared_ptr host_messenger_; std::array host_buffer_; boost::asio::deadline_timer host_notify_timer_; + std::unique_lock lock_; + + static std::mutex active_instance_; }; } // namespace graphics } // namespace anbox diff --git a/src/anbox/qemu/at_parser.h b/src/anbox/qemu/at_parser.h index 3935376..def3881 100644 --- a/src/anbox/qemu/at_parser.h +++ b/src/anbox/qemu/at_parser.h @@ -18,6 +18,7 @@ #ifndef ANBOX_QEMU_AT_PARSER_H_ #define ANBOX_QEMU_AT_PARSER_H_ +#include #include #include #include diff --git a/src/anbox/qemu/pipe_connection_creator.cpp b/src/anbox/qemu/pipe_connection_creator.cpp index 78bb258..722b227 100644 --- a/src/anbox/qemu/pipe_connection_creator.cpp +++ b/src/anbox/qemu/pipe_connection_creator.cpp @@ -73,7 +73,7 @@ PipeConnectionCreator::PipeConnectionCreator(const std::shared_ptr &re std::make_shared>()) { } -PipeConnectionCreator::~PipeConnectionCreator() { +PipeConnectionCreator::~PipeConnectionCreator() noexcept { connections_->clear(); } diff --git a/src/anbox/rpc/connection_creator.cpp b/src/anbox/rpc/connection_creator.cpp index 2989675..2f3f864 100644 --- a/src/anbox/rpc/connection_creator.cpp +++ b/src/anbox/rpc/connection_creator.cpp @@ -34,7 +34,7 @@ ConnectionCreator::ConnectionCreator(const std::shared_ptr& rt, std::make_shared>()), message_processor_factory_(factory) {} -ConnectionCreator::~ConnectionCreator() {} +ConnectionCreator::~ConnectionCreator() noexcept {} void ConnectionCreator::create_connection_for( std::shared_ptr const& @@ -43,8 +43,7 @@ void ConnectionCreator::create_connection_for( socket->close(); WARNING( "A second client tried to connect. Denied request as we already have " - "one" - "and only allow a single client"); + "one and only allow a single client"); return; } diff --git a/src/anbox/runtime.cpp b/src/anbox/runtime.cpp index 1be2d1c..cbfefa7 100644 --- a/src/anbox/runtime.cpp +++ b/src/anbox/runtime.cpp @@ -57,7 +57,7 @@ Runtime::Runtime(std::uint32_t pool_size) strand_{service_}, keep_alive_{service_} {} -Runtime::~Runtime() { +Runtime::~Runtime() noexcept(true) { try { stop(); } catch (...) { diff --git a/src/anbox/ubuntu/platform_policy.cpp b/src/anbox/ubuntu/platform_policy.cpp index 6c88c1b..cea5551 100644 --- a/src/anbox/ubuntu/platform_policy.cpp +++ b/src/anbox/ubuntu/platform_policy.cpp @@ -68,8 +68,7 @@ PlatformPolicy::PlatformPolicy( window_size_immutable_ = true; } - display_info_.horizontal_resolution = display_frame.width(); - display_info_.vertical_resolution = display_frame.height(); + graphics::emugl::DisplayInfo::get()->set_resolution(display_frame.width(), display_frame.height()); pointer_ = input_manager->create_device(); pointer_->set_name("anbox-pointer"); @@ -285,10 +284,6 @@ void PlatformPolicy::window_resized(const Window::Id &id, } } -DisplayManager::DisplayInfo PlatformPolicy::display_info() const { - return display_info_; -} - void PlatformPolicy::set_clipboard_data(const ClipboardData &data) { if (data.text.empty()) return; diff --git a/src/anbox/ubuntu/platform_policy.h b/src/anbox/ubuntu/platform_policy.h index 9a09c0d..62d69f5 100644 --- a/src/anbox/ubuntu/platform_policy.h +++ b/src/anbox/ubuntu/platform_policy.h @@ -41,8 +41,7 @@ class Manager; namespace ubuntu { class PlatformPolicy : public std::enable_shared_from_this, public platform::Policy, - public Window::Observer, - public DisplayManager { + public Window::Observer { public: PlatformPolicy(const std::shared_ptr &input_manager, const graphics::Rect &static_display_frame = graphics::Rect::Invalid, @@ -61,8 +60,6 @@ class PlatformPolicy : public std::enable_shared_from_this, void window_resized(const Window::Id &id, const std::int32_t &width, const std::int32_t &height) override; - DisplayInfo display_info() const override; - void set_renderer(const std::shared_ptr &renderer); void set_window_manager(const std::shared_ptr &window_manager); @@ -89,7 +86,6 @@ class PlatformPolicy : public std::enable_shared_from_this, bool event_thread_running_; std::shared_ptr pointer_; std::shared_ptr keyboard_; - DisplayManager::DisplayInfo display_info_; bool window_size_immutable_ = false; bool single_window_ = false; }; diff --git a/src/anbox/ui/splash_screen.cpp b/src/anbox/ui/splash_screen.cpp new file mode 100644 index 0000000..b52c817 --- /dev/null +++ b/src/anbox/ui/splash_screen.cpp @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2017 Simon Fels + * + * 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 . + * + */ + +#include "anbox/ui/splash_screen.h" +#include "anbox/config.h" +#include "anbox/utils.h" + +#include + +namespace anbox { +namespace ui { +SplashScreen::SplashScreen() { + if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS) < 0) { + const auto message = utils::string_format("Failed to initialize SDL: %s", SDL_GetError()); + BOOST_THROW_EXCEPTION(std::runtime_error(message)); + } + + const auto width = 1024, height = 768; + window_ = SDL_CreateWindow("", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, + width, height, SDL_WINDOW_OPENGL | SDL_WINDOW_BORDERLESS); + if (!window_) { + const auto message = utils::string_format("Failed to create window: %s", SDL_GetError()); + BOOST_THROW_EXCEPTION(std::runtime_error(message)); + } + + auto surface = SDL_GetWindowSurface(window_); + if (!surface) + BOOST_THROW_EXCEPTION(std::runtime_error("Could not retrieve surface for our window")); + + SDL_FillRect(surface, nullptr, SDL_MapRGB(surface->format, 0xee, 0xee, 0xee)); + SDL_UpdateWindowSurface(window_); + + auto renderer = SDL_CreateRenderer(window_, -1, SDL_RENDERER_ACCELERATED); + if (!renderer) + BOOST_THROW_EXCEPTION(std::runtime_error("Could not create renderer")); + + const auto icon_path = utils::string_format("%s/ui/loading-screen.png", SystemConfiguration::instance().resource_dir()); + auto img = IMG_LoadTexture(renderer, icon_path.c_str()); + if (!img) { + const auto msg = utils::string_format("Failed to create texture from %s", icon_path); + BOOST_THROW_EXCEPTION(std::runtime_error(msg)); + } + + SDL_RenderClear(renderer); + + SDL_Rect r{0, 0, width, height}; + SDL_RenderCopy(renderer, img, nullptr, &r); + SDL_RenderPresent(renderer); + + SDL_ShowWindow(window_); + + event_thread_ = std::thread(&SplashScreen::process_events, this); +} + +SplashScreen::~SplashScreen() { + if (event_thread_running_) { + event_thread_running_ = false; + if (event_thread_.joinable()) + event_thread_.join(); + } + + if (window_) + SDL_DestroyWindow(window_); +} + +void SplashScreen::process_events() { + event_thread_running_ = true; + while (event_thread_running_) { + SDL_Event event; + while (SDL_WaitEventTimeout(&event, 100)) { + // Keep running until we're terminated + } + } +} +} // namespace ui +} // namespace anbox diff --git a/src/anbox/ui/splash_screen.h b/src/anbox/ui/splash_screen.h new file mode 100644 index 0000000..bee6f6d --- /dev/null +++ b/src/anbox/ui/splash_screen.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2017 Simon Fels + * + * 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 . + * + */ + +#ifndef ANBOX_UI_SPLASH_SCREEN_H_ +#define ANBOX_UI_SPLASH_SCREEN_H_ + +#include + +#include + +namespace anbox { +namespace ui { +class SplashScreen { + public: + SplashScreen(); + ~SplashScreen(); + + private: + void process_events(); + + std::thread event_thread_; + bool event_thread_running_; + SDL_Window *window_; +}; +} // namespace ui +} // namespace anbox + +#endif diff --git a/src/anbox/version.h b/src/anbox/version.h deleted file mode 100644 index 11617ae..0000000 --- a/src/anbox/version.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * 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 . - * - * Authored by: Thomas Voß - * - */ - -#ifndef ANBOX_VERSION_H_ -#define ANBOX_VERSION_H_ - -#include - -namespace anbox { -namespace build { -/// @brief version_major marks the major version of the library. The constant is -/// meant to be used -/// by client code both at build and runtime, enabling version checks. -static constexpr const std::uint32_t version_major{0}; -/// @brief version_major marks the minor version of the library. The constant is -/// meant to be used -/// by client code both at build and runtime, enabling version checks. -static constexpr const std::uint32_t version_minor{1}; -/// @brief version_patch marks the major version of the library. The constant is -/// meant to be used -/// by client code both at build and runtime, enabling version checks. -static constexpr const std::uint32_t version_patch{0}; -} // namespace build - -/// @brief version queries the version of the library, placing the result in -/// major, minor and patch. -void version(std::uint32_t& major, std::uint32_t& minor, std::uint32_t& patch); -} // namespace build - -#endif // ANBOX_VERSION_H_ diff --git a/src/anbox/wm/multi_window_manager.cpp b/src/anbox/wm/multi_window_manager.cpp index 83ab0b6..04806f3 100644 --- a/src/anbox/wm/multi_window_manager.cpp +++ b/src/anbox/wm/multi_window_manager.cpp @@ -25,7 +25,7 @@ namespace anbox { namespace wm { -MultiWindowManager::MultiWindowManager(const std::shared_ptr &policy, +MultiWindowManager::MultiWindowManager(const std::weak_ptr &policy, const std::shared_ptr &android_api_stub, const std::shared_ptr &app_db) : platform_policy_(policy), android_api_stub_(android_api_stub), app_db_(app_db) {} @@ -67,9 +67,16 @@ void MultiWindowManager::apply_window_state_update(const WindowState::List &upda if (app.valid()) title = app.name; - auto platform_window = platform_policy_->create_window(window.task(), window.frame(), title); - platform_window->attach(); - windows_.insert({window.task(), platform_window}); + if (auto p = platform_policy_.lock()) { + auto w = p->create_window(window.task(), window.frame(), title); + if (w) { + w->attach(); + windows_.insert({window.task(), w}); + } else { + // FIXME can we call this here safely or do we need to schedule the removal? + remove_task(window.task()); + } + } } // Send updates we collected per task down to the corresponding window diff --git a/src/anbox/wm/multi_window_manager.h b/src/anbox/wm/multi_window_manager.h index 4e2d7c4..845986c 100644 --- a/src/anbox/wm/multi_window_manager.h +++ b/src/anbox/wm/multi_window_manager.h @@ -37,7 +37,7 @@ class Policy; namespace wm { class MultiWindowManager : public Manager { public: - MultiWindowManager(const std::shared_ptr &policy, + MultiWindowManager(const std::weak_ptr &policy, const std::shared_ptr &android_api_stub, const std::shared_ptr &app_db); ~MultiWindowManager(); @@ -53,7 +53,7 @@ class MultiWindowManager : public Manager { private: std::mutex mutex_; - std::shared_ptr platform_policy_; + std::weak_ptr platform_policy_; std::shared_ptr android_api_stub_; std::shared_ptr app_db_; std::map> windows_; diff --git a/src/anbox/wm/single_window_manager.cpp b/src/anbox/wm/single_window_manager.cpp index 6adb43e..10dd8f9 100644 --- a/src/anbox/wm/single_window_manager.cpp +++ b/src/anbox/wm/single_window_manager.cpp @@ -26,7 +26,7 @@ namespace anbox { namespace wm { -SingleWindowManager::SingleWindowManager(const std::shared_ptr &policy, +SingleWindowManager::SingleWindowManager(const std::weak_ptr &policy, const graphics::Rect &window_size, const std::shared_ptr &app_db) : platform_policy_(policy), window_size_(window_size), app_db_(app_db) {} @@ -34,9 +34,13 @@ SingleWindowManager::SingleWindowManager(const std::shared_ptr SingleWindowManager::~SingleWindowManager() {} void SingleWindowManager::setup() { - window_ = platform_policy_->create_window(0, window_size_, "Anbox - Android in a Box"); - if (!window_->attach()) - WARNING("Failed to attach window to renderer"); + if (auto p = platform_policy_.lock()) { + window_ = p->create_window(0, window_size_, "Anbox - Android in a Box"); + if (!window_->attach()) + WARNING("Failed to attach window to renderer"); + } else { + throw std::runtime_error("Can't create window as we don't have a platform abstraction"); + } } void SingleWindowManager::apply_window_state_update(const WindowState::List &updated, const WindowState::List &removed) { diff --git a/src/anbox/wm/single_window_manager.h b/src/anbox/wm/single_window_manager.h index 6d0d2ca..7595776 100644 --- a/src/anbox/wm/single_window_manager.h +++ b/src/anbox/wm/single_window_manager.h @@ -35,7 +35,7 @@ namespace wm { class Window; class SingleWindowManager : public Manager { public: - SingleWindowManager(const std::shared_ptr &policy, + SingleWindowManager(const std::weak_ptr &policy, const graphics::Rect &window_size, const std::shared_ptr &app_db); ~SingleWindowManager(); @@ -52,7 +52,7 @@ class SingleWindowManager : public Manager { void remove_task(const Task::Id &task) override; private: - std::shared_ptr platform_policy_; + std::weak_ptr platform_policy_; graphics::Rect window_size_; std::shared_ptr app_db_; std::shared_ptr window_; diff --git a/tests/anbox/CMakeLists.txt b/tests/anbox/CMakeLists.txt index 41692fa..ca13908 100644 --- a/tests/anbox/CMakeLists.txt +++ b/tests/anbox/CMakeLists.txt @@ -1,3 +1,4 @@ +add_subdirectory(android) add_subdirectory(support) add_subdirectory(common) add_subdirectory(graphics) diff --git a/tests/anbox/android/CMakeLists.txt b/tests/anbox/android/CMakeLists.txt new file mode 100644 index 0000000..9753aa8 --- /dev/null +++ b/tests/anbox/android/CMakeLists.txt @@ -0,0 +1 @@ +ANBOX_ADD_TEST(intent_tests intent_tests.cpp) diff --git a/tests/anbox/android/intent_tests.cpp b/tests/anbox/android/intent_tests.cpp new file mode 100644 index 0000000..395a69d --- /dev/null +++ b/tests/anbox/android/intent_tests.cpp @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2017 Simon Fels + * + * 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 . + * + */ + +#include "anbox/android/intent.h" + +#include + +TEST(Intent, IsValid) { + anbox::android::Intent intent; + ASSERT_FALSE(intent.valid()); + intent.component = "foo"; + ASSERT_TRUE(intent.valid()); + intent.package = "bla"; + ASSERT_TRUE(intent.valid()); + intent.component = ""; + ASSERT_TRUE(intent.valid()); +} diff --git a/tests/anbox/common/CMakeLists.txt b/tests/anbox/common/CMakeLists.txt index 4d04ebe..facd443 100644 --- a/tests/anbox/common/CMakeLists.txt +++ b/tests/anbox/common/CMakeLists.txt @@ -2,3 +2,4 @@ ANBOX_ADD_TEST(message_channel_tests message_channel_tests.cpp) ANBOX_ADD_TEST(small_vector_tests small_vector_tests.cpp) ANBOX_ADD_TEST(type_traits_tests type_traits_tests.cpp) ANBOX_ADD_TEST(scope_ptr_tests scope_ptr_tests.cpp) +ANBOX_ADD_TEST(binary_writer_tests binary_writer_tests.cpp) diff --git a/tests/anbox/common/binary_writer_tests.cpp b/tests/anbox/common/binary_writer_tests.cpp new file mode 100644 index 0000000..6bab562 --- /dev/null +++ b/tests/anbox/common/binary_writer_tests.cpp @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2017 Simon Fels + * + * 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 . + * + */ + +#include "anbox/common/binary_writer.h" + +#include + +namespace ac = anbox::common; + +using namespace ::testing; + +TEST(BinaryWriter, WriteUint32) { + std::vector buffer; + buffer.resize(sizeof(std::uint32_t) * 2); + ac::BinaryWriter writer(buffer.begin(), buffer.end()); + + writer.write_uint32(0x10); + writer.write_uint32(0x3322); + + ASSERT_EQ(writer.bytes_written(), 8); + ASSERT_THAT(buffer, ElementsAre(0x10, 0x00, 0x00, 0x00, 0x22, 0x33, 0x00, 0x00)); +} + +TEST(BinaryWriter, WriteUint32FailsWithExhaustedError) { + std::vector buffer; + ac::BinaryWriter writer(buffer.begin(), buffer.end()); + EXPECT_THROW(writer.write_uint32(0x11), std::out_of_range); +} + +TEST(BinaryWriter, WriteUint32WithChangedBinaryOrder) { + std::vector buffer; + buffer.resize(sizeof(std::uint32_t)); + ac::BinaryWriter writer(buffer.begin(), buffer.end()); + + writer.set_byte_order(ac::BinaryWriter::Order::Big); + writer.write_uint32(0x11223344); + + ASSERT_EQ(writer.bytes_written(), 4); + ASSERT_THAT(buffer, ElementsAre(0x11, 0x22, 0x33, 0x44)); + + buffer.clear(); + buffer.resize(sizeof(std::uint32_t)); + + writer = ac::BinaryWriter(buffer.begin(), buffer.end()); + + writer.set_byte_order(ac::BinaryWriter::Order::Little); + writer.write_uint32(0x11223344); + + ASSERT_EQ(writer.bytes_written(), 4); + ASSERT_THAT(buffer, ElementsAre(0x44, 0x33, 0x22, 0x11)); +} + +TEST(BinaryWriter, WriteUint16) { + std::vector buffer; + buffer.resize(sizeof(std::uint16_t) * 2); + ac::BinaryWriter writer(buffer.begin(), buffer.end()); + + writer.write_uint16(0x10); + writer.write_uint16(0x3322); + + ASSERT_EQ(writer.bytes_written(), 4); + ASSERT_THAT(buffer, ElementsAre(0x10, 0x00, 0x22, 0x33)); +} + +TEST(BinaryWriter, WriteUint16FailsWithExhaustedError) { + std::vector buffer; + ac::BinaryWriter writer(buffer.begin(), buffer.end()); + EXPECT_THROW(writer.write_uint16(0x11), std::out_of_range); +} + +TEST(BinaryWriter, WriteUint16WithChangedBinaryOrder) { + std::vector buffer; + buffer.resize(sizeof(std::uint16_t)); + ac::BinaryWriter writer(buffer.begin(), buffer.end()); + + writer.set_byte_order(ac::BinaryWriter::Order::Big); + writer.write_uint16(0x1122); + + ASSERT_EQ(writer.bytes_written(), 2); + ASSERT_THAT(buffer, ElementsAre(0x11, 0x22)); + + buffer.clear(); + buffer.resize(sizeof(std::uint16_t)); + + writer = ac::BinaryWriter(buffer.begin(), buffer.end()); + + writer.set_byte_order(ac::BinaryWriter::Order::Little); + writer.write_uint16(0x1122); + + ASSERT_EQ(writer.bytes_written(), 2); + ASSERT_THAT(buffer, ElementsAre(0x22, 0x11)); +} + +TEST(BinaryWriter, WriteString) { + std::vector buffer; + buffer.resize(sizeof(std::uint8_t) * 4); + ac::BinaryWriter writer(buffer.begin(), buffer.end()); + + writer.write_string("test", 4); + + ASSERT_EQ(writer.bytes_written(), 4); + ASSERT_THAT(buffer, ElementsAre(0x74, 0x65, 0x73, 0x74)); +} + +TEST(BinaryWriter, WriteStringWithSize) { + std::vector buffer; + buffer.resize(sizeof(std::uint8_t) * 6); + ac::BinaryWriter writer(buffer.begin(), buffer.end()); + + writer.set_byte_order(ac::BinaryWriter::Order::Big); + + writer.write_string_with_size("test"); + + ASSERT_EQ(writer.bytes_written(), 6); + ASSERT_THAT(buffer, ElementsAre(0x00, 0x04, 0x74, 0x65, 0x73, 0x74)); +} diff --git a/tests/anbox/graphics/CMakeLists.txt b/tests/anbox/graphics/CMakeLists.txt index 85b07c2..ce02330 100644 --- a/tests/anbox/graphics/CMakeLists.txt +++ b/tests/anbox/graphics/CMakeLists.txt @@ -1,3 +1,4 @@ ANBOX_ADD_TEST(buffer_queue_tests buffer_queue_tests.cpp) ANBOX_ADD_TEST(buffered_io_stream_tests buffered_io_stream_tests.cpp) ANBOX_ADD_TEST(layer_composer_tests layer_composer_tests.cpp) +ANBOX_ADD_TEST(render_control_tests render_control_tests.cpp) diff --git a/tests/anbox/graphics/render_control_tests.cpp b/tests/anbox/graphics/render_control_tests.cpp new file mode 100644 index 0000000..5a97f04 --- /dev/null +++ b/tests/anbox/graphics/render_control_tests.cpp @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2017 Simon Fels + * + * 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 . + * + */ + +#include + +#include "anbox/graphics/emugl/DisplayManager.h" + +extern int rcGetDisplayWidth(uint32_t display_id); +extern int rcGetDisplayHeight(uint32_t display_id); + +TEST(RenderControl, WidthHeightAreCorrectlyAssigned) { + anbox::graphics::emugl::DisplayInfo::get()->set_resolution(640, 480); + ASSERT_EQ(rcGetDisplayWidth(0), 640); + ASSERT_EQ(rcGetDisplayHeight(0), 480); +}