You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
223 lines
8.5 KiB
223 lines
8.5 KiB
/*
|
|
* 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
|