diff options
author | 2019-10-22 16:49:19 -0700 | |
---|---|---|
committer | 2019-12-19 20:17:06 -0800 | |
commit | 9f503a4285ea6818a572e56e47f9f6814c2b65c5 (patch) | |
tree | f766e951b6259bb2626478790d12f056c22f8910 | |
parent | 6b243fdf227f80be3039c193a081e510b5737572 (diff) |
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
-rw-r--r-- | cmds/servicemanager/ServiceManager.cpp | 207 | ||||
-rw-r--r-- | cmds/servicemanager/ServiceManager.h | 23 | ||||
-rw-r--r-- | cmds/servicemanager/main.cpp | 92 | ||||
-rw-r--r-- | libs/binder/Android.bp | 2 | ||||
-rw-r--r-- | libs/binder/IServiceManager.cpp | 2 | ||||
-rw-r--r-- | libs/binder/LazyServiceRegistrar.cpp | 167 | ||||
-rw-r--r-- | libs/binder/aidl/android/os/IClientCallback.aidl | 39 | ||||
-rw-r--r-- | libs/binder/aidl/android/os/IServiceManager.aidl | 12 | ||||
-rw-r--r-- | libs/binder/include/binder/LazyServiceRegistrar.h | 44 |
9 files changed, 580 insertions, 8 deletions
diff --git a/cmds/servicemanager/ServiceManager.cpp b/cmds/servicemanager/ServiceManager.cpp index 141171bf68..4a92ed862d 100644 --- a/cmds/servicemanager/ServiceManager.cpp +++ b/cmds/servicemanager/ServiceManager.cpp @@ -18,6 +18,9 @@ #include <android-base/logging.h> #include <android-base/properties.h> +#include <binder/BpBinder.h> +#include <binder/IPCThreadState.h> +#include <binder/ProcessState.h> #include <binder/Stability.h> #include <cutils/android_filesystem_config.h> #include <cutils/multiuser.h> @@ -108,10 +111,11 @@ sp<IBinder> ServiceManager::tryGetService(const std::string& name, bool startIfN auto ctx = mAccess->getCallingContext(); sp<IBinder> out; + Service* service = nullptr; if (auto it = mNameToService.find(name); it != mNameToService.end()) { - const Service& service = it->second; + service = &(it->second); - if (!service.allowIsolated) { + if (!service->allowIsolated) { uid_t appid = multiuser_get_app_id(ctx.uid); bool isIsolated = appid >= AID_ISOLATED_START && appid <= AID_ISOLATED_END; @@ -119,7 +123,7 @@ sp<IBinder> ServiceManager::tryGetService(const std::string& name, bool startIfN return nullptr; } } - out = service.binder; + out = service->binder; } if (!mAccess->canFind(ctx, name)) { @@ -130,6 +134,12 @@ sp<IBinder> ServiceManager::tryGetService(const std::string& name, bool startIfN tryStartService(name); } + if (out) { + // Setting this guarantee each time we hand out a binder ensures that the client-checking + // loop knows about the event even if the client immediately drops the service + service->guaranteeClient = true; + } + return out; } @@ -182,15 +192,17 @@ Status ServiceManager::addService(const std::string& name, const sp<IBinder>& bi return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE); } - mNameToService[name] = Service { + auto entry = mNameToService.emplace(name, Service { .binder = binder, .allowIsolated = allowIsolated, .dumpPriority = dumpPriority, - }; + .debugPid = ctx.debugPid, + }); auto it = mNameToCallback.find(name); if (it != mNameToCallback.end()) { for (const sp<IServiceCallback>& cb : it->second) { + entry.first->second.guaranteeClient = true; // permission checked in registerForNotifications cb->onRegistration(name, binder); } @@ -330,6 +342,10 @@ void ServiceManager::binderDied(const wp<IBinder>& who) { for (auto it = mNameToCallback.begin(); it != mNameToCallback.end();) { removeCallback(who, &it, nullptr /*found*/); } + + for (auto it = mNameToClientCallback.begin(); it != mNameToClientCallback.end();) { + removeClientCallback(who, &it); + } } void ServiceManager::tryStartService(const std::string& name) { @@ -341,4 +357,183 @@ void ServiceManager::tryStartService(const std::string& name) { }).detach(); } -} // namespace android +Status ServiceManager::registerClientCallback(const std::string& name, const sp<IBinder>& service, + const sp<IClientCallback>& cb) { + if (cb == nullptr) { + return Status::fromExceptionCode(Status::EX_NULL_POINTER); + } + + auto ctx = mAccess->getCallingContext(); + if (!mAccess->canAdd(ctx, name)) { + return Status::fromExceptionCode(Status::EX_SECURITY); + } + + auto serviceIt = mNameToService.find(name); + if (serviceIt == mNameToService.end()) { + LOG(ERROR) << "Could not add callback for nonexistent service: " << name; + return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT); + } + + if (serviceIt->second.debugPid != IPCThreadState::self()->getCallingPid()) { + LOG(WARNING) << "Only a server can register for client callbacks (for " << name << ")"; + return Status::fromExceptionCode(Status::EX_UNSUPPORTED_OPERATION); + } + + if (serviceIt->second.binder != service) { + LOG(WARNING) << "Tried to register client callback for " << name + << " but a different service is registered under this name."; + return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT); + } + + if (OK != IInterface::asBinder(cb)->linkToDeath(this)) { + LOG(ERROR) << "Could not linkToDeath when adding client callback for " << name; + return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE); + } + + mNameToClientCallback[name].push_back(cb); + + return Status::ok(); +} + +void ServiceManager::removeClientCallback(const wp<IBinder>& who, + ClientCallbackMap::iterator* it) { + std::vector<sp<IClientCallback>>& listeners = (*it)->second; + + for (auto lit = listeners.begin(); lit != listeners.end();) { + if (IInterface::asBinder(*lit) == who) { + lit = listeners.erase(lit); + } else { + ++lit; + } + } + + if (listeners.empty()) { + *it = mNameToClientCallback.erase(*it); + } else { + (*it)++; + } +} + +ssize_t ServiceManager::Service::getNodeStrongRefCount() { + sp<BpBinder> bpBinder = binder->remoteBinder(); + if (bpBinder == nullptr) return -1; + + return ProcessState::self()->getStrongRefCountForNodeByHandle(bpBinder->handle()); +} + +void ServiceManager::handleClientCallbacks() { + for (const auto& [name, service] : mNameToService) { + handleServiceClientCallback(name); + } +} + +ssize_t ServiceManager::handleServiceClientCallback(const std::string& serviceName) { + auto serviceIt = mNameToService.find(serviceName); + if (serviceIt == mNameToService.end() || mNameToClientCallback.count(serviceName) < 1) { + return -1; + } + + Service& service = serviceIt->second; + ssize_t count = service.getNodeStrongRefCount(); + + // binder driver doesn't support this feature + if (count == -1) return count; + + bool hasClients = count > 1; // this process holds a strong count + + if (service.guaranteeClient) { + // we have no record of this client + if (!service.hasClients && !hasClients) { + sendClientCallbackNotifications(serviceName, true); + } + + // guarantee is temporary + service.guaranteeClient = false; + } + + if (hasClients && !service.hasClients) { + // client was retrieved in some other way + sendClientCallbackNotifications(serviceName, true); + } + + // there are no more clients, but the callback has not been called yet + if (!hasClients && service.hasClients) { + sendClientCallbackNotifications(serviceName, false); + } + + return count; +} + +void ServiceManager::sendClientCallbackNotifications(const std::string& serviceName, bool hasClients) { + auto serviceIt = mNameToService.find(serviceName); + if (serviceIt == mNameToService.end()) { + LOG(WARNING) << "sendClientCallbackNotifications could not find service " << serviceName; + return; + } + Service& service = serviceIt->second; + + CHECK(hasClients != service.hasClients) << "Record shows: " << service.hasClients + << " so we can't tell clients again that we have client: " << hasClients; + + LOG(INFO) << "Notifying " << serviceName << " they have clients: " << hasClients; + + auto ccIt = mNameToClientCallback.find(serviceName); + CHECK(ccIt != mNameToClientCallback.end()) + << "sendClientCallbackNotifications could not find callbacks for service "; + + for (const auto& callback : ccIt->second) { + callback->onClients(service.binder, hasClients); + } + + service.hasClients = hasClients; +} + +Status ServiceManager::tryUnregisterService(const std::string& name, const sp<IBinder>& binder) { + if (binder == nullptr) { + return Status::fromExceptionCode(Status::EX_NULL_POINTER); + } + + auto ctx = mAccess->getCallingContext(); + if (!mAccess->canAdd(ctx, name)) { + return Status::fromExceptionCode(Status::EX_SECURITY); + } + + auto serviceIt = mNameToService.find(name); + if (serviceIt == mNameToService.end()) { + LOG(WARNING) << "Tried to unregister " << name + << ", but that service wasn't registered to begin with."; + return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE); + } + + if (serviceIt->second.debugPid != IPCThreadState::self()->getCallingPid()) { + LOG(WARNING) << "Only a server can unregister itself (for " << name << ")"; + return Status::fromExceptionCode(Status::EX_UNSUPPORTED_OPERATION); + } + + sp<IBinder> storedBinder = serviceIt->second.binder; + + if (binder != storedBinder) { + LOG(WARNING) << "Tried to unregister " << name + << ", but a different service is registered under this name."; + return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE); + } + + int clients = handleServiceClientCallback(name); + + // clients < 0: feature not implemented or other error. Assume clients. + // Otherwise: + // - kernel driver will hold onto one refcount (during this transaction) + // - servicemanager has a refcount (guaranteed by this transaction) + // So, if clients > 2, then at least one other service on the system must hold a refcount. + if (clients < 0 || clients > 2) { + // client callbacks are either disabled or there are other clients + LOG(INFO) << "Tried to unregister " << name << " but there are clients: " << clients; + return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE); + } + + mNameToService.erase(name); + + return Status::ok(); +} + +} // namespace android
\ No newline at end of file diff --git a/cmds/servicemanager/ServiceManager.h b/cmds/servicemanager/ServiceManager.h index 7dcdaa4661..d3c89ee2ad 100644 --- a/cmds/servicemanager/ServiceManager.h +++ b/cmds/servicemanager/ServiceManager.h @@ -17,12 +17,14 @@ #pragma once #include <android/os/BnServiceManager.h> +#include <android/os/IClientCallback.h> #include <android/os/IServiceCallback.h> #include "Access.h" namespace android { +using os::IClientCallback; using os::IServiceCallback; class ServiceManager : public os::BnServiceManager, public IBinder::DeathRecipient { @@ -40,9 +42,13 @@ public: const sp<IServiceCallback>& callback) override; binder::Status unregisterForNotifications(const std::string& name, const sp<IServiceCallback>& callback) override; - binder::Status isDeclared(const std::string& name, bool* outReturn) override; + binder::Status isDeclared(const std::string& name, bool* outReturn) override; + binder::Status registerClientCallback(const std::string& name, const sp<IBinder>& service, + const sp<IClientCallback>& cb) override; + binder::Status tryUnregisterService(const std::string& name, const sp<IBinder>& binder) override; void binderDied(const wp<IBinder>& who) override; + void handleClientCallbacks(); protected: virtual void tryStartService(const std::string& name); @@ -52,9 +58,16 @@ private: sp<IBinder> binder; // not null bool allowIsolated; int32_t dumpPriority; + bool hasClients = false; // notifications sent on true -> false. + bool guaranteeClient = false; // forces the client check to true + pid_t debugPid = 0; // the process in which this service runs + + // the number of clients of the service, including servicemanager itself + ssize_t getNodeStrongRefCount(); }; using CallbackMap = std::map<std::string, std::vector<sp<IServiceCallback>>>; + using ClientCallbackMap = std::map<std::string, std::vector<sp<IClientCallback>>>; using ServiceMap = std::map<std::string, Service>; // removes a callback from mNameToCallback, removing it if the vector is empty @@ -62,10 +75,18 @@ private: void removeCallback(const wp<IBinder>& who, CallbackMap::iterator* it, bool* found); + ssize_t handleServiceClientCallback(const std::string& serviceName); + // Also updates mHasClients (of what the last callback was) + void sendClientCallbackNotifications(const std::string& serviceName, bool hasClients); + // removes a callback from mNameToClientCallback, deleting the entry if the vector is empty + // this updates the iterator to the next location + void removeClientCallback(const wp<IBinder>& who, ClientCallbackMap::iterator* it); + sp<IBinder> tryGetService(const std::string& name, bool startIfNotFound); CallbackMap mNameToCallback; ServiceMap mNameToService; + ClientCallbackMap mNameToClientCallback; std::unique_ptr<Access> mAccess; }; diff --git a/cmds/servicemanager/main.cpp b/cmds/servicemanager/main.cpp index 4b12fc6e72..2618906261 100644 --- a/cmds/servicemanager/main.cpp +++ b/cmds/servicemanager/main.cpp @@ -18,18 +18,101 @@ #include <binder/IPCThreadState.h> #include <binder/ProcessState.h> #include <binder/Status.h> +#include <sys/timerfd.h> +#include <utils/Looper.h> #include <utils/StrongPointer.h> #include "Access.h" #include "ServiceManager.h" using ::android::Access; +using ::android::sp; +using ::android::Looper; +using ::android::LooperCallback; +using ::android::ProcessState; using ::android::IPCThreadState; using ::android::ProcessState; using ::android::ServiceManager; using ::android::os::IServiceManager; using ::android::sp; +class BinderCallback : public LooperCallback { +public: + static sp<BinderCallback> setupTo(const sp<Looper>& looper) { + sp<BinderCallback> cb = new BinderCallback; + + int binder_fd = -1; + IPCThreadState::self()->setupPolling(&binder_fd); + LOG_ALWAYS_FATAL_IF(binder_fd < 0, "Failed to setupPolling: %d", binder_fd); + + // Flush after setupPolling(), to make sure the binder driver + // knows about this thread handling commands. + IPCThreadState::self()->flushCommands(); + + int ret = looper->addFd(binder_fd, + Looper::POLL_CALLBACK, + Looper::EVENT_INPUT, + cb, + nullptr /*data*/); + LOG_ALWAYS_FATAL_IF(ret != 1, "Failed to add binder FD to Looper"); + + return cb; + } + + int handleEvent(int /* fd */, int /* events */, void* /* data */) override { + IPCThreadState::self()->handlePolledCommands(); + return 1; // Continue receiving callbacks. + } +}; + +// LooperCallback for IClientCallback +class ClientCallbackCallback : public LooperCallback { +public: + static sp<ClientCallbackCallback> setupTo(const sp<Looper>& looper, const sp<ServiceManager>& manager) { + sp<ClientCallbackCallback> cb = new ClientCallbackCallback(manager); + + int fdTimer = timerfd_create(CLOCK_MONOTONIC, 0 /*flags*/); + LOG_ALWAYS_FATAL_IF(fdTimer < 0, "Failed to timerfd_create: fd: %d err: %d", fdTimer, errno); + + itimerspec timespec { + .it_interval = { + .tv_sec = 5, + .tv_nsec = 0, + }, + .it_value = { + .tv_sec = 5, + .tv_nsec = 0, + }, + }; + + int timeRes = timerfd_settime(fdTimer, 0 /*flags*/, ×pec, nullptr); + LOG_ALWAYS_FATAL_IF(timeRes < 0, "Failed to timerfd_settime: res: %d err: %d", timeRes, errno); + + int addRes = looper->addFd(fdTimer, + Looper::POLL_CALLBACK, + Looper::EVENT_INPUT, + cb, + nullptr); + LOG_ALWAYS_FATAL_IF(addRes != 1, "Failed to add client callback FD to Looper"); + + return cb; + } + + int handleEvent(int fd, int /*events*/, void* /*data*/) override { + uint64_t expirations; + int ret = read(fd, &expirations, sizeof(expirations)); + if (ret != sizeof(expirations)) { + ALOGE("Read failed to callback FD: ret: %d err: %d", ret, errno); + } + + mManager->handleClientCallbacks(); + return 1; // Continue receiving callbacks. + } +private: + ClientCallbackCallback(const sp<ServiceManager>& manager) : mManager(manager) {} + sp<ServiceManager> mManager; +}; + int main(int argc, char** argv) { if (argc > 2) { LOG(FATAL) << "usage: " << argv[0] << " [binder driver]"; @@ -49,7 +132,14 @@ int main(int argc, char** argv) { IPCThreadState::self()->setTheContextObject(manager); ps->becomeContextManager(nullptr, nullptr); - IPCThreadState::self()->joinThreadPool(); + sp<Looper> looper = Looper::prepare(false /*allowNonCallbacks*/); + + BinderCallback::setupTo(looper); + ClientCallbackCallback::setupTo(looper, manager); + + while(true) { + looper->pollAll(-1); + } // should not be reached return EXIT_FAILURE; diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp index 7ee4882b6a..079dd82ecd 100644 --- a/libs/binder/Android.bp +++ b/libs/binder/Android.bp @@ -90,6 +90,7 @@ cc_library { "IResultReceiver.cpp", "IServiceManager.cpp", "IShellCallback.cpp", + "LazyServiceRegistrar.cpp", "MemoryBase.cpp", "MemoryDealer.cpp", "MemoryHeapBase.cpp", @@ -160,6 +161,7 @@ filegroup { 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 bac8b6604b..5ca9156fb2 100644 --- a/libs/binder/IServiceManager.cpp +++ b/libs/binder/IServiceManager.cpp @@ -271,6 +271,8 @@ sp<IBinder> ServiceManagerShim::waitForService(const String16& name16) 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 0000000000..dc9482c536 --- /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 0000000000..36d7ee61fb --- /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 b965881e7f..ff154603e4 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 @@ interface IServiceManager { * 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 0000000000..efdecc4eca --- /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 |