summaryrefslogtreecommitdiff
path: root/transport/HidlLazyUtils.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'transport/HidlLazyUtils.cpp')
-rw-r--r--transport/HidlLazyUtils.cpp190
1 files changed, 190 insertions, 0 deletions
diff --git a/transport/HidlLazyUtils.cpp b/transport/HidlLazyUtils.cpp
new file mode 100644
index 0000000..8e3fdf3
--- /dev/null
+++ b/transport/HidlLazyUtils.cpp
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#include <hidl/HidlLazyUtils.h>
+#include <hidl/HidlTransportSupport.h>
+
+#include <android-base/logging.h>
+
+#include <android/hidl/manager/1.2/IClientCallback.h>
+#include <android/hidl/manager/1.2/IServiceManager.h>
+
+namespace android {
+namespace hardware {
+namespace details {
+
+using ::android::hidl::base::V1_0::IBase;
+
+class ClientCounterCallback : public ::android::hidl::manager::V1_2::IClientCallback {
+ public:
+ ClientCounterCallback() : mNumConnectedServices(0) {}
+
+ bool addRegisteredService(const sp<IBase>& service, const std::string& name);
+
+ protected:
+ Return<void> onClients(const sp<IBase>& service, bool clients) override;
+
+ private:
+ /**
+ * Registers or re-registers services. Returns whether successful.
+ */
+ bool registerService(const sp<IBase>& service, const std::string& name);
+
+ /**
+ * 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<IBase> service;
+ std::string name;
+ };
+ /**
+ * Number of services that have been registered.
+ */
+ std::vector<Service> mRegisteredServices;
+};
+
+class LazyServiceRegistrarImpl {
+ public:
+ LazyServiceRegistrarImpl() : mClientCallback(new ClientCounterCallback) {}
+
+ status_t registerService(const sp<::android::hidl::base::V1_0::IBase>& service,
+ const std::string& name);
+
+ private:
+ sp<ClientCounterCallback> mClientCallback;
+};
+
+bool ClientCounterCallback::addRegisteredService(const sp<IBase>& service,
+ const std::string& name) {
+ bool success = registerService(service, name);
+
+ if (success) {
+ mRegisteredServices.push_back({service, name});
+ }
+
+ return success;
+}
+
+bool ClientCounterCallback::registerService(const sp<IBase>& service, const std::string& name) {
+ auto manager = hardware::defaultServiceManager1_2();
+
+ const std::string descriptor = getDescriptor(service.get());
+
+ LOG(INFO) << "Registering HAL: " << descriptor << " with name: " << name;
+
+ status_t res = android::hardware::details::registerAsServiceInternal(service, name);
+ if (res != android::OK) {
+ LOG(ERROR) << "Failed to register as service.";
+ return false;
+ }
+
+ bool ret = manager->registerClientCallback(getDescriptor(service.get()), name, service, this);
+ if (!ret) {
+ LOG(ERROR) << "Failed to add client callback.";
+ return false;
+ }
+
+ 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.
+ */
+Return<void> ClientCounterCallback::onClients(const sp<::android::hidl::base::V1_0::IBase>& service,
+ bool clients) {
+ if (clients) {
+ mNumConnectedServices++;
+ } else {
+ mNumConnectedServices--;
+ }
+
+ LOG(INFO) << "Process has " << mNumConnectedServices << " (of " << mRegisteredServices.size()
+ << " available) client(s) in use after notification " << getDescriptor(service.get())
+ << " has clients: " << clients;
+
+ if (mNumConnectedServices == 0) {
+ tryShutdown();
+ }
+
+ return Status::ok();
+}
+
+void ClientCounterCallback::tryShutdown() {
+ LOG(INFO) << "Trying to exit HAL. No clients in use for any service in process.";
+
+ auto manager = hardware::defaultServiceManager1_2();
+
+ auto unRegisterIt = mRegisteredServices.begin();
+ for (; unRegisterIt != mRegisteredServices.end(); ++unRegisterIt) {
+ auto& entry = (*unRegisterIt);
+
+ const std::string descriptor = getDescriptor(entry.service.get());
+ bool success = manager->tryUnregister(descriptor, entry.name, entry.service);
+
+ if (!success) {
+ LOG(INFO) << "Failed to unregister HAL " << descriptor << "/" << entry.name;
+ break;
+ }
+ }
+
+ if (unRegisterIt == mRegisteredServices.end()) {
+ LOG(INFO) << "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)) {
+ // Must restart. Otherwise, clients will never be able to get ahold of this service.
+ LOG(FATAL) << "Bad state: could not re-register " << getDescriptor(entry.service.get());
+ }
+ }
+}
+
+status_t LazyServiceRegistrarImpl::registerService(
+ const sp<::android::hidl::base::V1_0::IBase>& service, const std::string& name) {
+ if (!mClientCallback->addRegisteredService(service, name)) {
+ return ::android::UNKNOWN_ERROR;
+ }
+
+ return ::android::OK;
+}
+
+} // namespace details
+
+LazyServiceRegistrar::LazyServiceRegistrar() {
+ mImpl = std::make_shared<details::LazyServiceRegistrarImpl>();
+}
+
+status_t LazyServiceRegistrar::registerService(
+ const sp<::android::hidl::base::V1_0::IBase>& service, const std::string& name) {
+ return mImpl->registerService(service, name);
+}
+
+} // namespace hardware
+} // namespace android