blob: 671f3dd881df880994abbb12e02125190f295d01 [file] [log] [blame] [edit]
/*
* 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();
}
void SuspendControlService::setSuspendService(const wp<SystemSuspend>& suspend) {
mSuspend = suspend;
}
binder::Status SuspendControlService::enableAutosuspend(bool* _aidl_return) {
const auto suspendService = mSuspend.promote();
return retOk(suspendService != nullptr && suspendService->enableAutosuspend(), _aidl_return);
}
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::forceSuspend(bool* _aidl_return) {
const auto suspendService = mSuspend.promote();
return retOk(suspendService != nullptr && suspendService->forceSuspend(), _aidl_return);
}
void SuspendControlService::binderDied(const wp<IBinder>& who) {
auto l = std::lock_guard(mCallbackLock);
mCallbacks.erase(findCb(who));
}
void SuspendControlService::notifyWakeup(bool success) {
// 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).isOk(); // ignore errors
}
}
binder::Status SuspendControlService::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();
}
static std::string dumpUsage() {
return "\nUsage: adb shell dumpsys suspend_control [option]\n\n"
" Options:\n"
" --wakelocks : returns wakelock stats.\n"
" --suspend_stats : returns suspend 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 SuspendControlService::dump(int fd, const Vector<String16>& args) {
register_sig_handler();
const auto suspendService = mSuspend.promote();
if (!suspendService) {
return DEAD_OBJECT;
}
bool wakelocks = true;
bool suspend_stats = true;
if (args.size() > 0) {
std::string arg(String8(args[0]).string());
if (arg == "--wakelocks") {
suspend_stats = false;
} else if (arg == "--suspend_stats") {
wakelocks = false;
} else if (arg == "-h" || arg == "--help") {
std::string usage = dumpUsage();
dprintf(fd, "%s\n", usage.c_str());
return OK;
}
}
if (wakelocks) {
suspendService->updateStatsNow();
std::stringstream wlStats;
wlStats << suspendService->getStatsList();
dprintf(fd, "\n%s\n", wlStats.str().c_str());
}
if (suspend_stats) {
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());
}
return OK;
}
} // namespace V1_0
} // namespace suspend
} // namespace system
} // namespace android