| /* | 
 |  * 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), mForcePersist(false) {} | 
 |  | 
 |     bool registerService(const sp<IBinder>& service, const std::string& name, | 
 |                          bool allowIsolated, int dumpFlags); | 
 |  | 
 |     /** | 
 |      * Set a flag to prevent services from automatically shutting down | 
 |      */ | 
 |     void forcePersist(bool persist); | 
 |  | 
 | protected: | 
 |     Status onClients(const sp<IBinder>& service, bool clients) override; | 
 |  | 
 | private: | 
 |     struct Service { | 
 |         sp<IBinder> service; | 
 |         bool allowIsolated; | 
 |         int dumpFlags; | 
 |  | 
 |         // whether, based on onClients calls, we know we have a client for this | 
 |         // service or not | 
 |         bool clients = false; | 
 |     }; | 
 |  | 
 |     /** | 
 |      * Looks up a service guaranteed to be registered (service from onClients). | 
 |      */ | 
 |     std::map<std::string, Service>::iterator assertRegisteredService(const sp<IBinder>& service); | 
 |  | 
 |     /** | 
 |      * Unregisters all services that we can. If we can't unregister all, re-register other | 
 |      * services. | 
 |      */ | 
 |     void tryShutdown(); | 
 |  | 
 |     // count of services with clients | 
 |     size_t mNumConnectedServices; | 
 |  | 
 |     // map of registered names and services | 
 |     std::map<std::string, Service> mRegisteredServices; | 
 |  | 
 |     bool mForcePersist; | 
 | }; | 
 |  | 
 | bool ClientCounterCallback::registerService(const sp<IBinder>& service, const std::string& name, | 
 |                                             bool allowIsolated, int dumpFlags) { | 
 |     auto manager = interface_cast<AidlServiceManager>(asBinder(defaultServiceManager())); | 
 |  | 
 |     bool reRegister = mRegisteredServices.count(name) > 0; | 
 |     std::string regStr = (reRegister) ? "Re-registering" : "Registering"; | 
 |     ALOGI("%s service %s", regStr.c_str(), 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 (!reRegister) { | 
 |         if (!manager->registerClientCallback(name, service, this).isOk()) { | 
 |             ALOGE("Failed to add client callback for service %s", name.c_str()); | 
 |             return false; | 
 |         } | 
 |  | 
 |         // Only add this when a service is added for the first time, as it is not removed | 
 |         mRegisteredServices[name] = { | 
 |               .service = service, | 
 |               .allowIsolated = allowIsolated, | 
 |               .dumpFlags = dumpFlags | 
 |         }; | 
 |     } | 
 |  | 
 |     return true; | 
 | } | 
 |  | 
 | std::map<std::string, ClientCounterCallback::Service>::iterator ClientCounterCallback::assertRegisteredService(const sp<IBinder>& service) { | 
 |     LOG_ALWAYS_FATAL_IF(service == nullptr, "Got onClients callback for null service"); | 
 |     for (auto it = mRegisteredServices.begin(); it != mRegisteredServices.end(); ++it) { | 
 |         auto const& [name, registered] = *it; | 
 |         (void) name; | 
 |         if (registered.service != service) continue; | 
 |         return it; | 
 |     } | 
 |     LOG_ALWAYS_FATAL("Got callback on service which we did not register: %s", String8(service->getInterfaceDescriptor()).c_str()); | 
 |     __builtin_unreachable(); | 
 | } | 
 |  | 
 | void ClientCounterCallback::forcePersist(bool persist) { | 
 |     mForcePersist = persist; | 
 |     if(!mForcePersist) { | 
 |         // Attempt a shutdown in case the number of clients hit 0 while the flag was on | 
 |         tryShutdown(); | 
 |     } | 
 | } | 
 |  | 
 | /** | 
 |  * 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) { | 
 |     auto & [name, registered] = *assertRegisteredService(service); | 
 |     if (registered.clients == clients) { | 
 |         LOG_ALWAYS_FATAL("Process already thought %s had clients: %d but servicemanager has " | 
 |                          "notified has clients: %d", name.c_str(), registered.clients, clients); | 
 |     } | 
 |     registered.clients = clients; | 
 |  | 
 |     // update cache count of clients | 
 |     { | 
 |          size_t numWithClients = 0; | 
 |          for (const auto& [name, registered] : mRegisteredServices) { | 
 |              (void) name; | 
 |              if (registered.clients) numWithClients++; | 
 |          } | 
 |          mNumConnectedServices = numWithClients; | 
 |     } | 
 |  | 
 |     ALOGI("Process has %zu (of %zu available) client(s) in use after notification %s has clients: %d", | 
 |           mNumConnectedServices, mRegisteredServices.size(), name.c_str(), clients); | 
 |  | 
 |     tryShutdown(); | 
 |     return Status::ok(); | 
 | } | 
 |  | 
 | void ClientCounterCallback::tryShutdown() { | 
 |     if(mNumConnectedServices > 0) { | 
 |         // Should only shut down if there are no clients | 
 |         return; | 
 |     } | 
 |  | 
 |     if(mForcePersist) { | 
 |         ALOGI("Shutdown prevented by forcePersist override flag."); | 
 |         return; | 
 |     } | 
 |  | 
 |     ALOGI("Trying to shut down the service. No clients in use for any service in process."); | 
 |  | 
 |     auto manager = interface_cast<AidlServiceManager>(asBinder(defaultServiceManager())); | 
 |  | 
 |     auto unRegisterIt = mRegisteredServices.begin(); | 
 |     for (; unRegisterIt != mRegisteredServices.end(); ++unRegisterIt) { | 
 |         auto& entry = (*unRegisterIt); | 
 |  | 
 |         bool success = manager->tryUnregisterService(entry.first, entry.second.service).isOk(); | 
 |  | 
 |  | 
 |         if (!success) { | 
 |             ALOGI("Failed to unregister service %s", entry.first.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.second.service, entry.first, entry.second.allowIsolated, | 
 |                              entry.second.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; | 
 | } | 
 |  | 
 | void LazyServiceRegistrar::forcePersist(bool persist) { | 
 |     mClientCC->forcePersist(persist); | 
 | } | 
 |  | 
 | }  // namespace hardware | 
 | }  // namespace android |