| /* |
| * 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. |
| */ |
| |
| #include <health2impl/BinderHealth.h> |
| |
| #include <android-base/logging.h> |
| #include <hidl/HidlTransportSupport.h> |
| #include <hwbinder/IPCThreadState.h> |
| |
| #include <health2impl/Callback.h> |
| #include <health2impl/Health.h> |
| |
| using android::hardware::handleTransportPoll; |
| using android::hardware::IPCThreadState; |
| using android::hardware::setupTransportPolling; |
| |
| using android::hardware::health::V2_0::Result; |
| |
| namespace android { |
| namespace hardware { |
| namespace health { |
| namespace V2_1 { |
| namespace implementation { |
| |
| bool IsDeadObject(const Return<void>& ret) { |
| if (ret.isOk()) return false; |
| if (ret.isDeadObject()) return true; |
| return false; |
| } |
| |
| BinderHealth::BinderHealth(const std::string& name, const sp<IHealth>& impl) |
| : HalHealthLoop(name, impl) { |
| CHECK_NE(this, impl.get()); |
| CHECK(!impl->isRemote()); |
| } |
| |
| // |
| // Methods that handle callbacks. |
| // |
| |
| Return<Result> BinderHealth::registerCallback(const sp<V2_0::IHealthInfoCallback>& callback) { |
| if (callback == nullptr) { |
| return Result::SUCCESS; |
| } |
| |
| Callback* wrapped = nullptr; |
| { |
| std::lock_guard<decltype(callbacks_lock_)> lock(callbacks_lock_); |
| wrapped = callbacks_.emplace_back(Wrap(callback)).get(); |
| // unlock |
| } |
| |
| auto linkRet = callback->linkToDeath(this, 0u /* cookie */); |
| if (!linkRet.withDefault(false)) { |
| LOG(WARNING) << __func__ << "Cannot link to death: " |
| << (linkRet.isOk() ? "linkToDeath returns false" : linkRet.description()); |
| // ignore the error |
| } |
| |
| getHealthInfo_2_1([&](auto res, const auto& health_info) { |
| if (res != Result::SUCCESS) { |
| LOG(ERROR) << "Cannot call getHealthInfo_2_1: " << toString(res); |
| return; |
| } |
| auto ret = wrapped->Notify(health_info); |
| if (IsDeadObject(ret)) { |
| // Remove callback reference. |
| std::lock_guard<decltype(callbacks_lock_)> lock(callbacks_lock_); |
| auto it = std::find_if(callbacks_.begin(), callbacks_.end(), |
| [wrapped](const auto& cb) { return cb.get() == wrapped; }); |
| if (it != callbacks_.end()) { |
| callbacks_.erase(it); |
| } |
| // unlock |
| } |
| }); |
| |
| return Result::SUCCESS; |
| } |
| |
| bool BinderHealth::unregisterCallbackInternal(const sp<IBase>& callback) { |
| if (callback == nullptr) { |
| return false; |
| } |
| |
| bool removed = false; |
| std::lock_guard<decltype(callbacks_lock_)> lock(callbacks_lock_); |
| for (auto it = callbacks_.begin(); it != callbacks_.end();) { |
| if (interfacesEqual((*it)->Get(), callback)) { |
| it = callbacks_.erase(it); |
| removed = true; |
| } else { |
| ++it; |
| } |
| } |
| (void)callback->unlinkToDeath(this).isOk(); // ignore errors |
| return removed; |
| } |
| |
| Return<Result> BinderHealth::update() { |
| Result result = service()->update(); |
| if (result != Result::SUCCESS) return result; |
| getHealthInfo_2_1([&](auto res, const auto& health_info) { |
| if (res != Result::SUCCESS) { |
| result = res; |
| return; |
| } |
| OnHealthInfoChanged(health_info); |
| }); |
| return result; |
| } |
| |
| Return<Result> BinderHealth::unregisterCallback(const sp<V2_0::IHealthInfoCallback>& callback) { |
| return unregisterCallbackInternal(callback) ? Result::SUCCESS : Result::NOT_FOUND; |
| } |
| |
| void BinderHealth::OnHealthInfoChanged(const HealthInfo& health_info) { |
| // Notify all callbacks |
| std::unique_lock<decltype(callbacks_lock_)> lock(callbacks_lock_); |
| for (auto it = callbacks_.begin(); it != callbacks_.end();) { |
| auto ret = (*it)->Notify(health_info); |
| if (IsDeadObject(ret)) { |
| it = callbacks_.erase(it); |
| } else { |
| ++it; |
| } |
| } |
| lock.unlock(); |
| |
| // adjusts uevent / wakealarm periods |
| HalHealthLoop::OnHealthInfoChanged(health_info); |
| } |
| |
| void BinderHealth::serviceDied(uint64_t /* cookie */, const wp<IBase>& who) { |
| (void)unregisterCallbackInternal(who.promote()); |
| } |
| |
| void BinderHealth::BinderEvent(uint32_t /*epevents*/) { |
| if (binder_fd_ >= 0) { |
| handleTransportPoll(binder_fd_); |
| } |
| } |
| |
| void BinderHealth::Init(struct healthd_config* config) { |
| // Set up epoll and get uevent / wake alarm periods |
| HalHealthLoop::Init(config); |
| |
| LOG(INFO) << instance_name() << " instance initializing with healthd_config..."; |
| |
| binder_fd_ = setupTransportPolling(); |
| |
| if (binder_fd_ >= 0) { |
| auto binder_event = [](auto* health_loop, uint32_t epevents) { |
| static_cast<BinderHealth*>(health_loop)->BinderEvent(epevents); |
| }; |
| if (RegisterEvent(binder_fd_, binder_event, EVENT_NO_WAKEUP_FD) != 0) { |
| PLOG(ERROR) << instance_name() << " instance: Register for binder events failed"; |
| } |
| } |
| |
| CHECK_EQ(registerAsService(instance_name()), android::OK) |
| << instance_name() << ": Failed to register HAL"; |
| |
| LOG(INFO) << instance_name() << ": Hal init done"; |
| } |
| |
| int BinderHealth::PrepareToWait(void) { |
| IPCThreadState::self()->flushCommands(); |
| return HalHealthLoop::PrepareToWait(); |
| } |
| |
| } // namespace implementation |
| } // namespace V2_1 |
| } // namespace health |
| } // namespace hardware |
| } // namespace android |