334 lines
13 KiB
C++
334 lines
13 KiB
C++
/*
|
|
* 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
|