| /* |
| * 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 |