252 lines
		
	
	
		
			7.1 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			252 lines
		
	
	
		
			7.1 KiB
		
	
	
	
		
			C++
		
	
	
	
| /*
 | |
|  * Copyright (C) 2016 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 "hwservicemanager"
 | |
| #include "HidlService.h"
 | |
| 
 | |
| #include <android-base/logging.h>
 | |
| #include <hidl/HidlTransportSupport.h>
 | |
| #include <hwbinder/BpHwBinder.h>
 | |
| #include <sstream>
 | |
| 
 | |
| using ::android::hardware::interfacesEqual;
 | |
| 
 | |
| namespace android {
 | |
| namespace hidl {
 | |
| namespace manager {
 | |
| namespace implementation {
 | |
| 
 | |
| static constexpr int kNoClientRepeatLimit = 2;
 | |
| 
 | |
| HidlService::HidlService(
 | |
|     const std::string &interfaceName,
 | |
|     const std::string &instanceName,
 | |
|     const sp<IBase> &service,
 | |
|     pid_t pid)
 | |
| : mInterfaceName(interfaceName),
 | |
|   mInstanceName(instanceName),
 | |
|   mService(service),
 | |
|   mPid(pid)
 | |
| {}
 | |
| 
 | |
| sp<IBase> HidlService::getService() const {
 | |
|     return mService;
 | |
| }
 | |
| void HidlService::setService(sp<IBase> service, pid_t pid) {
 | |
|     mService = service;
 | |
|     mPid = pid;
 | |
| 
 | |
|     mClientCallbacks.clear();
 | |
|     mHasClients = false;
 | |
|     mGuaranteeClient = false;
 | |
|     mNoClientsCounter = 0;
 | |
| 
 | |
|     sendRegistrationNotifications();
 | |
| }
 | |
| 
 | |
| pid_t HidlService::getDebugPid() const {
 | |
|     return mPid;
 | |
| }
 | |
| const std::string &HidlService::getInterfaceName() const {
 | |
|     return mInterfaceName;
 | |
| }
 | |
| const std::string &HidlService::getInstanceName() const {
 | |
|     return mInstanceName;
 | |
| }
 | |
| 
 | |
| void HidlService::addListener(const sp<IServiceNotification> &listener) {
 | |
|     if (mService != nullptr) {
 | |
|         auto ret = listener->onRegistration(
 | |
|             mInterfaceName, mInstanceName, true /* preexisting */);
 | |
|         if (!ret.isOk()) {
 | |
|             LOG(ERROR) << "Not adding listener for " << mInterfaceName << "/"
 | |
|                        << mInstanceName << ": transport error when sending "
 | |
|                        << "notification for already registered instance.";
 | |
|             return;
 | |
|         }
 | |
|     }
 | |
|     mListeners.push_back(listener);
 | |
| }
 | |
| 
 | |
| bool HidlService::removeListener(const wp<IBase>& listener) {
 | |
|     bool found = false;
 | |
| 
 | |
|     for (auto it = mListeners.begin(); it != mListeners.end();) {
 | |
|         if (interfacesEqual(*it, listener.promote())) {
 | |
|             it = mListeners.erase(it);
 | |
|             found = true;
 | |
|         } else {
 | |
|             ++it;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     return found;
 | |
| }
 | |
| 
 | |
| void HidlService::registerPassthroughClient(pid_t pid) {
 | |
|     mPassthroughClients.insert(pid);
 | |
| }
 | |
| 
 | |
| const std::set<pid_t> &HidlService::getPassthroughClients() const {
 | |
|     return mPassthroughClients;
 | |
| }
 | |
| 
 | |
| void HidlService::addClientCallback(const sp<IClientCallback>& callback, size_t knownClientCount) {
 | |
|     if (mHasClients) {
 | |
|         // we have this kernel feature, so make sure we're in an updated state
 | |
|         forceHandleClientCallbacks(false /*onInterval*/, knownClientCount);
 | |
|     }
 | |
| 
 | |
|     if (mHasClients) {
 | |
|         // make sure this callback is in the same state as all of the rest
 | |
|         sendClientCallbackNotification(callback, true /*hasClients*/);
 | |
|     }
 | |
| 
 | |
|     mClientCallbacks.push_back(callback);
 | |
| }
 | |
| 
 | |
| bool HidlService::removeClientCallback(const sp<IClientCallback>& callback) {
 | |
|     bool found = false;
 | |
| 
 | |
|     for (auto it = mClientCallbacks.begin(); it != mClientCallbacks.end();) {
 | |
|         if (interfacesEqual(*it, callback)) {
 | |
|             it = mClientCallbacks.erase(it);
 | |
|             found = true;
 | |
|         } else {
 | |
|             ++it;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     return found;
 | |
| }
 | |
| 
 | |
| bool HidlService::handleClientCallbacks(bool isCalledOnInterval, size_t knownClientCount) {
 | |
|     if (!mClientCallbacks.empty()) {
 | |
|         return forceHandleClientCallbacks(isCalledOnInterval, knownClientCount);
 | |
|     }
 | |
| 
 | |
|     return false;
 | |
| }
 | |
| 
 | |
| bool HidlService::forceHandleClientCallbacks(bool isCalledOnInterval, size_t knownClientCount) {
 | |
|     ssize_t count = getNodeStrongRefCount();
 | |
| 
 | |
|     // binder driver doesn't support this feature
 | |
|     if (count < 0) return false;
 | |
| 
 | |
|     bool hasClients = (size_t)count > knownClientCount;
 | |
| 
 | |
|     if (mGuaranteeClient) {
 | |
|         // we have no record of this client
 | |
|         if (!mHasClients && !hasClients) {
 | |
|             sendClientCallbackNotifications(true);
 | |
|         }
 | |
| 
 | |
|         // guarantee is temporary
 | |
|         mGuaranteeClient = false;
 | |
|     }
 | |
| 
 | |
|     if (hasClients && !mHasClients) {
 | |
|         // client was retrieved in some other way
 | |
|         sendClientCallbackNotifications(true);
 | |
|     }
 | |
| 
 | |
|     // there are no more clients, but the callback has not been called yet
 | |
|     if (!hasClients && mHasClients && isCalledOnInterval) {
 | |
|         mNoClientsCounter++;
 | |
| 
 | |
|         if (mNoClientsCounter >= kNoClientRepeatLimit) {
 | |
|             sendClientCallbackNotifications(false);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     return mHasClients;
 | |
| }
 | |
| 
 | |
| void HidlService::guaranteeClient() {
 | |
|     mGuaranteeClient = true;
 | |
| }
 | |
| 
 | |
| std::string HidlService::string() const {
 | |
|     std::stringstream ss;
 | |
|     ss << mInterfaceName << "/" << mInstanceName;
 | |
|     return ss.str();
 | |
| }
 | |
| 
 | |
| ssize_t HidlService::getNodeStrongRefCount() {
 | |
|     using ::android::hardware::toBinder;
 | |
|     using ::android::hardware::BpHwBinder;
 | |
|     using ::android::hardware::IBinder;
 | |
| 
 | |
|     if (mService == nullptr) return -1;
 | |
| 
 | |
|     // this justifies the bp cast below, no in-process HALs need this
 | |
|     if (!mService->isRemote()) return -1;
 | |
| 
 | |
|     sp<IBinder> binder = toBinder(mService);
 | |
|     if (binder == nullptr) return -1;
 | |
| 
 | |
|     sp<BpHwBinder> bpBinder = static_cast<BpHwBinder*>(binder.get());
 | |
|     return bpBinder->getNodeStrongRefCount();
 | |
| }
 | |
| 
 | |
| void HidlService::sendRegistrationNotifications() {
 | |
|     if (mListeners.size() == 0 || mService == nullptr) {
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     hidl_string iface = mInterfaceName;
 | |
|     hidl_string name = mInstanceName;
 | |
| 
 | |
|     for (auto it = mListeners.begin(); it != mListeners.end();) {
 | |
|         auto ret = (*it)->onRegistration(iface, name, false /* preexisting */);
 | |
|         if (ret.isOk()) {
 | |
|             ++it;
 | |
|         } else {
 | |
|             LOG(ERROR) << "Dropping registration callback for " << iface << "/" << name
 | |
|                        << ": transport error.";
 | |
|             it = mListeners.erase(it);
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| void HidlService::sendClientCallbackNotifications(bool hasClients) {
 | |
|     CHECK(hasClients != mHasClients) << "Record shows: " << mHasClients
 | |
|         << " so we can't tell clients again that we have client: " << hasClients;
 | |
| 
 | |
|     LOG(INFO) << "Notifying " << string() << " they have clients: " << hasClients;
 | |
| 
 | |
|     for (const auto& cb : mClientCallbacks) {
 | |
|         sendClientCallbackNotification(cb, hasClients);
 | |
|     }
 | |
| 
 | |
|     mNoClientsCounter = 0;
 | |
|     mHasClients = hasClients;
 | |
| }
 | |
| 
 | |
| void HidlService::sendClientCallbackNotification(const sp<IClientCallback>& callback, bool hasClients) {
 | |
|     Return<void> ret = callback->onClients(getService(), hasClients);
 | |
|     if (!ret.isOk()) {
 | |
|         LOG(WARNING) << "onClients callback failed for " << string() << ": " << ret.description();
 | |
|     }
 | |
| }
 | |
| 
 | |
| 
 | |
| }  // namespace implementation
 | |
| }  // namespace manager
 | |
| }  // namespace hidl
 | |
| }  // namespace android
 |