blob: 071e16dda8879dee6ca13423bd467151443eeb91 [file] [log] [blame]
/*
* Copyright (C) 2020 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.
*/
// TODO(b/167628903): Delete this file
#define LOG_TAG "libpixelpowerstats"
#include <android-base/chrono_utils.h>
#include <android-base/logging.h>
#include <android-base/properties.h>
#include <pixelpowerstats/DisplayStateResidencyDataProvider.h>
#include <pixelpowerstats/PowerStatsUtils.h>
#include <chrono>
#include <cstdio>
#include <cstring>
namespace android {
namespace hardware {
namespace google {
namespace pixel {
namespace powerstats {
DisplayStateResidencyDataProvider::DisplayStateResidencyDataProvider(
uint32_t id, std::string path, std::vector<std::string> states)
: mPath(std::move(path)),
mPowerEntityId(id),
mStates(states),
mCurState(-1),
mLooper(new Looper(true)) {
// Construct mResidencies
mResidencies.reserve(mStates.size());
for (uint32_t i = 0; i < mStates.size(); ++i) {
PowerEntityStateResidencyData p = {.powerEntityStateId = i};
mResidencies.emplace_back(p);
}
// Open display state file descriptor
LOG(VERBOSE) << "Opening " << mPath;
mFd = open(mPath.c_str(), O_RDONLY | O_NONBLOCK);
if (mFd < 0) {
PLOG(ERROR) << ":Failed to open file " << mPath;
return;
}
// Add display state file descriptor to be polled by the looper
mLooper->addFd(mFd, 0, Looper::EVENT_ERROR, nullptr, nullptr);
// Run the thread that will poll for changes to display state
LOG(VERBOSE) << "Starting DisplayStateWatcherThread";
mThread = std::thread(&DisplayStateResidencyDataProvider::pollLoop, this);
}
DisplayStateResidencyDataProvider::~DisplayStateResidencyDataProvider() {
if (mFd > 0) {
close(mFd);
}
}
bool DisplayStateResidencyDataProvider::getResults(
std::unordered_map<uint32_t, PowerEntityStateResidencyResult> &results) {
std::scoped_lock lk(mLock);
// Get current time since boot in milliseconds
uint64_t now = std::chrono::duration_cast<std::chrono::milliseconds>(
::android::base::boot_clock::now().time_since_epoch())
.count();
// Construct residency result based on current residency data
PowerEntityStateResidencyResult result = {.powerEntityId = mPowerEntityId,
.stateResidencyData = mResidencies};
if (mCurState > -1) {
result.stateResidencyData[mCurState].totalTimeInStateMs +=
now - result.stateResidencyData[mCurState].lastEntryTimestampMs;
}
results.emplace(mPowerEntityId, result);
return true;
}
std::vector<PowerEntityStateSpace> DisplayStateResidencyDataProvider::getStateSpaces() {
PowerEntityStateSpace s = {.powerEntityId = mPowerEntityId};
s.states.resize(mStates.size());
for (uint32_t i = 0; i < mStates.size(); ++i) {
s.states[i] = {.powerEntityStateId = i, .powerEntityStateName = mStates[i]};
}
return {s};
}
// Called when there is new data to be read from
// display state file descriptor indicating a state change
void DisplayStateResidencyDataProvider::updateStats() {
char data[32];
// Get current time since boot in milliseconds
uint64_t now = std::chrono::duration_cast<std::chrono::milliseconds>(
::android::base::boot_clock::now().time_since_epoch())
.count();
// Read display state
ssize_t ret = pread(mFd, data, sizeof(data) - 1, 0);
if (ret < 0) {
PLOG(WARNING) << "Failed to read display state";
return;
}
data[ret] = '\0';
LOG(VERBOSE) << "display state: " << data;
// Update residency stats based on state read
{ // acquire lock
std::scoped_lock lk(mLock);
for (uint32_t i = 0; i < mStates.size(); ++i) {
if (strstr(data, mStates[i].c_str())) {
// Update total time of the previous state
if (mCurState > -1) {
mResidencies[mCurState].totalTimeInStateMs +=
now - mResidencies[mCurState].lastEntryTimestampMs;
}
// Set current state
mCurState = i;
mResidencies[i].totalStateEntryCount++;
mResidencies[i].lastEntryTimestampMs = now;
break;
}
}
} // release lock
}
void DisplayStateResidencyDataProvider::pollLoop() {
LOG(VERBOSE) << "DisplayStateResidencyDataProvider polling...";
while (true) {
// Poll for display state changes. Timeout set to poll indefinitely
if (mLooper->pollOnce(-1) >= 0) {
updateStats();
}
}
}
} // namespace powerstats
} // namespace pixel
} // namespace google
} // namespace hardware
} // namespace android