blob: 0ba76400fef79c318c089bf2befa4e85fdb1acd2 [file] [log] [blame]
/*
* 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.
*/
#define LOG_TAG "libpixelpowerstats"
#include <android-base/logging.h>
#include <android-base/stringprintf.h>
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
#include <binder/ProcessState.h>
#include <pixelpowerstats/AidlStateResidencyDataProvider.h>
#include <time.h>
using android::base::StringPrintf;
static const uint64_t MAX_LATENCY_US = 2000;
namespace android {
namespace hardware {
namespace google {
namespace pixel {
namespace powerstats {
void AidlStateResidencyDataProvider::addEntity(uint32_t id, std::string entityName,
std::vector<std::string> stateNames) {
std::lock_guard<std::mutex> lock(mLock);
// Create a new entry in the map of power entities
mEntityInfos.emplace(entityName, StateSpace{.powerEntityId = id, .stateInfos = {}});
// Create an entry for each state and assign an Id.
uint32_t stateId = 0;
auto &stateInfos = mEntityInfos.at(entityName).stateInfos;
for (auto stateName : stateNames) {
stateInfos.emplace(stateName, stateId++);
}
}
binderStatus AidlStateResidencyDataProvider::unregisterCallbackInternal(
const sp<IBinder> &callback) {
if (callback == nullptr) {
// Callback pointer is null. Return an error.
return binderStatus::fromExceptionCode(binderStatus::EX_NULL_POINTER, "callback is null");
}
bool removed = false;
std::lock_guard<std::mutex> lock(mLock);
// Iterate over collection of callbacks and remove the one that matches
for (auto it = mCallbacks.begin(); it != mCallbacks.end();) {
if (asBinder(it->second) == callback) {
LOG(INFO) << "Unregistering callback for " << it->first;
it = mCallbacks.erase(it);
removed = true;
} else {
it++;
}
}
(void)callback->unlinkToDeath(this); // ignore errors
return removed ? binderStatus::ok()
: binderStatus::fromExceptionCode(binderStatus::EX_ILLEGAL_ARGUMENT,
"callback not found");
}
void AidlStateResidencyDataProvider::binderDied(const wp<IBinder> &who) {
binderStatus status = unregisterCallbackInternal(who.promote());
if (!status.isOk()) {
LOG(ERROR) << __func__ << "failed to unregister callback " << status.toString8();
}
}
binderStatus AidlStateResidencyDataProvider::unregisterCallback(
const sp<IPixelPowerStatsCallback> &callback) {
return unregisterCallbackInternal(asBinder(callback));
}
binderStatus AidlStateResidencyDataProvider::registerCallback(
const std::string &entityName, const sp<IPixelPowerStatsCallback> &callback) {
LOG(INFO) << "Registering callback for " << entityName;
if (callback == nullptr) {
// Callback pointer is null. Return an error.
LOG(ERROR) << __func__ << ": "
<< "Invalid callback. Callback is null";
return binderStatus::fromExceptionCode(
binderStatus::EX_NULL_POINTER, "Invalid callback. Callback is null");
}
std::lock_guard<std::mutex> lock(mLock);
if (mEntityInfos.find(entityName) == mEntityInfos.end()) {
// Could not find the entity associated with this callback. Return an error.
LOG(ERROR) << __func__ << ": "
<< "Invalid entity";
return binderStatus::fromExceptionCode(binderStatus::EX_ILLEGAL_ARGUMENT, "Invalid entity");
}
mCallbacks.emplace(entityName, callback);
// death recipient
auto linkRet = asBinder(callback)->linkToDeath(this, 0u /* cookie */);
if (linkRet != android::OK) {
LOG(WARNING) << __func__ << "Cannot link to death: " << linkRet;
// ignore the error
}
return binderStatus::ok();
}
static binderStatus getStatsTimed(
const std::pair<std::string, sp<IPixelPowerStatsCallback>> &cb,
std::vector<android::vendor::powerstats::StateResidencyData> &stats) {
struct timespec then;
struct timespec now;
clock_gettime(CLOCK_BOOTTIME, &then);
binderStatus status = cb.second->getStats(&stats);
clock_gettime(CLOCK_BOOTTIME, &now);
uint64_t time_elapsed_us =
((now.tv_sec - then.tv_sec) * 1000000) + ((now.tv_nsec - then.tv_nsec) / 1000);
if (time_elapsed_us > MAX_LATENCY_US) {
LOG(WARNING) << "getStats for " << cb.first << " exceeded time allowed: " << time_elapsed_us
<< "us";
}
return status;
}
bool AidlStateResidencyDataProvider::buildResult(
std::string entityName,
const std::vector<android::vendor::powerstats::StateResidencyData> &stats,
PowerEntityStateResidencyResult &result) {
auto infosEntry = mEntityInfos.find(entityName);
if (infosEntry == mEntityInfos.end()) {
LOG(ERROR) << __func__ << " failed: " << entityName << " is not registered.";
return false;
}
auto stateSpace = infosEntry->second;
result.powerEntityId = stateSpace.powerEntityId;
size_t numStates = stateSpace.stateInfos.size();
result.stateResidencyData.resize(numStates);
size_t numStatesFound = 0;
for (auto stat = stats.begin(); (numStatesFound < numStates) && (stat != stats.end()); stat++) {
auto stateInfosEntry = stateSpace.stateInfos.find(stat->state);
if (stateInfosEntry != stateSpace.stateInfos.end()) {
PowerEntityStateResidencyData &data = result.stateResidencyData[numStatesFound++];
data.powerEntityStateId = stateInfosEntry->second;
data.totalTimeInStateMs = static_cast<uint64_t>(stat->totalTimeInStateMs);
data.totalStateEntryCount = static_cast<uint64_t>(stat->totalStateEntryCount);
data.lastEntryTimestampMs = static_cast<uint64_t>(stat->lastEntryTimestampMs);
} else {
LOG(WARNING) << "getStats for " << entityName << " returned data for unknown state "
<< stat->state;
}
}
return (numStatesFound == numStates);
}
bool AidlStateResidencyDataProvider::getResults(
std::unordered_map<uint32_t, PowerEntityStateResidencyResult> &results) {
std::lock_guard<std::mutex> lock(mLock);
// TODO (b/126260512): return cached results if time elapsed isn't large
size_t numResultsFound = 0;
size_t numResults = mEntityInfos.size();
for (auto cb : mCallbacks) {
std::vector<android::vendor::powerstats::StateResidencyData> stats;
// Get stats for the current callback
binderStatus status = getStatsTimed(cb, stats);
if (!status.isOk()) {
LOG(ERROR) << "getStats for " << cb.first << " failed: " << status.toString8();
}
PowerEntityStateResidencyResult result;
if (buildResult(cb.first, stats, result)) {
results.emplace(result.powerEntityId, result);
numResultsFound++;
} else {
LOG(ERROR) << "State residency data missing for " << cb.first;
}
}
bool ret = (numResultsFound == numResults);
// TODO (b/126260512): Cache results of the call, the return value, and the timestamp.
return ret;
}
std::vector<PowerEntityStateSpace> AidlStateResidencyDataProvider::getStateSpaces() {
std::lock_guard<std::mutex> lock(mLock);
std::vector<PowerEntityStateSpace> stateSpaces;
stateSpaces.reserve(mEntityInfos.size());
// Return state space information for every entity for which this is configured to provide data
for (auto info : mEntityInfos) {
PowerEntityStateSpace statespace = {
.powerEntityId = info.second.powerEntityId, .states = {}};
statespace.states.resize(info.second.stateInfos.size());
size_t i = 0;
for (auto state : info.second.stateInfos) {
statespace.states[i++] = {
.powerEntityStateId = state.second, .powerEntityStateName = state.first};
}
stateSpaces.emplace_back(statespace);
}
return stateSpaces;
}
} // namespace powerstats
} // namespace pixel
} // namespace google
} // namespace hardware
} // namespace android