Dynamically stop lazy services
Services can choose to register with the new LazyServiceRegistrar.
ServiceManager perpetually checks the reference counts of services
registered in this way. If ServiceManager detects that a service no
longer has any clients, it will notify the LazyServiceRegistrar, which
will attempt to shut down the service.
Bug: 143108344
Test: aidl_lazy_test
Change-Id: Ic01981b767ab4402e7aecdf1cdf9ed64df1f5af4
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index 7ee4882..079dd82 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -90,6 +90,7 @@
"IResultReceiver.cpp",
"IServiceManager.cpp",
"IShellCallback.cpp",
+ "LazyServiceRegistrar.cpp",
"MemoryBase.cpp",
"MemoryDealer.cpp",
"MemoryHeapBase.cpp",
@@ -160,6 +161,7 @@
name: "libbinder_aidl",
srcs: [
"aidl/android/content/pm/IPackageManagerNative.aidl",
+ "aidl/android/os/IClientCallback.aidl",
"aidl/android/os/IServiceCallback.aidl",
"aidl/android/os/IServiceManager.aidl",
],
diff --git a/libs/binder/IServiceManager.cpp b/libs/binder/IServiceManager.cpp
index bac8b66..5ca9156 100644
--- a/libs/binder/IServiceManager.cpp
+++ b/libs/binder/IServiceManager.cpp
@@ -271,6 +271,8 @@
std::unique_lock<std::mutex> lock(mMutex);
mBinder = binder;
lock.unlock();
+ // Flushing here helps ensure the service's ref count remains accurate
+ IPCThreadState::self()->flushCommands();
mCv.notify_one();
return Status::ok();
}
diff --git a/libs/binder/LazyServiceRegistrar.cpp b/libs/binder/LazyServiceRegistrar.cpp
new file mode 100644
index 0000000..dc9482c
--- /dev/null
+++ b/libs/binder/LazyServiceRegistrar.cpp
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "AidlLazyServiceRegistrar"
+
+#include <binder/LazyServiceRegistrar.h>
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <android/os/BnClientCallback.h>
+#include <android/os/IServiceManager.h>
+#include <utils/Log.h>
+
+namespace android {
+namespace binder {
+namespace internal {
+
+using AidlServiceManager = android::os::IServiceManager;
+
+class ClientCounterCallback : public ::android::os::BnClientCallback {
+public:
+ ClientCounterCallback() : mNumConnectedServices(0) {}
+
+ bool registerService(const sp<IBinder>& service, const std::string& name,
+ bool allowIsolated, int dumpFlags);
+
+protected:
+ Status onClients(const sp<IBinder>& service, bool clients) override;
+
+private:
+ /**
+ * Unregisters all services that we can. If we can't unregister all, re-register other
+ * services.
+ */
+ void tryShutdown();
+
+ /**
+ * Counter of the number of services that currently have at least one client.
+ */
+ size_t mNumConnectedServices;
+
+ struct Service {
+ sp<IBinder> service;
+ std::string name;
+ bool allowIsolated;
+ int dumpFlags;
+ };
+ /**
+ * Number of services that have been registered.
+ */
+ std::vector<Service> mRegisteredServices;
+};
+
+bool ClientCounterCallback::registerService(const sp<IBinder>& service, const std::string& name,
+ bool allowIsolated, int dumpFlags) {
+ auto manager = interface_cast<AidlServiceManager>(
+ ProcessState::self()->getContextObject(nullptr));
+
+ ALOGI("Registering service %s", name.c_str());
+
+ if (!manager->addService(name.c_str(), service, allowIsolated, dumpFlags).isOk()) {
+ ALOGE("Failed to register service %s", name.c_str());
+ return false;
+ }
+
+ if (!manager->registerClientCallback(name, service, this).isOk())
+ {
+ ALOGE("Failed to add client callback for service %s", name.c_str());
+ return false;
+ }
+
+ mRegisteredServices.push_back({service, name, allowIsolated, dumpFlags});
+
+ return true;
+}
+
+/**
+ * onClients is oneway, so no need to worry about multi-threading. Note that this means multiple
+ * invocations could occur on different threads however.
+ */
+Status ClientCounterCallback::onClients(const sp<IBinder>& service, bool clients) {
+ if (clients) {
+ mNumConnectedServices++;
+ } else {
+ mNumConnectedServices--;
+ }
+
+ ALOGI("Process has %zu (of %zu available) client(s) in use after notification %s has clients: %d",
+ mNumConnectedServices, mRegisteredServices.size(),
+ String8(service->getInterfaceDescriptor()).string(), clients);
+
+ if (mNumConnectedServices == 0) {
+ tryShutdown();
+ }
+
+ return Status::ok();
+}
+
+void ClientCounterCallback::tryShutdown() {
+ ALOGI("Trying to shut down the service. No clients in use for any service in process.");
+
+ // This makes the same assumption as IServiceManager.cpp. Could dedupe if used in more places.
+ auto manager = interface_cast<AidlServiceManager>(
+ ProcessState::self()->getContextObject(nullptr));
+
+ auto unRegisterIt = mRegisteredServices.begin();
+ for (; unRegisterIt != mRegisteredServices.end(); ++unRegisterIt) {
+ auto& entry = (*unRegisterIt);
+
+ bool success = manager->tryUnregisterService(entry.name, entry.service).isOk();
+
+ if (!success) {
+ ALOGI("Failed to unregister service %s", entry.name.c_str());
+ break;
+ }
+ }
+
+ if (unRegisterIt == mRegisteredServices.end()) {
+ ALOGI("Unregistered all clients and exiting");
+ exit(EXIT_SUCCESS);
+ }
+
+ for (auto reRegisterIt = mRegisteredServices.begin(); reRegisterIt != unRegisterIt;
+ reRegisterIt++) {
+ auto& entry = (*reRegisterIt);
+
+ // re-register entry
+ if (!registerService(entry.service, entry.name, entry.allowIsolated, entry.dumpFlags)) {
+ // Must restart. Otherwise, clients will never be able to get a hold of this service.
+ ALOGE("Bad state: could not re-register services");
+ }
+ }
+}
+
+} // namespace internal
+
+LazyServiceRegistrar::LazyServiceRegistrar() {
+ mClientCC = std::make_shared<internal::ClientCounterCallback>();
+}
+
+LazyServiceRegistrar& LazyServiceRegistrar::getInstance() {
+ static auto registrarInstance = new LazyServiceRegistrar();
+ return *registrarInstance;
+}
+
+status_t LazyServiceRegistrar::registerService(const sp<IBinder>& service, const std::string& name,
+ bool allowIsolated, int dumpFlags) {
+ if (!mClientCC->registerService(service, name, allowIsolated, dumpFlags)) {
+ return UNKNOWN_ERROR;
+ }
+ return OK;
+}
+
+} // namespace hardware
+} // namespace android
\ No newline at end of file
diff --git a/libs/binder/aidl/android/os/IClientCallback.aidl b/libs/binder/aidl/android/os/IClientCallback.aidl
new file mode 100644
index 0000000..36d7ee6
--- /dev/null
+++ b/libs/binder/aidl/android/os/IClientCallback.aidl
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+/**
+ * @hide
+ */
+oneway interface IClientCallback {
+ /**
+ * This is called when there is a transition between having >= 1 clients and having 0 clients
+ * (or vice versa).
+ *
+ * Upon receiving hasClients false, if the process decides to exit, it is recommended to try to
+ * unregister using IServiceManager's tryUnregister before quitting in case another client
+ * associates.
+ *
+ * @param registered binder 'server' registered with IServiceManager's registerClientCallback
+ * @param hasClients whether there are currently clients
+ * true - when there are >= 1 clients. This must be called as soon as IServiceManager::get
+ * is called (no race).
+ * false - when there are 0 clients. This may be delayed if it is thought that another
+ * may be used again soon.
+ */
+ void onClients(IBinder registered, boolean hasClients);
+}
diff --git a/libs/binder/aidl/android/os/IServiceManager.aidl b/libs/binder/aidl/android/os/IServiceManager.aidl
index b965881..ff15460 100644
--- a/libs/binder/aidl/android/os/IServiceManager.aidl
+++ b/libs/binder/aidl/android/os/IServiceManager.aidl
@@ -16,6 +16,7 @@
package android.os;
+import android.os.IClientCallback;
import android.os.IServiceCallback;
/**
@@ -96,4 +97,15 @@
* manifest.
*/
boolean isDeclared(@utf8InCpp String name);
+
+ /**
+ * Request a callback when the number of clients of the service changes.
+ * Used by LazyServiceRegistrar to dynamically stop services that have no clients.
+ */
+ void registerClientCallback(@utf8InCpp String name, IBinder service, IClientCallback callback);
+
+ /**
+ * Attempt to unregister and remove a service. Will fail if the service is still in use.
+ */
+ void tryUnregisterService(@utf8InCpp String name, IBinder service);
}
diff --git a/libs/binder/include/binder/LazyServiceRegistrar.h b/libs/binder/include/binder/LazyServiceRegistrar.h
new file mode 100644
index 0000000..efdecc4
--- /dev/null
+++ b/libs/binder/include/binder/LazyServiceRegistrar.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <binder/IServiceManager.h>
+#include <binder/Status.h>
+#include <utils/StrongPointer.h>
+
+namespace android {
+namespace binder {
+namespace internal {
+class ClientCounterCallback;
+} // namespace internal
+
+/** Exits when all services registered through this object have 0 clients */
+class LazyServiceRegistrar {
+ public:
+ static LazyServiceRegistrar& getInstance();
+ status_t registerService(const sp<IBinder>& service,
+ const std::string& name = "default",
+ bool allowIsolated = false,
+ int dumpFlags = IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT);
+
+ private:
+ std::shared_ptr<internal::ClientCounterCallback> mClientCC;
+ LazyServiceRegistrar();
+};
+
+} // namespace binder
+} // namespace android
\ No newline at end of file