/* * 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 #include #include #include #include #include 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 IsDeadObjectLogged(const Return& ret) { if (ret.isOk()) return false; if (ret.isDeadObject()) return true; LOG(ERROR) << "Cannot call healthInfoChanged* on callback: " << ret.description(); return false; } BinderHealth::BinderHealth(const std::string& name, const sp& impl) : HalHealthLoop(name, impl) { CHECK_NE(this, impl.get()); CHECK(!impl->isRemote()); } // // Methods that handle callbacks. // Return BinderHealth::registerCallback(const sp& callback) { if (callback == nullptr) { return Result::SUCCESS; } Callback* wrapped = nullptr; { std::lock_guard 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 (IsDeadObjectLogged(ret)) { // Remove callback reference. std::lock_guard 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& callback) { if (callback == nullptr) { return false; } bool removed = false; std::lock_guard 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 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 BinderHealth::unregisterCallback(const sp& callback) { return unregisterCallbackInternal(callback) ? Result::SUCCESS : Result::NOT_FOUND; } void BinderHealth::OnHealthInfoChanged(const HealthInfo& health_info) { // Notify all callbacks std::unique_lock lock(callbacks_lock_); for (auto it = callbacks_.begin(); it != callbacks_.end();) { auto ret = (*it)->Notify(health_info); if (IsDeadObjectLogged(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& 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(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