| /* |
| * Copyright 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 "SuspendControlService.h" |
| |
| #include <android-base/logging.h> |
| #include <android-base/stringprintf.h> |
| #include <signal.h> |
| |
| #include "SystemSuspend.h" |
| |
| using ::android::base::Result; |
| using ::android::base::StringPrintf; |
| |
| namespace android { |
| namespace system { |
| namespace suspend { |
| namespace V1_0 { |
| |
| static void register_sig_handler() { |
| signal(SIGPIPE, SIG_IGN); |
| } |
| |
| template <typename T> |
| binder::Status retOk(const T& value, T* ret_val) { |
| *ret_val = value; |
| return binder::Status::ok(); |
| } |
| |
| binder::Status SuspendControlService::registerCallback(const sp<ISuspendCallback>& callback, |
| bool* _aidl_return) { |
| if (!callback) { |
| return retOk(false, _aidl_return); |
| } |
| |
| auto l = std::lock_guard(mCallbackLock); |
| sp<IBinder> cb = IInterface::asBinder(callback); |
| // Only remote binders can be linked to death |
| if (cb->remoteBinder() != nullptr) { |
| if (findCb(cb) == mCallbacks.end()) { |
| auto status = cb->linkToDeath(this); |
| if (status != NO_ERROR) { |
| LOG(ERROR) << __func__ << " Cannot link to death: " << status; |
| return retOk(false, _aidl_return); |
| } |
| } |
| } |
| mCallbacks.push_back(callback); |
| return retOk(true, _aidl_return); |
| } |
| |
| binder::Status SuspendControlService::registerWakelockCallback( |
| const sp<IWakelockCallback>& callback, const std::string& name, bool* _aidl_return) { |
| if (!callback || name.empty()) { |
| return retOk(false, _aidl_return); |
| } |
| |
| auto l = std::lock_guard(mWakelockCallbackLock); |
| if (std::find_if(mWakelockCallbacks[name].begin(), mWakelockCallbacks[name].end(), |
| [&callback](const sp<IWakelockCallback>& i) { |
| return IInterface::asBinder(callback) == IInterface::asBinder(i); |
| }) != mWakelockCallbacks[name].end()) { |
| LOG(ERROR) << __func__ << " Same wakelock callback has already been registered"; |
| return retOk(false, _aidl_return); |
| } |
| |
| if (IInterface::asBinder(callback)->remoteBinder() && |
| IInterface::asBinder(callback)->linkToDeath(this) != NO_ERROR) { |
| LOG(WARNING) << __func__ << " Cannot link to death"; |
| return retOk(false, _aidl_return); |
| } |
| mWakelockCallbacks[name].push_back(callback); |
| |
| return retOk(true, _aidl_return); |
| } |
| |
| void SuspendControlService::binderDied(const wp<IBinder>& who) { |
| auto l = std::lock_guard(mCallbackLock); |
| mCallbacks.erase(std::remove_if(mCallbacks.begin(), mCallbacks.end(), |
| [&who](const sp<ISuspendCallback>& i) { |
| return who == IInterface::asBinder(i); |
| }), |
| mCallbacks.end()); |
| |
| auto lWakelock = std::lock_guard(mWakelockCallbackLock); |
| // Iterate through all wakelock names as same callback can be registered with different |
| // wakelocks. |
| for (auto wakelockIt = mWakelockCallbacks.begin(); wakelockIt != mWakelockCallbacks.end();) { |
| wakelockIt->second.erase( |
| std::remove_if( |
| wakelockIt->second.begin(), wakelockIt->second.end(), |
| [&who](const sp<IWakelockCallback>& i) { return who == IInterface::asBinder(i); }), |
| wakelockIt->second.end()); |
| if (wakelockIt->second.empty()) { |
| wakelockIt = mWakelockCallbacks.erase(wakelockIt); |
| } else { |
| ++wakelockIt; |
| } |
| } |
| } |
| |
| void SuspendControlService::notifyWakelock(const std::string& name, bool isAcquired) { |
| // A callback could potentially modify mWakelockCallbacks (e.g., via registerCallback). That |
| // must not result in a deadlock. To that end, we make a copy of the callback is an entry can be |
| // found for the particular wakelock and release mCallbackLock before calling the copied |
| // callbacks. |
| auto callbackLock = std::unique_lock(mWakelockCallbackLock); |
| auto it = mWakelockCallbacks.find(name); |
| if (it == mWakelockCallbacks.end()) { |
| return; |
| } |
| auto callbacksCopy = it->second; |
| callbackLock.unlock(); |
| |
| for (const auto& callback : callbacksCopy) { |
| if (isAcquired) { |
| callback->notifyAcquired().isOk(); // ignore errors |
| } else { |
| callback->notifyReleased().isOk(); // ignore errors |
| } |
| } |
| } |
| |
| void SuspendControlService::notifyWakeup(bool success, std::vector<std::string>& wakeupReasons) { |
| // A callback could potentially modify mCallbacks (e.g., via registerCallback). That must not |
| // result in a deadlock. To that end, we make a copy of mCallbacks and release mCallbackLock |
| // before calling the copied callbacks. |
| auto callbackLock = std::unique_lock(mCallbackLock); |
| auto callbacksCopy = mCallbacks; |
| callbackLock.unlock(); |
| |
| for (const auto& callback : callbacksCopy) { |
| callback->notifyWakeup(success, wakeupReasons).isOk(); // ignore errors |
| } |
| } |
| |
| void SuspendControlServiceInternal::setSuspendService(const wp<SystemSuspend>& suspend) { |
| mSuspend = suspend; |
| } |
| |
| binder::Status SuspendControlServiceInternal::enableAutosuspend(const sp<IBinder>& token, |
| bool* _aidl_return) { |
| const auto suspendService = mSuspend.promote(); |
| return retOk(suspendService != nullptr && suspendService->enableAutosuspend(token), |
| _aidl_return); |
| } |
| |
| binder::Status SuspendControlServiceInternal::forceSuspend(bool* _aidl_return) { |
| const auto suspendService = mSuspend.promote(); |
| return retOk(suspendService != nullptr && suspendService->forceSuspend(), _aidl_return); |
| } |
| |
| binder::Status SuspendControlServiceInternal::getSuspendStats(SuspendInfo* _aidl_return) { |
| const auto suspendService = mSuspend.promote(); |
| if (!suspendService) { |
| return binder::Status::fromExceptionCode(binder::Status::Exception::EX_NULL_POINTER, |
| String8("Null reference to suspendService")); |
| } |
| |
| suspendService->getSuspendInfo(_aidl_return); |
| return binder::Status::ok(); |
| } |
| |
| binder::Status SuspendControlServiceInternal::getWakeLockStats( |
| std::vector<WakeLockInfo>* _aidl_return) { |
| const auto suspendService = mSuspend.promote(); |
| if (!suspendService) { |
| return binder::Status::fromExceptionCode(binder::Status::Exception::EX_NULL_POINTER, |
| String8("Null reference to suspendService")); |
| } |
| |
| suspendService->updateStatsNow(); |
| suspendService->getStatsList().getWakeLockStats(_aidl_return); |
| |
| return binder::Status::ok(); |
| } |
| |
| binder::Status SuspendControlServiceInternal::getWakeupStats( |
| std::vector<WakeupInfo>* _aidl_return) { |
| const auto suspendService = mSuspend.promote(); |
| if (!suspendService) { |
| return binder::Status::fromExceptionCode(binder::Status::Exception::EX_NULL_POINTER, |
| String8("Null reference to suspendService")); |
| } |
| |
| suspendService->getWakeupList().getWakeupStats(_aidl_return); |
| return binder::Status::ok(); |
| } |
| |
| static std::string dumpUsage() { |
| return "\nUsage: adb shell dumpsys suspend_control_internal [option]\n\n" |
| " Options:\n" |
| " --wakelocks : returns wakelock stats.\n" |
| " --wakeups : returns wakeup stats.\n" |
| " --kernel_suspends : returns suspend success/error stats from the kernel\n" |
| " --suspend_controls : returns suspend control stats\n" |
| " --all or -a : returns all stats.\n" |
| " --help or -h : prints this message.\n\n" |
| " Note: All stats are returned if no or (an\n" |
| " invalid) option is specified.\n\n"; |
| } |
| |
| status_t SuspendControlServiceInternal::dump(int fd, const Vector<String16>& args) { |
| register_sig_handler(); |
| |
| const auto suspendService = mSuspend.promote(); |
| if (!suspendService) { |
| return DEAD_OBJECT; |
| } |
| |
| enum : int32_t { |
| OPT_WAKELOCKS = 1 << 0, |
| OPT_WAKEUPS = 1 << 1, |
| OPT_KERNEL_SUSPENDS = 1 << 2, |
| OPT_SUSPEND_CONTROLS = 1 << 3, |
| OPT_ALL = ~0, |
| }; |
| int opts = 0; |
| |
| if (args.empty()) { |
| opts = OPT_ALL; |
| } else { |
| for (const auto& arg : args) { |
| if (arg == String16("--wakelocks")) { |
| opts |= OPT_WAKELOCKS; |
| } else if (arg == String16("--wakeups")) { |
| opts |= OPT_WAKEUPS; |
| } else if (arg == String16("--kernel_suspends")) { |
| opts |= OPT_KERNEL_SUSPENDS; |
| } else if (arg == String16("--suspend_controls")) { |
| opts |= OPT_SUSPEND_CONTROLS; |
| } else if (arg == String16("-a") || arg == String16("--all")) { |
| opts = OPT_ALL; |
| } else if (arg == String16("-h") || arg == String16("--help")) { |
| std::string usage = dumpUsage(); |
| dprintf(fd, "%s\n", usage.c_str()); |
| return OK; |
| } |
| } |
| } |
| |
| if (opts & OPT_WAKELOCKS) { |
| suspendService->updateStatsNow(); |
| std::stringstream wlStats; |
| wlStats << suspendService->getStatsList(); |
| dprintf(fd, "\n%s\n", wlStats.str().c_str()); |
| } |
| |
| if (opts & OPT_WAKEUPS) { |
| std::ostringstream wakeupStats; |
| std::vector<WakeupInfo> wakeups; |
| suspendService->getWakeupList().getWakeupStats(&wakeups); |
| for (const auto& w : wakeups) { |
| wakeupStats << w.toString() << std::endl; |
| } |
| dprintf(fd, "Wakeups:\n%s\n", wakeupStats.str().c_str()); |
| } |
| |
| if (opts & OPT_KERNEL_SUSPENDS) { |
| Result<SuspendStats> res = suspendService->getSuspendStats(); |
| if (!res.ok()) { |
| LOG(ERROR) << "SuspendControlService: " << res.error().message(); |
| return OK; |
| } |
| |
| SuspendStats stats = res.value(); |
| // clang-format off |
| std::string suspendStats = StringPrintf( |
| "----- Suspend Stats -----\n" |
| "%s: %d\n%s: %d\n%s: %d\n%s: %d\n%s: %d\n" |
| "%s: %d\n%s: %d\n%s: %d\n%s: %d\n%s: %d\n" |
| "\nLast Failures:\n" |
| " %s: %s\n" |
| " %s: %d\n" |
| " %s: %s\n" |
| "----------\n\n", |
| |
| "success", stats.success, |
| "fail", stats.fail, |
| "failed_freeze", stats.failedFreeze, |
| "failed_prepare", stats.failedPrepare, |
| "failed_suspend", stats.failedSuspend, |
| "failed_suspend_late", stats.failedSuspendLate, |
| "failed_suspend_noirq", stats.failedSuspendNoirq, |
| "failed_resume", stats.failedResume, |
| "failed_resume_early", stats.failedResumeEarly, |
| "failed_resume_noirq", stats.failedResumeNoirq, |
| "last_failed_dev", stats.lastFailedDev.c_str(), |
| "last_failed_errno", stats.lastFailedErrno, |
| "last_failed_step", stats.lastFailedStep.c_str()); |
| // clang-format on |
| dprintf(fd, "\n%s\n", suspendStats.c_str()); |
| } |
| |
| if (opts & OPT_SUSPEND_CONTROLS) { |
| std::ostringstream suspendInfo; |
| SuspendInfo info; |
| suspendService->getSuspendInfo(&info); |
| suspendInfo << "suspend attempts: " << info.suspendAttemptCount << std::endl; |
| suspendInfo << "failed suspends: " << info.failedSuspendCount << std::endl; |
| suspendInfo << "short suspends: " << info.shortSuspendCount << std::endl; |
| suspendInfo << "total suspend time: " << info.suspendTimeMillis << " ms" << std::endl; |
| suspendInfo << "short suspend time: " << info.shortSuspendTimeMillis << " ms" << std::endl; |
| suspendInfo << "suspend overhead: " << info.suspendOverheadTimeMillis << " ms" << std::endl; |
| suspendInfo << "failed suspend overhead: " << info.failedSuspendOverheadTimeMillis << " ms" |
| << std::endl; |
| suspendInfo << "new backoffs: " << info.newBackoffCount << std::endl; |
| suspendInfo << "backoff continuations: " << info.backoffContinueCount << std::endl; |
| suspendInfo << "total sleep time between suspends: " << info.sleepTimeMillis << " ms" |
| << std::endl; |
| dprintf(fd, "Suspend Info:\n%s\n", suspendInfo.str().c_str()); |
| } |
| |
| return OK; |
| } |
| |
| } // namespace V1_0 |
| } // namespace suspend |
| } // namespace system |
| } // namespace android |