diff --git a/android/appmgr/src/org/anbox/appmgr/LauncherService.java b/android/appmgr/src/org/anbox/appmgr/LauncherService.java index 98657c84dce1228d8535b430a88ec5607c5f86ac..09a94ec2a681f6262fcbbeeb974f9c7a3b44bea5 100644 --- a/android/appmgr/src/org/anbox/appmgr/LauncherService.java +++ b/android/appmgr/src/org/anbox/appmgr/LauncherService.java @@ -20,12 +20,14 @@ package org.anbox.appmgr; import android.app.Service; import android.util.Log; import android.content.Intent; +import android.content.IntentFilter; import android.os.IBinder; public final class LauncherService extends Service { public static final String TAG = "AnboxAppMgr"; private PlatformService mPlatformService; + private PackageEventReceiver mPkgEventReceiver; public LauncherService() { super(); @@ -35,9 +37,20 @@ public final class LauncherService extends Service { @Override public void onCreate() { mPlatformService = new PlatformService(getBaseContext()); - // Send all necessary initial updates + + // Send the current list of applications over to the host so + // it can rebuild its list of available applications. mPlatformService.sendApplicationListUpdate(); + IntentFilter filter = new IntentFilter(); + filter.addAction(Intent.ACTION_PACKAGE_ADDED); + filter.addAction(Intent.ACTION_PACKAGE_CHANGED); + filter.addAction(Intent.ACTION_PACKAGE_REMOVED); + filter.addDataScheme("package"); + + mPkgEventReceiver = new PackageEventReceiver(); + registerReceiver(mPkgEventReceiver, filter); + Log.i(TAG, "Service started"); } diff --git a/android/appmgr/src/org/anbox/appmgr/PackageEventReceiver.java b/android/appmgr/src/org/anbox/appmgr/PackageEventReceiver.java index 77de8570525e1c2b300694e3a3521a408db653b7..1f299ba160ff5e268e415c1819729d00ba3077d4 100644 --- a/android/appmgr/src/org/anbox/appmgr/PackageEventReceiver.java +++ b/android/appmgr/src/org/anbox/appmgr/PackageEventReceiver.java @@ -20,13 +20,35 @@ package org.anbox.appmgr; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; +import android.net.Uri; import android.util.Log; public class PackageEventReceiver extends BroadcastReceiver { private static final String TAG = "AnboxAppMgr"; + private PlatformService mPlatformService; + + private String getPackageName(Intent intent) { + Uri uri = intent.getData(); + String package_name = (uri != null ? uri.getSchemeSpecificPart() : null); + return package_name; + } + @Override public void onReceive(Context context, Intent intent) { - Log.i(TAG, "Received intent " + intent.toString()); + if (mPlatformService == null) + mPlatformService = new PlatformService(context); + + if (intent.getAction() == Intent.ACTION_PACKAGE_ADDED || + intent.getAction() == Intent.ACTION_PACKAGE_CHANGED) { + // Send updated list of applications to the host so that it + // can update the list of applications available for the user. + mPlatformService.sendApplicationListUpdate(); + } else if (intent.getAction() == Intent.ACTION_PACKAGE_REMOVED) { + // Only send notification when package got removed and not replaced + if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) { + mPlatformService.notifyPackageRemoved(getPackageName(intent)); + } + } } } diff --git a/android/appmgr/src/org/anbox/appmgr/PlatformService.java b/android/appmgr/src/org/anbox/appmgr/PlatformService.java index 7f9ac698c91285b2e1bd4e6f63ac00bbcb24cf72..3602f0e9468589a80a8f7c46bcb57f323541af43 100644 --- a/android/appmgr/src/org/anbox/appmgr/PlatformService.java +++ b/android/appmgr/src/org/anbox/appmgr/PlatformService.java @@ -62,6 +62,31 @@ public final class PlatformService { Log.i(TAG, "Connected to platform service"); } + public void notifyPackageRemoved(String packageName) { + connectService(); + + if (mService == null) + return; + + Log.i(TAG, "Sending package removed notification to host service"); + + Parcel data = Parcel.obtain(); + data.writeInterfaceToken(DESCRIPTOR); + // No added or updated applications to report + data.writeInt(0); + // .. but a single removed application + data.writeInt(1); + data.writeString(packageName); + + 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 sendApplicationListUpdate() { connectService(); @@ -112,6 +137,9 @@ public final class PlatformService { data.writeByteArray(outStream.toByteArray()); } + // We don't have any removed applications to include in the update + data.writeInt(0); + Parcel reply = Parcel.obtain(); try { mService.transact(TRANSACTION_updateApplicationList, data, reply, 0); diff --git a/android/service/platform_api_stub.cpp b/android/service/platform_api_stub.cpp index 5abe59a15146784440bd3eb8328af4590f0e6151..6f4768e5e8e8e864dda78e1035a78d7ca9ec271a 100644 --- a/android/service/platform_api_stub.cpp +++ b/android/service/platform_api_stub.cpp @@ -88,6 +88,12 @@ void PlatformApiStub::update_application_list(const ApplicationListUpdate &updat app->set_icon(a.icon.data(), a.icon.size()); } + for (const auto &package : update.removed_applications) { + auto app = event->add_removed_applications(); + app->set_name("unknown"); + app->set_package(package); + } + rpc_channel_->send_event(seq); } diff --git a/android/service/platform_api_stub.h b/android/service/platform_api_stub.h index 2397437030ad46a2db6c4417e5eb7a4258622a35..7ddfb880874d2ff035e7132d752c96ded2665004 100644 --- a/android/service/platform_api_stub.h +++ b/android/service/platform_api_stub.h @@ -79,6 +79,7 @@ public: std::vector icon; }; std::vector applications; + std::vector removed_applications; }; void update_application_list(const ApplicationListUpdate &update); diff --git a/android/service/platform_service.cpp b/android/service/platform_service.cpp index d6257dfd36a3f35d639163350c9fef654b75a1ba..8e2c57a4233c4fae8bd2309903cef80179d5dc72 100644 --- a/android/service/platform_service.cpp +++ b/android/service/platform_service.cpp @@ -120,6 +120,12 @@ status_t PlatformService::update_application_list(const Parcel &data) { update.applications.push_back(p); } + const auto num_removed_packages = data.readInt32(); + for (auto n = 0; n < num_removed_packages; n++) { + String8 package_name(data.readString16()); + update.removed_applications.push_back(package_name.string()); + } + platform_api_stub_->update_application_list(update); return OK; diff --git a/src/anbox/application/launcher_storage.cpp b/src/anbox/application/launcher_storage.cpp index ae036ef58fb08bfb85bc94fdf7b3a26ee33cc57e..cb8f5f10fdad82aff50c0461fcd2713199686be9 100644 --- a/src/anbox/application/launcher_storage.cpp +++ b/src/anbox/application/launcher_storage.cpp @@ -41,16 +41,27 @@ void LauncherStorage::reset() { fs::remove_all(icon_path_); } -void LauncherStorage::add(const Item &item) { +std::string LauncherStorage::clean_package_name(const std::string &package_name) { + auto cleaned_package_name = package_name; + std::replace(cleaned_package_name.begin(), cleaned_package_name.end(), '.', '-'); + return cleaned_package_name; +} + +fs::path LauncherStorage::path_for_item(const std::string &package_name) { + return path_ / utils::string_format("anbox-%s.desktop", package_name); +} + +fs::path LauncherStorage::path_for_item_icon(const std::string &package_name) { + return icon_path_ / utils::string_format("anbox-%s.png", package_name); +} + +void LauncherStorage::add_or_update(const Item &item) { if (!fs::exists(path_)) fs::create_directories(path_); if (!fs::exists(icon_path_)) fs::create_directories(icon_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); - const auto item_icon_path = icon_path_ / utils::string_format("anbox-%s.png", package_name); - std::string exec = utils::string_format("%s launch ", utils::process_get_exe_path(getpid())); if (!item.launch_intent.action.empty()) @@ -68,7 +79,8 @@ void LauncherStorage::add(const Item &item) { if (!item.launch_intent.component.empty()) exec += utils::string_format("--component=%s ", item.launch_intent.component); - if (auto desktop_item = std::ofstream(item_path.string())) { + const auto item_icon_path = path_for_item_icon(package_name); + if (auto desktop_item = std::ofstream(path_for_item(package_name).string())) { desktop_item << "[Desktop Entry]" << std::endl << "Name=" << item.package << std::endl << "Exec=" << exec << std::endl @@ -84,5 +96,18 @@ void LauncherStorage::add(const Item &item) { else BOOST_THROW_EXCEPTION(std::runtime_error("Failed to write icon")); } + +void LauncherStorage::remove(const Item &item) { + auto package_name = clean_package_name(item.package); + + const auto item_path = path_for_item(package_name); + if (fs::exists(item_path)) + fs::remove(item_path); + + const auto item_icon_path = path_for_item_icon(package_name); + if (fs::exists(item_icon_path)) + fs::remove(item_icon_path); +} + } // namespace application } // namespace anbox diff --git a/src/anbox/application/launcher_storage.h b/src/anbox/application/launcher_storage.h index e019d90fd93c34d477bda3ac78fcd74ad246324b..e045ea215cf9c53be03ce27090bfdc688c08b9fc 100644 --- a/src/anbox/application/launcher_storage.h +++ b/src/anbox/application/launcher_storage.h @@ -41,9 +41,14 @@ class LauncherStorage { }; void reset(); - void add(const Item &item); + void add_or_update(const Item &item); + void remove(const Item &item); private: + std::string clean_package_name(const std::string &package_name); + boost::filesystem::path path_for_item(const std::string &package_name); + boost::filesystem::path path_for_item_icon(const std::string &package_name); + boost::filesystem::path path_; boost::filesystem::path icon_path_; }; diff --git a/src/anbox/bridge/platform_api_skeleton.cpp b/src/anbox/bridge/platform_api_skeleton.cpp index 32f53decc6d3d35c09face80f604aecf43f88bfd..55a24951a784745e78d74e1aef82ae0950102efc 100644 --- a/src/anbox/bridge/platform_api_skeleton.cpp +++ b/src/anbox/bridge/platform_api_skeleton.cpp @@ -62,15 +62,13 @@ void PlatformApiSkeleton::get_clipboard_data(anbox::protobuf::rpc::Void const *r done->Run(); } -void PlatformApiSkeleton::handle_boot_finished_event( - const anbox::protobuf::bridge::BootFinishedEvent &event) { +void PlatformApiSkeleton::handle_boot_finished_event(const anbox::protobuf::bridge::BootFinishedEvent &event) { (void)event; if (boot_finished_handler_) boot_finished_handler_(); } -void PlatformApiSkeleton::handle_window_state_update_event( - const anbox::protobuf::bridge::WindowStateUpdateEvent &event) { +void PlatformApiSkeleton::handle_window_state_update_event(const anbox::protobuf::bridge::WindowStateUpdateEvent &event) { auto convert_window_state = []( const ::anbox::protobuf::bridge::WindowStateUpdateEvent_WindowState &window) { @@ -97,12 +95,18 @@ void PlatformApiSkeleton::handle_window_state_update_event( window_manager_->apply_window_state_update(updated, removed); } -void PlatformApiSkeleton::handle_application_list_update_event( - const anbox::protobuf::bridge::ApplicationListUpdateEvent &event) { - // Remove all existing items to start from scratch for all - // applications. We may need to improve this in the future - // to guarantee correct integration into desktop environments - launcher_storage_->reset(); +void PlatformApiSkeleton::handle_application_list_update_event(const anbox::protobuf::bridge::ApplicationListUpdateEvent &event) { + for (int n = 0; n < event.removed_applications_size(); n++) { + application::LauncherStorage::Item item; + + const auto app = event.removed_applications(n); + item.package = app.package(); + + if (item.package.empty()) + continue; + + launcher_storage_->remove(item); + } for (int n = 0; n < event.applications_size(); n++) { application::LauncherStorage::Item item; @@ -126,13 +130,11 @@ void PlatformApiSkeleton::handle_application_list_update_event( if (item.package.empty()) continue; - // If the item is already stored it will be updated - launcher_storage_->add(item); + launcher_storage_->add_or_update(item); } } -void PlatformApiSkeleton::register_boot_finished_handler( - const std::function &action) { +void PlatformApiSkeleton::register_boot_finished_handler(const std::function &action) { boot_finished_handler_ = action; } } // namespace bridge diff --git a/src/anbox/protobuf/anbox_bridge.proto b/src/anbox/protobuf/anbox_bridge.proto index e57649ed464014bf5790edb9aaf4f863a028cd59..2210188d0a715d9988fa8331503e224135292ada 100644 --- a/src/anbox/protobuf/anbox_bridge.proto +++ b/src/anbox/protobuf/anbox_bridge.proto @@ -84,6 +84,7 @@ message ApplicationListUpdateEvent { optional bytes icon = 4; } repeated Application applications = 1; + repeated Application removed_applications = 2; } message EventSequence {