diff --git a/Android.mk b/Android.mk
index d476f9e..e7b0220 100644
--- a/Android.mk
+++ b/Android.mk
@@ -34,6 +34,7 @@ LOCAL_SRC_FILES := \
android/service/platform_service_interface.cpp \
android/service/platform_service.cpp \
android/service/platform_api_stub.cpp \
+ android/service/intent.cpp \
src/anbox/common/fd.cpp \
src/anbox/common/wait_handle.cpp \
src/anbox/rpc/message_processor.cpp \
@@ -60,15 +61,6 @@ LOCAL_SHARED_LIBRARIES := \
LOCAL_CFLAGS := \
-fexceptions \
-std=c++1y
-# Drop a few packages we don't want in our images as they
-# are not needed (e.g. a home/launcher application as we
-# don't have this concept).
-LOCAL_OVERRIDES_PACKAGES := \
- Launcher \
- Launcher2 \
- LatinIME \
- Home \
- QuickSearchBox
include $(BUILD_EXECUTABLE)
include $(CLEAR_VARS)
@@ -107,3 +99,5 @@ include $(BUILD_SHARED_LIBRARY)
# Include the Android.mk files below will override LOCAL_PATH so we
# have to take a copy of it here.
TMP_PATH := $(LOCAL_PATH)
+
+include $(TMP_PATH)/android/appmgr/Android.mk
diff --git a/android/appmgr/Android.mk b/android/appmgr/Android.mk
new file mode 100644
index 0000000..e122453
--- /dev/null
+++ b/android/appmgr/Android.mk
@@ -0,0 +1,19 @@
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE_TAGS := optional
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ android-common \
+ android-support-v13
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+# LOCAL_SDK_VERSION := current
+LOCAL_PACKAGE_NAME := AnboxAppMgr
+LOCAL_CERTIFICATE := shared
+LOCAL_PRIVILEGED_MODULE := true
+LOCAL_OVERRIDES_PACKAGES := \
+ Home \
+ Launcher2 \
+ Launcher3 \
+ LatinIME \
+ QuickSearchBox
+include $(BUILD_PACKAGE)
diff --git a/android/appmgr/AndroidManifest.xml b/android/appmgr/AndroidManifest.xml
new file mode 100644
index 0000000..7ffbb79
--- /dev/null
+++ b/android/appmgr/AndroidManifest.xml
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/android/appmgr/res/values/strings.xml b/android/appmgr/res/values/strings.xml
new file mode 100644
index 0000000..8926490
--- /dev/null
+++ b/android/appmgr/res/values/strings.xml
@@ -0,0 +1,3 @@
+
+ Anbox Application Manager
+
diff --git a/android/appmgr/src/org/anbox/appmgr/LauncherActivity.java b/android/appmgr/src/org/anbox/appmgr/LauncherActivity.java
new file mode 100644
index 0000000..d89b03e
--- /dev/null
+++ b/android/appmgr/src/org/anbox/appmgr/LauncherActivity.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2016 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 .
+ *
+ */
+
+package org.anbox.appmgr;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.util.Log;
+import android.content.Intent;
+
+public final class LauncherActivity extends Activity {
+ private static final String TAG = "AnboxAppMgr";
+
+ @Override
+ public void onCreate(Bundle info) {
+ super.onCreate(info);
+
+ Intent intent = new Intent(this, LauncherService.class);
+ startService(intent);
+
+ Log.i(TAG, "Created launcher activity");
+ }
+
+ @Override
+ public void onDestroy() {
+ Log.i(TAG, "Destroyed launcher activity");
+
+ Intent intent = new Intent(this, LauncherService.class);
+ stopService(intent);
+
+ super.onDestroy();
+ }
+}
diff --git a/android/appmgr/src/org/anbox/appmgr/LauncherService.java b/android/appmgr/src/org/anbox/appmgr/LauncherService.java
new file mode 100644
index 0000000..98657c8
--- /dev/null
+++ b/android/appmgr/src/org/anbox/appmgr/LauncherService.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2016 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 .
+ *
+ */
+
+package org.anbox.appmgr;
+
+import android.app.Service;
+import android.util.Log;
+import android.content.Intent;
+import android.os.IBinder;
+
+public final class LauncherService extends Service {
+ public static final String TAG = "AnboxAppMgr";
+
+ private PlatformService mPlatformService;
+
+ public LauncherService() {
+ super();
+ Log.i(TAG, "Service created");
+ }
+
+ @Override
+ public void onCreate() {
+ mPlatformService = new PlatformService(getBaseContext());
+ // Send all necessary initial updates
+ mPlatformService.sendApplicationListUpdate();
+
+ Log.i(TAG, "Service started");
+ }
+
+ @Override
+ public void onDestroy() {
+ Log.i(TAG, "Service stopped");
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return null;
+ }
+}
diff --git a/android/appmgr/src/org/anbox/appmgr/MainApplication.java b/android/appmgr/src/org/anbox/appmgr/MainApplication.java
new file mode 100644
index 0000000..db74d49
--- /dev/null
+++ b/android/appmgr/src/org/anbox/appmgr/MainApplication.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2016 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 .
+ *
+ */
+
+package org.anbox.appmgr;
+
+import android.app.Application;
+
+public final class MainApplication extends Application {
+}
diff --git a/android/appmgr/src/org/anbox/appmgr/PackageEventReceiver.java b/android/appmgr/src/org/anbox/appmgr/PackageEventReceiver.java
new file mode 100644
index 0000000..77de857
--- /dev/null
+++ b/android/appmgr/src/org/anbox/appmgr/PackageEventReceiver.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2016 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 .
+ *
+ */
+
+package org.anbox.appmgr;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.util.Log;
+
+public class PackageEventReceiver extends BroadcastReceiver {
+ private static final String TAG = "AnboxAppMgr";
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ Log.i(TAG, "Received intent " + intent.toString());
+ }
+}
diff --git a/android/appmgr/src/org/anbox/appmgr/PlatformService.java b/android/appmgr/src/org/anbox/appmgr/PlatformService.java
new file mode 100644
index 0000000..5ede408
--- /dev/null
+++ b/android/appmgr/src/org/anbox/appmgr/PlatformService.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2016 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 .
+ *
+ */
+
+package org.anbox.appmgr;
+
+import android.os.ServiceManager;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.RemoteException;
+import android.util.Log;
+import android.content.Intent;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.pm.ApplicationInfo;
+import android.net.Uri;
+
+import java.util.List;
+
+public final class PlatformService {
+ private static final String TAG = "AnboxAppMgr";
+ private static final String SERVICE_NAME = "org.anbox.PlatformService";
+ private static final String DESCRIPTOR = "org.anbox.IPlatformService";
+
+ private static final int TRANSACTION_updateApplicationList = (IBinder.FIRST_CALL_TRANSACTION + 2);
+
+ private IBinder mService = null;
+ private PackageManager mPm = null;
+
+ private void connectService() {
+ if (mService != null)
+ return;
+ mService = ServiceManager.getService(SERVICE_NAME);
+ }
+
+ public PlatformService(Context context) {
+ if (context != null) {
+ mPm = context.getPackageManager();
+ } else {
+ Log.w(TAG, "No context available");
+ }
+
+ Log.i(TAG, "Connected to platform service");
+ }
+
+ public void sendApplicationListUpdate() {
+ connectService();
+
+ if (mPm == null || mService == null)
+ return;
+
+ Parcel data = Parcel.obtain();
+ data.writeInterfaceToken(DESCRIPTOR);
+
+ List apps = mPm.getInstalledApplications(0);
+ data.writeInt(apps.size());
+ for (int n = 0; n < apps.size(); n++) {
+ ApplicationInfo appInfo = apps.get(n);
+ data.writeString(appInfo.name);
+ data.writeString(appInfo.packageName);
+
+ Intent launchIntent = mPm.getLaunchIntentForPackage(appInfo.packageName);
+ if (launchIntent != null) {
+ data.writeInt(1);
+ data.writeString(launchIntent.getAction());
+ if (launchIntent.getData() != null)
+ data.writeString(launchIntent.getData().toString());
+ else
+ data.writeString("");
+ data.writeString(launchIntent.getType());
+ data.writeString(launchIntent.getComponent().getPackageName());
+ data.writeString(launchIntent.getComponent().getClassName());
+ data.writeInt(launchIntent.getCategories().size());
+ for (String category : launchIntent.getCategories())
+ data.writeString(category);
+ } else {
+ data.writeInt(0);
+ }
+
+ // FIXME add icon, flags, ...
+ }
+
+ Parcel reply = Parcel.obtain();
+ try {
+ mService.transact(TRANSACTION_updateApplicationList, data, reply, 0);
+ }
+ catch (RemoteException ex) {
+ Log.w(TAG, "Failed to send updatePackageList request to remote binder service: " + ex.getMessage());
+ }
+ }
+
+ public void notifyPackageAdded(Intent intent) {
+ }
+
+ public void notifyPackageRemoved(Intent intent) {
+ }
+}
diff --git a/android/service/android_api_skeleton.cpp b/android/service/android_api_skeleton.cpp
index 63d2f6b..fa388d5 100644
--- a/android/service/android_api_skeleton.cpp
+++ b/android/service/android_api_skeleton.cpp
@@ -27,6 +27,8 @@
#include
+#include
+
namespace {
std::map common_env = {
{"ANDROID_DATA", "/data"},
@@ -60,69 +62,60 @@ void AndroidApiSkeleton::connect_services() {
}
}
-void AndroidApiSkeleton::install_application(anbox::protobuf::bridge::InstallApplication const *request,
- anbox::protobuf::rpc::Void *response,
- google::protobuf::Closure *done) {
- (void) response;
-
- std::vector argv = {
- "/system/bin/pm",
- "install",
- request->path(),
- };
-
- auto process = core::posix::exec("/system/bin/sh", argv, common_env, core::posix::StandardStream::empty);
- wait_for_process(process, response);
-
- done->Run();
-}
-
void AndroidApiSkeleton::launch_application(anbox::protobuf::bridge::LaunchApplication const *request,
anbox::protobuf::rpc::Void *response,
google::protobuf::Closure *done) {
(void) response;
- std::string intent = request->package_name();
- if (request->has_activity()) {
- intent += "/";
- intent += request->activity();
- }
+ auto intent = request->intent();
std::vector argv = {
"/system/bin/am",
"start",
- // Launch any applications always in freeform stack
+ // Launch any application always in the freeform stack
"--stack", "2",
- intent,
};
+ if (intent.has_action()) {
+ argv.push_back("-a");
+ argv.push_back(intent.action());
+ }
+
+ if (intent.has_uri()) {
+ argv.push_back("-d");
+ argv.push_back(intent.uri());
+ }
+
+ if (intent.has_type()) {
+ argv.push_back("-t");
+ argv.push_back(intent.type());
+ }
+
+ std::string component;
+ if (intent.has_package())
+ component += intent.package();
+ if (!component.empty() && intent.has_component()) {
+ component += "/";
+ component += intent.component();
+ }
+
+ if (!component.empty())
+ argv.push_back(component);
+
+ ALOGI("Launch am with the following arguments: ");
+ std::string test;
+ for (const auto &a : argv) {
+ test += a;
+ test += " ";
+ }
+ ALOGI("%s", test.c_str());
+
auto process = core::posix::exec("/system/bin/sh", argv, common_env, core::posix::StandardStream::empty);
wait_for_process(process, response);
done->Run();
}
-void AndroidApiSkeleton::set_dns_servers(anbox::protobuf::bridge::SetDnsServers const *request,
- anbox::protobuf::rpc::Void *response,
- google::protobuf::Closure *done) {
- (void) response;
-
- std::vector argv = {
- "resolver",
- "setnetdns",
- "0",
- request->domain(),
- };
-
- for (int n = 0; n < request->servers_size(); n++)
- argv.push_back(request->servers(n).address());
-
- auto process = core::posix::exec("/system/bin/ndc", argv, common_env, core::posix::StandardStream::empty);
- wait_for_process(process, response);
-
- done->Run();
-}
-
void AndroidApiSkeleton::set_focused_task(anbox::protobuf::bridge::SetFocusedTask const *request,
anbox::protobuf::rpc::Void *response,
google::protobuf::Closure *done) {
diff --git a/android/service/android_api_skeleton.h b/android/service/android_api_skeleton.h
index 4cdd582..c4d2fd3 100644
--- a/android/service/android_api_skeleton.h
+++ b/android/service/android_api_skeleton.h
@@ -49,18 +49,10 @@ public:
AndroidApiSkeleton();
~AndroidApiSkeleton();
- void install_application(anbox::protobuf::bridge::InstallApplication const *request,
- anbox::protobuf::rpc::Void *response,
- google::protobuf::Closure *done);
-
void launch_application(anbox::protobuf::bridge::LaunchApplication const *request,
anbox::protobuf::rpc::Void *response,
google::protobuf::Closure *done);
- void set_dns_servers(anbox::protobuf::bridge::SetDnsServers const *request,
- anbox::protobuf::rpc::Void *response,
- google::protobuf::Closure *done);
-
void set_focused_task(anbox::protobuf::bridge::SetFocusedTask const *request,
anbox::protobuf::rpc::Void *response,
google::protobuf::Closure *done);
diff --git a/android/service/message_processor.cpp b/android/service/message_processor.cpp
index 4036917..0fc2054 100644
--- a/android/service/message_processor.cpp
+++ b/android/service/message_processor.cpp
@@ -35,12 +35,8 @@ MessageProcessor::~MessageProcessor() {
}
void MessageProcessor::dispatch(rpc::Invocation const& invocation) {
- if (invocation.method_name() == "install_application")
- invoke(this, platform_api_.get(), &AndroidApiSkeleton::install_application, invocation);
- else if (invocation.method_name() == "launch_application")
+ if (invocation.method_name() == "launch_application")
invoke(this, platform_api_.get(), &AndroidApiSkeleton::launch_application, invocation);
- else if (invocation.method_name() == "set_dns_servers")
- invoke(this, platform_api_.get(), &AndroidApiSkeleton::set_dns_servers, invocation);
else if (invocation.method_name() == "set_focused_task")
invoke(this, platform_api_.get(), &AndroidApiSkeleton::set_focused_task, invocation);
}
diff --git a/android/service/platform_api_stub.cpp b/android/service/platform_api_stub.cpp
index fd2d605..85deddf 100644
--- a/android/service/platform_api_stub.cpp
+++ b/android/service/platform_api_stub.cpp
@@ -61,4 +61,28 @@ void PlatformApiStub::update_window_state(const WindowStateUpdate &state) {
rpc_channel_->send_event(seq);
}
+
+void PlatformApiStub::update_application_list(const ApplicationListUpdate &update) {
+ protobuf::bridge::EventSequence seq;
+ auto event = seq.mutable_application_list_update();
+
+ for (const auto &a : update.applications) {
+ auto app = event->add_applications();
+ app->set_name(a.name);
+ app->set_package(a.package);
+
+ auto launch_intent = app->mutable_launch_intent();
+ launch_intent->set_action(a.launch_intent.action);
+ launch_intent->set_uri(a.launch_intent.uri);
+ launch_intent->set_type(a.launch_intent.type);
+ launch_intent->set_package(a.launch_intent.package);
+ launch_intent->set_component(a.launch_intent.component);
+ for (const auto &category : a.launch_intent.categories) {
+ auto c = launch_intent->add_categories();
+ *c = category;
+ }
+ }
+
+ rpc_channel_->send_event(seq);
+}
} // namespace anbox
diff --git a/android/service/platform_api_stub.h b/android/service/platform_api_stub.h
index 436facf..4f14b3a 100644
--- a/android/service/platform_api_stub.h
+++ b/android/service/platform_api_stub.h
@@ -60,6 +60,25 @@ public:
void update_window_state(const WindowStateUpdate &state);
+ struct ApplicationListUpdate {
+ struct Application {
+ std::string name;
+ std::string package;
+ struct Intent {
+ std::string action;
+ std::string uri;
+ std::string type;
+ std::string package;
+ std::string component;
+ std::vector categories;
+ };
+ Intent launch_intent;
+ };
+ std::vector applications;
+ };
+
+ void update_application_list(const ApplicationListUpdate &update);
+
private:
template
struct Request {
diff --git a/android/service/platform_service.cpp b/android/service/platform_service.cpp
index 321791b..f1e6993 100644
--- a/android/service/platform_service.cpp
+++ b/android/service/platform_service.cpp
@@ -16,6 +16,7 @@
*/
#include "android/service/platform_service.h"
+#include "android/service/intent.h"
#include "anbox/rpc/channel.h"
#include "anbox_rpc.pb.h"
@@ -84,4 +85,44 @@ status_t PlatformService::update_window_state(const Parcel &data) {
return OK;
}
+
+status_t PlatformService::update_application_list(const Parcel &data) {
+ anbox::PlatformApiStub::ApplicationListUpdate update;
+ const auto num_packages = data.readInt32();
+ for (auto n = 0; n < num_packages; n++) {
+ String8 name(data.readString16());
+ String8 package_name(data.readString16());
+
+ auto p = anbox::PlatformApiStub::ApplicationListUpdate::Application{
+ name.string(),
+ package_name.string(),
+ };
+
+ if (data.readInt32() == 1) {
+ String8 action(data.readString16());
+ String8 uri(data.readString16());
+ String8 type(data.readString16());
+ String8 component_package(data.readString16());
+ String8 component_class(data.readString16());
+
+ std::vector categories;
+ unsigned int num_categories = data.readInt32();
+ for (int m = 0; m < num_categories; m++)
+ categories.push_back(String8(data.readString16()).string());
+
+ p.launch_intent.action = action;
+ p.launch_intent.uri = uri;
+ p.launch_intent.type = type;
+ p.launch_intent.package = component_package;
+ p.launch_intent.component = component_class;
+ p.launch_intent.categories = categories;
+
+ update.applications.push_back(p);
+ };
+ }
+
+ platform_api_stub_->update_application_list(update);
+
+ return OK;
+}
} // namespace android
diff --git a/android/service/platform_service.h b/android/service/platform_service.h
index 8a4eadf..6ba6bbf 100644
--- a/android/service/platform_service.h
+++ b/android/service/platform_service.h
@@ -34,6 +34,7 @@ public:
status_t boot_finished() override;
status_t update_window_state(const Parcel &data) override;
+ status_t update_application_list(const Parcel &data) override;
private:
anbox::PlatformApiStub::WindowStateUpdate::Window unpack_window_state(const Parcel &data);
diff --git a/android/service/platform_service_interface.cpp b/android/service/platform_service_interface.cpp
index ff2e92c..0df9c14 100644
--- a/android/service/platform_service_interface.cpp
+++ b/android/service/platform_service_interface.cpp
@@ -34,6 +34,12 @@ status_t BpPlatformService::update_window_state(const Parcel&) {
return remote()->transact(IPlatformService::UPDATE_WINDOW_STATE, data, &reply);
}
+status_t BpPlatformService::update_application_list(const Parcel&) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IPlatformService::getInterfaceDescriptor());
+ return remote()->transact(IPlatformService::UPDATE_APPLICATION_LIST, data, &reply);
+}
+
IMPLEMENT_META_INTERFACE(PlatformService, "org.anbox.IPlatformService");
status_t BnPlatformService::onTransact(uint32_t code, const Parcel &data,
@@ -45,6 +51,9 @@ status_t BnPlatformService::onTransact(uint32_t code, const Parcel &data,
case UPDATE_WINDOW_STATE:
CHECK_INTERFACE(IPlatformService, data, reply);
return update_window_state(data);
+ case UPDATE_APPLICATION_LIST:
+ CHECK_INTERFACE(IPlatformService, data, reply);
+ return update_application_list(data);
default:
break;
}
diff --git a/android/service/platform_service_interface.h b/android/service/platform_service_interface.h
index 876cf95..dac8643 100644
--- a/android/service/platform_service_interface.h
+++ b/android/service/platform_service_interface.h
@@ -36,10 +36,12 @@ public:
// Keep this synchronized with frameworks/base/services/java/com/android/server/wm/AnboxPlatformServiceProxy.java
BOOT_FINISHED = IBinder::FIRST_CALL_TRANSACTION,
UPDATE_WINDOW_STATE = IBinder::FIRST_CALL_TRANSACTION + 1,
+ UPDATE_APPLICATION_LIST = IBinder::FIRST_CALL_TRANSACTION + 2,
};
virtual status_t boot_finished() = 0;
virtual status_t update_window_state(const Parcel &data) = 0;
+ virtual status_t update_application_list(const Parcel &data) = 0;
};
class BpPlatformService : public BpInterface {
@@ -48,6 +50,7 @@ public:
status_t boot_finished() override;
status_t update_window_state(const Parcel &data) override;
+ status_t update_application_list(const Parcel &data) override;
};
class BnPlatformService : public BnInterface {
diff --git a/external/CMakeLists.txt b/external/CMakeLists.txt
index f20617c..24ebcab 100644
--- a/external/CMakeLists.txt
+++ b/external/CMakeLists.txt
@@ -1,3 +1,4 @@
add_subdirectory(bubblewrap)
add_subdirectory(process-cpp-minimal)
add_subdirectory(android-emugl)
+add_subdirectory(xdg)
diff --git a/external/xdg/CMakeLists.txt b/external/xdg/CMakeLists.txt
new file mode 100644
index 0000000..57089c3
--- /dev/null
+++ b/external/xdg/CMakeLists.txt
@@ -0,0 +1,24 @@
+# We have to manually alter the cxx flags to have a working
+# travis-ci build. Its container-based infrastructure only features
+# a very old cmake that does not support the more current:
+# set_property(TARGET xdg_test PROPERTY CXX_STANDARD 11)
+set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
+
+find_package(Boost COMPONENTS filesystem system unit_test_framework)
+
+include_directories(
+ .
+ ${Boost_INCLUDE_DIRS}
+)
+
+add_library(xdg xdg.cpp)
+set_property(TARGET xdg PROPERTY CXX_STANDARD 11)
+target_link_libraries(xdg ${Boost_LIBRARIES})
+
+enable_testing()
+add_definitions(-DBOOST_TEST_DYN_LINK -DBOOST_TEST_MAIN -DBOOST_TEST_MODULE=xdg)
+add_executable(xdg_test xdg_test.cpp)
+set_property(TARGET xdg_test PROPERTY CXX_STANDARD 11)
+target_link_libraries(xdg_test xdg)
+
+add_test(xdg_test xdg_test)
diff --git a/external/xdg/LICENSE b/external/xdg/LICENSE
new file mode 100644
index 0000000..341c30b
--- /dev/null
+++ b/external/xdg/LICENSE
@@ -0,0 +1,166 @@
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc.
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+
+ This version of the GNU Lesser General Public License incorporates
+the terms and conditions of version 3 of the GNU General Public
+License, supplemented by the additional permissions listed below.
+
+ 0. Additional Definitions.
+
+ As used herein, "this License" refers to version 3 of the GNU Lesser
+General Public License, and the "GNU GPL" refers to version 3 of the GNU
+General Public License.
+
+ "The Library" refers to a covered work governed by this License,
+other than an Application or a Combined Work as defined below.
+
+ An "Application" is any work that makes use of an interface provided
+by the Library, but which is not otherwise based on the Library.
+Defining a subclass of a class defined by the Library is deemed a mode
+of using an interface provided by the Library.
+
+ A "Combined Work" is a work produced by combining or linking an
+Application with the Library. The particular version of the Library
+with which the Combined Work was made is also called the "Linked
+Version".
+
+ The "Minimal Corresponding Source" for a Combined Work means the
+Corresponding Source for the Combined Work, excluding any source code
+for portions of the Combined Work that, considered in isolation, are
+based on the Application, and not on the Linked Version.
+
+ The "Corresponding Application Code" for a Combined Work means the
+object code and/or source code for the Application, including any data
+and utility programs needed for reproducing the Combined Work from the
+Application, but excluding the System Libraries of the Combined Work.
+
+ 1. Exception to Section 3 of the GNU GPL.
+
+ You may convey a covered work under sections 3 and 4 of this License
+without being bound by section 3 of the GNU GPL.
+
+ 2. Conveying Modified Versions.
+
+ If you modify a copy of the Library, and, in your modifications, a
+facility refers to a function or data to be supplied by an Application
+that uses the facility (other than as an argument passed when the
+facility is invoked), then you may convey a copy of the modified
+version:
+
+ a) under this License, provided that you make a good faith effort to
+ ensure that, in the event an Application does not supply the
+ function or data, the facility still operates, and performs
+ whatever part of its purpose remains meaningful, or
+
+ b) under the GNU GPL, with none of the additional permissions of
+ this License applicable to that copy.
+
+ 3. Object Code Incorporating Material from Library Header Files.
+
+ The object code form of an Application may incorporate material from
+a header file that is part of the Library. You may convey such object
+code under terms of your choice, provided that, if the incorporated
+material is not limited to numerical parameters, data structure
+layouts and accessors, or small macros, inline functions and templates
+(ten or fewer lines in length), you do both of the following:
+
+ a) Give prominent notice with each copy of the object code that the
+ Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the object code with a copy of the GNU GPL and this license
+ document.
+
+ 4. Combined Works.
+
+ You may convey a Combined Work under terms of your choice that,
+taken together, effectively do not restrict modification of the
+portions of the Library contained in the Combined Work and reverse
+engineering for debugging such modifications, if you also do each of
+the following:
+
+ a) Give prominent notice with each copy of the Combined Work that
+ the Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the Combined Work with a copy of the GNU GPL and this license
+ document.
+
+ c) For a Combined Work that displays copyright notices during
+ execution, include the copyright notice for the Library among
+ these notices, as well as a reference directing the user to the
+ copies of the GNU GPL and this license document.
+
+ d) Do one of the following:
+
+ 0) Convey the Minimal Corresponding Source under the terms of this
+ License, and the Corresponding Application Code in a form
+ suitable for, and under terms that permit, the user to
+ recombine or relink the Application with a modified version of
+ the Linked Version to produce a modified Combined Work, in the
+ manner specified by section 6 of the GNU GPL for conveying
+ Corresponding Source.
+
+ 1) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (a) uses at run time
+ a copy of the Library already present on the user's computer
+ system, and (b) will operate properly with a modified version
+ of the Library that is interface-compatible with the Linked
+ Version.
+
+ e) Provide Installation Information, but only if you would otherwise
+ be required to provide such information under section 6 of the
+ GNU GPL, and only to the extent that such information is
+ necessary to install and execute a modified version of the
+ Combined Work produced by recombining or relinking the
+ Application with a modified version of the Linked Version. (If
+ you use option 4d0, the Installation Information must accompany
+ the Minimal Corresponding Source and Corresponding Application
+ Code. If you use option 4d1, you must provide the Installation
+ Information in the manner specified by section 6 of the GNU GPL
+ for conveying Corresponding Source.)
+
+ 5. Combined Libraries.
+
+ You may place library facilities that are a work based on the
+Library side by side in a single library together with other library
+facilities that are not Applications and are not covered by this
+License, and convey such a combined library under terms of your
+choice, if you do both of the following:
+
+ a) Accompany the combined library with a copy of the same work based
+ on the Library, uncombined with any other library facilities,
+ conveyed under the terms of this License.
+
+ b) Give prominent notice with the combined library that part of it
+ is a work based on the Library, and explaining where to find the
+ accompanying uncombined form of the same work.
+
+ 6. Revised Versions of the GNU Lesser General Public License.
+
+ The Free Software Foundation may publish revised and/or new versions
+of the GNU Lesser General Public License from time to time. Such new
+versions will be similar in spirit to the present version, but may
+differ in detail to address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Library as you received it specifies that a certain numbered version
+of the GNU Lesser General Public License "or any later version"
+applies to it, you have the option of following the terms and
+conditions either of that published version or of any later version
+published by the Free Software Foundation. If the Library as you
+received it does not specify a version number of the GNU Lesser
+General Public License, you may choose any version of the GNU Lesser
+General Public License ever published by the Free Software Foundation.
+
+ If the Library as you received it specifies that a proxy can decide
+whether future versions of the GNU Lesser General Public License shall
+apply, that proxy's public statement of acceptance of any version is
+permanent authorization for you to choose that version for the
+Library.
+
diff --git a/external/xdg/xdg.cpp b/external/xdg/xdg.cpp
new file mode 100644
index 0000000..de9b043
--- /dev/null
+++ b/external/xdg/xdg.cpp
@@ -0,0 +1,203 @@
+// Copyright (C) 2015 Thomas Voß
+//
+// This library 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, either version 3 of the License, or
+// (at your option) any later version.
+//
+// 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 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 .
+
+#include
+
+#include
+
+#include
+#include
+
+namespace fs = boost::filesystem;
+
+namespace
+{
+
+fs::path throw_if_not_absolute(const fs::path& p)
+{
+ if (p.has_root_directory())
+ return p;
+
+ throw std::runtime_error{"Directores MUST be absolute."};
+}
+
+namespace env
+{
+std::string get(const std::string& key, const std::string& default_value)
+{
+ if (auto value = std::getenv(key.c_str()))
+ return value;
+ return default_value;
+}
+
+std::string get_or_throw(const std::string& key)
+{
+ if (auto value = std::getenv(key.c_str()))
+ {
+ return value;
+ }
+
+ throw std::runtime_error{key + " not set in environment"};
+}
+
+constexpr const char* xdg_data_home{"XDG_DATA_HOME"};
+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
+{
+class BaseDirSpecification : public xdg::BaseDirSpecification
+{
+public:
+ static const BaseDirSpecification& instance()
+ {
+ static const BaseDirSpecification spec;
+ return spec;
+ }
+
+ BaseDirSpecification()
+ {
+ }
+
+ const xdg::Data& data() const override
+ {
+ return data_;
+ }
+
+ const xdg::Config& config() const override
+ {
+ return config_;
+ }
+
+ const xdg::Cache& cache() const override
+ {
+ return cache_;
+ }
+
+ const xdg::Runtime& runtime() const override
+ {
+ return runtime_;
+ }
+
+private:
+ xdg::Data data_;
+ xdg::Config config_;
+ xdg::Cache cache_;
+ xdg::Runtime runtime_;
+};
+}
+}
+
+fs::path xdg::Data::home() const
+{
+ auto v = env::get(env::xdg_data_home, "");
+ if (v.empty())
+ return throw_if_not_absolute(fs::path{env::get_or_throw("HOME")} / ".local" / "share");
+
+ return throw_if_not_absolute(fs::path(v));
+}
+
+std::vector xdg::Data::dirs() const
+{
+ auto v = env::get(env::xdg_data_dirs, "");
+ if (v.empty())
+ return {fs::path{"/usr/local/share"}, fs::path{"/usr/share"}};
+
+ std::vector tokens;
+ tokens = boost::split(tokens, v, boost::is_any_of(":"));
+ std::vector result;
+ for (const auto& token : tokens)
+ {
+ result.push_back(throw_if_not_absolute(fs::path(token)));
+ }
+ return result;
+}
+
+fs::path xdg::Config::home() const
+{
+ auto v = env::get(env::xdg_config_home, "");
+ if (v.empty())
+ return throw_if_not_absolute(fs::path{env::get_or_throw("HOME")} / ".config");
+
+ return throw_if_not_absolute(fs::path(v));
+}
+
+std::vector xdg::Config::dirs() const
+{
+ auto v = env::get(env::xdg_config_dirs, "");
+ if (v.empty())
+ return {fs::path{"/etc/xdg"}};
+
+ std::vector tokens;
+ tokens = boost::split(tokens, v, boost::is_any_of(":"));
+ std::vector result;
+ for (const auto& token : tokens)
+ {
+ fs::path p(token);
+ result.push_back(throw_if_not_absolute(p));
+ }
+ return result;
+}
+
+fs::path xdg::Cache::home() const
+{
+ auto v = env::get(env::xdg_cache_home, "");
+ if (v.empty())
+ return throw_if_not_absolute(fs::path{env::get_or_throw("HOME")} / ".cache");
+
+ return throw_if_not_absolute(fs::path(v));
+}
+
+fs::path xdg::Runtime::dir() const
+{
+ auto v = env::get(env::xdg_config_home, "");
+ if (v.empty())
+ {
+ // We do not fall back gracefully and instead throw, dispatching to calling
+ // code for handling the case of a safe user-specfic runtime directory missing.
+ throw std::runtime_error{"Runtime directory not set"};
+ }
+
+ return throw_if_not_absolute(fs::path(v));
+}
+
+std::shared_ptr xdg::BaseDirSpecification::create()
+{
+ return std::make_shared();
+}
+
+const xdg::Data& xdg::data()
+{
+ return impl::BaseDirSpecification::instance().data();
+}
+
+const xdg::Config& xdg::config()
+{
+ return impl::BaseDirSpecification::instance().config();
+}
+
+const xdg::Cache& xdg::cache()
+{
+ return impl::BaseDirSpecification::instance().cache();
+}
+
+const xdg::Runtime& xdg::runtime()
+{
+ return impl::BaseDirSpecification::instance().runtime();
+}
diff --git a/external/xdg/xdg.h b/external/xdg/xdg.h
new file mode 100644
index 0000000..379f0ff
--- /dev/null
+++ b/external/xdg/xdg.h
@@ -0,0 +1,118 @@
+// Copyright (C) 2015 Thomas Voß
+//
+// This library 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, either version 3 of the License, or
+// (at your option) any later version.
+//
+// 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 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 .
+#ifndef XDG_H_
+#define XDG_H_
+
+#include
+
+#include
+#include
+
+namespace xdg
+{
+// NotCopyable deletes the copy c'tor and the assignment operator.
+struct NotCopyable
+{
+ NotCopyable() = default;
+ NotCopyable(const NotCopyable&) = delete;
+ virtual ~NotCopyable() = default;
+ NotCopyable& operator=(const NotCopyable&) = delete;
+};
+
+// NotMoveable deletes the move c'tor and the move assignment operator.
+struct NotMoveable
+{
+ NotMoveable() = default;
+ NotMoveable(NotMoveable&&) = delete;
+ virtual ~NotMoveable() = default;
+ NotMoveable& operator=(NotMoveable&&) = delete;
+};
+
+// Data provides functions to query the XDG_DATA_* entries.
+class Data : NotCopyable, NotMoveable
+{
+public:
+ // home returns the base directory relative to which user specific
+ // data files should be stored.
+ virtual boost::filesystem::path home() const;
+ // dirs returns the preference-ordered set of base directories to
+ // search for data files in addition to the $XDG_DATA_HOME base
+ // directory.
+ virtual std::vector dirs() const;
+};
+
+// Config provides functions to query the XDG_CONFIG_* entries.
+class Config : NotCopyable, NotMoveable
+{
+public:
+ // home returns the base directory relative to which user specific
+ // configuration files should be stored.
+ virtual boost::filesystem::path home() const;
+ // dirs returns the preference-ordered set of base directories to
+ // search for configuration files in addition to the
+ // $XDG_CONFIG_HOME base directory.
+ virtual std::vector dirs() const;
+};
+
+// Cache provides functions to query the XDG_CACHE_HOME entry.
+class Cache : NotCopyable, NotMoveable
+{
+public:
+ // home returns the base directory relative to which user specific
+ // non-essential data files should be stored.
+ virtual boost::filesystem::path home() const;
+};
+
+// Runtime provides functions to query the XDG_RUNTIME_DIR entry.
+class Runtime : NotCopyable, NotMoveable
+{
+public:
+ // home returns the base directory relative to which user-specific
+ // non-essential runtime files and other file objects (such as
+ // sockets, named pipes, ...) should be stored.
+ virtual boost::filesystem::path dir() const;
+};
+
+// A BaseDirSpecification implements the XDG base dir specification:
+// http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html
+class BaseDirSpecification : NotCopyable, NotMoveable
+{
+public:
+ // create returns an Implementation of BaseDirSpecification.
+ static std::shared_ptr create();
+
+ // data returns an immutable Data instance.
+ virtual const Data& data() const = 0;
+ // config returns an immutable Config instance.
+ virtual const Config& config() const = 0;
+ // cache returns an immutable Cache instance.
+ virtual const Cache& cache() const = 0;
+ // runtime returns an immutable Runtime instance.
+ virtual const Runtime& runtime() const = 0;
+protected:
+ BaseDirSpecification() = default;
+};
+
+// data returns an immutable reference to a Data instance.
+const Data& data();
+// config returns an immutable reference to a Config instance.
+const Config& config();
+// cache returns an immutable reference to a Cache instance.
+const Cache& cache();
+// runtime returns an immutable reference to a Runtime instance.
+const Runtime& runtime();
+}
+
+#endif // XDG_H_
diff --git a/external/xdg/xdg_test.cpp b/external/xdg/xdg_test.cpp
new file mode 100644
index 0000000..d5b3632
--- /dev/null
+++ b/external/xdg/xdg_test.cpp
@@ -0,0 +1,130 @@
+// Copyright (C) 2015 Thomas Voß
+//
+// This library 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, either version 3 of the License, or
+// (at your option) any later version.
+//
+// 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 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 .
+
+#include
+
+#include
+
+#include
+#include
+
+BOOST_AUTO_TEST_CASE(XdgDataHomeThrowsForRelativeDirectoryFromEnv)
+{
+ ::setenv("XDG_DATA_HOME", "tmp", 1);
+ BOOST_CHECK_THROW(xdg::BaseDirSpecification::create()->data().home(), std::runtime_error);
+ BOOST_CHECK_THROW(xdg::data().home(), std::runtime_error);
+}
+
+BOOST_AUTO_TEST_CASE(XdgDataHomeReturnsDefaultValueForEmptyEnv)
+{
+ ::setenv("HOME", "/tmp", 1);
+ ::setenv("XDG_DATA_HOME", "", 1);
+ BOOST_CHECK_EQUAL("/tmp/.local/share", xdg::BaseDirSpecification::create()->data().home());
+ BOOST_CHECK_EQUAL("/tmp/.local/share", xdg::data().home());
+}
+
+BOOST_AUTO_TEST_CASE(XdgDataDirsCorrectlyTokenizesEnv)
+{
+ ::setenv("XDG_DATA_DIRS", "/tmp:/tmp", 1);
+ BOOST_CHECK(2 == xdg::BaseDirSpecification::create()->data().dirs().size());
+ BOOST_CHECK(2 == xdg::data().dirs().size());
+}
+
+BOOST_AUTO_TEST_CASE(XdgDataDirsThrowsForRelativeDirectoryFromEnv)
+{
+ ::setenv("XDG_DATA_DIRS", "/tmp:tmp", 1);
+ BOOST_CHECK_THROW(xdg::BaseDirSpecification::create()->data().dirs(), std::runtime_error);
+ BOOST_CHECK_THROW(xdg::data().dirs(), std::runtime_error);
+}
+
+BOOST_AUTO_TEST_CASE(XdgDataDirsReturnsDefaultValueForEmptyEnv)
+{
+ ::setenv("XDG_DATA_DIRS", "", 1);
+ auto dirs = xdg::data().dirs();
+ BOOST_CHECK_EQUAL("/usr/local/share", dirs[0]);
+ BOOST_CHECK_EQUAL("/usr/share", dirs[1]);
+
+ dirs = xdg::BaseDirSpecification::create()->data().dirs();
+ BOOST_CHECK_EQUAL("/usr/local/share", dirs[0]);
+ BOOST_CHECK_EQUAL("/usr/share", dirs[1]);
+}
+
+BOOST_AUTO_TEST_CASE(XdgConfigHomeThrowsForRelativeDirectoryFromEnv)
+{
+ ::setenv("XDG_CONFIG_HOME", "tmp", 1);
+ BOOST_CHECK_THROW(xdg::BaseDirSpecification::create()->config().home(), std::runtime_error);
+ BOOST_CHECK_THROW(xdg::config().home(), std::runtime_error);
+}
+
+BOOST_AUTO_TEST_CASE(XdgConfigHomeReturnsDefaultValueForEmptyEnv)
+{
+ ::setenv("HOME", "/tmp", 1);
+ ::setenv("XDG_CONFIG_HOME", "", 1);
+ BOOST_CHECK_EQUAL("/tmp/.config", xdg::BaseDirSpecification::create()->config().home());
+ BOOST_CHECK_EQUAL("/tmp/.config", xdg::config().home());
+}
+
+BOOST_AUTO_TEST_CASE(XdgConfigDirsCorrectlyTokenizesEnv)
+{
+ ::setenv("XDG_CONFIG_DIRS", "/tmp:/tmp", 1);
+ BOOST_CHECK(2 == xdg::BaseDirSpecification::create()->config().dirs().size());
+ BOOST_CHECK(2 == xdg::config().dirs().size());
+}
+
+BOOST_AUTO_TEST_CASE(XdgConfigDirsThrowsForRelativeDirectoryFromEnv)
+{
+ ::setenv("XDG_CONFIG_DIRS", "/tmp:tmp", 1);
+ BOOST_CHECK_THROW(xdg::BaseDirSpecification::create()->config().dirs(), std::runtime_error);
+ BOOST_CHECK_THROW(xdg::config().dirs(), std::runtime_error);
+}
+
+BOOST_AUTO_TEST_CASE(XdgConfigDirsReturnsDefaultValueForEmptyEnv)
+{
+ ::setenv("XDG_CONFIG_DIRS", "", 1);
+ auto dirs = xdg::config().dirs();
+ BOOST_CHECK_EQUAL("/etc/xdg", dirs[0]);
+ dirs = xdg::BaseDirSpecification::create()->config().dirs();
+ BOOST_CHECK_EQUAL("/etc/xdg", dirs[0]);
+}
+
+BOOST_AUTO_TEST_CASE(XdgCacheHomeThrowsForRelativeDirectoryFromEnv)
+{
+ ::setenv("XDG_CACHE_HOME", "tmp", 1);
+ BOOST_CHECK_THROW(xdg::BaseDirSpecification::create()->cache().home(), std::runtime_error);
+ BOOST_CHECK_THROW(xdg::cache().home(), std::runtime_error);
+}
+
+BOOST_AUTO_TEST_CASE(XdgCacheHomeReturnsDefaultValueForEmptyEnv)
+{
+ ::setenv("HOME", "/tmp", 1);
+ ::setenv("XDG_CACHE_HOME", "", 1);
+ BOOST_CHECK_EQUAL("/tmp/.cache", xdg::BaseDirSpecification::create()->cache().home());
+ BOOST_CHECK_EQUAL("/tmp/.cache", xdg::cache().home());
+}
+
+BOOST_AUTO_TEST_CASE(XdgRuntimeDirThrowsForRelativeDirectoryFromEnv)
+{
+ ::setenv("XDG_RUNTIME_DIR", "tmp", 1);
+ BOOST_CHECK_THROW(xdg::BaseDirSpecification::create()->runtime().dir(), std::runtime_error);
+ BOOST_CHECK_THROW(xdg::runtime().dir(), std::runtime_error);
+}
+
+BOOST_AUTO_TEST_CASE(XdgRuntimeDirThrowsForEmptyEnv)
+{
+ ::setenv("XDG_RUNTIME_DIR", "", 1);
+ BOOST_CHECK_THROW(xdg::BaseDirSpecification::create()->runtime().dir(), std::runtime_error);
+ BOOST_CHECK_THROW(xdg::runtime().dir(), std::runtime_error);
+}
+
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 84d5209..fb7c899 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -60,6 +60,8 @@ set(SOURCES
anbox/not_reachable.cpp
anbox/application_manager.h
+ anbox/android/intent.cpp
+
anbox/common/fd.cpp
anbox/common/fd_sets.h
anbox/common/variable_length_array.h
@@ -168,9 +170,10 @@ set(SOURCES
anbox/dbus/skeleton/application_manager.cpp
anbox/dbus/stub/application_manager.cpp
+ anbox/application/launcher_storage.cpp
+
anbox/cmds/version.cpp
anbox/cmds/run.cpp
- anbox/cmds/install.cpp
anbox/cmds/launch.cpp
anbox/cmds/container_manager.cpp
@@ -193,7 +196,8 @@ target_link_libraries(anbox-core
renderControl_dec
OpenGLESDispatch
OpenglCodecCommon
- anbox-protobuf)
+ anbox-protobuf
+ xdg)
add_executable(anbox main.cpp)
target_link_libraries(anbox
diff --git a/src/anbox/android/intent.cpp b/src/anbox/android/intent.cpp
new file mode 100644
index 0000000..61c927b
--- /dev/null
+++ b/src/anbox/android/intent.cpp
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2016 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
+
+namespace anbox {
+namespace android {
+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 << "]]";
+ return out;
+}
+} // namespace android
+} // namespace anbox
+
diff --git a/src/anbox/android/intent.h b/src/anbox/android/intent.h
new file mode 100644
index 0000000..107dd05
--- /dev/null
+++ b/src/anbox/android/intent.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2016 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_INTENT_H_
+#define ANBOX_ANDROID_INTENT_H_
+
+#include
+#include
+
+namespace anbox {
+namespace android {
+struct Intent {
+ std::string action;
+ std::string uri;
+ std::string type;
+ int flags = 0;
+ std::string package;
+ std::string component;
+ std::vector categories;
+};
+
+std::ostream& operator<<(std::ostream &out, const Intent &intent);
+} // namespace android
+} // namespace anbox
+
+
+#endif
diff --git a/src/anbox/application/launcher_storage.cpp b/src/anbox/application/launcher_storage.cpp
new file mode 100644
index 0000000..871f709
--- /dev/null
+++ b/src/anbox/application/launcher_storage.cpp
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2016 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/application/launcher_storage.h"
+#include "anbox/utils.h"
+
+#include
+#include
+#include
+
+namespace fs = boost::filesystem;
+
+namespace anbox {
+namespace application {
+LauncherStorage::LauncherStorage(const fs::path &path) :
+ path_(path) {
+}
+
+LauncherStorage::~LauncherStorage() {
+}
+
+void LauncherStorage::add(const Item &item) {
+ if (!fs::exists(path_))
+ fs::create_directories(path_);
+
+ auto package_name = item.package;
+ std::replace(package_name.begin(), package_name.end(), '.', '-');
+
+ const auto item_path = path_ / utils::string_format("anbox-%s.desktop", package_name);
+ std::string exec = "anbox launch ";
+
+ if (!item.launch_intent.action.empty())
+ exec += utils::string_format("--action=%s ", item.launch_intent.action);
+
+ if (!item.launch_intent.type.empty())
+ exec += utils::string_format("--type=%s ", item.launch_intent.type);
+
+ if (!item.launch_intent.uri.empty())
+ exec += utils::string_format("--uri=%s ", item.launch_intent.uri);
+
+ if (!item.launch_intent.package.empty())
+ exec += utils::string_format("--package=%s ", item.launch_intent.package);
+
+ if (!item.launch_intent.component.empty())
+ exec += utils::string_format("--component=%s ", item.launch_intent.component);
+
+ std::ofstream f(item_path.string());
+ f << "[Desktop Entry]" << std::endl
+ << "Name=" << item.package << std::endl
+ << "Exec=" << exec << std::endl
+ << "Terminal=false" << std::endl
+ << "Type=Application" << std::endl
+ << "Encoding=UTF-8" << std::endl;
+ f.close();
+}
+} // namespace application
+} // namespace anbox
diff --git a/src/anbox/application/launcher_storage.h b/src/anbox/application/launcher_storage.h
new file mode 100644
index 0000000..c6c21eb
--- /dev/null
+++ b/src/anbox/application/launcher_storage.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2016 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_APPLICATION_LAUNCHER_STORAGE_H_
+#define ANBOX_APPLICATION_LAUNCHER_STORAGE_H_
+
+#include "anbox/android/intent.h"
+
+#include
+#include
+
+#include
+
+namespace anbox {
+namespace application {
+class LauncherStorage {
+public:
+ LauncherStorage(const boost::filesystem::path &path);
+ ~LauncherStorage();
+
+ struct Item {
+ std::string name;
+ std::string package;
+ android::Intent launch_intent;
+ };
+
+ void add(const Item &item);
+
+private:
+ boost::filesystem::path path_;
+};
+} // namespace application
+} // namespace anbox
+
+#endif
diff --git a/src/anbox/application_manager.h b/src/anbox/application_manager.h
index 2d06b9c..52dea49 100644
--- a/src/anbox/application_manager.h
+++ b/src/anbox/application_manager.h
@@ -19,14 +19,14 @@
#define ANBOX_APPLICATION_MANAGER_H_
#include "anbox/do_not_copy_or_move.h"
+#include "anbox/android/intent.h"
#include
namespace anbox {
class ApplicationManager : public DoNotCopyOrMove {
public:
- virtual void install(const std::string &path) = 0;
- virtual void launch(const std::string &package, const std::string &activity) = 0;
+ virtual void launch(const android::Intent &intent) = 0;
};
} // namespace anbox
diff --git a/src/anbox/bridge/android_api_stub.cpp b/src/anbox/bridge/android_api_stub.cpp
index a4a9f46..a6f5b6f 100644
--- a/src/anbox/bridge/android_api_stub.cpp
+++ b/src/anbox/bridge/android_api_stub.cpp
@@ -49,57 +49,39 @@ void AndroidApiStub::ensure_rpc_channel() {
throw std::runtime_error("No remote client connected");
}
-void AndroidApiStub::install(const std::string &path) {
- ensure_rpc_channel();
-
- const auto target_path = utils::string_format("/data/anbox-share/%s", fs::path(path).filename().string());
-
- if (fs::exists(target_path))
- fs::remove(target_path);
-
- fs::copy(path, target_path);
-
- const auto container_path = utils::string_format("/data/anbox-share/%s", fs::path(path).filename().string());
-
- auto c = std::make_shared>();
- protobuf::bridge::InstallApplication message;
- message.set_path(container_path);
-
- {
- std::lock_guard lock(mutex_);
- install_wait_handle_.expect_result();
- }
-
- channel_->call_method("install_application",
- &message,
- c->response.get(),
- google::protobuf::NewCallback(this, &AndroidApiStub::application_installed, c.get()));
-
- install_wait_handle_.wait_for_all();
-
- if (c->response->has_error())
- throw std::runtime_error(c->response->error());
-}
-
-void AndroidApiStub::application_installed(Request *request) {
- (void) request;
- install_wait_handle_.result_received();
-}
-
-void AndroidApiStub::launch(const std::string &package, const std::string &activity) {
+void AndroidApiStub::launch(const android::Intent &intent) {
ensure_rpc_channel();
auto c = std::make_shared>();
protobuf::bridge::LaunchApplication message;
- message.set_package_name(package);
- if (activity.length() > 0)
- message.set_activity(activity);
{
std::lock_guard lock(mutex_);
launch_wait_handle_.expect_result();
}
+ auto launch_intent = message.mutable_intent();
+
+ if (!intent.action.empty())
+ launch_intent->set_action(intent.action);
+
+ if (!intent.uri.empty())
+ launch_intent->set_uri(intent.uri);
+
+ if (!intent.type.empty())
+ launch_intent->set_type(intent.type);
+
+ if (!intent.package.empty())
+ launch_intent->set_package(intent.package);
+
+ if (!intent.component.empty())
+ launch_intent->set_component(intent.component);
+
+ for (const auto &category : intent.categories) {
+ auto c = launch_intent->add_categories();
+ *c = category;
+ }
+
channel_->call_method("launch_application",
&message,
c->response.get(),
@@ -116,40 +98,6 @@ void AndroidApiStub::application_launched(Request *request)
launch_wait_handle_.result_received();
}
-void AndroidApiStub::set_dns_servers(const std::string &domain, const std::vector &servers) {
- ensure_rpc_channel();
-
- auto c = std::make_shared>();
-
- protobuf::bridge::SetDnsServers message;
- message.set_domain(domain);
-
- for (const auto &server : servers) {
- auto server_message = message.add_servers();
- server_message->set_address(server);
- }
-
- {
- std::lock_guard lock(mutex_);
- set_dns_servers_wait_handle_.expect_result();
- }
-
- channel_->call_method("set_dns_servers",
- &message,
- c->response.get(),
- google::protobuf::NewCallback(this, &AndroidApiStub::dns_servers_set, c.get()));
-
- set_dns_servers_wait_handle_.wait_for_all();
-
- if (c->response->has_error())
- throw std::runtime_error(c->response->error());
-}
-
-void AndroidApiStub::dns_servers_set(Request *request) {
- (void) request;
- set_dns_servers_wait_handle_.result_received();
-}
-
void AndroidApiStub::set_focused_task(const std::int32_t &id) {
ensure_rpc_channel();
diff --git a/src/anbox/bridge/android_api_stub.h b/src/anbox/bridge/android_api_stub.h
index 73cff7c..9f8d1ac 100644
--- a/src/anbox/bridge/android_api_stub.h
+++ b/src/anbox/bridge/android_api_stub.h
@@ -42,10 +42,8 @@ public:
void set_rpc_channel(const std::shared_ptr &channel);
void reset_rpc_channel();
- void install(const std::string &path) override;
- void launch(const std::string &package, const std::string &activity) override;
+ void launch(const android::Intent &intent) override;
- void set_dns_servers(const std::string &domain, const std::vector &servers);
void set_focused_task(const std::int32_t &id);
private:
@@ -58,16 +56,12 @@ private:
bool success;
};
- void application_installed(Request *request);
void application_launched(Request *request);
- void dns_servers_set(Request *request);
void focused_task_set(Request *request);
mutable std::mutex mutex_;
std::shared_ptr channel_;
- common::WaitHandle install_wait_handle_;
common::WaitHandle launch_wait_handle_;
- common::WaitHandle set_dns_servers_wait_handle_;
common::WaitHandle set_focused_task_handle_;
};
} // namespace bridge
diff --git a/src/anbox/bridge/platform_api_skeleton.cpp b/src/anbox/bridge/platform_api_skeleton.cpp
index cb21e67..02966f8 100644
--- a/src/anbox/bridge/platform_api_skeleton.cpp
+++ b/src/anbox/bridge/platform_api_skeleton.cpp
@@ -16,6 +16,7 @@
*/
#include "anbox/bridge/platform_api_skeleton.h"
+#include "anbox/application/launcher_storage.h"
#include "anbox/wm/manager.h"
#include "anbox/wm/window_state.h"
#include "anbox/logger.h"
@@ -25,9 +26,11 @@
namespace anbox {
namespace bridge {
PlatformApiSkeleton::PlatformApiSkeleton(const std::shared_ptr &pending_calls,
- const std::shared_ptr &window_manager) :
+ const std::shared_ptr &window_manager,
+ const std::shared_ptr &launcher_storage) :
pending_calls_(pending_calls),
- window_manager_(window_manager) {
+ window_manager_(window_manager),
+ launcher_storage_(launcher_storage) {
}
PlatformApiSkeleton::~PlatformApiSkeleton() {
@@ -66,6 +69,29 @@ void PlatformApiSkeleton::handle_window_state_update_event(const anbox::protobuf
window_manager_->apply_window_state_update(updated, removed);
}
+void PlatformApiSkeleton::handle_application_list_update_event(const anbox::protobuf::bridge::ApplicationListUpdateEvent &event) {
+ for (int n = 0; n < event.applications_size(); n++) {
+ application::LauncherStorage::Item item;
+
+ const auto app = event.applications(n);
+ item.name = app.name();
+ item.package = app.package();
+
+ const auto li = app.launch_intent();
+ item.launch_intent.action = li.action();
+ item.launch_intent.uri = li.uri();
+ item.launch_intent.type = li.uri();
+ item.launch_intent.package = li.package();
+ item.launch_intent.component = li.component();
+
+ for (int m = 0; m < li.categories_size(); m++)
+ item.launch_intent.categories.push_back(li.categories(m));
+
+ // If the item is already stored it will be updated
+ launcher_storage_->add(item);
+ }
+}
+
void PlatformApiSkeleton::register_boot_finished_handler(const std::function &action) {
boot_finished_handler_ = action;
}
diff --git a/src/anbox/bridge/platform_api_skeleton.h b/src/anbox/bridge/platform_api_skeleton.h
index 030e7ea..0cf0c3b 100644
--- a/src/anbox/bridge/platform_api_skeleton.h
+++ b/src/anbox/bridge/platform_api_skeleton.h
@@ -34,6 +34,7 @@ class Void;
namespace bridge {
class BootFinishedEvent;
class WindowStateUpdateEvent;
+class ApplicationListUpdateEvent;
} // namespace bridge
} // namespace protobuf
namespace rpc {
@@ -42,21 +43,27 @@ class PendingCallCache;
namespace wm {
class Manager;
} // namespace wm
+namespace application {
+class LauncherStorage;
+} // namespace application
namespace bridge {
class PlatformApiSkeleton {
public:
PlatformApiSkeleton(const std::shared_ptr &pending_calls,
- const std::shared_ptr &window_manager);
+ const std::shared_ptr &window_manager,
+ const std::shared_ptr &launcher_storage);
virtual ~PlatformApiSkeleton();
void handle_boot_finished_event(const anbox::protobuf::bridge::BootFinishedEvent &event);
void handle_window_state_update_event(const anbox::protobuf::bridge::WindowStateUpdateEvent &event);
+ void handle_application_list_update_event(const anbox::protobuf::bridge::ApplicationListUpdateEvent &event);
void register_boot_finished_handler(const std::function &action);
private:
std::shared_ptr pending_calls_;
std::shared_ptr window_manager_;
+ std::shared_ptr launcher_storage_;
std::function boot_finished_handler_;
};
} // namespace bridge
diff --git a/src/anbox/bridge/platform_message_processor.cpp b/src/anbox/bridge/platform_message_processor.cpp
index 5b9892c..4bffa79 100644
--- a/src/anbox/bridge/platform_message_processor.cpp
+++ b/src/anbox/bridge/platform_message_processor.cpp
@@ -44,11 +44,14 @@ void PlatformMessageProcessor::process_event_sequence(const std::string &raw_eve
return;
}
+ if (seq.has_boot_finished())
+ server_->handle_boot_finished_event(seq.boot_finished());
+
if (seq.has_window_state_update())
server_->handle_window_state_update_event(seq.window_state_update());
- if (seq.has_boot_finished())
- server_->handle_boot_finished_event(seq.boot_finished());
+ if (seq.has_application_list_update())
+ server_->handle_application_list_update_event(seq.application_list_update());
}
} // namespace anbox
} // namespace network
diff --git a/src/anbox/cmds/launch.cpp b/src/anbox/cmds/launch.cpp
index 809926e..b86da8a 100644
--- a/src/anbox/cmds/launch.cpp
+++ b/src/anbox/cmds/launch.cpp
@@ -25,19 +25,20 @@
namespace fs = boost::filesystem;
anbox::cmds::Launch::Launch()
- : CommandWithFlagsAndAction{cli::Name{"launch"}, cli::Usage{"launch"}, cli::Description{"Launch specified application in the Android container"}}
+ : CommandWithFlagsAndAction{cli::Name{"launch"}, cli::Usage{"launch"}, cli::Description{"Launch an Activity by sending an intent"}}
{
- flag(cli::make_flag(cli::Name{"package"}, cli::Description{"Package the application is part of"}, package_));
- flag(cli::make_flag(cli::Name{"activity"}, cli::Description{"Activity of the application to start"}, activity_));
- action([this](const cli::Command::Context&) {
- if (package_.empty() && activity_.empty())
- BOOST_THROW_EXCEPTION(std::runtime_error("Package or activity name not specified"));
+ flag(cli::make_flag(cli::Name{"action"}, cli::Description{"Action of the intent"}, intent_.action));
+ flag(cli::make_flag(cli::Name{"type"}, cli::Description{"MIME type for the intent"}, intent_.type));
+ flag(cli::make_flag(cli::Name{"uri"}, cli::Description{"URI used as data within the intent"}, intent_.uri));
+ flag(cli::make_flag(cli::Name{"package"}, cli::Description{"Package the intent should go to"}, intent_.package));
+ flag(cli::make_flag(cli::Name{"component"}, cli::Description{"Component of a package the intent should go"}, intent_.component));
+ action([this](const cli::Command::Context&) {
auto bus = std::make_shared(core::dbus::WellKnownBus::session);
bus->install_executor(core::dbus::asio::make_executor(bus));
auto stub = dbus::stub::ApplicationManager::create_for_bus(bus);
- stub->launch(package_, activity_);
+ stub->launch(intent_);
return EXIT_SUCCESS;
});
diff --git a/src/anbox/cmds/launch.h b/src/anbox/cmds/launch.h
index bbbbe54..98075a0 100644
--- a/src/anbox/cmds/launch.h
+++ b/src/anbox/cmds/launch.h
@@ -23,6 +23,7 @@
#include
#include "anbox/cli.h"
+#include "anbox/android/intent.h"
namespace anbox {
namespace cmds {
@@ -31,8 +32,7 @@ public:
Launch();
private:
- std::string package_;
- std::string activity_;
+ android::Intent intent_;
};
} // namespace cmds
} // namespace anbox
diff --git a/src/anbox/cmds/run.cpp b/src/anbox/cmds/run.cpp
index f32c522..84a4b66 100644
--- a/src/anbox/cmds/run.cpp
+++ b/src/anbox/cmds/run.cpp
@@ -37,6 +37,9 @@
#include "anbox/container/client.h"
#include "anbox/wm/manager.h"
#include "anbox/ubuntu/platform_policy.h"
+#include "anbox/application/launcher_storage.h"
+
+#include "external/xdg/xdg.h"
#include
@@ -97,6 +100,9 @@ anbox::cmds::Run::Run(const BusFactory& bus_factory)
auto window_manager = std::make_shared(policy);
+ auto launcher_storage = std::make_shared(
+ xdg::data().home() / "applications");
+
auto renderer = std::make_shared(window_manager);
renderer->start();
@@ -130,14 +136,11 @@ anbox::cmds::Run::Run(const BusFactory& bus_factory)
// more than one one day we need proper dispatching to the right one.
android_api_stub->set_rpc_channel(rpc_channel);
- auto server = std::make_shared(pending_calls, window_manager);
+ auto server = std::make_shared(pending_calls,
+ window_manager,
+ launcher_storage);
server->register_boot_finished_handler([&]() {
DEBUG("Android successfully booted");
- dispatcher->dispatch([&]() {
- // FIXME make this configurable or once we have a bridge let the host
- // act as a DNS proxy.
- android_api_stub->set_dns_servers("anbox", std::vector{ "8.8.8.8" });
- });
});
return std::make_shared(sender, server, pending_calls);
}));
diff --git a/src/anbox/daemon.cpp b/src/anbox/daemon.cpp
index dd91e39..5953ab3 100644
--- a/src/anbox/daemon.cpp
+++ b/src/anbox/daemon.cpp
@@ -24,7 +24,6 @@
#include "anbox/cmds/version.h"
#include "anbox/cmds/run.h"
-#include "anbox/cmds/install.h"
#include "anbox/cmds/launch.h"
#include "anbox/cmds/container_manager.h"
@@ -38,7 +37,6 @@ Daemon::Daemon() :
cmd.command(std::make_shared())
.command(std::make_shared())
- .command(std::make_shared())
.command(std::make_shared())
.command(std::make_shared());
}
diff --git a/src/anbox/dbus/interface.h b/src/anbox/dbus/interface.h
index 43b1f43..bc9a202 100644
--- a/src/anbox/dbus/interface.h
+++ b/src/anbox/dbus/interface.h
@@ -33,12 +33,6 @@ struct Service {
struct ApplicationManager {
static inline std::string name() { return "org.anbox.ApplicationManager"; }
struct Methods {
- struct Install {
- static inline std::string name() { return "Install"; }
- typedef anbox::dbus::interface::ApplicationManager Interface;
- typedef void ResultType;
- static inline std::chrono::milliseconds default_timeout() { return std::chrono::seconds{240}; }
- };
struct Launch {
static inline std::string name() { return "Launch"; }
typedef anbox::dbus::interface::ApplicationManager Interface;
diff --git a/src/anbox/dbus/skeleton/application_manager.cpp b/src/anbox/dbus/skeleton/application_manager.cpp
index 04f0d26..6db626b 100644
--- a/src/anbox/dbus/skeleton/application_manager.cpp
+++ b/src/anbox/dbus/skeleton/application_manager.cpp
@@ -17,6 +17,7 @@
#include "anbox/dbus/skeleton/application_manager.h"
#include "anbox/dbus/interface.h"
+#include "anbox/android/intent.h"
#include "anbox/logger.h"
namespace anbox {
@@ -29,41 +30,22 @@ ApplicationManager::ApplicationManager(const core::dbus::Bus::Ptr &bus,
object_(object),
impl_(impl) {
- object_->install_method_handler(
- [this](const core::dbus::Message::Ptr &msg) {
- std::string path;
- auto reader = msg->reader();
- reader >> path;
-
- core::dbus::Message::Ptr reply;
-
- try {
- install(path);
- DEBUG("install done");
- reply = core::dbus::Message::make_method_return(msg);
- DEBUG("Successfully installed application");
- }
- catch (std::exception const &err) {
- DEBUG("Failed to install application: %s", err.what());
- reply = core::dbus::Message::make_error(msg,
- "org.anbox.Error.Failed",
- err.what());
- }
-
- bus_->send(reply);
- });
-
object_->install_method_handler(
[this](const core::dbus::Message::Ptr &msg) {
- std::string package, activity;
auto reader = msg->reader();
- reader >> package;
- reader >> activity;
+
+ android::Intent intent;
+ reader >> intent.action;
+ reader >> intent.uri;
+ reader >> intent.type;
+ reader >> intent.flags;
+ reader >> intent.package;
+ reader >> intent.component;
core::dbus::Message::Ptr reply;
try {
- launch(package, activity);
+ launch(intent);
reply = core::dbus::Message::make_method_return(msg);
}
catch (std::exception const &err) {
@@ -77,15 +59,10 @@ ApplicationManager::ApplicationManager(const core::dbus::Bus::Ptr &bus,
}
ApplicationManager::~ApplicationManager() {
- object_->uninstall_method_handler();
}
-void ApplicationManager::install(const std::string &path) {
- impl_->install(path);
-}
-
-void ApplicationManager::launch(const std::string &package, const std::string &activity) {
- impl_->launch(package, activity);
+void ApplicationManager::launch(const android::Intent &intent) {
+ impl_->launch(intent);
}
} // namespace skeleton
} // namespace dbus
diff --git a/src/anbox/dbus/skeleton/application_manager.h b/src/anbox/dbus/skeleton/application_manager.h
index 3ccba27..d894e1f 100644
--- a/src/anbox/dbus/skeleton/application_manager.h
+++ b/src/anbox/dbus/skeleton/application_manager.h
@@ -34,8 +34,7 @@ public:
const std::shared_ptr &impl);
~ApplicationManager();
- void install(const std::string &path) override;
- void launch(const std::string &package, const std::string &activity) override;
+ void launch(const android::Intent &intent) override;
private:
core::dbus::Bus::Ptr bus_;
diff --git a/src/anbox/dbus/stub/application_manager.cpp b/src/anbox/dbus/stub/application_manager.cpp
index e9f4bb7..3388a9d 100644
--- a/src/anbox/dbus/stub/application_manager.cpp
+++ b/src/anbox/dbus/stub/application_manager.cpp
@@ -39,23 +39,16 @@ ApplicationManager::ApplicationManager(const core::dbus::Bus::Ptr &bus,
ApplicationManager::~ApplicationManager() {
}
-void ApplicationManager::install(const std::string &path) {
- DEBUG("path %s", path);
-
- auto result = object_->invoke_method_synchronously<
- anbox::dbus::interface::ApplicationManager::Methods::Install,
- anbox::dbus::interface::ApplicationManager::Methods::Install::ResultType>(path);
-
- if (result.is_error())
- throw std::runtime_error(result.error().print());
-}
-
-void ApplicationManager::launch(const std::string &package, const std::string &activity) {
- DEBUG("package %s activity %s", package, activity);
-
+void ApplicationManager::launch(const android::Intent &intent) {
auto result = object_->invoke_method_synchronously<
anbox::dbus::interface::ApplicationManager::Methods::Launch,
- anbox::dbus::interface::ApplicationManager::Methods::Launch::ResultType>(package, activity);
+ anbox::dbus::interface::ApplicationManager::Methods::Launch::ResultType>(
+ intent.action,
+ intent.uri,
+ intent.type,
+ intent.flags,
+ intent.package,
+ intent.component);
if (result.is_error())
throw std::runtime_error(result.error().print());
diff --git a/src/anbox/dbus/stub/application_manager.h b/src/anbox/dbus/stub/application_manager.h
index f01ef16..b7ceca8 100644
--- a/src/anbox/dbus/stub/application_manager.h
+++ b/src/anbox/dbus/stub/application_manager.h
@@ -36,8 +36,7 @@ public:
const core::dbus::Object::Ptr& object);
~ApplicationManager();
- void install(const std::string &path) override;
- void launch(const std::string &package, const std::string &activity) override;
+ void launch(const android::Intent &intent) override;
private:
core::dbus::Bus::Ptr bus_;
diff --git a/src/anbox/protobuf/anbox_bridge.proto b/src/anbox/protobuf/anbox_bridge.proto
index 382a2b8..9c448ba 100644
--- a/src/anbox/protobuf/anbox_bridge.proto
+++ b/src/anbox/protobuf/anbox_bridge.proto
@@ -7,6 +7,15 @@ message StructuredError {
optional uint32 code = 2;
}
+message Intent {
+ optional string action = 1;
+ optional string uri = 2;
+ optional string type = 3;
+ optional string package = 4;
+ optional string component = 5;
+ repeated string categories = 6;
+}
+
message Notification {
required string package_name = 1;
required string category = 2;
@@ -15,21 +24,8 @@ message Notification {
optional string text = 5;
}
-message InstallApplication {
- required string path = 1;
-}
-
message LaunchApplication {
- required string package_name = 1;
- optional string activity = 2;
-}
-
-message SetDnsServers {
- required string domain = 1;
- message Server {
- required string address = 1;
- }
- repeated Server servers = 2;
+ required Intent intent = 1;
}
message SetFocusedTask {
@@ -55,9 +51,19 @@ message WindowStateUpdateEvent {
repeated WindowState removed_windows = 2;
}
+message ApplicationListUpdateEvent {
+ message Application {
+ required string name = 1;
+ required string package = 2;
+ optional Intent launch_intent = 3;
+ }
+ repeated Application applications = 1;
+}
+
message EventSequence {
optional BootFinishedEvent boot_finished = 1;
optional WindowStateUpdateEvent window_state_update = 2;
+ optional ApplicationListUpdateEvent application_list_update = 3;
optional string error = 127;
optional StructuredError structured_error = 128;