sdm710-common: power: re-import power HAL

from android-13.0.0_r3

Signed-off-by: Chenyang Zhong <zhongcy95@gmail.com>
This commit is contained in:
Chenyang Zhong 2022-09-10 07:09:51 -04:00 committed by giasen
parent 1c28990a9a
commit 39646561d1
58 changed files with 185516 additions and 209 deletions

View file

@ -1,13 +1,27 @@
cc_library_headers { //
name: "lenovo_power_headers", // Copyright (C) 2018 The Android Open Source Project
vendor_available: true, //
export_include_dirs: ["."], // 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.
package {
default_applicable_licenses: ["Android-Apache-2.0"],
} }
cc_library { cc_library {
name: "libdisppower-lenovo", name: "libdisppower-pixel",
vendor: true, proprietary: true,
srcs: [ srcs: [
"disp-power/DisplayLowPower.cpp",
"disp-power/InteractionHandler.cpp", "disp-power/InteractionHandler.cpp",
], ],
shared_libs: [ shared_libs: [
@ -19,26 +33,94 @@ cc_library {
], ],
} }
cc_library {
name: "libadaptivecpu",
proprietary: true,
vendor: true,
srcs: [
"adaptivecpu/AdaptiveCpu.cpp",
"adaptivecpu/AdaptiveCpuConfig.cpp",
"adaptivecpu/AdaptiveCpuStats.cpp",
"adaptivecpu/CpuFrequencyReader.cpp",
"adaptivecpu/CpuLoadReaderProcStat.cpp",
"adaptivecpu/CpuLoadReaderSysDevices.cpp",
"adaptivecpu/Device.cpp",
"adaptivecpu/KernelCpuFeatureReader.cpp",
"adaptivecpu/Model.cpp",
"adaptivecpu/RealFilesystem.cpp",
"adaptivecpu/ThrottleDecision.cpp",
"adaptivecpu/TimeSource.cpp",
"adaptivecpu/WorkDurationProcessor.cpp",
],
shared_libs: [
"android.hardware.power-V3-ndk",
"libbase",
"liblog",
"libperfmgr",
"libutils",
"libcutils",
],
}
cc_test {
name: "libadaptivecpu_test",
proprietary: true,
vendor: true,
srcs: [
"adaptivecpu/tests/AdaptiveCpuConfigTest.cpp",
"adaptivecpu/tests/AdaptiveCpuStatsTest.cpp",
"adaptivecpu/tests/CpuFrequencyReaderTest.cpp",
"adaptivecpu/tests/CpuLoadReaderProcStatTest.cpp",
"adaptivecpu/tests/CpuLoadReaderSysDevicesTest.cpp",
"adaptivecpu/tests/KernelCpuFeatureReaderTest.cpp",
"adaptivecpu/tests/ModelTest.cpp",
"adaptivecpu/tests/WorkDurationProcessorTest.cpp",
],
static_libs: [
"libadaptivecpu",
"libgmock",
"android.hardware.power-V3-ndk",
],
shared_libs: [
"liblog",
"libbase",
"libcutils",
],
test_suites: ["device-tests"],
}
// Deprecated, do not use
// Use pixel-power-ext for vendor extension
cc_library_headers {
name: "pixel_power_headers",
vendor: true,
export_include_dirs: ["hidl"],
}
cc_binary { cc_binary {
name: "android.hardware.power-service.lenovo", name: "android.hardware.power-service.pixel-libperfmgr",
relative_install_path: "hw", relative_install_path: "hw",
init_rc: ["aidl/android.hardware.power-service.lenovo.rc"], init_rc: ["aidl/android.hardware.power-service.pixel-libperfmgr.rc"],
vintf_fragments: ["aidl/android.hardware.power-service.lenovo.xml"], vintf_fragments: ["aidl/android.hardware.power-service.pixel.xml"],
vendor: true, vendor: true,
shared_libs: [ shared_libs: [
"android.hardware.power-V1-ndk", "android.hardware.power-V3-ndk",
"libadaptivecpu",
"libbase", "libbase",
"libcutils", "libcutils",
"liblog", "liblog",
"libutils", "libutils",
"libbinder_ndk", "libbinder_ndk",
"libdisppower-lenovo", "libdisppower-pixel",
"libperfmgr", "libperfmgr",
"libprocessgroup",
"pixel-power-ext-V1-ndk", "pixel-power-ext-V1-ndk",
], ],
srcs: [ srcs: [
"aidl/service.cpp", "aidl/service.cpp",
"aidl/Power.cpp", "aidl/Power.cpp",
"aidl/PowerExt.cpp", "aidl/PowerExt.cpp",
"aidl/PowerHintSession.cpp",
"aidl/PowerSessionManager.cpp",
], ],
} }

View file

@ -0,0 +1,233 @@
/*
* Copyright (C) 2021 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 "powerhal-adaptivecpu"
#define ATRACE_TAG (ATRACE_TAG_POWER | ATRACE_TAG_HAL)
#include "AdaptiveCpu.h"
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/properties.h>
#include <perfmgr/HintManager.h>
#include <sys/resource.h>
#include <utils/Trace.h>
#include <chrono>
#include <deque>
#include <numeric>
#include "CpuLoadReaderSysDevices.h"
#include "Model.h"
namespace aidl {
namespace google {
namespace hardware {
namespace power {
namespace impl {
namespace pixel {
using ::android::perfmgr::HintManager;
// We pass the previous N ModelInputs to the model, including the most recent ModelInput.
constexpr uint32_t kNumHistoricalModelInputs = 3;
// TODO(b/207662659): Add config for changing between different reader types.
AdaptiveCpu::AdaptiveCpu() {}
bool AdaptiveCpu::IsEnabled() const {
return mIsEnabled;
}
void AdaptiveCpu::HintReceived(bool enable) {
ATRACE_CALL();
LOG(INFO) << "AdaptiveCpu received hint: enable=" << enable;
if (enable) {
StartThread();
} else {
SuspendThread();
}
}
void AdaptiveCpu::StartThread() {
ATRACE_CALL();
std::lock_guard lock(mThreadCreationMutex);
LOG(INFO) << "Starting AdaptiveCpu thread";
mIsEnabled = true;
mShouldReloadConfig = true;
mLastEnabledHintTime = mTimeSource.GetTime();
if (!mLoopThread.joinable()) {
mLoopThread = std::thread([&]() {
pthread_setname_np(pthread_self(), "AdaptiveCpu");
// Parent threads may have higher priorities, so we reset to the default.
int ret = setpriority(PRIO_PROCESS, 0, 0);
if (ret != 0) {
PLOG(ERROR) << "setpriority on AdaptiveCpu thread failed: " << ret;
}
LOG(INFO) << "Started AdaptiveCpu thread successfully";
RunMainLoop();
LOG(ERROR) << "AdaptiveCpu thread ended, this should never happen!";
});
}
}
void AdaptiveCpu::SuspendThread() {
ATRACE_CALL();
LOG(INFO) << "Stopping AdaptiveCpu thread";
// This stops the thread from receiving work durations in ReportWorkDurations, which means the
// thread blocks indefinitely.
mIsEnabled = false;
}
void AdaptiveCpu::ReportWorkDurations(const std::vector<WorkDuration> &workDurations,
std::chrono::nanoseconds targetDuration) {
ATRACE_CALL();
if (!mIsEnabled) {
return;
}
if (!mWorkDurationProcessor.ReportWorkDurations(workDurations, targetDuration)) {
mIsEnabled = false;
return;
}
mWorkDurationsAvailableCondition.notify_one();
}
void AdaptiveCpu::WaitForEnabledAndWorkDurations() {
ATRACE_CALL();
std::unique_lock<std::mutex> lock(mWaitMutex);
// TODO(b/188770301) Once the gating logic is implemented, don't block indefinitely.
mWorkDurationsAvailableCondition.wait(
lock, [&] { return mIsEnabled && mWorkDurationProcessor.HasWorkDurations(); });
}
void AdaptiveCpu::RunMainLoop() {
ATRACE_CALL();
std::deque<ModelInput> historicalModelInputs;
ThrottleDecision previousThrottleDecision = ThrottleDecision::NO_THROTTLE;
while (true) {
ATRACE_NAME("loop");
WaitForEnabledAndWorkDurations();
if (mLastEnabledHintTime + mConfig.enabledHintTimeout < mTimeSource.GetTime()) {
LOG(INFO) << "Adaptive CPU hint timed out, last enabled time="
<< mLastEnabledHintTime.count() << "ns";
mIsEnabled = false;
continue;
}
if (mShouldReloadConfig) {
if (!AdaptiveCpuConfig::ReadFromSystemProperties(&mConfig)) {
mIsEnabled = false;
continue;
}
LOG(INFO) << "Read config: " << mConfig;
mShouldReloadConfig = false;
}
ATRACE_BEGIN("compute");
mAdaptiveCpuStats.RegisterStartRun();
if (!mIsInitialized) {
if (!mKernelCpuFeatureReader.Init()) {
mIsEnabled = false;
continue;
}
mDevice = ReadDevice();
mIsInitialized = true;
}
ModelInput modelInput;
modelInput.previousThrottleDecision = previousThrottleDecision;
modelInput.workDurationFeatures = mWorkDurationProcessor.GetFeatures();
LOG(VERBOSE) << "Got work durations: count=" << modelInput.workDurationFeatures.numDurations
<< ", average=" << modelInput.workDurationFeatures.averageDuration.count()
<< "ns";
if (modelInput.workDurationFeatures.numDurations == 0) {
continue;
}
if (!mKernelCpuFeatureReader.GetRecentCpuFeatures(&modelInput.cpuPolicyAverageFrequencyHz,
&modelInput.cpuCoreIdleTimesPercentage)) {
mIsEnabled = false;
continue;
}
modelInput.LogToAtrace();
historicalModelInputs.push_back(modelInput);
if (historicalModelInputs.size() > kNumHistoricalModelInputs) {
historicalModelInputs.pop_front();
}
const ThrottleDecision throttleDecision = mModel.Run(historicalModelInputs, mConfig);
LOG(VERBOSE) << "Model decision: " << static_cast<uint32_t>(throttleDecision);
ATRACE_INT("AdaptiveCpu_throttleDecision", static_cast<uint32_t>(throttleDecision));
{
ATRACE_NAME("sendHints");
const auto now = mTimeSource.GetTime();
// Resend the throttle hints, even if they've not changed, if the previous send is close
// to timing out. We define "close to" as half the hint timeout, as we can't guarantee
// we will run again before the actual timeout.
const bool throttleHintMayTimeout =
now - mLastThrottleHintTime > mConfig.hintTimeout / 2;
if (throttleDecision != previousThrottleDecision || throttleHintMayTimeout) {
ATRACE_NAME("sendNewHints");
mLastThrottleHintTime = now;
for (const auto &hintName : THROTTLE_DECISION_TO_HINT_NAMES.at(throttleDecision)) {
HintManager::GetInstance()->DoHint(hintName, mConfig.hintTimeout);
}
}
if (throttleDecision != previousThrottleDecision) {
ATRACE_NAME("endOldHints");
for (const auto &hintName :
THROTTLE_DECISION_TO_HINT_NAMES.at(previousThrottleDecision)) {
HintManager::GetInstance()->EndHint(hintName);
}
previousThrottleDecision = throttleDecision;
}
}
mAdaptiveCpuStats.RegisterSuccessfulRun(previousThrottleDecision, throttleDecision,
modelInput.workDurationFeatures, mConfig);
ATRACE_END(); // compute
{
ATRACE_NAME("sleep");
std::this_thread::sleep_for(mConfig.iterationSleepDuration);
}
}
}
void AdaptiveCpu::DumpToFd(int fd) const {
std::stringstream result;
result << "========== Begin Adaptive CPU stats ==========\n";
result << "Enabled: " << mIsEnabled << "\n";
result << "Config: " << mConfig << "\n";
mKernelCpuFeatureReader.DumpToStream(result);
mAdaptiveCpuStats.DumpToStream(result);
result << "========== End Adaptive CPU stats ==========\n";
if (!::android::base::WriteStringToFd(result.str(), fd)) {
PLOG(ERROR) << "Failed to dump state to fd";
}
}
} // namespace pixel
} // namespace impl
} // namespace power
} // namespace hardware
} // namespace google
} // namespace aidl

View file

@ -0,0 +1,112 @@
/*
* Copyright (C) 2021 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.
*/
#pragma once
#include <aidl/android/hardware/power/WorkDuration.h>
#include <perfmgr/HintManager.h>
#include <chrono>
#include <thread>
#include <unordered_map>
#include <vector>
#include "AdaptiveCpuConfig.h"
#include "AdaptiveCpuStats.h"
#include "Device.h"
#include "KernelCpuFeatureReader.h"
#include "Model.h"
#include "WorkDurationProcessor.h"
namespace aidl {
namespace google {
namespace hardware {
namespace power {
namespace impl {
namespace pixel {
using std::chrono_literals::operator""ms;
using ::aidl::android::hardware::power::WorkDuration;
using ::android::perfmgr::HintManager;
// Applies CPU frequency hints infered by an ML model based on the recent CPU statistics and work
// durations.
// This class's public members are not synchronised and should not be used from multiple threads,
// with the exception of ReportWorkDuration, which can be called from an arbitrary thread.
class AdaptiveCpu {
public:
AdaptiveCpu();
bool IsEnabled() const;
// Called when the Adaptive CPU hint is received. This method enables/disables the Adaptive CPU
// thread.
void HintReceived(bool enable);
// Reports work durations for processing. This method returns immediately as work durations are
// processed asynchonuously.
void ReportWorkDurations(const std::vector<WorkDuration> &workDurations,
std::chrono::nanoseconds targetDuration);
// Dump info to a file descriptor. Called when dumping service info.
void DumpToFd(int fd) const;
// When PowerExt receives a hint with this name, HintReceived() is called.
static constexpr char HINT_NAME[] = "ADAPTIVE_CPU";
private:
void StartThread();
void SuspendThread();
// The main loop of Adaptive CPU, which runs in a separate thread.
void RunMainLoop();
void WaitForEnabledAndWorkDurations();
Model mModel;
WorkDurationProcessor mWorkDurationProcessor;
KernelCpuFeatureReader mKernelCpuFeatureReader;
AdaptiveCpuStats mAdaptiveCpuStats;
const TimeSource mTimeSource;
// The thread in which work durations are processed.
std::thread mLoopThread;
// Guards against creating multiple threads in the case HintReceived(true) is called on separate
// threads simultaneously.
std::mutex mThreadCreationMutex;
// Used when waiting in WaitForEnabledAndWorkDurations().
std::mutex mWaitMutex;
// A condition variable that will be notified when new work durations arrive.
std::condition_variable mWorkDurationsAvailableCondition;
volatile bool mIsEnabled = false;
bool mIsInitialized = false;
volatile bool mShouldReloadConfig = false;
std::chrono::nanoseconds mLastEnabledHintTime;
std::chrono::nanoseconds mLastThrottleHintTime;
Device mDevice;
AdaptiveCpuConfig mConfig = AdaptiveCpuConfig::DEFAULT;
};
} // namespace pixel
} // namespace impl
} // namespace power
} // namespace hardware
} // namespace google
} // namespace aidl

View file

@ -0,0 +1,174 @@
/*
* Copyright (C) 2022 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 "powerhal-adaptivecpu"
#define ATRACE_TAG (ATRACE_TAG_POWER | ATRACE_TAG_HAL)
#include "AdaptiveCpuConfig.h"
#include <android-base/logging.h>
#include <android-base/properties.h>
#include <inttypes.h>
#include <utils/Trace.h>
#include <sstream>
#include <string>
#include <string_view>
namespace aidl {
namespace google {
namespace hardware {
namespace power {
namespace impl {
namespace pixel {
using std::chrono_literals::operator""ms;
using std::chrono_literals::operator""min;
constexpr std::string_view kIterationSleepDurationProperty(
"debug.adaptivecpu.iteration_sleep_duration_ms");
static const std::chrono::milliseconds kIterationSleepDurationMin = 20ms;
constexpr std::string_view kHintTimeoutProperty("debug.adaptivecpu.hint_timeout_ms");
// "percent" as range is 0-100, while the in-memory is "probability" as range is 0-1.
constexpr std::string_view kRandomThrottleDecisionPercentProperty(
"debug.adaptivecpu.random_throttle_decision_percent");
constexpr std::string_view kRandomThrottleOptionsProperty(
"debug.adaptivecpu.random_throttle_options");
constexpr std::string_view kEnabledHintTimeoutProperty("debug.adaptivecpu.enabled_hint_timeout_ms");
bool ParseThrottleDecisions(const std::string &input, std::vector<ThrottleDecision> *output);
std::string FormatThrottleDecisions(const std::vector<ThrottleDecision> &throttleDecisions);
const AdaptiveCpuConfig AdaptiveCpuConfig::DEFAULT{
// N.B.: The model will typically be trained with this value set to 25ms. We set it to 1s as
// a safety measure, but best performance will be seen at 25ms.
.iterationSleepDuration = 1000ms,
.hintTimeout = 2000ms,
.randomThrottleDecisionProbability = 0,
.randomThrottleOptions = {ThrottleDecision::NO_THROTTLE, ThrottleDecision::THROTTLE_50,
ThrottleDecision::THROTTLE_60, ThrottleDecision::THROTTLE_70,
ThrottleDecision::THROTTLE_80, ThrottleDecision::THROTTLE_90},
.enabledHintTimeout = 120min,
};
bool AdaptiveCpuConfig::ReadFromSystemProperties(AdaptiveCpuConfig *output) {
ATRACE_CALL();
output->iterationSleepDuration = std::chrono::milliseconds(
::android::base::GetUintProperty<uint32_t>(kIterationSleepDurationProperty.data(),
DEFAULT.iterationSleepDuration.count()));
output->iterationSleepDuration =
std::max(output->iterationSleepDuration, kIterationSleepDurationMin);
output->hintTimeout = std::chrono::milliseconds(::android::base::GetUintProperty<uint32_t>(
kHintTimeoutProperty.data(), DEFAULT.hintTimeout.count()));
output->randomThrottleDecisionProbability =
static_cast<double>(::android::base::GetUintProperty<uint32_t>(
kRandomThrottleDecisionPercentProperty.data(),
DEFAULT.randomThrottleDecisionProbability * 100)) /
100;
if (output->randomThrottleDecisionProbability > 1.0) {
LOG(ERROR) << "Received bad value for " << kRandomThrottleDecisionPercentProperty << ": "
<< output->randomThrottleDecisionProbability;
return false;
}
const std::string randomThrottleOptionsStr =
::android::base::GetProperty(kRandomThrottleOptionsProperty.data(),
FormatThrottleDecisions(DEFAULT.randomThrottleOptions));
output->randomThrottleOptions.clear();
if (!ParseThrottleDecisions(randomThrottleOptionsStr, &output->randomThrottleOptions)) {
return false;
}
output->enabledHintTimeout =
std::chrono::milliseconds(::android::base::GetUintProperty<uint32_t>(
kEnabledHintTimeoutProperty.data(), DEFAULT.enabledHintTimeout.count()));
return true;
}
bool AdaptiveCpuConfig::operator==(const AdaptiveCpuConfig &other) const {
return iterationSleepDuration == other.iterationSleepDuration &&
hintTimeout == other.hintTimeout &&
randomThrottleDecisionProbability == other.randomThrottleDecisionProbability &&
enabledHintTimeout == other.enabledHintTimeout &&
randomThrottleOptions == other.randomThrottleOptions;
}
std::ostream &operator<<(std::ostream &stream, const AdaptiveCpuConfig &config) {
stream << "AdaptiveCpuConfig(";
stream << "iterationSleepDuration=" << config.iterationSleepDuration.count() << "ms, ";
stream << "hintTimeout=" << config.hintTimeout.count() << "ms, ";
stream << "randomThrottleDecisionProbability=" << config.randomThrottleDecisionProbability
<< ", ";
stream << "enabledHintTimeout=" << config.enabledHintTimeout.count() << "ms, ";
stream << "randomThrottleOptions=[" << FormatThrottleDecisions(config.randomThrottleOptions)
<< "]";
stream << ")";
return stream;
}
bool ParseThrottleDecisions(const std::string &input, std::vector<ThrottleDecision> *output) {
std::stringstream ss(input);
while (ss.good()) {
std::string throttleDecisionStr;
if (std::getline(ss, throttleDecisionStr, ',').fail()) {
LOG(ERROR) << "Failed to getline on throttle decisions string: " << input;
return false;
}
uint32_t throttleDecisionInt;
int scanEnd;
if (std::sscanf(throttleDecisionStr.c_str(), "%" PRIu32 "%n", &throttleDecisionInt,
&scanEnd) != 1 ||
scanEnd != throttleDecisionStr.size()) {
LOG(ERROR) << "Failed to parse as int: str=" << throttleDecisionStr
<< ", input=" << input << ", scanEnd=" << scanEnd;
return false;
}
if (throttleDecisionInt < static_cast<uint32_t>(ThrottleDecision::FIRST) ||
throttleDecisionInt > static_cast<uint32_t>(ThrottleDecision::LAST)) {
LOG(ERROR) << "Failed to parse throttle decision: throttleDecision="
<< throttleDecisionInt << ", input=" << input;
return false;
}
output->push_back(static_cast<ThrottleDecision>(throttleDecisionInt));
}
if (output->empty()) {
LOG(ERROR) << "Failed to find any throttle decisions, must have at least one: " << input;
return false;
}
return true;
}
std::string FormatThrottleDecisions(const std::vector<ThrottleDecision> &throttleDecisions) {
std::stringstream ss;
for (size_t i = 0; i < throttleDecisions.size(); i++) {
ss << static_cast<uint32_t>(throttleDecisions[i]);
if (i < throttleDecisions.size() - 1) {
ss << ",";
}
}
return ss.str();
}
} // namespace pixel
} // namespace impl
} // namespace power
} // namespace hardware
} // namespace google
} // namespace aidl

View file

@ -0,0 +1,58 @@
/*
* Copyright (C) 2022 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.
*/
#pragma once
#include <chrono>
#include <iostream>
#include "ThrottleDecision.h"
namespace aidl {
namespace google {
namespace hardware {
namespace power {
namespace impl {
namespace pixel {
struct AdaptiveCpuConfig {
static bool ReadFromSystemProperties(AdaptiveCpuConfig *output);
static const AdaptiveCpuConfig DEFAULT;
// How long to sleep for between Adaptive CPU runs.
std::chrono::milliseconds iterationSleepDuration;
// Timeout applied to hints. If Adaptive CPU doesn't receive any frames in this time, CPU
// throttling hints are cancelled.
std::chrono::milliseconds hintTimeout;
// Instead of throttling based on model output, choose a random throttle X% of the time. Must be
// between 0 and 1 inclusive.
double randomThrottleDecisionProbability;
std::vector<ThrottleDecision> randomThrottleOptions;
// Setting AdaptiveCpu to enabled only lasts this long. For a continuous run, AdaptiveCpu needs
// to receive the enabled hint more frequently than this value.
std::chrono::milliseconds enabledHintTimeout;
bool operator==(const AdaptiveCpuConfig &other) const;
};
std::ostream &operator<<(std::ostream &os, const AdaptiveCpuConfig &config);
} // namespace pixel
} // namespace impl
} // namespace power
} // namespace hardware
} // namespace google
} // namespace aidl

View file

@ -0,0 +1,131 @@
/*
* Copyright (C) 2021 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 "powerhal-adaptivecpu"
#define ATRACE_TAG (ATRACE_TAG_POWER | ATRACE_TAG_HAL)
#include "AdaptiveCpuStats.h"
#include <utils/Trace.h>
#include "AdaptiveCpu.h"
using std::chrono_literals::operator""ns;
namespace aidl {
namespace google {
namespace hardware {
namespace power {
namespace impl {
namespace pixel {
void AdaptiveCpuStats::RegisterStartRun() {
ATRACE_CALL();
mNumStartedRuns++;
mLastRunStartTime = mTimeSource->GetTime();
if (mStartTime == 0ns) {
mStartTime = mLastRunStartTime;
}
}
void AdaptiveCpuStats::RegisterSuccessfulRun(ThrottleDecision previousThrottleDecision,
ThrottleDecision throttleDecision,
WorkDurationFeatures workDurationFeatures,
const AdaptiveCpuConfig &config) {
ATRACE_CALL();
mNumSuccessfulRuns++;
mNumThrottles[throttleDecision]++;
const auto runSuccessTime = mTimeSource->GetTime();
mTotalRunDuration += runSuccessTime - mLastRunStartTime;
// Don't update previousThrottleDecision entries if we haven't run successfully before.
if (mLastRunSuccessTime != 0ns) {
mThrottleDurations[previousThrottleDecision] +=
std::min(runSuccessTime - mLastRunSuccessTime,
std::chrono::duration_cast<std::chrono::nanoseconds>(config.hintTimeout));
mNumDurations[previousThrottleDecision] += workDurationFeatures.numDurations;
mNumMissedDeadlines[previousThrottleDecision] += workDurationFeatures.numMissedDeadlines;
}
mLastRunSuccessTime = runSuccessTime;
}
void AdaptiveCpuStats::DumpToStream(std::ostream &stream) const {
stream << "Stats:\n";
stream << "- Successful runs / total runs: " << mNumSuccessfulRuns << " / " << mNumStartedRuns
<< "\n";
stream << "- Total run duration: " << FormatDuration(mTotalRunDuration) << "\n";
stream << "- Average run duration: " << FormatDuration(mTotalRunDuration / mNumSuccessfulRuns)
<< "\n";
stream << "- Running time fraction: "
<< static_cast<double>(mTotalRunDuration.count()) /
(mTimeSource->GetTime() - mStartTime).count()
<< "\n";
stream << "- Number of throttles:\n";
size_t totalNumThrottles = 0;
for (const auto &[throttleDecision, numThrottles] : mNumThrottles) {
stream << " - " << ThrottleString(throttleDecision) << ": " << numThrottles << "\n";
totalNumThrottles += numThrottles;
}
stream << " - Total: " << totalNumThrottles << "\n";
stream << "- Time spent throttling:\n";
std::chrono::nanoseconds totalThrottleDuration;
for (const auto &[throttleDecision, throttleDuration] : mThrottleDurations) {
stream << " - " << ThrottleString(throttleDecision) << ": "
<< FormatDuration(throttleDuration) << "\n";
totalThrottleDuration += throttleDuration;
}
stream << " - Total: " << FormatDuration(totalThrottleDuration) << "\n";
stream << "- Missed deadlines per throttle:\n";
size_t totalNumDurations = 0;
size_t totalNumMissedDeadlines = 0;
for (const auto &[throttleDecision, numDurations] : mNumDurations) {
const size_t numMissedDeadlines = mNumMissedDeadlines.at(throttleDecision);
stream << " - " << ThrottleString(throttleDecision) << ": " << numMissedDeadlines << " / "
<< numDurations << " (" << static_cast<double>(numMissedDeadlines) / numDurations
<< ")\n";
totalNumDurations += numDurations;
totalNumMissedDeadlines += numMissedDeadlines;
}
stream << " - Total: " << totalNumMissedDeadlines << " / " << totalNumDurations << " ("
<< static_cast<double>(totalNumMissedDeadlines) / totalNumDurations << ")\n";
}
std::string AdaptiveCpuStats::FormatDuration(std::chrono::nanoseconds duration) {
double count = static_cast<double>(duration.count());
std::string suffix;
if (count < 1000.0) {
suffix = "ns";
} else if (count < 1000.0 * 1000) {
suffix = "us";
count /= 1000;
} else if (count < 1000.0 * 1000 * 100) {
suffix = "ms";
count /= 1000 * 1000;
} else {
suffix = "s";
count /= 1000 * 1000 * 1000;
}
return std::to_string(count) + suffix;
}
} // namespace pixel
} // namespace impl
} // namespace power
} // namespace hardware
} // namespace google
} // namespace aidl

View file

@ -0,0 +1,73 @@
/*
* Copyright (C) 2021 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.
*/
#pragma once
#include <ostream>
#include "AdaptiveCpuConfig.h"
#include "ITimeSource.h"
#include "Model.h"
#include "TimeSource.h"
#include "WorkDurationProcessor.h"
namespace aidl {
namespace google {
namespace hardware {
namespace power {
namespace impl {
namespace pixel {
// Collects statistics about Adaptive CPU.
// These are only Used during a dumpsys to improve bug report quality.
class AdaptiveCpuStats {
public:
AdaptiveCpuStats() : mTimeSource(std::make_unique<TimeSource>()) {}
AdaptiveCpuStats(std::unique_ptr<ITimeSource> timeSource)
: mTimeSource(std::move(timeSource)) {}
void RegisterStartRun();
void RegisterSuccessfulRun(ThrottleDecision previousThrottleDecision,
ThrottleDecision throttleDecision,
WorkDurationFeatures workDurationFeatures,
const AdaptiveCpuConfig &config);
void DumpToStream(std::ostream &stream) const;
private:
const std::unique_ptr<ITimeSource> mTimeSource;
size_t mNumStartedRuns = 0;
size_t mNumSuccessfulRuns = 0;
std::chrono::nanoseconds mStartTime;
std::chrono::nanoseconds mLastRunStartTime;
std::chrono::nanoseconds mLastRunSuccessTime;
std::chrono::nanoseconds mTotalRunDuration;
std::map<ThrottleDecision, size_t> mNumThrottles;
std::map<ThrottleDecision, std::chrono::nanoseconds> mThrottleDurations;
std::map<ThrottleDecision, size_t> mNumDurations;
std::map<ThrottleDecision, size_t> mNumMissedDeadlines;
static std::string FormatDuration(std::chrono::nanoseconds duration);
};
} // namespace pixel
} // namespace impl
} // namespace power
} // namespace hardware
} // namespace google
} // namespace aidl

View file

@ -0,0 +1,149 @@
/*
* Copyright (C) 2021 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 "powerhal-adaptivecpu"
#define ATRACE_TAG (ATRACE_TAG_POWER | ATRACE_TAG_HAL)
#include "CpuFrequencyReader.h"
#include <android-base/logging.h>
#include <inttypes.h>
#include <utils/Trace.h>
#include <fstream>
#include <memory>
#include <sstream>
using std::chrono_literals::operator""ms;
constexpr std::string_view kCpuPolicyDirectory("/sys/devices/system/cpu/cpufreq");
namespace aidl {
namespace google {
namespace hardware {
namespace power {
namespace impl {
namespace pixel {
bool CpuFrequencyReader::Init() {
ATRACE_CALL();
mCpuPolicyIds.clear();
if (!ReadCpuPolicyIds(&mCpuPolicyIds)) {
return false;
}
mPreviousCpuPolicyFrequencies.clear();
return ReadCpuPolicyFrequencies(&mPreviousCpuPolicyFrequencies);
}
bool CpuFrequencyReader::GetRecentCpuPolicyFrequencies(
std::vector<CpuPolicyAverageFrequency> *result) {
ATRACE_CALL();
std::map<uint32_t, std::map<uint64_t, std::chrono::milliseconds>> cpuPolicyFrequencies;
if (!ReadCpuPolicyFrequencies(&cpuPolicyFrequencies)) {
return false;
}
for (const auto &[policyId, cpuFrequencies] : cpuPolicyFrequencies) {
const auto &previousCpuFrequencies = mPreviousCpuPolicyFrequencies.find(policyId);
if (previousCpuFrequencies == mPreviousCpuPolicyFrequencies.end()) {
LOG(ERROR) << "Couldn't find policy " << policyId << " in previous frequencies";
return false;
}
uint64_t weightedFrequenciesSumHz = 0;
std::chrono::milliseconds timeSum = 0ms;
for (const auto &[frequencyHz, time] : cpuFrequencies) {
const auto &previousCpuFrequency = previousCpuFrequencies->second.find(frequencyHz);
if (previousCpuFrequency == previousCpuFrequencies->second.end()) {
LOG(ERROR) << "Couldn't find frequency " << frequencyHz
<< " in previous frequencies";
return false;
}
const std::chrono::milliseconds recentTime = time - previousCpuFrequency->second;
weightedFrequenciesSumHz += frequencyHz * recentTime.count();
timeSum += recentTime;
}
const uint64_t averageFrequencyHz =
timeSum != 0ms ? weightedFrequenciesSumHz / timeSum.count() : 0;
result->push_back({.policyId = policyId, .averageFrequencyHz = averageFrequencyHz});
}
mPreviousCpuPolicyFrequencies = cpuPolicyFrequencies;
return true;
}
std::map<uint32_t, std::map<uint64_t, std::chrono::milliseconds>>
CpuFrequencyReader::GetPreviousCpuPolicyFrequencies() const {
return mPreviousCpuPolicyFrequencies;
}
bool CpuFrequencyReader::ReadCpuPolicyFrequencies(
std::map<uint32_t, std::map<uint64_t, std::chrono::milliseconds>> *result) {
ATRACE_CALL();
for (const uint32_t cpuPolicyId : mCpuPolicyIds) {
std::stringstream timeInStatePath;
timeInStatePath << "/sys/devices/system/cpu/cpufreq/policy" << cpuPolicyId
<< "/stats/time_in_state";
std::unique_ptr<std::istream> timeInStateFile;
if (!mFilesystem->ReadFileStream(timeInStatePath.str(), &timeInStateFile)) {
return false;
}
std::map<uint64_t, std::chrono::milliseconds> cpuFrequencies;
std::string timeInStateLine;
while (std::getline(*timeInStateFile, timeInStateLine)) {
// Time format in time_in_state is 10s of milliseconds:
// https://www.kernel.org/doc/Documentation/cpu-freq/cpufreq-stats.txt
uint64_t frequencyHz, time10Ms;
if (std::sscanf(timeInStateLine.c_str(), "%" PRIu64 " %" PRIu64 "\n", &frequencyHz,
&time10Ms) != 2) {
LOG(ERROR) << "Failed to parse time_in_state line: " << timeInStateLine;
return false;
}
cpuFrequencies[frequencyHz] = time10Ms * 10ms;
}
if (cpuFrequencies.size() > 500) {
LOG(ERROR) << "Found " << cpuFrequencies.size() << " frequencies for policy "
<< cpuPolicyId << ", aborting";
return false;
}
(*result)[cpuPolicyId] = cpuFrequencies;
}
return true;
}
bool CpuFrequencyReader::ReadCpuPolicyIds(std::vector<uint32_t> *result) const {
ATRACE_CALL();
std::vector<std::string> entries;
if (!mFilesystem->ListDirectory(kCpuPolicyDirectory.data(), &entries)) {
return false;
}
for (const auto &entry : entries) {
uint32_t cpuPolicyId;
if (!sscanf(entry.c_str(), "policy%d", &cpuPolicyId)) {
continue;
}
result->push_back(cpuPolicyId);
}
// Sort the list, so that getRecentCpuPolicyFrequencies always returns frequencies sorted by
// policy ID.
std::sort(result->begin(), result->end());
return true;
}
} // namespace pixel
} // namespace impl
} // namespace power
} // namespace hardware
} // namespace google
} // namespace aidl

View file

@ -0,0 +1,91 @@
#pragma once
/*
* Copyright (C) 2021 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 <chrono>
#include <map>
#include <ostream>
#include <set>
#include <vector>
#include "IFilesystem.h"
#include "RealFilesystem.h"
namespace aidl {
namespace google {
namespace hardware {
namespace power {
namespace impl {
namespace pixel {
struct CpuPolicyAverageFrequency {
const uint32_t policyId;
const uint64_t averageFrequencyHz;
bool operator==(const CpuPolicyAverageFrequency &other) const {
return policyId == other.policyId && averageFrequencyHz == other.averageFrequencyHz;
}
};
class CpuFrequencyReader {
public:
CpuFrequencyReader() : mFilesystem(std::make_unique<RealFilesystem>()) {}
CpuFrequencyReader(std::unique_ptr<IFilesystem> filesystem)
: mFilesystem(std::move(filesystem)) {}
// Initialize reading, must be done before calling other methods.
// Work is not done in constructor as it accesses files.
// Returns true on success.
bool Init();
// Gets the average frequency each CPU policy was using, since this method was last called.
// Results are returned sorted by policyId.
// Returns true on success.
bool GetRecentCpuPolicyFrequencies(std::vector<CpuPolicyAverageFrequency> *result);
// The most recently read frequencies for each CPU policy. See readCpuPolicyFrequencies for type
// explanation. Used for dumping to bug reports.
std::map<uint32_t, std::map<uint64_t, std::chrono::milliseconds>>
GetPreviousCpuPolicyFrequencies() const;
private:
// CPU policy IDs read from /sys. Initialized in #init(). Sorted ascending.
std::vector<uint32_t> mCpuPolicyIds;
// The CPU frequencies when #getRecentCpuPolicyFrequencies was last called (or #init if it has
// not been called yet).
// See readCpuPolicyFrequencies for explanation of type.
std::map<uint32_t, std::map<uint64_t, std::chrono::milliseconds>> mPreviousCpuPolicyFrequencies;
const std::unique_ptr<IFilesystem> mFilesystem;
// Reads, from the /sys filesystem, the CPU frequencies used by each policy.
// - The outer map's key is the CPU policy ID.
// - The inner map's key is the CPU frequency in Hz.
// - The inner map's value is the time the policy has been running at that frequency, aggregated
// since boot.
// Returns true on success.
bool ReadCpuPolicyFrequencies(
std::map<uint32_t, std::map<uint64_t, std::chrono::milliseconds>> *result);
bool ReadCpuPolicyIds(std::vector<uint32_t> *result) const;
};
} // namespace pixel
} // namespace impl
} // namespace power
} // namespace hardware
} // namespace google
} // namespace aidl

View file

@ -0,0 +1,132 @@
/*
* Copyright (C) 2021 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 "powerhal-adaptivecpu"
#define ATRACE_TAG (ATRACE_TAG_POWER | ATRACE_TAG_HAL)
#include "CpuLoadReaderProcStat.h"
#include <android-base/logging.h>
#include <inttypes.h>
#include <utils/Trace.h>
#include <array>
#include <fstream>
#include <sstream>
#include <string>
namespace aidl {
namespace google {
namespace hardware {
namespace power {
namespace impl {
namespace pixel {
bool CpuLoadReaderProcStat::Init() {
mPreviousCpuTimes.clear();
return ReadCpuTimes(&mPreviousCpuTimes);
}
bool CpuLoadReaderProcStat::GetRecentCpuLoads(
std::array<double, NUM_CPU_CORES> *cpuCoreIdleTimesPercentage) {
ATRACE_CALL();
if (cpuCoreIdleTimesPercentage == nullptr) {
LOG(ERROR) << "Got nullptr output in getRecentCpuLoads";
return false;
}
std::map<uint32_t, CpuTime> cpuTimes;
if (!ReadCpuTimes(&cpuTimes)) {
return false;
}
if (cpuTimes.empty()) {
LOG(ERROR) << "Failed to find any CPU times";
return false;
}
for (const auto &[cpuId, cpuTime] : cpuTimes) {
const auto previousCpuTime = mPreviousCpuTimes.find(cpuId);
if (previousCpuTime == mPreviousCpuTimes.end()) {
LOG(ERROR) << "Couldn't find CPU " << cpuId << " in previous CPU times";
return false;
}
const auto recentIdleTimeMs = cpuTime.idleTimeMs - previousCpuTime->second.idleTimeMs;
const auto recentTotalTimeMs = cpuTime.totalTimeMs - previousCpuTime->second.totalTimeMs;
if (recentIdleTimeMs > recentTotalTimeMs) {
LOG(ERROR) << "Found more recent idle time than total time: idle=" << recentIdleTimeMs
<< ", total=" << recentTotalTimeMs;
return false;
}
const double idleTimePercentage =
static_cast<double>(recentIdleTimeMs) / (recentTotalTimeMs);
LOG(VERBOSE) << "Read CPU idle time: cpuId=" << cpuId
<< ", idleTimePercentage=" << idleTimePercentage;
(*cpuCoreIdleTimesPercentage)[cpuId] = idleTimePercentage;
}
mPreviousCpuTimes = cpuTimes;
return true;
}
bool CpuLoadReaderProcStat::ReadCpuTimes(std::map<uint32_t, CpuTime> *result) {
ATRACE_CALL();
std::unique_ptr<std::istream> file;
if (!mFilesystem->ReadFileStream("/proc/stat", &file)) {
return false;
}
std::string line;
ATRACE_BEGIN("loop");
while (std::getline(*file, line)) {
ATRACE_NAME("parse");
uint32_t cpuId;
// Times reported when the CPU is active.
uint64_t user, nice, system, irq, softIrq, steal, guest, guestNice;
// Times reported when the CPU is idle.
uint64_t idle, ioWait;
// Order & values taken from `fs/proc/stat.c`.
if (std::sscanf(line.c_str(),
"cpu%d %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64
" %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64 " ",
&cpuId, &user, &nice, &system, &idle, &ioWait, &irq, &softIrq, &steal,
&guest, &guestNice) != 11) {
continue;
}
uint64_t idleTimeJiffies = idle + ioWait;
uint64_t totalTimeJiffies =
user + nice + system + irq + softIrq + steal + guest + guestNice + idleTimeJiffies;
(*result)[cpuId] = {.idleTimeMs = JiffiesToMs(idleTimeJiffies),
.totalTimeMs = JiffiesToMs(totalTimeJiffies)};
}
ATRACE_END();
return true;
}
void CpuLoadReaderProcStat::DumpToStream(std::stringstream &stream) const {
stream << "CPU loads from /proc/stat:\n";
for (const auto &[cpuId, cpuTime] : mPreviousCpuTimes) {
stream << "- CPU=" << cpuId << ", idleTime=" << cpuTime.idleTimeMs
<< "ms, totalTime=" << cpuTime.totalTimeMs << "ms\n";
}
}
uint64_t CpuLoadReaderProcStat::JiffiesToMs(uint64_t jiffies) {
return (jiffies * 1000) / sysconf(_SC_CLK_TCK);
}
} // namespace pixel
} // namespace impl
} // namespace power
} // namespace hardware
} // namespace google
} // namespace aidl

View file

@ -0,0 +1,63 @@
#pragma once
/*
* Copyright (C) 2021 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 <map>
#include "ICpuLoadReader.h"
#include "IFilesystem.h"
#include "RealFilesystem.h"
namespace aidl {
namespace google {
namespace hardware {
namespace power {
namespace impl {
namespace pixel {
struct CpuTime {
uint64_t idleTimeMs;
uint64_t totalTimeMs;
};
// Reads CPU idle stats from /proc/stat.
class CpuLoadReaderProcStat : public ICpuLoadReader {
public:
CpuLoadReaderProcStat() : mFilesystem(std::make_unique<RealFilesystem>()) {}
CpuLoadReaderProcStat(std::unique_ptr<IFilesystem> filesystem)
: mFilesystem(std::move(filesystem)) {}
bool Init() override;
bool GetRecentCpuLoads(std::array<double, NUM_CPU_CORES> *cpuCoreIdleTimesPercentage) override;
void DumpToStream(std::stringstream &stream) const override;
private:
std::map<uint32_t, CpuTime> mPreviousCpuTimes;
const std::unique_ptr<IFilesystem> mFilesystem;
bool ReadCpuTimes(std::map<uint32_t, CpuTime> *result);
// Converts jiffies to milliseconds. Jiffies is the granularity the kernel reports times in,
// including the timings in CPU statistics.
static uint64_t JiffiesToMs(uint64_t jiffies);
};
} // namespace pixel
} // namespace impl
} // namespace power
} // namespace hardware
} // namespace google
} // namespace aidl

View file

@ -0,0 +1,151 @@
/*
* Copyright (C) 2021 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 "powerhal-adaptivecpu"
#define ATRACE_TAG (ATRACE_TAG_POWER | ATRACE_TAG_HAL)
#include "CpuLoadReaderSysDevices.h"
#include <android-base/logging.h>
#include <inttypes.h>
#include <utils/Trace.h>
#include <fstream>
#include <sstream>
#include <string>
namespace aidl {
namespace google {
namespace hardware {
namespace power {
namespace impl {
namespace pixel {
std::chrono::nanoseconds getKernelTime() {
timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
return std::chrono::nanoseconds(ts.tv_sec * 1000000000UL + ts.tv_nsec);
}
bool CpuLoadReaderSysDevices::Init() {
mIdleStateNames.clear();
if (!ReadIdleStateNames(&mIdleStateNames)) {
return false;
}
return ReadCpuTimes(&mPreviousCpuTimes);
}
bool CpuLoadReaderSysDevices::GetRecentCpuLoads(
std::array<double, NUM_CPU_CORES> *cpuCoreIdleTimesPercentage) {
ATRACE_CALL();
if (cpuCoreIdleTimesPercentage == nullptr) {
LOG(ERROR) << "Got nullptr output in getRecentCpuLoads";
return false;
}
std::array<CpuTime, NUM_CPU_CORES> cpuTimes;
if (!ReadCpuTimes(&cpuTimes)) {
return false;
}
if (cpuTimes.empty()) {
LOG(ERROR) << "Failed to find any CPU times";
return false;
}
for (size_t cpuId = 0; cpuId < NUM_CPU_CORES; cpuId++) {
const auto cpuTime = cpuTimes[cpuId];
const auto previousCpuTime = mPreviousCpuTimes[cpuId];
auto recentIdleTime = cpuTime.idleTime - previousCpuTime.idleTime;
const auto recentTotalTime = cpuTime.totalTime - previousCpuTime.totalTime;
if (recentIdleTime > recentTotalTime) {
// This happens occasionally, as we use the idle time from the kernel, and the current
// time from userspace.
recentIdleTime = recentTotalTime;
}
const double idleTimePercentage =
static_cast<double>(recentIdleTime.count()) / recentTotalTime.count();
(*cpuCoreIdleTimesPercentage)[cpuId] = idleTimePercentage;
}
mPreviousCpuTimes = cpuTimes;
return true;
}
void CpuLoadReaderSysDevices::DumpToStream(std::stringstream &stream) const {
stream << "CPU loads from /sys/devices/system/cpu/cpuN/cpuidle:\n";
for (size_t cpuId = 0; cpuId < NUM_CPU_CORES; cpuId++) {
stream << "- CPU=" << cpuId << ", idleTime=" << mPreviousCpuTimes[cpuId].idleTime.count()
<< "ms, totalTime=" << mPreviousCpuTimes[cpuId].totalTime.count() << "ms\n";
}
}
bool CpuLoadReaderSysDevices::ReadCpuTimes(std::array<CpuTime, NUM_CPU_CORES> *result) const {
ATRACE_CALL();
const auto totalTime = mTimeSource->GetTime();
for (size_t cpuId = 0; cpuId < NUM_CPU_CORES; cpuId++) {
std::chrono::microseconds idleTime;
for (const auto &idleStateName : mIdleStateNames) {
std::stringstream cpuIdlePath;
cpuIdlePath << "/sys/devices/system/cpu/"
<< "cpu" << cpuId << "/cpuidle/" << idleStateName << "/time";
std::unique_ptr<std::istream> file;
if (!mFilesystem->ReadFileStream(cpuIdlePath.str(), &file)) {
return false;
}
// Times are reported in microseconds:
// https://www.kernel.org/doc/Documentation/cpuidle/sysfs.txt
std::string idleTimeUs(std::istreambuf_iterator<char>(*file), {});
idleTime += std::chrono::microseconds(std::atoi(idleTimeUs.c_str()));
}
(*result)[cpuId] = {
.idleTime = idleTime,
.totalTime = std::chrono::duration_cast<std::chrono::microseconds>(totalTime),
};
}
return true;
}
bool CpuLoadReaderSysDevices::ReadIdleStateNames(std::vector<std::string> *result) const {
std::vector<std::string> idleStateNames;
if (!mFilesystem->ListDirectory("/sys/devices/system/cpu/cpu0/cpuidle", &idleStateNames)) {
return false;
}
for (const auto &idleStateName : idleStateNames) {
if (idleStateName.length() == 0 || idleStateName[0] == '.') {
continue;
}
std::vector<std::string> files;
if (!mFilesystem->ListDirectory(
std::string("/sys/devices/system/cpu/cpu0/cpuidle/") + idleStateName, &files)) {
return false;
}
if (std::find(files.begin(), files.end(), "time") == files.end()) {
continue;
}
result->push_back(idleStateName);
}
if (idleStateNames.empty()) {
LOG(ERROR) << "Found no idle state names";
return false;
}
return true;
}
} // namespace pixel
} // namespace impl
} // namespace power
} // namespace hardware
} // namespace google
} // namespace aidl

View file

@ -0,0 +1,71 @@
#pragma once
/*
* Copyright (C) 2021 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 <chrono>
#include <map>
#include "ICpuLoadReader.h"
#include "IFilesystem.h"
#include "ITimeSource.h"
#include "Model.h"
#include "RealFilesystem.h"
#include "TimeSource.h"
namespace aidl {
namespace google {
namespace hardware {
namespace power {
namespace impl {
namespace pixel {
struct CpuTime {
std::chrono::microseconds idleTime;
std::chrono::microseconds totalTime;
};
// Reads CPU idle stats from /sys/devices/system/cpu/cpuN/cpuidle.
class CpuLoadReaderSysDevices : public ICpuLoadReader {
public:
CpuLoadReaderSysDevices()
: mFilesystem(std::make_unique<RealFilesystem>()),
mTimeSource(std::make_unique<TimeSource>()) {}
CpuLoadReaderSysDevices(std::unique_ptr<IFilesystem> filesystem,
std::unique_ptr<ITimeSource> timeSource)
: mFilesystem(std::move(filesystem)), mTimeSource(std::move(timeSource)) {}
bool Init() override;
bool GetRecentCpuLoads(std::array<double, NUM_CPU_CORES> *cpuCoreIdleTimesPercentage) override;
void DumpToStream(std::stringstream &stream) const override;
private:
const std::unique_ptr<IFilesystem> mFilesystem;
const std::unique_ptr<ITimeSource> mTimeSource;
std::array<CpuTime, NUM_CPU_CORES> mPreviousCpuTimes;
std::vector<std::string> mIdleStateNames;
bool ReadCpuTimes(std::array<CpuTime, NUM_CPU_CORES> *result) const;
bool ReadIdleStateNames(std::vector<std::string> *result) const;
};
} // namespace pixel
} // namespace impl
} // namespace power
} // namespace hardware
} // namespace google
} // namespace aidl

View file

@ -0,0 +1,55 @@
/*
* Copyright (C) 2022 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 "powerhal-libperfmgr"
#define ATRACE_TAG (ATRACE_TAG_POWER | ATRACE_TAG_HAL)
#include "Device.h"
#include <android-base/logging.h>
#include <android-base/properties.h>
#include <utils/Trace.h>
namespace aidl {
namespace google {
namespace hardware {
namespace power {
namespace impl {
namespace pixel {
Device ReadDevice() {
ATRACE_CALL();
const std::string deviceProperty = ::android::base::GetProperty("ro.product.device", "");
Device device;
if (deviceProperty == "raven") {
device = Device::RAVEN;
} else if (deviceProperty == "oriole") {
device = Device::ORIOLE;
} else {
LOG(WARNING) << "Failed to parse device property, setting to UNKNOWN: " << deviceProperty;
device = Device::UNKNOWN;
}
LOG(DEBUG) << "Parsed device: deviceProperty=" << deviceProperty
<< ", device=" << static_cast<uint32_t>(device);
return device;
}
} // namespace pixel
} // namespace impl
} // namespace power
} // namespace hardware
} // namespace google
} // namespace aidl

View file

@ -0,0 +1,35 @@
/*
* Copyright (C) 2022 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.
*/
#pragma once
namespace aidl {
namespace google {
namespace hardware {
namespace power {
namespace impl {
namespace pixel {
enum class Device { UNKNOWN = 0, RAVEN = 1, ORIOLE = 2 };
Device ReadDevice();
} // namespace pixel
} // namespace impl
} // namespace power
} // namespace hardware
} // namespace google
} // namespace aidl

View file

@ -0,0 +1,49 @@
#pragma once
/*
* Copyright (C) 2021 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 <array>
#include <sstream>
#include "Model.h"
namespace aidl {
namespace google {
namespace hardware {
namespace power {
namespace impl {
namespace pixel {
class ICpuLoadReader {
public:
// Initialize reading, must be done before calling other methods.
// Work is not done in constructor as it accesses files.
virtual bool Init() = 0;
// Get the load of each CPU, since the last time this method was called.
virtual bool GetRecentCpuLoads(
std::array<double, NUM_CPU_CORES> *cpuCoreIdleTimesPercentage) = 0;
// Dump internal state to a string stream. Used for dumpsys.
virtual void DumpToStream(std::stringstream &stream) const = 0;
virtual ~ICpuLoadReader() {}
};
} // namespace pixel
} // namespace impl
} // namespace power
} // namespace hardware
} // namespace google
} // namespace aidl

View file

@ -0,0 +1,49 @@
#pragma once
/*
* Copyright (C) 2021 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 <chrono>
#include <memory>
#include <ostream>
#include <vector>
namespace aidl {
namespace google {
namespace hardware {
namespace power {
namespace impl {
namespace pixel {
// Abstracted so we can mock in tests.
class IFilesystem {
public:
virtual ~IFilesystem() {}
virtual bool ListDirectory(const std::string &path, std::vector<std::string> *result) const = 0;
virtual bool ReadFileStream(const std::string &path,
std::unique_ptr<std::istream> *result) const = 0;
// Resets the file stream, so that the next read will read from the beginning.
// This function exists in IFilesystem rather than using istream::seekg directly. This is
// so we can mock this function in tests, allowing us to return different data on reset.
virtual bool ResetFileStream(const std::unique_ptr<std::istream> &fileStream) const = 0;
};
} // namespace pixel
} // namespace impl
} // namespace power
} // namespace hardware
} // namespace google
} // namespace aidl

View file

@ -0,0 +1,41 @@
#pragma once
/*
* Copyright (C) 2021 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 <chrono>
namespace aidl {
namespace google {
namespace hardware {
namespace power {
namespace impl {
namespace pixel {
// Abstracted so we can mock in tests.
class ITimeSource {
public:
virtual ~ITimeSource() {}
virtual std::chrono::nanoseconds GetTime() const = 0;
virtual std::chrono::nanoseconds GetKernelTime() const = 0;
};
} // namespace pixel
} // namespace impl
} // namespace power
} // namespace hardware
} // namespace google
} // namespace aidl

View file

@ -0,0 +1,135 @@
/*
* Copyright (C) 2022 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 "powerhal-adaptivecpu"
#define ATRACE_TAG (ATRACE_TAG_POWER | ATRACE_TAG_HAL)
#include "KernelCpuFeatureReader.h"
#include <android-base/logging.h>
#include <utils/Trace.h>
#include <ostream>
namespace aidl {
namespace google {
namespace hardware {
namespace power {
namespace impl {
namespace pixel {
constexpr std::string_view kKernelFilePath("/proc/vendor_sched/acpu_stats");
constexpr size_t kReadBufferSize = sizeof(acpu_stats) * NUM_CPU_CORES;
bool KernelCpuFeatureReader::Init() {
ATRACE_CALL();
if (!OpenStatsFile(&mStatsFile)) {
return false;
}
return ReadStats(&mPreviousStats, &mPreviousReadTime);
}
bool KernelCpuFeatureReader::GetRecentCpuFeatures(
std::array<double, NUM_CPU_POLICIES> *cpuPolicyAverageFrequencyHz,
std::array<double, NUM_CPU_CORES> *cpuCoreIdleTimesPercentage) {
ATRACE_CALL();
std::array<acpu_stats, NUM_CPU_CORES> stats;
std::chrono::nanoseconds readTime;
if (!ReadStats(&stats, &readTime)) {
return false;
}
const std::chrono::nanoseconds timeDelta = readTime - mPreviousReadTime;
for (size_t i = 0; i < NUM_CPU_POLICIES; i++) {
// acpu_stats has data per-CPU, but frequency data is equivalent for all CPUs in a policy.
// So, we only read the first CPU in each policy.
const size_t statsIdx = CPU_POLICY_INDICES[i];
if (stats[statsIdx].weighted_sum_freq < mPreviousStats[statsIdx].weighted_sum_freq) {
LOG(WARNING) << "New weighted_sum_freq is less than old: new="
<< stats[statsIdx].weighted_sum_freq
<< ", old=" << mPreviousStats[statsIdx].weighted_sum_freq;
mPreviousStats[statsIdx].weighted_sum_freq = stats[statsIdx].weighted_sum_freq;
}
(*cpuPolicyAverageFrequencyHz)[i] =
static_cast<double>(stats[statsIdx].weighted_sum_freq -
mPreviousStats[statsIdx].weighted_sum_freq) /
timeDelta.count();
}
for (size_t i = 0; i < NUM_CPU_CORES; i++) {
if (stats[i].total_idle_time_ns < mPreviousStats[i].total_idle_time_ns) {
LOG(WARNING) << "New total_idle_time_ns is less than old: new="
<< stats[i].total_idle_time_ns
<< ", old=" << mPreviousStats[i].total_idle_time_ns;
mPreviousStats[i].total_idle_time_ns = stats[i].total_idle_time_ns;
}
(*cpuCoreIdleTimesPercentage)[i] =
static_cast<double>(stats[i].total_idle_time_ns -
mPreviousStats[i].total_idle_time_ns) /
timeDelta.count();
}
mPreviousStats = stats;
mPreviousReadTime = readTime;
return true;
}
bool KernelCpuFeatureReader::OpenStatsFile(std::unique_ptr<std::istream> *file) {
ATRACE_CALL();
return mFilesystem->ReadFileStream(kKernelFilePath.data(), file);
}
bool KernelCpuFeatureReader::ReadStats(std::array<acpu_stats, NUM_CPU_CORES> *stats,
std::chrono::nanoseconds *readTime) {
ATRACE_CALL();
*readTime = mTimeSource->GetKernelTime();
if (!mFilesystem->ResetFileStream(mStatsFile)) {
return false;
}
char buffer[kReadBufferSize];
{
ATRACE_NAME("read");
if (!mStatsFile->read(buffer, kReadBufferSize).good()) {
LOG(ERROR) << "Failed to read stats file";
return false;
}
}
const size_t bytesRead = mStatsFile->gcount();
if (bytesRead != kReadBufferSize) {
LOG(ERROR) << "Didn't read full data: expected=" << kReadBufferSize
<< ", actual=" << bytesRead;
return false;
}
const auto kernelStructs = reinterpret_cast<acpu_stats *>(buffer);
std::copy(kernelStructs, kernelStructs + NUM_CPU_CORES, stats->begin());
return true;
}
void KernelCpuFeatureReader::DumpToStream(std::ostream &stream) const {
ATRACE_CALL();
stream << "CPU features from acpu_stats:\n";
for (size_t i = 0; i < NUM_CPU_CORES; i++) {
stream << "- CPU " << i << ": weighted_sum_freq=" << mPreviousStats[i].weighted_sum_freq
<< ", total_idle_time_ns=" << mPreviousStats[i].total_idle_time_ns << "\n";
}
stream << "- Last read time: " << mPreviousReadTime.count() << "ns\n";
}
} // namespace pixel
} // namespace impl
} // namespace power
} // namespace hardware
} // namespace google
} // namespace aidl

View file

@ -0,0 +1,83 @@
/*
* Copyright (C) 2022 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.
*/
#pragma once
#include <array>
#include <ostream>
#include "IFilesystem.h"
#include "ITimeSource.h"
#include "Model.h"
#include "RealFilesystem.h"
#include "TimeSource.h"
namespace aidl {
namespace google {
namespace hardware {
namespace power {
namespace impl {
namespace pixel {
// Kernel <-> Userspace ABI for CPU features. See kernel/sched/acpu.c.
// Contains CPU statistics for a single CPU. The kernel reports an `acpu_stats` struct for each CPU
// on the system.
struct acpu_stats {
// Sum of the CPU frequencies that the CPU used, multiplied by how much time was spent in each
// frequency. Measured in ns*KHz. E.g.:
// 10ns at 100MHz, 2ns at 50MHz = 10*100,000 + 2*50,000 = 1,100,000
// This is used to calculate the average frequency the CPU was running at between two times:
// (new.weighted_sum_freq - old.weighted_sum_freq) / elapsed_time_ns
uint64_t weighted_sum_freq;
// The total time (in nanoseconds) that the CPU was idle.
// This is ued to calculate the percent of time the CPU was idle between two times:
// (new.total_idle_time_ns - old.total_idle_time_ns) / elapsed_time_ns
uint64_t total_idle_time_ns;
};
class KernelCpuFeatureReader {
public:
KernelCpuFeatureReader()
: mFilesystem(std::make_unique<RealFilesystem>()),
mTimeSource(std::make_unique<TimeSource>()) {}
KernelCpuFeatureReader(std::unique_ptr<IFilesystem> filesystem,
std::unique_ptr<ITimeSource> timeSource)
: mFilesystem(std::move(filesystem)), mTimeSource(std::move(timeSource)) {}
bool Init();
bool GetRecentCpuFeatures(std::array<double, NUM_CPU_POLICIES> *cpuPolicyAverageFrequencyHz,
std::array<double, NUM_CPU_CORES> *cpuCoreIdleTimesPercentage);
void DumpToStream(std::ostream &stream) const;
private:
const std::unique_ptr<IFilesystem> mFilesystem;
const std::unique_ptr<ITimeSource> mTimeSource;
// We only open the stats file once and reuse the file descriptor. We find this reduces
// ReadStats runtime by 2x.
std::unique_ptr<std::istream> mStatsFile;
std::array<acpu_stats, NUM_CPU_CORES> mPreviousStats;
std::chrono::nanoseconds mPreviousReadTime;
bool OpenStatsFile(std::unique_ptr<std::istream> *file);
bool ReadStats(std::array<acpu_stats, NUM_CPU_CORES> *stats,
std::chrono::nanoseconds *readTime);
};
} // namespace pixel
} // namespace impl
} // namespace power
} // namespace hardware
} // namespace google
} // namespace aidl

View file

@ -0,0 +1,106 @@
/*
* Copyright (C) 2021 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 "powerhal-adaptivecpu"
#define ATRACE_TAG (ATRACE_TAG_POWER | ATRACE_TAG_HAL)
#include "Model.h"
#include <android-base/logging.h>
#include <utils/Trace.h>
namespace aidl {
namespace google {
namespace hardware {
namespace power {
namespace impl {
namespace pixel {
bool ModelInput::SetCpuFreqiencies(
const std::vector<CpuPolicyAverageFrequency> &cpuPolicyAverageFrequencies) {
ATRACE_CALL();
if (cpuPolicyAverageFrequencies.size() != cpuPolicyAverageFrequencyHz.size()) {
LOG(ERROR) << "Received incorrect amount of CPU policy frequencies, expected "
<< cpuPolicyAverageFrequencyHz.size() << ", received "
<< cpuPolicyAverageFrequencies.size();
return false;
}
int32_t previousPolicyId = -1;
for (uint32_t i = 0; i < cpuPolicyAverageFrequencies.size(); i++) {
if (previousPolicyId >= static_cast<int32_t>(cpuPolicyAverageFrequencies[i].policyId)) {
LOG(ERROR) << "CPU frequencies weren't sorted by policy ID, found " << previousPolicyId
<< " " << cpuPolicyAverageFrequencies[i].policyId;
return false;
}
previousPolicyId = cpuPolicyAverageFrequencies[i].policyId;
cpuPolicyAverageFrequencyHz[i] = cpuPolicyAverageFrequencies[i].averageFrequencyHz;
}
return true;
}
void ModelInput::LogToAtrace() const {
if (!ATRACE_ENABLED()) {
return;
}
ATRACE_CALL();
for (int i = 0; i < cpuPolicyAverageFrequencyHz.size(); i++) {
ATRACE_INT((std::string("ModelInput_frequency_") + std::to_string(i)).c_str(),
static_cast<int>(cpuPolicyAverageFrequencyHz[i]));
}
for (int i = 0; i < cpuCoreIdleTimesPercentage.size(); i++) {
ATRACE_INT((std::string("ModelInput_idle_") + std::to_string(i)).c_str(),
static_cast<int>(cpuCoreIdleTimesPercentage[i] * 100));
}
ATRACE_INT("ModelInput_workDurations_averageDurationNs",
workDurationFeatures.averageDuration.count());
ATRACE_INT("ModelInput_workDurations_maxDurationNs", workDurationFeatures.maxDuration.count());
ATRACE_INT("ModelInput_workDurations_numMissedDeadlines",
workDurationFeatures.numMissedDeadlines);
ATRACE_INT("ModelInput_workDurations_numDurations", workDurationFeatures.numDurations);
ATRACE_INT("ModelInput_prevThrottle", (int)previousThrottleDecision);
ATRACE_INT("ModelInput_device", static_cast<int>(device));
}
ThrottleDecision Model::Run(const std::deque<ModelInput> &modelInputs,
const AdaptiveCpuConfig &config) {
ATRACE_CALL();
if (config.randomThrottleDecisionProbability > 0 &&
mShouldRandomThrottleDistribution(mGenerator) < config.randomThrottleDecisionProbability) {
std::uniform_int_distribution<uint32_t> optionDistribution(
0, config.randomThrottleOptions.size() - 1);
const ThrottleDecision throttleDecision =
config.randomThrottleOptions[optionDistribution(mGenerator)];
LOG(VERBOSE) << "Randomly overrided throttle decision: "
<< static_cast<uint32_t>(throttleDecision);
ATRACE_INT("AdaptiveCpu_randomThrottleDecision", static_cast<uint32_t>(throttleDecision));
return throttleDecision;
}
ATRACE_INT("AdaptiveCpu_randomThrottleDecision", -1);
return RunDecisionTree(modelInputs);
}
ThrottleDecision Model::RunDecisionTree(const std::deque<ModelInput> &modelInputs
__attribute__((unused))) {
ATRACE_CALL();
#include "models/model.inc"
}
} // namespace pixel
} // namespace impl
} // namespace power
} // namespace hardware
} // namespace google
} // namespace aidl

View file

@ -0,0 +1,88 @@
/*
* Copyright (C) 2021 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.
*/
#pragma once
#include <inttypes.h>
#include <array>
#include <chrono>
#include <deque>
#include <random>
#include <vector>
#include "AdaptiveCpuConfig.h"
#include "CpuFrequencyReader.h"
#include "Device.h"
#include "ThrottleDecision.h"
#include "WorkDurationProcessor.h"
namespace aidl {
namespace google {
namespace hardware {
namespace power {
namespace impl {
namespace pixel {
// Currently Adaptive CPU is targeted to only raven/oriole, so we can hardcode the CPU architecture.
// If we extend to other architectures, this will have to vary per-device or be dynamically loaded.
constexpr uint32_t NUM_CPU_CORES = 8;
constexpr uint32_t NUM_CPU_POLICIES = 3;
constexpr std::array<uint32_t, NUM_CPU_POLICIES> CPU_POLICY_INDICES{0, 4, 6};
struct ModelInput {
std::array<double, NUM_CPU_POLICIES> cpuPolicyAverageFrequencyHz;
std::array<double, NUM_CPU_CORES> cpuCoreIdleTimesPercentage;
WorkDurationFeatures workDurationFeatures;
ThrottleDecision previousThrottleDecision;
Device device;
bool SetCpuFreqiencies(
const std::vector<CpuPolicyAverageFrequency> &cpuPolicyAverageFrequencies);
void LogToAtrace() const;
bool operator==(const ModelInput &other) const {
return cpuPolicyAverageFrequencyHz == other.cpuPolicyAverageFrequencyHz &&
cpuCoreIdleTimesPercentage == other.cpuCoreIdleTimesPercentage &&
workDurationFeatures == other.workDurationFeatures &&
previousThrottleDecision == other.previousThrottleDecision;
}
};
class Model {
public:
Model()
: mShouldRandomThrottleDistribution(0, 1),
mRandomThrottleDistribution(static_cast<uint32_t>(ThrottleDecision::FIRST),
static_cast<uint32_t>(ThrottleDecision::LAST)) {}
ThrottleDecision Run(const std::deque<ModelInput> &modelInputs,
const AdaptiveCpuConfig &config);
private:
std::default_random_engine mGenerator;
std::uniform_real_distribution<double> mShouldRandomThrottleDistribution;
std::uniform_int_distribution<uint32_t> mRandomThrottleDistribution;
ThrottleDecision RunDecisionTree(const std::deque<ModelInput> &modelInputs);
};
} // namespace pixel
} // namespace impl
} // namespace power
} // namespace hardware
} // namespace google
} // namespace aidl

View file

@ -0,0 +1,76 @@
/*
* Copyright (C) 2021 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 "powerhal-adaptivecpu"
#define ATRACE_TAG (ATRACE_TAG_POWER | ATRACE_TAG_HAL)
#include "RealFilesystem.h"
#include <android-base/logging.h>
#include <dirent.h>
#include <utils/Trace.h>
#include <fstream>
#include <istream>
namespace aidl {
namespace google {
namespace hardware {
namespace power {
namespace impl {
namespace pixel {
bool RealFilesystem::ListDirectory(const std::string &path,
std::vector<std::string> *result) const {
ATRACE_CALL();
// We can't use std::filesystem, see aosp/894015 & b/175635923.
auto dir = std::unique_ptr<DIR, decltype(&closedir)>{opendir(path.c_str()), closedir};
if (!dir) {
LOG(ERROR) << "Failed to open directory " << path;
return false;
}
dirent *entry;
while ((entry = readdir(&*dir)) != nullptr) {
result->emplace_back(entry->d_name);
}
return true;
}
bool RealFilesystem::ReadFileStream(const std::string &path,
std::unique_ptr<std::istream> *result) const {
ATRACE_CALL();
*result = std::make_unique<std::ifstream>(path);
if ((*result)->fail()) {
LOG(ERROR) << "Failed to read file stream: " << path;
return false;
}
return true;
}
bool RealFilesystem::ResetFileStream(const std::unique_ptr<std::istream> &fileStream) const {
if (fileStream->seekg(0).bad()) {
LOG(ERROR) << "Failed to reset file stream";
return false;
}
return true;
}
} // namespace pixel
} // namespace impl
} // namespace power
} // namespace hardware
} // namespace google
} // namespace aidl

View file

@ -0,0 +1,46 @@
#pragma once
/*
* Copyright (C) 2021 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 <memory>
#include <ostream>
#include <vector>
#include "IFilesystem.h"
namespace aidl {
namespace google {
namespace hardware {
namespace power {
namespace impl {
namespace pixel {
class RealFilesystem : public IFilesystem {
public:
virtual ~RealFilesystem() {}
bool ListDirectory(const std::string &path, std::vector<std::string> *result) const override;
bool ReadFileStream(const std::string &path,
std::unique_ptr<std::istream> *result) const override;
bool ResetFileStream(const std::unique_ptr<std::istream> &fileStream) const override;
};
} // namespace pixel
} // namespace impl
} // namespace power
} // namespace hardware
} // namespace google
} // namespace aidl

View file

@ -0,0 +1,48 @@
/*
* Copyright (C) 2022 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 "ThrottleDecision.h"
namespace aidl {
namespace google {
namespace hardware {
namespace power {
namespace impl {
namespace pixel {
std::string ThrottleString(ThrottleDecision throttleDecision) {
switch (throttleDecision) {
case ThrottleDecision::NO_THROTTLE:
return "NO_THROTTLE";
case ThrottleDecision::THROTTLE_60:
return "THROTTLE_60";
case ThrottleDecision::THROTTLE_70:
return "THROTTLE_70";
case ThrottleDecision::THROTTLE_80:
return "THROTTLE_80";
case ThrottleDecision::THROTTLE_90:
return "THROTTLE_90";
default:
return "unknown";
}
}
} // namespace pixel
} // namespace impl
} // namespace power
} // namespace hardware
} // namespace google
} // namespace aidl

View file

@ -0,0 +1,62 @@
/*
* Copyright (C) 2022 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.
*/
#pragma once
#include <string>
#include <unordered_map>
#include <vector>
namespace aidl {
namespace google {
namespace hardware {
namespace power {
namespace impl {
namespace pixel {
enum class ThrottleDecision {
NO_THROTTLE = 0,
THROTTLE_50 = 1,
THROTTLE_60 = 2,
THROTTLE_70 = 3,
THROTTLE_80 = 4,
THROTTLE_90 = 5,
FIRST = NO_THROTTLE,
LAST = THROTTLE_90,
};
std::string ThrottleString(ThrottleDecision throttleDecision);
static const std::unordered_map<ThrottleDecision, std::vector<std::string>>
THROTTLE_DECISION_TO_HINT_NAMES = {
{ThrottleDecision::NO_THROTTLE, {}},
{ThrottleDecision::THROTTLE_50,
{"LOW_POWER_LITTLE_CLUSTER_50", "LOW_POWER_MID_CLUSTER_50", "LOW_POWER_CPU_50"}},
{ThrottleDecision::THROTTLE_60,
{"LOW_POWER_LITTLE_CLUSTER_60", "LOW_POWER_MID_CLUSTER_60", "LOW_POWER_CPU_60"}},
{ThrottleDecision::THROTTLE_70,
{"LOW_POWER_LITTLE_CLUSTER_70", "LOW_POWER_MID_CLUSTER_70", "LOW_POWER_CPU_70"}},
{ThrottleDecision::THROTTLE_80,
{"LOW_POWER_LITTLE_CLUSTER_80", "LOW_POWER_MID_CLUSTER_80", "LOW_POWER_CPU_80"}},
{ThrottleDecision::THROTTLE_90,
{"LOW_POWER_LITTLE_CLUSTER_90", "LOW_POWER_MID_CLUSTER_90", "LOW_POWER_CPU_90"}}};
} // namespace pixel
} // namespace impl
} // namespace power
} // namespace hardware
} // namespace google
} // namespace aidl

View file

@ -0,0 +1,48 @@
/*
* Copyright (C) 2021 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 ATRACE_TAG (ATRACE_TAG_POWER | ATRACE_TAG_HAL)
#include "TimeSource.h"
#include <utils/Trace.h>
namespace aidl {
namespace google {
namespace hardware {
namespace power {
namespace impl {
namespace pixel {
std::chrono::nanoseconds TimeSource::GetTime() const {
ATRACE_CALL();
return std::chrono::duration_cast<std::chrono::nanoseconds>(
std::chrono::system_clock::now().time_since_epoch());
}
std::chrono::nanoseconds TimeSource::GetKernelTime() const {
ATRACE_CALL();
timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
return std::chrono::nanoseconds(ts.tv_sec * 1000000000UL + ts.tv_nsec);
}
} // namespace pixel
} // namespace impl
} // namespace power
} // namespace hardware
} // namespace google
} // namespace aidl

View file

@ -0,0 +1,40 @@
#pragma once
/*
* Copyright (C) 2021 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 "ITimeSource.h"
namespace aidl {
namespace google {
namespace hardware {
namespace power {
namespace impl {
namespace pixel {
class TimeSource : public ITimeSource {
public:
~TimeSource() override {}
std::chrono::nanoseconds GetTime() const override;
std::chrono::nanoseconds GetKernelTime() const override;
};
} // namespace pixel
} // namespace impl
} // namespace power
} // namespace hardware
} // namespace google
} // namespace aidl

View file

@ -0,0 +1,120 @@
/*
* Copyright (C) 2021 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 "powerhal-adaptivecpu"
#define ATRACE_TAG (ATRACE_TAG_POWER | ATRACE_TAG_HAL)
#include "WorkDurationProcessor.h"
#include <android-base/logging.h>
#include <utils/Trace.h>
using std::chrono_literals::operator""ms;
using std::chrono_literals::operator""ns;
// The standard target duration, based on 60 FPS. Durations submitted with different targets are
// normalized against this target. For example, a duration that was at 80% of its target will be
// scaled to 0.8 * kNormalTargetDuration.
constexpr std::chrono::nanoseconds kNormalTargetDuration = 16666666ns;
// All durations shorter than this are ignored.
constexpr std::chrono::nanoseconds kMinDuration = 0ns;
// All durations longer than this are ignored.
constexpr std::chrono::nanoseconds kMaxDuration = 600 * kNormalTargetDuration;
// If we haven't processed a lot of batches, stop accepting new ones. In cases where the processing
// thread has crashed, but the reporting thread is still reporting, this prevents consuming large
// amounts of memory.
// TODO(b/213160386): Move to AdaptiveCpuConfig.
constexpr size_t kMaxUnprocessedBatches = 1000;
namespace aidl {
namespace google {
namespace hardware {
namespace power {
namespace impl {
namespace pixel {
bool WorkDurationProcessor::ReportWorkDurations(const std::vector<WorkDuration> &workDurations,
std::chrono::nanoseconds targetDuration) {
ATRACE_CALL();
LOG(VERBOSE) << "Received " << workDurations.size() << " work durations with target "
<< targetDuration.count() << "ns";
std::unique_lock<std::mutex> lock(mMutex);
if (mWorkDurationBatches.size() >= kMaxUnprocessedBatches) {
LOG(ERROR) << "Adaptive CPU isn't processing work durations fast enough";
mWorkDurationBatches.clear();
return false;
}
mWorkDurationBatches.emplace_back(workDurations, targetDuration);
return true;
}
WorkDurationFeatures WorkDurationProcessor::GetFeatures() {
ATRACE_CALL();
std::vector<WorkDurationBatch> workDurationBatches;
{
ATRACE_NAME("lock");
std::unique_lock<std::mutex> lock(mMutex);
mWorkDurationBatches.swap(workDurationBatches);
}
std::chrono::nanoseconds durationsSum = 0ns;
std::chrono::nanoseconds maxDuration = 0ns;
uint32_t numMissedDeadlines = 0;
uint32_t numDurations = 0;
for (const WorkDurationBatch &batch : workDurationBatches) {
for (const WorkDuration workDuration : batch.workDurations) {
std::chrono::nanoseconds duration(workDuration.durationNanos);
if (duration < kMinDuration || duration > kMaxDuration) {
continue;
}
// Normalise the duration and add it to the total.
// kMaxDuration * kStandardTarget.count() fits comfortably within int64_t.
std::chrono::nanoseconds durationNormalized =
(duration * kNormalTargetDuration.count()) / batch.targetDuration.count();
durationsSum += durationNormalized;
maxDuration = std::max(maxDuration, durationNormalized);
if (duration > batch.targetDuration) {
++numMissedDeadlines;
}
++numDurations;
}
}
std::chrono::nanoseconds averageDuration = durationsSum / numDurations;
return {
.averageDuration = averageDuration,
.maxDuration = maxDuration,
.numMissedDeadlines = numMissedDeadlines,
.numDurations = numDurations,
};
}
bool WorkDurationProcessor::HasWorkDurations() {
std::unique_lock<std::mutex> lock(mMutex);
return !mWorkDurationBatches.empty();
}
} // namespace pixel
} // namespace impl
} // namespace power
} // namespace hardware
} // namespace google
} // namespace aidl

View file

@ -0,0 +1,76 @@
#pragma once
/*
* Copyright (C) 2021 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 <aidl/android/hardware/power/WorkDuration.h>
#include <chrono>
#include <vector>
namespace aidl {
namespace google {
namespace hardware {
namespace power {
namespace impl {
namespace pixel {
using ::aidl::android::hardware::power::WorkDuration;
struct WorkDurationBatch {
WorkDurationBatch(const std::vector<WorkDuration> &workDurations,
std::chrono::nanoseconds targetDuration)
: workDurations(workDurations), targetDuration(targetDuration) {}
const std::vector<WorkDuration> workDurations;
const std::chrono::nanoseconds targetDuration;
};
struct WorkDurationFeatures {
std::chrono::nanoseconds averageDuration;
std::chrono::nanoseconds maxDuration;
uint32_t numMissedDeadlines;
uint32_t numDurations;
bool operator==(const WorkDurationFeatures &other) const {
return averageDuration == other.averageDuration && maxDuration == other.maxDuration &&
numMissedDeadlines == other.numMissedDeadlines && numDurations == other.numDurations;
}
};
class WorkDurationProcessor {
public:
bool ReportWorkDurations(const std::vector<WorkDuration> &workDurations,
std::chrono::nanoseconds targetDuration);
WorkDurationFeatures GetFeatures();
// True if ReportWorkDurations has been called since GetFeatures was last called.
bool HasWorkDurations();
private:
// The work durations reported since GetFeatures() was last called.
// Ordered from least recent to most recent.
std::vector<WorkDurationBatch> mWorkDurationBatches;
// Guards reading/writing mWorkDurationBatches.
std::mutex mMutex;
};
} // namespace pixel
} // namespace impl
} // namespace power
} // namespace hardware
} // namespace google
} // namespace aidl

View file

@ -0,0 +1 @@
exclude_files=.*

View file

@ -0,0 +1,4 @@
benm@google.com
miloslav@google.com
mishaw@google.com
silviavinyes@google.com

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,131 @@
/*
* Copyright (C) 2021 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 <android-base/properties.h>
#include <gtest/gtest.h>
#include "adaptivecpu/AdaptiveCpuConfig.h"
namespace aidl {
namespace google {
namespace hardware {
namespace power {
namespace impl {
namespace pixel {
using std::chrono_literals::operator""ms;
using std::chrono_literals::operator""min;
class AdaptiveCpuConfigTest : public ::testing::Test {
protected:
void SetUp() override {
android::base::SetProperty("debug.adaptivecpu.iteration_sleep_duration_ms", "");
android::base::SetProperty("debug.adaptivecpu.hint_timeout_ms", "");
android::base::SetProperty("debug.adaptivecpu.random_throttle_decision_percent", "");
android::base::SetProperty("debug.adaptivecpu.random_throttle_options", "");
android::base::SetProperty("debug.adaptivecpu.enabled_hint_timeout_ms", "");
}
};
TEST(AdaptiveCpuConfigTest, valid) {
android::base::SetProperty("debug.adaptivecpu.iteration_sleep_duration_ms", "25");
android::base::SetProperty("debug.adaptivecpu.hint_timeout_ms", "500");
android::base::SetProperty("debug.adaptivecpu.random_throttle_decision_percent", "25");
android::base::SetProperty("debug.adaptivecpu.random_throttle_options", "0,3,4");
android::base::SetProperty("debug.adaptivecpu.enabled_hint_timeout_ms", "1000");
const AdaptiveCpuConfig expectedConfig{
.iterationSleepDuration = 25ms,
.hintTimeout = 500ms,
.randomThrottleDecisionProbability = 0.25,
.enabledHintTimeout = 1000ms,
.randomThrottleOptions = {ThrottleDecision::NO_THROTTLE, ThrottleDecision::THROTTLE_70,
ThrottleDecision::THROTTLE_80},
};
AdaptiveCpuConfig actualConfig;
ASSERT_TRUE(AdaptiveCpuConfig::ReadFromSystemProperties(&actualConfig));
ASSERT_EQ(actualConfig, expectedConfig);
}
TEST(AdaptiveCpuConfigTest, defaultConfig) {
android::base::SetProperty("debug.adaptivecpu.iteration_sleep_duration_ms", "");
android::base::SetProperty("debug.adaptivecpu.hint_timeout_ms", "");
android::base::SetProperty("debug.adaptivecpu.random_throttle_decision_percent", "");
android::base::SetProperty("debug.adaptivecpu.random_throttle_options", "");
android::base::SetProperty("debug.adaptivecpu.enabled_hint_timeout_ms", "");
AdaptiveCpuConfig actualConfig;
ASSERT_TRUE(AdaptiveCpuConfig::ReadFromSystemProperties(&actualConfig));
ASSERT_EQ(actualConfig, AdaptiveCpuConfig::DEFAULT);
}
TEST(AdaptiveCpuConfigTest, iterationSleepDuration_belowMin) {
android::base::SetProperty("debug.adaptivecpu.iteration_sleep_duration_ms", "2");
AdaptiveCpuConfig actualConfig;
ASSERT_TRUE(AdaptiveCpuConfig::ReadFromSystemProperties(&actualConfig));
ASSERT_EQ(actualConfig.iterationSleepDuration, 20ms);
}
TEST(AdaptiveCpuConfigTest, iterationSleepDuration_negative) {
android::base::SetProperty("debug.adaptivecpu.iteration_sleep_duration_ms", "-100");
AdaptiveCpuConfig actualConfig;
ASSERT_TRUE(AdaptiveCpuConfig::ReadFromSystemProperties(&actualConfig));
ASSERT_EQ(actualConfig.iterationSleepDuration, 1000ms);
}
TEST(AdaptiveCpuConfigTest, randomThrottleDecisionProbability_float) {
android::base::SetProperty("debug.adaptivecpu.random_throttle_decision_percent", "0.5");
AdaptiveCpuConfig actualConfig;
ASSERT_TRUE(AdaptiveCpuConfig::ReadFromSystemProperties(&actualConfig));
ASSERT_EQ(actualConfig.randomThrottleDecisionProbability, 0);
}
TEST(AdaptiveCpuConfigTest, randomThrottleDecisionProbability_tooBig) {
android::base::SetProperty("debug.adaptivecpu.random_throttle_decision_percent", "101");
AdaptiveCpuConfig actualConfig;
ASSERT_FALSE(AdaptiveCpuConfig::ReadFromSystemProperties(&actualConfig));
}
TEST(AdaptiveCpuConfigTest, randomThrottleOptions_invalidThrottle) {
android::base::SetProperty("debug.adaptivecpu.random_throttle_options", "0,1,2,9");
AdaptiveCpuConfig actualConfig;
ASSERT_FALSE(AdaptiveCpuConfig::ReadFromSystemProperties(&actualConfig));
}
TEST(AdaptiveCpuConfigTest, randomThrottleOptions_wrongDelim) {
android::base::SetProperty("debug.adaptivecpu.random_throttle_options", "0,1.2,3");
AdaptiveCpuConfig actualConfig;
ASSERT_FALSE(AdaptiveCpuConfig::ReadFromSystemProperties(&actualConfig));
}
TEST(AdaptiveCpuConfigTest, randomThrottleOptions_wrongNumber) {
android::base::SetProperty("debug.adaptivecpu.random_throttle_options", "0,1,2a,3");
AdaptiveCpuConfig actualConfig;
ASSERT_FALSE(AdaptiveCpuConfig::ReadFromSystemProperties(&actualConfig));
}
TEST(AdaptiveCpuConfigTest, randomThrottleOptions_straySpace) {
android::base::SetProperty("debug.adaptivecpu.random_throttle_options", "0,1 ,2,3");
AdaptiveCpuConfig actualConfig;
ASSERT_FALSE(AdaptiveCpuConfig::ReadFromSystemProperties(&actualConfig));
android::base::SetProperty("debug.adaptivecpu.random_throttle_options", "0,1,2,3 ");
ASSERT_FALSE(AdaptiveCpuConfig::ReadFromSystemProperties(&actualConfig));
}
} // namespace pixel
} // namespace impl
} // namespace power
} // namespace hardware
} // namespace google
} // namespace aidl

View file

@ -0,0 +1,129 @@
/*
* Copyright (C) 2021 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 <gtest/gtest.h>
#include "adaptivecpu/AdaptiveCpuStats.h"
#include "mocks.h"
using testing::HasSubstr;
using testing::Return;
using std::chrono_literals::operator""ns;
namespace aidl {
namespace google {
namespace hardware {
namespace power {
namespace impl {
namespace pixel {
TEST(AdaptiveCpuStatsTest, singleRun) {
std::unique_ptr<MockTimeSource> timeSource = std::make_unique<MockTimeSource>();
EXPECT_CALL(*timeSource, GetTime())
.Times(3)
.WillOnce(Return(1000ns))
.WillOnce(Return(1100ns))
.WillOnce(Return(1200ns));
AdaptiveCpuStats stats(std::move(timeSource));
stats.RegisterStartRun();
stats.RegisterSuccessfulRun(ThrottleDecision::NO_THROTTLE, ThrottleDecision::THROTTLE_60, {},
AdaptiveCpuConfig::DEFAULT);
std::stringstream stream;
stats.DumpToStream(stream);
EXPECT_THAT(stream.str(), HasSubstr("- Successful runs / total runs: 1 / 1\n"));
EXPECT_THAT(stream.str(), HasSubstr("- Total run duration: 100.000000ns\n"));
EXPECT_THAT(stream.str(), HasSubstr("- Average run duration: 100.000000ns\n"));
EXPECT_THAT(stream.str(), HasSubstr("- Running time fraction: 0.5\n"));
EXPECT_THAT(stream.str(), HasSubstr("- THROTTLE_60: 1\n"));
}
TEST(AdaptiveCpuStatsTest, multipleRuns) {
std::unique_ptr<MockTimeSource> timeSource = std::make_unique<MockTimeSource>();
EXPECT_CALL(*timeSource, GetTime())
.Times(9)
.WillOnce(Return(1000ns)) // start #1
.WillOnce(Return(1100ns)) // success #1
.WillOnce(Return(2000ns)) // start #2
.WillOnce(Return(2200ns)) // success #2
.WillOnce(Return(3000ns)) // start #3
.WillOnce(Return(3100ns)) // success #3
.WillOnce(Return(4000ns)) // start #4
.WillOnce(Return(4800ns)) // success #4
.WillOnce(Return(5000ns)); // dump
AdaptiveCpuStats stats(std::move(timeSource));
stats.RegisterStartRun();
stats.RegisterSuccessfulRun(ThrottleDecision::NO_THROTTLE, ThrottleDecision::THROTTLE_60,
// Ignored, as this is the first call.
{.numDurations = 100000, .numMissedDeadlines = 123},
AdaptiveCpuConfig::DEFAULT);
stats.RegisterStartRun();
stats.RegisterSuccessfulRun(ThrottleDecision::THROTTLE_60, ThrottleDecision::THROTTLE_70,
{.numDurations = 100, .numMissedDeadlines = 10},
AdaptiveCpuConfig::DEFAULT);
stats.RegisterStartRun();
stats.RegisterSuccessfulRun(ThrottleDecision::THROTTLE_70, ThrottleDecision::THROTTLE_60,
{.numDurations = 50, .numMissedDeadlines = 1},
AdaptiveCpuConfig::DEFAULT);
stats.RegisterStartRun();
stats.RegisterSuccessfulRun(ThrottleDecision::THROTTLE_60, ThrottleDecision::THROTTLE_80,
{.numDurations = 200, .numMissedDeadlines = 20},
AdaptiveCpuConfig::DEFAULT);
std::stringstream stream;
stats.DumpToStream(stream);
EXPECT_THAT(stream.str(), HasSubstr("- Successful runs / total runs: 4 / 4\n"));
EXPECT_THAT(stream.str(), HasSubstr("- Total run duration: 1.200000us\n"));
EXPECT_THAT(stream.str(), HasSubstr("- Average run duration: 300.000000ns\n"));
EXPECT_THAT(stream.str(), HasSubstr("- Running time fraction: 0.3\n"));
EXPECT_THAT(stream.str(), HasSubstr("- THROTTLE_60: 2\n"));
EXPECT_THAT(stream.str(), HasSubstr("- THROTTLE_70: 1\n"));
EXPECT_THAT(stream.str(), HasSubstr("- THROTTLE_60: 30 / 300 (0.1)\n"));
EXPECT_THAT(stream.str(), HasSubstr("- THROTTLE_70: 1 / 50 (0.02)\n"));
}
TEST(AdaptiveCpuStatsTest, failedRun) {
std::unique_ptr<MockTimeSource> timeSource = std::make_unique<MockTimeSource>();
EXPECT_CALL(*timeSource, GetTime())
.Times(4)
.WillOnce(Return(1000ns))
.WillOnce(Return(1100ns))
.WillOnce(Return(1200ns))
.WillOnce(Return(1300ns));
AdaptiveCpuStats stats(std::move(timeSource));
stats.RegisterStartRun();
stats.RegisterStartRun();
stats.RegisterSuccessfulRun(ThrottleDecision::NO_THROTTLE, ThrottleDecision::THROTTLE_60, {},
AdaptiveCpuConfig::DEFAULT);
std::stringstream stream;
stats.DumpToStream(stream);
EXPECT_THAT(stream.str(), HasSubstr("- Successful runs / total runs: 1 / 2\n"));
}
} // namespace pixel
} // namespace impl
} // namespace power
} // namespace hardware
} // namespace google
} // namespace aidl

View file

@ -0,0 +1,159 @@
/*
* Copyright (C) 2021 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 <gtest/gtest.h>
#include "adaptivecpu/CpuFrequencyReader.h"
#include "mocks.h"
using testing::_;
using testing::ByMove;
using testing::Return;
using std::chrono_literals::operator""ms;
namespace aidl {
namespace google {
namespace hardware {
namespace power {
namespace impl {
namespace pixel {
std::ostream &operator<<(std::ostream &stream, const CpuPolicyAverageFrequency &frequency) {
return stream << "CpuPolicyAverageFrequency(" << frequency.policyId << ", "
<< frequency.averageFrequencyHz << ")";
}
TEST(CpuFrequencyReaderTest, cpuPolicyIds) {
std::unique_ptr<MockFilesystem> filesystem = std::make_unique<MockFilesystem>();
EXPECT_CALL(*filesystem, ListDirectory("/sys/devices/system/cpu/cpufreq", _))
.WillOnce([](auto _path __attribute__((unused)), auto result) {
*result = std::vector<std::string>{"ignored1", "policy1", "ignored2",
"policy5", "policy10", "policybad"};
return true;
});
EXPECT_CALL(*filesystem, ReadFileStream(_, _))
.WillRepeatedly([](auto _path __attribute__((unused)), auto result) {
*result = std::make_unique<std::istringstream>("1 2\n3 4\n");
return true;
});
CpuFrequencyReader reader(std::move(filesystem));
EXPECT_TRUE(reader.Init());
std::map<uint32_t, std::map<uint64_t, std::chrono::milliseconds>> expected = {
{1, {{1, 20ms}, {3, 40ms}}}, {5, {{1, 20ms}, {3, 40ms}}}, {10, {{1, 20ms}, {3, 40ms}}}};
EXPECT_EQ(reader.GetPreviousCpuPolicyFrequencies(), expected);
}
TEST(CpuFrequencyReaderTest, getRecentCpuPolicyFrequencies) {
std::unique_ptr<MockFilesystem> filesystem = std::make_unique<MockFilesystem>();
EXPECT_CALL(*filesystem, ListDirectory("/sys/devices/system/cpu/cpufreq", _))
.WillOnce([](auto _path __attribute__((unused)), auto result) {
*result = std::vector<std::string>{"policy1", "policy2"};
return true;
});
EXPECT_CALL(*filesystem,
ReadFileStream("/sys/devices/system/cpu/cpufreq/policy1/stats/time_in_state", _))
.Times(2)
.WillOnce([](auto _path __attribute__((unused)), auto result) {
*result = std::make_unique<std::istringstream>("1000 5\n2000 4");
return true;
})
.WillOnce([](auto _path __attribute__((unused)), auto result) {
*result = std::make_unique<std::istringstream>("1000 7\n2000 10");
return true;
});
EXPECT_CALL(*filesystem,
ReadFileStream("/sys/devices/system/cpu/cpufreq/policy2/stats/time_in_state", _))
.Times(2)
.WillOnce([](auto _path __attribute__((unused)), auto result) {
*result = std::make_unique<std::istringstream>("1500 1\n2500 23");
return true;
})
.WillOnce([](auto _path __attribute__((unused)), auto result) {
*result = std::make_unique<std::istringstream>("1500 5\n2500 23");
return true;
});
CpuFrequencyReader reader(std::move(filesystem));
EXPECT_TRUE(reader.Init());
std::vector<CpuPolicyAverageFrequency> actual;
EXPECT_TRUE(reader.GetRecentCpuPolicyFrequencies(&actual));
EXPECT_EQ(actual, std::vector<CpuPolicyAverageFrequency>({
{.policyId = 1, .averageFrequencyHz = 1750},
{.policyId = 2, .averageFrequencyHz = 1500},
}));
}
TEST(CpuFrequencyReaderTest, getRecentCpuPolicyFrequencies_frequenciesChange) {
std::unique_ptr<MockFilesystem> filesystem = std::make_unique<MockFilesystem>();
EXPECT_CALL(*filesystem, ListDirectory("/sys/devices/system/cpu/cpufreq", _))
.WillOnce([](auto _path __attribute__((unused)), auto result) {
*result = std::vector<std::string>{"policy1"};
return true;
});
EXPECT_CALL(*filesystem,
ReadFileStream("/sys/devices/system/cpu/cpufreq/policy1/stats/time_in_state", _))
.Times(2)
.WillOnce([](auto _path __attribute__((unused)), auto result) {
*result = std::make_unique<std::istringstream>("1000 5\n2000 4");
return true;
})
.WillOnce([](auto _path __attribute__((unused)), auto result) {
*result = std::make_unique<std::istringstream>("1000 6\n2001 4");
return true;
});
CpuFrequencyReader reader(std::move(filesystem));
EXPECT_TRUE(reader.Init());
std::vector<CpuPolicyAverageFrequency> actual;
EXPECT_FALSE(reader.GetRecentCpuPolicyFrequencies(&actual));
}
TEST(CpuFrequencyReaderTest, getRecentCpuPolicyFrequencies_badFormat) {
std::unique_ptr<MockFilesystem> filesystem = std::make_unique<MockFilesystem>();
EXPECT_CALL(*filesystem, ListDirectory("/sys/devices/system/cpu/cpufreq", _))
.WillOnce([](auto _path __attribute__((unused)), auto result) {
*result = std::vector<std::string>{"policy1"};
return true;
});
EXPECT_CALL(*filesystem,
ReadFileStream("/sys/devices/system/cpu/cpufreq/policy1/stats/time_in_state", _))
.Times(2)
.WillOnce([](auto _path __attribute__((unused)), auto result) {
*result = std::make_unique<std::istringstream>("1000 1");
return true;
})
.WillOnce([](auto _path __attribute__((unused)), auto result) {
*result = std::make_unique<std::istringstream>("1000 2\nfoo");
return true;
});
CpuFrequencyReader reader(std::move(filesystem));
EXPECT_TRUE(reader.Init());
std::vector<CpuPolicyAverageFrequency> actual;
EXPECT_FALSE(reader.GetRecentCpuPolicyFrequencies(&actual));
}
} // namespace pixel
} // namespace impl
} // namespace power
} // namespace hardware
} // namespace google
} // namespace aidl

View file

@ -0,0 +1,139 @@
/*
* Copyright (C) 2021 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 <gtest/gtest.h>
#include "adaptivecpu/CpuLoadReaderProcStat.h"
#include "mocks.h"
#define LOG_TAG "powerhal-adaptivecpu"
using testing::_;
using testing::ByMove;
using testing::Return;
namespace aidl {
namespace google {
namespace hardware {
namespace power {
namespace impl {
namespace pixel {
TEST(CpuLoadReaderProcStatTest, GetRecentCpuLoads) {
std::unique_ptr<MockFilesystem> filesystem = std::make_unique<MockFilesystem>();
EXPECT_CALL(*filesystem, ReadFileStream("/proc/stat", _))
.Times(2)
.WillOnce([](auto _path __attribute__((unused)), auto result) {
std::stringstream ss;
ss << "bad line\n";
ss << "cpu1 100 0 0 50 0 0 0 0 0 0\n";
ss << "cpu2 200 0 0 50 0 0 0 0 0 0\n";
*result = std::make_unique<std::istringstream>(ss.str());
return true;
})
.WillOnce([](auto _path __attribute__((unused)), auto result) {
std::stringstream ss;
ss << "bad line\n";
ss << "cpu1 200 0 0 150 0 0 0 0 0 0\n";
ss << "cpu2 500 0 0 150 0 0 0 0 0 0\n";
*result = std::make_unique<std::istringstream>(ss.str());
return true;
});
CpuLoadReaderProcStat reader(std::move(filesystem));
reader.Init();
std::array<double, NUM_CPU_CORES> actualPercentages;
ASSERT_TRUE(reader.GetRecentCpuLoads(&actualPercentages));
std::array<double, NUM_CPU_CORES> expectedPercentages({0, 0.5, 0.25, 0, 0, 0, 0, 0});
ASSERT_EQ(actualPercentages, expectedPercentages);
}
TEST(CpuLoadReaderProcStatTest, GetRecentCpuLoads_failsWithMissingValues) {
std::unique_ptr<MockFilesystem> filesystem = std::make_unique<MockFilesystem>();
EXPECT_CALL(*filesystem, ReadFileStream("/proc/stat", _))
.Times(2)
.WillOnce([](auto _path __attribute__((unused)), auto result) {
std::stringstream ss;
ss << "bad line\n";
ss << "cpu1 100 0 0 50 0 0 0\n";
ss << "cpu2 200 0 0 50 0 0 0\n";
*result = std::make_unique<std::istringstream>(ss.str());
return true;
})
.WillOnce([](auto _path __attribute__((unused)), auto result) {
std::stringstream ss;
ss << "bad line\n";
ss << "cpu1 200 0 0 150 0 0 0\n";
ss << "cpu2 500 0 0 150 0 0 0\n";
*result = std::make_unique<std::istringstream>(ss.str());
return true;
});
CpuLoadReaderProcStat reader(std::move(filesystem));
reader.Init();
std::array<double, NUM_CPU_CORES> actualPercentages;
ASSERT_FALSE(reader.GetRecentCpuLoads(&actualPercentages));
}
TEST(CpuLoadReaderProcStatTest, GetRecentCpuLoads_failsWithEmptyFile) {
std::unique_ptr<MockFilesystem> filesystem = std::make_unique<MockFilesystem>();
EXPECT_CALL(*filesystem, ReadFileStream("/proc/stat", _))
.Times(2)
.WillRepeatedly([](auto _path __attribute__((unused)), auto result) {
*result = std::make_unique<std::istringstream>("");
return true;
});
CpuLoadReaderProcStat reader(std::move(filesystem));
reader.Init();
std::array<double, NUM_CPU_CORES> actualPercentages;
ASSERT_FALSE(reader.GetRecentCpuLoads(&actualPercentages));
}
TEST(CpuLoadReaderProcStatTest, GetRecentCpuLoads_failsWithDifferentCpus) {
std::unique_ptr<MockFilesystem> filesystem = std::make_unique<MockFilesystem>();
EXPECT_CALL(*filesystem, ReadFileStream("/proc/stat", _))
.Times(2)
.WillOnce([](auto _path __attribute__((unused)), auto result) {
std::stringstream ss;
ss << "bad line\n";
ss << "cpu1 100 0 0 50 0 0 0 0 0 0\n";
ss << "cpu2 200 0 0 50 0 0 0 0 0 0\n";
*result = std::make_unique<std::istringstream>(ss.str());
return true;
})
.WillOnce([](auto _path __attribute__((unused)), auto result) {
std::stringstream ss;
ss << "bad line\n";
ss << "cpu1 200 0 0 150 0 0 0 0 0 0\n";
ss << "cpu3 500 0 0 150 0 0 0 0 0 0\n";
*result = std::make_unique<std::istringstream>(ss.str());
return true;
});
CpuLoadReaderProcStat reader(std::move(filesystem));
reader.Init();
std::array<double, NUM_CPU_CORES> actualPercentages;
ASSERT_FALSE(reader.GetRecentCpuLoads(&actualPercentages));
}
} // namespace pixel
} // namespace impl
} // namespace power
} // namespace hardware
} // namespace google
} // namespace aidl

View file

@ -0,0 +1,126 @@
/*
* Copyright (C) 2021 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 <gtest/gtest.h>
#include "adaptivecpu/CpuLoadReaderSysDevices.h"
#include "mocks.h"
#define LOG_TAG "powerhal-adaptivecpu"
using testing::_;
using testing::ByMove;
using testing::MatchesRegex;
using testing::Return;
using std::chrono_literals::operator""ms;
namespace aidl {
namespace google {
namespace hardware {
namespace power {
namespace impl {
namespace pixel {
TEST(CpuLoadReaderSysDevicesTest, GetRecentCpuLoads) {
std::unique_ptr<MockFilesystem> filesystem = std::make_unique<MockFilesystem>();
std::unique_ptr<MockTimeSource> timeSource = std::make_unique<MockTimeSource>();
EXPECT_CALL(*filesystem, ListDirectory(MatchesRegex("/sys/devices/system/cpu/cpu0/cpuidle"), _))
.WillOnce([](auto _path __attribute__((unused)), auto result) {
*result = std::vector<std::string>{"foo", "bar", "baz"};
return true;
});
EXPECT_CALL(*filesystem,
ListDirectory(MatchesRegex("/sys/devices/system/cpu/cpu0/cpuidle/(foo|bar)"), _))
.Times(2)
.WillRepeatedly([](auto _path __attribute__((unused)), auto result) {
*result = std::vector<std::string>{"abc", "time", "xyz"};
return true;
});
EXPECT_CALL(*filesystem,
ListDirectory(MatchesRegex("/sys/devices/system/cpu/cpu0/cpuidle/baz"), _))
.WillOnce([](auto _path __attribute__((unused)), auto result) {
*result = std::vector<std::string>{"abc", "xyz"};
return true;
});
EXPECT_CALL(*filesystem, ReadFileStream("/sys/devices/system/cpu/cpu0/cpuidle/foo/time", _))
.Times(2)
.WillOnce([](auto _path __attribute__((unused)), auto result) {
*result = std::make_unique<std::istringstream>("100");
return true;
})
.WillOnce([](auto _path __attribute__((unused)), auto result) {
*result = std::make_unique<std::istringstream>("200");
return true;
});
EXPECT_CALL(*filesystem, ReadFileStream("/sys/devices/system/cpu/cpu0/cpuidle/bar/time", _))
.Times(2)
.WillOnce([](auto _path __attribute__((unused)), auto result) {
*result = std::make_unique<std::istringstream>("500");
return true;
})
.WillOnce([](auto _path __attribute__((unused)), auto result) {
*result = std::make_unique<std::istringstream>("700");
return true;
});
EXPECT_CALL(*filesystem, ReadFileStream("/sys/devices/system/cpu/cpu1/cpuidle/foo/time", _))
.Times(2)
.WillOnce([](auto _path __attribute__((unused)), auto result) {
*result = std::make_unique<std::istringstream>("1000");
return true;
})
.WillOnce([](auto _path __attribute__((unused)), auto result) {
*result = std::make_unique<std::istringstream>("1010");
return true;
});
EXPECT_CALL(*filesystem, ReadFileStream("/sys/devices/system/cpu/cpu1/cpuidle/bar/time", _))
.Times(2)
.WillOnce([](auto _path __attribute__((unused)), auto result) {
*result = std::make_unique<std::istringstream>("50");
return true;
})
.WillOnce([](auto _path __attribute__((unused)), auto result) {
*result = std::make_unique<std::istringstream>("70");
return true;
});
EXPECT_CALL(*filesystem,
ReadFileStream(
MatchesRegex("/sys/devices/system/cpu/cpu[2-7]/cpuidle/(foo|bar)/time"), _))
.WillRepeatedly([](auto _path __attribute__((unused)), auto result) {
*result = std::make_unique<std::istringstream>("0");
return true;
});
EXPECT_CALL(*timeSource, GetTime()).Times(2).WillOnce(Return(1ms)).WillOnce(Return(2ms));
CpuLoadReaderSysDevices reader(std::move(filesystem), std::move(timeSource));
ASSERT_TRUE(reader.Init());
std::array<double, NUM_CPU_CORES> actualPercentage;
reader.GetRecentCpuLoads(&actualPercentage);
std::array<double, NUM_CPU_CORES> expectedPercentage{0.3, 0.03, 0, 0, 0, 0, 0, 0};
ASSERT_EQ(actualPercentage, expectedPercentage);
}
} // namespace pixel
} // namespace impl
} // namespace power
} // namespace hardware
} // namespace google
} // namespace aidl

View file

@ -0,0 +1,211 @@
/*
* Copyright (C) 2022 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 <gtest/gtest.h>
#include "adaptivecpu/KernelCpuFeatureReader.h"
#include "mocks.h"
using testing::_;
using testing::ByMove;
using testing::MatchesRegex;
using testing::Return;
using std::chrono_literals::operator""ns;
namespace aidl {
namespace google {
namespace hardware {
namespace power {
namespace impl {
namespace pixel {
TEST(KernelCpuFeatureReaderTest, valid) {
std::unique_ptr<MockFilesystem> filesystem = std::make_unique<MockFilesystem>();
std::unique_ptr<MockTimeSource> timeSource = std::make_unique<MockTimeSource>();
EXPECT_CALL(*timeSource, GetKernelTime())
.Times(2)
.WillOnce(Return(100ns))
.WillOnce(Return(200ns));
EXPECT_CALL(*filesystem, ReadFileStream("/proc/vendor_sched/acpu_stats", _))
.WillOnce([](auto path __attribute__((unused)), auto result) {
// Empty file, we will populate in ResetFileStream.
*result = std::make_unique<std::istringstream>("");
return true;
});
EXPECT_CALL(*filesystem, ResetFileStream(_))
.Times(2)
.WillOnce([](auto &result) {
std::array<acpu_stats, NUM_CPU_CORES> acpuStats{{
{.weighted_sum_freq = 100, .total_idle_time_ns = 100},
{.weighted_sum_freq = 100, .total_idle_time_ns = 100},
{.weighted_sum_freq = 100, .total_idle_time_ns = 100},
{.weighted_sum_freq = 100, .total_idle_time_ns = 100},
{.weighted_sum_freq = 200, .total_idle_time_ns = 200},
{.weighted_sum_freq = 200, .total_idle_time_ns = 200},
{.weighted_sum_freq = 300, .total_idle_time_ns = 200},
{.weighted_sum_freq = 300, .total_idle_time_ns = 200},
}};
char *bytes = reinterpret_cast<char *>(acpuStats.begin());
static_cast<std::istringstream &>(*result).str(
std::string(bytes, bytes + sizeof(acpuStats)));
return true;
})
.WillOnce([](auto &result) {
std::array<acpu_stats, NUM_CPU_CORES> acpuStats{{
{.weighted_sum_freq = 200, .total_idle_time_ns = 150},
{.weighted_sum_freq = 100, .total_idle_time_ns = 150},
{.weighted_sum_freq = 100, .total_idle_time_ns = 150},
{.weighted_sum_freq = 100, .total_idle_time_ns = 150},
{.weighted_sum_freq = 300, .total_idle_time_ns = 300},
{.weighted_sum_freq = 200, .total_idle_time_ns = 300},
{.weighted_sum_freq = 400, .total_idle_time_ns = 300},
{.weighted_sum_freq = 300, .total_idle_time_ns = 300},
}};
char *bytes = reinterpret_cast<char *>(acpuStats.begin());
static_cast<std::istringstream &>(*result).str(
std::string(bytes, bytes + sizeof(acpuStats)));
return true;
});
KernelCpuFeatureReader reader(std::move(filesystem), std::move(timeSource));
ASSERT_TRUE(reader.Init());
std::array<double, NUM_CPU_POLICIES> cpuPolicyAverageFrequencyHz;
std::array<double, NUM_CPU_CORES> cpuCoreIdleTimesPercentage;
ASSERT_TRUE(
reader.GetRecentCpuFeatures(&cpuPolicyAverageFrequencyHz, &cpuCoreIdleTimesPercentage));
std::array<double, NUM_CPU_POLICIES> expectedFrequencies{{1, 1, 1}};
std::array<double, NUM_CPU_CORES> expectedIdleTimes{{0.5, 0.5, 0.5, 0.5, 1, 1, 1, 1}};
ASSERT_EQ(cpuPolicyAverageFrequencyHz, expectedFrequencies);
ASSERT_EQ(cpuCoreIdleTimesPercentage, expectedIdleTimes);
}
TEST(KernelCpuFeatureReaderTest, noFile) {
std::unique_ptr<MockFilesystem> filesystem = std::make_unique<MockFilesystem>();
std::unique_ptr<MockTimeSource> timeSource = std::make_unique<MockTimeSource>();
EXPECT_CALL(*filesystem, ReadFileStream("/proc/vendor_sched/acpu_stats", _))
.WillOnce(Return(false));
KernelCpuFeatureReader reader(std::move(filesystem), std::move(timeSource));
ASSERT_FALSE(reader.Init());
}
TEST(KernelCpuFeatureReaderTest, frequencies_capsNegativeDiff) {
std::unique_ptr<MockFilesystem> filesystem = std::make_unique<MockFilesystem>();
std::unique_ptr<MockTimeSource> timeSource = std::make_unique<MockTimeSource>();
EXPECT_CALL(*timeSource, GetKernelTime())
.Times(2)
.WillOnce(Return(100ns))
.WillOnce(Return(200ns));
EXPECT_CALL(*filesystem, ReadFileStream("/proc/vendor_sched/acpu_stats", _))
.WillOnce([](auto path __attribute__((unused)), auto result) {
// Empty file, we will populate in ResetFileStream.
*result = std::make_unique<std::istringstream>("");
return true;
});
EXPECT_CALL(*filesystem, ResetFileStream(_))
.Times(2)
.WillOnce([](auto &result) {
std::array<acpu_stats, NUM_CPU_CORES> acpuStats{{
{.weighted_sum_freq = 200, .total_idle_time_ns = 100},
}};
char *bytes = reinterpret_cast<char *>(acpuStats.begin());
static_cast<std::istringstream &>(*result).str(
std::string(bytes, bytes + sizeof(acpuStats)));
return true;
})
.WillOnce([](auto &result) {
std::array<acpu_stats, NUM_CPU_CORES> acpuStats{{
{.weighted_sum_freq = 100, .total_idle_time_ns = 150},
}};
char *bytes = reinterpret_cast<char *>(acpuStats.begin());
static_cast<std::istringstream &>(*result).str(
std::string(bytes, bytes + sizeof(acpuStats)));
return true;
});
KernelCpuFeatureReader reader(std::move(filesystem), std::move(timeSource));
ASSERT_TRUE(reader.Init());
std::array<double, NUM_CPU_POLICIES> cpuPolicyAverageFrequencyHz;
std::array<double, NUM_CPU_CORES> cpuCoreIdleTimesPercentage;
ASSERT_TRUE(
reader.GetRecentCpuFeatures(&cpuPolicyAverageFrequencyHz, &cpuCoreIdleTimesPercentage));
std::array<double, NUM_CPU_POLICIES> expectedFrequencies{{0}};
ASSERT_EQ(cpuPolicyAverageFrequencyHz, expectedFrequencies);
}
TEST(KernelCpuFeatureReaderTest, idleTimes_capsNegativeDiff) {
std::unique_ptr<MockFilesystem> filesystem = std::make_unique<MockFilesystem>();
std::unique_ptr<MockTimeSource> timeSource = std::make_unique<MockTimeSource>();
EXPECT_CALL(*timeSource, GetKernelTime())
.Times(2)
.WillOnce(Return(100ns))
.WillOnce(Return(200ns));
EXPECT_CALL(*filesystem, ReadFileStream("/proc/vendor_sched/acpu_stats", _))
.WillOnce([](auto path __attribute__((unused)), auto result) {
// Empty file, we will populate in ResetFileStream.
*result = std::make_unique<std::istringstream>("");
return true;
});
EXPECT_CALL(*filesystem, ResetFileStream(_))
.Times(2)
.WillOnce([](auto &result) {
std::array<acpu_stats, NUM_CPU_CORES> acpuStats{{
{.weighted_sum_freq = 100, .total_idle_time_ns = 150},
}};
char *bytes = reinterpret_cast<char *>(acpuStats.begin());
static_cast<std::istringstream &>(*result).str(
std::string(bytes, bytes + sizeof(acpuStats)));
return true;
})
.WillOnce([](auto &result) {
std::array<acpu_stats, NUM_CPU_CORES> acpuStats{{
{.weighted_sum_freq = 200, .total_idle_time_ns = 100},
}};
char *bytes = reinterpret_cast<char *>(acpuStats.begin());
static_cast<std::istringstream &>(*result).str(
std::string(bytes, bytes + sizeof(acpuStats)));
return true;
});
KernelCpuFeatureReader reader(std::move(filesystem), std::move(timeSource));
ASSERT_TRUE(reader.Init());
std::array<double, NUM_CPU_POLICIES> cpuPolicyAverageFrequencyHz;
std::array<double, NUM_CPU_CORES> cpuCoreIdleTimesPercentage;
ASSERT_TRUE(
reader.GetRecentCpuFeatures(&cpuPolicyAverageFrequencyHz, &cpuCoreIdleTimesPercentage));
std::array<double, NUM_CPU_CORES> expectedIdleTimes{{0}};
ASSERT_EQ(cpuCoreIdleTimesPercentage, expectedIdleTimes);
}
} // namespace pixel
} // namespace impl
} // namespace power
} // namespace hardware
} // namespace google
} // namespace aidl

View file

@ -0,0 +1,126 @@
/*
* Copyright (C) 2021 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 <gtest/gtest.h>
#include <random>
#include <set>
#include "adaptivecpu/Model.h"
#include "mocks.h"
using std::chrono_literals::operator""ns;
using testing::_;
using testing::ByMove;
using testing::Return;
using testing::UnorderedElementsAre;
namespace aidl {
namespace google {
namespace hardware {
namespace power {
namespace impl {
namespace pixel {
TEST(ModelTest, ModelInput_SetCpuFreqiencies) {
const ModelInput expected{
.cpuPolicyAverageFrequencyHz = {100, 101, 102},
};
ModelInput actual;
ASSERT_TRUE(actual.SetCpuFreqiencies({
{.policyId = 0, .averageFrequencyHz = 100},
{.policyId = 4, .averageFrequencyHz = 101},
{.policyId = 6, .averageFrequencyHz = 102},
}));
ASSERT_EQ(actual, expected);
}
TEST(ModelTest, ModelInput_SetCpuFreqiencies_failsWithOutOfOrderFrquencies) {
ASSERT_FALSE(ModelInput().SetCpuFreqiencies({
{.policyId = 0, .averageFrequencyHz = 100},
{.policyId = 6, .averageFrequencyHz = 102},
{.policyId = 4, .averageFrequencyHz = 101},
}));
}
TEST(ModelTest, Run_randomInputs) {
std::default_random_engine generator;
std::uniform_real_distribution<double> frequencyDistribution(0, 1e6);
std::uniform_real_distribution<double> idleTimesDistribution(0, 1);
std::uniform_int_distribution<uint32_t> frameTimeDistribution(1, 100);
std::uniform_int_distribution<uint16_t> numRenderedFramesDistribution(1, 20);
std::uniform_int_distribution<uint32_t> throttleDecisionDistribution(0, 3);
const auto randomModelInput = [&]() {
return ModelInput{
.cpuPolicyAverageFrequencyHz = {frequencyDistribution(generator),
frequencyDistribution(generator),
frequencyDistribution(generator)},
.cpuCoreIdleTimesPercentage =
{idleTimesDistribution(generator), idleTimesDistribution(generator),
idleTimesDistribution(generator), idleTimesDistribution(generator),
idleTimesDistribution(generator), idleTimesDistribution(generator),
idleTimesDistribution(generator), idleTimesDistribution(generator)},
.workDurationFeatures =
{.averageDuration =
std::chrono::nanoseconds(frameTimeDistribution(generator)),
.maxDuration = std::chrono::nanoseconds(frameTimeDistribution(generator)),
.numMissedDeadlines = numRenderedFramesDistribution(generator),
.numDurations = numRenderedFramesDistribution(generator)},
.previousThrottleDecision =
static_cast<ThrottleDecision>(throttleDecisionDistribution(generator)),
};
};
for (int i = 0; i < 10; i++) {
std::deque<ModelInput> modelInputs{randomModelInput(), randomModelInput(),
randomModelInput()};
Model().Run(modelInputs, AdaptiveCpuConfig::DEFAULT);
}
}
TEST(ModelTest, Run_randomThrottling) {
ModelInput modelInput{
.cpuPolicyAverageFrequencyHz = {0, 0, 0},
.cpuCoreIdleTimesPercentage = {0, 0, 0, 0, 0, 0, 0, 0},
.workDurationFeatures = {.averageDuration = 0ns,
.maxDuration = 0ns,
.numMissedDeadlines = 0,
.numDurations = 0},
.previousThrottleDecision = ThrottleDecision::NO_THROTTLE,
};
std::deque<ModelInput> modelInputs{modelInput, modelInput, modelInput};
AdaptiveCpuConfig config = AdaptiveCpuConfig::DEFAULT;
config.randomThrottleOptions = {ThrottleDecision::THROTTLE_70, ThrottleDecision::THROTTLE_80};
config.randomThrottleDecisionProbability = 1;
std::set<ThrottleDecision> actualThrottleDecisions;
Model model;
for (int i = 0; i < 100; i++) {
ThrottleDecision throttleDecision = model.Run(modelInputs, config);
actualThrottleDecisions.insert(throttleDecision);
}
ASSERT_THAT(actualThrottleDecisions,
UnorderedElementsAre(ThrottleDecision::THROTTLE_70, ThrottleDecision::THROTTLE_80));
}
} // namespace pixel
} // namespace impl
} // namespace power
} // namespace hardware
} // namespace google
} // namespace aidl

View file

@ -0,0 +1,126 @@
/*
* Copyright (C) 2021 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 <gtest/gtest.h>
#include "adaptivecpu/WorkDurationProcessor.h"
using ::aidl::android::hardware::power::WorkDuration;
using std::chrono_literals::operator""ns;
namespace aidl {
namespace google {
namespace hardware {
namespace power {
namespace impl {
namespace pixel {
static const std::chrono::nanoseconds kNormalTargetDuration = 16666666ns;
TEST(WorkDurationProcessorTest, GetFeatures) {
WorkDurationProcessor processor;
ASSERT_TRUE(processor.ReportWorkDurations(
std::vector<WorkDuration>{
{.timeStampNanos = 0, .durationNanos = kNormalTargetDuration.count()},
{.timeStampNanos = 0, .durationNanos = kNormalTargetDuration.count() * 3}},
kNormalTargetDuration));
const WorkDurationFeatures expected = {.averageDuration = kNormalTargetDuration * 2,
.maxDuration = kNormalTargetDuration * 3,
.numMissedDeadlines = 1,
.numDurations = 2};
const WorkDurationFeatures actual = processor.GetFeatures();
ASSERT_EQ(actual, expected);
}
TEST(WorkDurationProcessorTest, GetFeatures_multipleBatches) {
WorkDurationProcessor processor;
ASSERT_TRUE(processor.ReportWorkDurations(
std::vector<WorkDuration>{
{.timeStampNanos = 0, .durationNanos = kNormalTargetDuration.count()},
{.timeStampNanos = 0, .durationNanos = kNormalTargetDuration.count() * 3}},
kNormalTargetDuration));
ASSERT_TRUE(processor.ReportWorkDurations(
std::vector<WorkDuration>{
{.timeStampNanos = 0, .durationNanos = kNormalTargetDuration.count() * 6},
{.timeStampNanos = 0, .durationNanos = kNormalTargetDuration.count() * 2}},
kNormalTargetDuration));
const WorkDurationFeatures expected = {.averageDuration = kNormalTargetDuration * 3,
.maxDuration = kNormalTargetDuration * 6,
.numMissedDeadlines = 3,
.numDurations = 4};
const WorkDurationFeatures actual = processor.GetFeatures();
ASSERT_EQ(actual, expected);
}
TEST(WorkDurationProcessorTest, GetFeatures_scalesDifferentTargetDurations) {
WorkDurationProcessor processor;
ASSERT_TRUE(processor.ReportWorkDurations(
std::vector<WorkDuration>{
{.timeStampNanos = 0, .durationNanos = kNormalTargetDuration.count() * 2},
{.timeStampNanos = 0, .durationNanos = kNormalTargetDuration.count() * 6}},
kNormalTargetDuration * 2));
const WorkDurationFeatures expected = {.averageDuration = kNormalTargetDuration * 2,
.maxDuration = kNormalTargetDuration * 3,
.numMissedDeadlines = 1,
.numDurations = 2};
const WorkDurationFeatures actual = processor.GetFeatures();
ASSERT_EQ(actual, expected);
}
TEST(WorkDurationProcessorTest, GetFeatures_noFrames) {
WorkDurationProcessor processor;
const WorkDurationFeatures expected = {
.averageDuration = 0ns, .maxDuration = 0ns, .numMissedDeadlines = 0, .numDurations = 0};
const WorkDurationFeatures actual = processor.GetFeatures();
ASSERT_EQ(actual, expected);
}
TEST(WorkDurationProcessorTest, HasWorkDurations) {
WorkDurationProcessor processor;
ASSERT_FALSE(processor.HasWorkDurations());
ASSERT_TRUE(processor.ReportWorkDurations(
std::vector<WorkDuration>{
{.timeStampNanos = 0, .durationNanos = kNormalTargetDuration.count()}},
kNormalTargetDuration * 2));
ASSERT_TRUE(processor.HasWorkDurations());
processor.GetFeatures();
ASSERT_FALSE(processor.HasWorkDurations());
}
TEST(WorkDurationProcessorTest, GetFeatures_tooManyUnprocessedThenClears) {
WorkDurationProcessor processor;
for (int i = 0; i < 1000; i++) {
ASSERT_TRUE(processor.ReportWorkDurations(
std::vector<WorkDuration>{
{.timeStampNanos = 0, .durationNanos = kNormalTargetDuration.count()}},
kNormalTargetDuration));
}
ASSERT_FALSE(processor.ReportWorkDurations(
std::vector<WorkDuration>{
{.timeStampNanos = 0, .durationNanos = kNormalTargetDuration.count()}},
kNormalTargetDuration));
ASSERT_EQ(processor.GetFeatures().numDurations, 0);
}
} // namespace pixel
} // namespace impl
} // namespace power
} // namespace hardware
} // namespace google
} // namespace aidl

View file

@ -0,0 +1,53 @@
/*
* Copyright (C) 2021 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 <gmock/gmock.h>
#include "adaptivecpu/IFilesystem.h"
#include "adaptivecpu/ITimeSource.h"
namespace aidl {
namespace google {
namespace hardware {
namespace power {
namespace impl {
namespace pixel {
class MockFilesystem : public IFilesystem {
public:
~MockFilesystem() override {}
MOCK_METHOD(bool, ListDirectory, (const std::string &path, std::vector<std::string> *result),
(const, override));
MOCK_METHOD(bool, ReadFileStream,
(const std::string &path, std::unique_ptr<std::istream> *result),
(const, override));
MOCK_METHOD(bool, ResetFileStream, (const std::unique_ptr<std::istream> &fileStream),
(const, override));
};
class MockTimeSource : public ITimeSource {
public:
~MockTimeSource() override {}
MOCK_METHOD(std::chrono::nanoseconds, GetTime, (), (const, override));
MOCK_METHOD(std::chrono::nanoseconds, GetKernelTime, (), (const, override));
};
} // namespace pixel
} // namespace impl
} // namespace power
} // namespace hardware
} // namespace google
} // namespace aidl

View file

@ -14,21 +14,23 @@
* limitations under the License. * limitations under the License.
*/ */
#define ATRACE_TAG (ATRACE_TAG_POWER | ATRACE_TAG_HAL) #define LOG_TAG "powerhal-libperfmgr"
#define LOG_TAG "android.hardware.power-service.pixel-libperfmgr"
#include "Power.h" #include "Power.h"
#include <mutex>
#include <android-base/file.h> #include <android-base/file.h>
#include <android-base/logging.h> #include <android-base/logging.h>
#include <android-base/properties.h> #include <android-base/properties.h>
#include <android-base/stringprintf.h> #include <android-base/stringprintf.h>
#include <android-base/strings.h> #include <android-base/strings.h>
#include <perfmgr/HintManager.h>
#include <utils/Log.h> #include <utils/Log.h>
#include <utils/Trace.h>
#include <mutex>
#include "PowerHintSession.h"
#include "PowerSessionManager.h"
#include "disp-power/DisplayLowPower.h"
namespace aidl { namespace aidl {
namespace google { namespace google {
@ -37,103 +39,82 @@ namespace power {
namespace impl { namespace impl {
namespace pixel { namespace pixel {
using ::aidl::google::hardware::power::impl::pixel::PowerHintSession;
using ::android::perfmgr::HintManager;
constexpr char kPowerHalStateProp[] = "vendor.powerhal.state"; constexpr char kPowerHalStateProp[] = "vendor.powerhal.state";
constexpr char kPowerHalAudioProp[] = "vendor.powerhal.audio"; constexpr char kPowerHalAudioProp[] = "vendor.powerhal.audio";
constexpr char kPowerHalRenderingProp[] = "vendor.powerhal.rendering"; constexpr char kPowerHalRenderingProp[] = "vendor.powerhal.rendering";
Power::Power(std::shared_ptr<HintManager> hm) Power::Power(std::shared_ptr<DisplayLowPower> dlpw, std::shared_ptr<AdaptiveCpu> adaptiveCpu)
: mHintManager(hm), : mDisplayLowPower(dlpw),
mAdaptiveCpu(adaptiveCpu),
mInteractionHandler(nullptr), mInteractionHandler(nullptr),
mVRModeOn(false), mVRModeOn(false),
mSustainedPerfModeOn(false) { mSustainedPerfModeOn(false) {
mInteractionHandler = std::make_unique<InteractionHandler>(mHintManager); mInteractionHandler = std::make_unique<InteractionHandler>();
mInteractionHandler->Init(); mInteractionHandler->Init();
std::string state = ::android::base::GetProperty(kPowerHalStateProp, ""); std::string state = ::android::base::GetProperty(kPowerHalStateProp, "");
if (state == "SUSTAINED_PERFORMANCE") { if (state == "SUSTAINED_PERFORMANCE") {
ALOGI("Initialize with SUSTAINED_PERFORMANCE on"); LOG(INFO) << "Initialize with SUSTAINED_PERFORMANCE on";
mHintManager->DoHint("SUSTAINED_PERFORMANCE"); HintManager::GetInstance()->DoHint("SUSTAINED_PERFORMANCE");
mSustainedPerfModeOn = true; mSustainedPerfModeOn = true;
} else if (state == "VR") { } else if (state == "VR") {
ALOGI("Initialize with VR on"); LOG(INFO) << "Initialize with VR on";
mHintManager->DoHint(state); HintManager::GetInstance()->DoHint(state);
mVRModeOn = true; mVRModeOn = true;
} else if (state == "VR_SUSTAINED_PERFORMANCE") { } else if (state == "VR_SUSTAINED_PERFORMANCE") {
ALOGI("Initialize with SUSTAINED_PERFORMANCE and VR on"); LOG(INFO) << "Initialize with SUSTAINED_PERFORMANCE and VR on";
mHintManager->DoHint("VR_SUSTAINED_PERFORMANCE"); HintManager::GetInstance()->DoHint("VR_SUSTAINED_PERFORMANCE");
mSustainedPerfModeOn = true; mSustainedPerfModeOn = true;
mVRModeOn = true; mVRModeOn = true;
} else { } else {
ALOGI("Initialize PowerHAL"); LOG(INFO) << "Initialize PowerHAL";
} }
state = ::android::base::GetProperty(kPowerHalAudioProp, ""); state = ::android::base::GetProperty(kPowerHalAudioProp, "");
if (state == "AUDIO_STREAMING_LOW_LATENCY") { if (state == "AUDIO_STREAMING_LOW_LATENCY") {
ALOGI("Initialize with AUDIO_LOW_LATENCY on"); LOG(INFO) << "Initialize with AUDIO_LOW_LATENCY on";
mHintManager->DoHint(state); HintManager::GetInstance()->DoHint(state);
} }
state = ::android::base::GetProperty(kPowerHalRenderingProp, ""); state = ::android::base::GetProperty(kPowerHalRenderingProp, "");
if (state == "EXPENSIVE_RENDERING") { if (state == "EXPENSIVE_RENDERING") {
ALOGI("Initialize with EXPENSIVE_RENDERING on"); LOG(INFO) << "Initialize with EXPENSIVE_RENDERING on";
mHintManager->DoHint("EXPENSIVE_RENDERING"); HintManager::GetInstance()->DoHint("EXPENSIVE_RENDERING");
} }
// Now start to take powerhint
ALOGI("PowerHAL ready to process hints");
}
static int sysfs_write(const char *path, const char *s)
{
char buf[80];
int len;
int ret = 0;
int fd = open(path, O_WRONLY);
if (fd < 0) {
strerror_r(errno, buf, sizeof(buf));
ALOGE("Error opening %s: %s\n", path, buf);
return -1 ;
}
len = write(fd, s, strlen(s));
if (len < 0) {
strerror_r(errno, buf, sizeof(buf));
ALOGE("Error writing to %s: %s\n", path, buf);
ret = -1;
}
close(fd);
return ret;
} }
ndk::ScopedAStatus Power::setMode(Mode type, bool enabled) { ndk::ScopedAStatus Power::setMode(Mode type, bool enabled) {
LOG(DEBUG) << "Power setMode: " << toString(type) << " to: " << enabled; LOG(DEBUG) << "Power setMode: " << toString(type) << " to: " << enabled;
ATRACE_INT(toString(type).c_str(), enabled); if (HintManager::GetInstance()->GetAdpfProfile() &&
switch (type) { HintManager::GetInstance()->GetAdpfProfile()->mReportingRateLimitNs > 0) {
case Mode::DOUBLE_TAP_TO_WAKE: PowerSessionManager::getInstance()->updateHintMode(toString(type), enabled);
{
sysfs_write("/sys/class/touch/tp_dev/gesture_on", enabled ? "1" : "0");
} }
break; switch (type) {
case Mode::LOW_POWER: case Mode::LOW_POWER:
mDisplayLowPower->SetDisplayLowPower(enabled);
if (enabled) {
HintManager::GetInstance()->DoHint(toString(type));
} else {
HintManager::GetInstance()->EndHint(toString(type));
}
break; break;
case Mode::SUSTAINED_PERFORMANCE: case Mode::SUSTAINED_PERFORMANCE:
if (enabled && !mSustainedPerfModeOn) { if (enabled && !mSustainedPerfModeOn) {
if (!mVRModeOn) { // Sustained mode only. if (!mVRModeOn) { // Sustained mode only.
mHintManager->DoHint("SUSTAINED_PERFORMANCE"); HintManager::GetInstance()->DoHint("SUSTAINED_PERFORMANCE");
} else { // Sustained + VR mode. } else { // Sustained + VR mode.
mHintManager->EndHint("VR"); HintManager::GetInstance()->EndHint("VR");
mHintManager->DoHint("VR_SUSTAINED_PERFORMANCE"); HintManager::GetInstance()->DoHint("VR_SUSTAINED_PERFORMANCE");
} }
mSustainedPerfModeOn = true; mSustainedPerfModeOn = true;
} else if (!enabled && mSustainedPerfModeOn) { } else if (!enabled && mSustainedPerfModeOn) {
mHintManager->EndHint("VR_SUSTAINED_PERFORMANCE"); HintManager::GetInstance()->EndHint("VR_SUSTAINED_PERFORMANCE");
mHintManager->EndHint("SUSTAINED_PERFORMANCE"); HintManager::GetInstance()->EndHint("SUSTAINED_PERFORMANCE");
if (mVRModeOn) { // Switch back to VR Mode. if (mVRModeOn) { // Switch back to VR Mode.
mHintManager->DoHint("VR"); HintManager::GetInstance()->DoHint("VR");
} }
mSustainedPerfModeOn = false; mSustainedPerfModeOn = false;
} }
@ -141,17 +122,17 @@ ndk::ScopedAStatus Power::setMode(Mode type, bool enabled) {
case Mode::VR: case Mode::VR:
if (enabled && !mVRModeOn) { if (enabled && !mVRModeOn) {
if (!mSustainedPerfModeOn) { // VR mode only. if (!mSustainedPerfModeOn) { // VR mode only.
mHintManager->DoHint("VR"); HintManager::GetInstance()->DoHint("VR");
} else { // Sustained + VR mode. } else { // Sustained + VR mode.
mHintManager->EndHint("SUSTAINED_PERFORMANCE"); HintManager::GetInstance()->EndHint("SUSTAINED_PERFORMANCE");
mHintManager->DoHint("VR_SUSTAINED_PERFORMANCE"); HintManager::GetInstance()->DoHint("VR_SUSTAINED_PERFORMANCE");
} }
mVRModeOn = true; mVRModeOn = true;
} else if (!enabled && mVRModeOn) { } else if (!enabled && mVRModeOn) {
mHintManager->EndHint("VR_SUSTAINED_PERFORMANCE"); HintManager::GetInstance()->EndHint("VR_SUSTAINED_PERFORMANCE");
mHintManager->EndHint("VR"); HintManager::GetInstance()->EndHint("VR");
if (mSustainedPerfModeOn) { // Switch back to sustained Mode. if (mSustainedPerfModeOn) { // Switch back to sustained Mode.
mHintManager->DoHint("SUSTAINED_PERFORMANCE"); HintManager::GetInstance()->DoHint("SUSTAINED_PERFORMANCE");
} }
mVRModeOn = false; mVRModeOn = false;
} }
@ -161,6 +142,8 @@ ndk::ScopedAStatus Power::setMode(Mode type, bool enabled) {
break; break;
} }
[[fallthrough]]; [[fallthrough]];
case Mode::DOUBLE_TAP_TO_WAKE:
[[fallthrough]];
case Mode::FIXED_PERFORMANCE: case Mode::FIXED_PERFORMANCE:
[[fallthrough]]; [[fallthrough]];
case Mode::EXPENSIVE_RENDERING: case Mode::EXPENSIVE_RENDERING:
@ -181,11 +164,13 @@ ndk::ScopedAStatus Power::setMode(Mode type, bool enabled) {
[[fallthrough]]; [[fallthrough]];
case Mode::CAMERA_STREAMING_HIGH: case Mode::CAMERA_STREAMING_HIGH:
[[fallthrough]]; [[fallthrough]];
case Mode::GAME_LOADING:
[[fallthrough]];
default: default:
if (enabled) { if (enabled) {
mHintManager->DoHint(toString(type)); HintManager::GetInstance()->DoHint(toString(type));
} else { } else {
mHintManager->EndHint(toString(type)); HintManager::GetInstance()->EndHint(toString(type));
} }
break; break;
} }
@ -194,9 +179,9 @@ ndk::ScopedAStatus Power::setMode(Mode type, bool enabled) {
} }
ndk::ScopedAStatus Power::isModeSupported(Mode type, bool *_aidl_return) { ndk::ScopedAStatus Power::isModeSupported(Mode type, bool *_aidl_return) {
bool supported = mHintManager->IsHintSupported(toString(type)); bool supported = HintManager::GetInstance()->IsHintSupported(toString(type));
// LOW_POWER and DOUBLE_TAP_TO_WAKE handled insides PowerHAL specifically // LOW_POWER handled insides PowerHAL specifically
if (type == Mode::LOW_POWER || type == Mode::DOUBLE_TAP_TO_WAKE) { if (type == Mode::LOW_POWER) {
supported = true; supported = true;
} }
LOG(INFO) << "Power mode " << toString(type) << " isModeSupported: " << supported; LOG(INFO) << "Power mode " << toString(type) << " isModeSupported: " << supported;
@ -206,7 +191,10 @@ ndk::ScopedAStatus Power::isModeSupported(Mode type, bool *_aidl_return) {
ndk::ScopedAStatus Power::setBoost(Boost type, int32_t durationMs) { ndk::ScopedAStatus Power::setBoost(Boost type, int32_t durationMs) {
LOG(DEBUG) << "Power setBoost: " << toString(type) << " duration: " << durationMs; LOG(DEBUG) << "Power setBoost: " << toString(type) << " duration: " << durationMs;
ATRACE_INT(toString(type).c_str(), durationMs); if (HintManager::GetInstance()->GetAdpfProfile() &&
HintManager::GetInstance()->GetAdpfProfile()->mReportingRateLimitNs > 0) {
PowerSessionManager::getInstance()->updateHintBoost(toString(type), durationMs);
}
switch (type) { switch (type) {
case Boost::INTERACTION: case Boost::INTERACTION:
if (mVRModeOn || mSustainedPerfModeOn) { if (mVRModeOn || mSustainedPerfModeOn) {
@ -229,11 +217,12 @@ ndk::ScopedAStatus Power::setBoost(Boost type, int32_t durationMs) {
break; break;
} }
if (durationMs > 0) { if (durationMs > 0) {
mHintManager->DoHint(toString(type), std::chrono::milliseconds(durationMs)); HintManager::GetInstance()->DoHint(toString(type),
std::chrono::milliseconds(durationMs));
} else if (durationMs == 0) { } else if (durationMs == 0) {
mHintManager->DoHint(toString(type)); HintManager::GetInstance()->DoHint(toString(type));
} else { } else {
mHintManager->EndHint(toString(type)); HintManager::GetInstance()->EndHint(toString(type));
} }
break; break;
} }
@ -242,7 +231,7 @@ ndk::ScopedAStatus Power::setBoost(Boost type, int32_t durationMs) {
} }
ndk::ScopedAStatus Power::isBoostSupported(Boost type, bool *_aidl_return) { ndk::ScopedAStatus Power::isBoostSupported(Boost type, bool *_aidl_return) {
bool supported = mHintManager->IsHintSupported(toString(type)); bool supported = HintManager::GetInstance()->IsHintSupported(toString(type));
LOG(INFO) << "Power boost " << toString(type) << " isBoostSupported: " << supported; LOG(INFO) << "Power boost " << toString(type) << " isBoostSupported: " << supported;
*_aidl_return = supported; *_aidl_return = supported;
return ndk::ScopedAStatus::ok(); return ndk::ScopedAStatus::ok();
@ -257,10 +246,12 @@ binder_status_t Power::dump(int fd, const char **, uint32_t) {
"HintManager Running: %s\n" "HintManager Running: %s\n"
"VRMode: %s\n" "VRMode: %s\n"
"SustainedPerformanceMode: %s\n", "SustainedPerformanceMode: %s\n",
boolToString(mHintManager->IsRunning()), boolToString(mVRModeOn), boolToString(HintManager::GetInstance()->IsRunning()), boolToString(mVRModeOn),
boolToString(mSustainedPerfModeOn))); boolToString(mSustainedPerfModeOn)));
// Dump nodes through libperfmgr // Dump nodes through libperfmgr
mHintManager->DumpToFd(fd); HintManager::GetInstance()->DumpToFd(fd);
PowerSessionManager::getInstance()->dumpToFd(fd);
mAdaptiveCpu->DumpToFd(fd);
if (!::android::base::WriteStringToFd(buf, fd)) { if (!::android::base::WriteStringToFd(buf, fd)) {
PLOG(ERROR) << "Failed to dump state to fd"; PLOG(ERROR) << "Failed to dump state to fd";
} }
@ -268,6 +259,37 @@ binder_status_t Power::dump(int fd, const char **, uint32_t) {
return STATUS_OK; return STATUS_OK;
} }
ndk::ScopedAStatus Power::createHintSession(int32_t tgid, int32_t uid,
const std::vector<int32_t> &threadIds,
int64_t durationNanos,
std::shared_ptr<IPowerHintSession> *_aidl_return) {
if (!HintManager::GetInstance()->GetAdpfProfile() ||
HintManager::GetInstance()->GetAdpfProfile()->mReportingRateLimitNs <= 0) {
*_aidl_return = nullptr;
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
}
if (threadIds.size() == 0) {
LOG(ERROR) << "Error: threadIds.size() shouldn't be " << threadIds.size();
*_aidl_return = nullptr;
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
std::shared_ptr<IPowerHintSession> session = ndk::SharedRefBase::make<PowerHintSession>(
mAdaptiveCpu, tgid, uid, threadIds, durationNanos);
*_aidl_return = session;
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus Power::getHintSessionPreferredRate(int64_t *outNanoseconds) {
*outNanoseconds = HintManager::GetInstance()->GetAdpfProfile()
? HintManager::GetInstance()->GetAdpfProfile()->mReportingRateLimitNs
: 0;
if (*outNanoseconds <= 0) {
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
}
return ndk::ScopedAStatus::ok();
}
} // namespace pixel } // namespace pixel
} // namespace impl } // namespace impl
} // namespace power } // namespace power

View file

@ -16,13 +16,14 @@
#pragma once #pragma once
#include <aidl/android/hardware/power/BnPower.h>
#include <atomic> #include <atomic>
#include <memory> #include <memory>
#include <thread> #include <thread>
#include <aidl/android/hardware/power/BnPower.h> #include "adaptivecpu/AdaptiveCpu.h"
#include <perfmgr/HintManager.h> #include "disp-power/DisplayLowPower.h"
#include "disp-power/InteractionHandler.h" #include "disp-power/InteractionHandler.h"
namespace aidl { namespace aidl {
@ -32,22 +33,27 @@ namespace power {
namespace impl { namespace impl {
namespace pixel { namespace pixel {
using ::InteractionHandler;
using ::aidl::android::hardware::power::Boost; using ::aidl::android::hardware::power::Boost;
using ::aidl::android::hardware::power::IPowerHintSession;
using ::aidl::android::hardware::power::Mode; using ::aidl::android::hardware::power::Mode;
using ::android::perfmgr::HintManager;
class Power : public ::aidl::android::hardware::power::BnPower { class Power : public ::aidl::android::hardware::power::BnPower {
public: public:
Power(std::shared_ptr<HintManager> hm); Power(std::shared_ptr<DisplayLowPower> dlpw, std::shared_ptr<AdaptiveCpu> adaptiveCpu);
ndk::ScopedAStatus setMode(Mode type, bool enabled) override; ndk::ScopedAStatus setMode(Mode type, bool enabled) override;
ndk::ScopedAStatus isModeSupported(Mode type, bool *_aidl_return) override; ndk::ScopedAStatus isModeSupported(Mode type, bool *_aidl_return) override;
ndk::ScopedAStatus setBoost(Boost type, int32_t durationMs) override; ndk::ScopedAStatus setBoost(Boost type, int32_t durationMs) override;
ndk::ScopedAStatus isBoostSupported(Boost type, bool *_aidl_return) override; ndk::ScopedAStatus isBoostSupported(Boost type, bool *_aidl_return) override;
ndk::ScopedAStatus createHintSession(int32_t tgid, int32_t uid,
const std::vector<int32_t> &threadIds,
int64_t durationNanos,
std::shared_ptr<IPowerHintSession> *_aidl_return) override;
ndk::ScopedAStatus getHintSessionPreferredRate(int64_t *outNanoseconds) override;
binder_status_t dump(int fd, const char **args, uint32_t numArgs) override; binder_status_t dump(int fd, const char **args, uint32_t numArgs) override;
private: private:
std::shared_ptr<HintManager> mHintManager; std::shared_ptr<DisplayLowPower> mDisplayLowPower;
std::shared_ptr<AdaptiveCpu> mAdaptiveCpu;
std::unique_ptr<InteractionHandler> mInteractionHandler; std::unique_ptr<InteractionHandler> mInteractionHandler;
std::atomic<bool> mVRModeOn; std::atomic<bool> mVRModeOn;
std::atomic<bool> mSustainedPerfModeOn; std::atomic<bool> mSustainedPerfModeOn;

View file

@ -14,21 +14,21 @@
* limitations under the License. * limitations under the License.
*/ */
#define ATRACE_TAG (ATRACE_TAG_POWER | ATRACE_TAG_HAL)
#define LOG_TAG "android.hardware.power-service.pixel.ext-libperfmgr" #define LOG_TAG "android.hardware.power-service.pixel.ext-libperfmgr"
#include "PowerExt.h" #include "PowerExt.h"
#include <mutex>
#include <android-base/file.h> #include <android-base/file.h>
#include <android-base/logging.h> #include <android-base/logging.h>
#include <android-base/properties.h> #include <android-base/properties.h>
#include <android-base/stringprintf.h> #include <android-base/stringprintf.h>
#include <android-base/strings.h> #include <android-base/strings.h>
#include <perfmgr/HintManager.h>
#include <utils/Log.h> #include <utils/Log.h>
#include <utils/Trace.h>
#include <mutex>
#include "PowerSessionManager.h"
namespace aidl { namespace aidl {
namespace google { namespace google {
@ -37,21 +37,31 @@ namespace power {
namespace impl { namespace impl {
namespace pixel { namespace pixel {
using ::android::perfmgr::HintManager;
ndk::ScopedAStatus PowerExt::setMode(const std::string &mode, bool enabled) { ndk::ScopedAStatus PowerExt::setMode(const std::string &mode, bool enabled) {
LOG(DEBUG) << "PowerExt setMode: " << mode << " to: " << enabled; LOG(DEBUG) << "PowerExt setMode: " << mode << " to: " << enabled;
ATRACE_INT(mode.c_str(), enabled);
if (enabled) { if (enabled) {
mHintManager->DoHint(mode); HintManager::GetInstance()->DoHint(mode);
} else { } else {
mHintManager->EndHint(mode); HintManager::GetInstance()->EndHint(mode);
}
if (HintManager::GetInstance()->GetAdpfProfile() &&
HintManager::GetInstance()->GetAdpfProfile()->mReportingRateLimitNs > 0) {
PowerSessionManager::getInstance()->updateHintMode(mode, enabled);
}
if (mode == AdaptiveCpu::HINT_NAME) {
LOG(DEBUG) << "AdaptiveCpu intercepted hint";
mAdaptiveCpu->HintReceived(enabled);
} }
return ndk::ScopedAStatus::ok(); return ndk::ScopedAStatus::ok();
} }
ndk::ScopedAStatus PowerExt::isModeSupported(const std::string &mode, bool *_aidl_return) { ndk::ScopedAStatus PowerExt::isModeSupported(const std::string &mode, bool *_aidl_return) {
bool supported = mHintManager->IsHintSupported(mode); bool supported = HintManager::GetInstance()->IsHintSupported(mode);
LOG(INFO) << "PowerExt mode " << mode << " isModeSupported: " << supported; LOG(INFO) << "PowerExt mode " << mode << " isModeSupported: " << supported;
*_aidl_return = supported; *_aidl_return = supported;
return ndk::ScopedAStatus::ok(); return ndk::ScopedAStatus::ok();
@ -59,21 +69,24 @@ ndk::ScopedAStatus PowerExt::isModeSupported(const std::string &mode, bool *_aid
ndk::ScopedAStatus PowerExt::setBoost(const std::string &boost, int32_t durationMs) { ndk::ScopedAStatus PowerExt::setBoost(const std::string &boost, int32_t durationMs) {
LOG(DEBUG) << "PowerExt setBoost: " << boost << " duration: " << durationMs; LOG(DEBUG) << "PowerExt setBoost: " << boost << " duration: " << durationMs;
ATRACE_INT(boost.c_str(), durationMs); if (HintManager::GetInstance()->GetAdpfProfile() &&
HintManager::GetInstance()->GetAdpfProfile()->mReportingRateLimitNs > 0) {
PowerSessionManager::getInstance()->updateHintBoost(boost, durationMs);
}
if (durationMs > 0) { if (durationMs > 0) {
mHintManager->DoHint(boost, std::chrono::milliseconds(durationMs)); HintManager::GetInstance()->DoHint(boost, std::chrono::milliseconds(durationMs));
} else if (durationMs == 0) { } else if (durationMs == 0) {
mHintManager->DoHint(boost); HintManager::GetInstance()->DoHint(boost);
} else { } else {
mHintManager->EndHint(boost); HintManager::GetInstance()->EndHint(boost);
} }
return ndk::ScopedAStatus::ok(); return ndk::ScopedAStatus::ok();
} }
ndk::ScopedAStatus PowerExt::isBoostSupported(const std::string &boost, bool *_aidl_return) { ndk::ScopedAStatus PowerExt::isBoostSupported(const std::string &boost, bool *_aidl_return) {
bool supported = mHintManager->IsHintSupported(boost); bool supported = HintManager::GetInstance()->IsHintSupported(boost);
LOG(INFO) << "PowerExt boost " << boost << " isBoostSupported: " << supported; LOG(INFO) << "PowerExt boost " << boost << " isBoostSupported: " << supported;
*_aidl_return = supported; *_aidl_return = supported;
return ndk::ScopedAStatus::ok(); return ndk::ScopedAStatus::ok();

View file

@ -16,12 +16,15 @@
#pragma once #pragma once
#include <aidl/google/hardware/power/extension/pixel/BnPowerExt.h>
#include <perfmgr/HintManager.h>
#include <atomic> #include <atomic>
#include <memory> #include <memory>
#include <thread> #include <thread>
#include <aidl/google/hardware/power/extension/pixel/BnPowerExt.h> #include "adaptivecpu/AdaptiveCpu.h"
#include <perfmgr/HintManager.h> #include "disp-power/DisplayLowPower.h"
namespace aidl { namespace aidl {
namespace google { namespace google {
@ -30,19 +33,18 @@ namespace power {
namespace impl { namespace impl {
namespace pixel { namespace pixel {
using ::android::perfmgr::HintManager;
class PowerExt : public ::aidl::google::hardware::power::extension::pixel::BnPowerExt { class PowerExt : public ::aidl::google::hardware::power::extension::pixel::BnPowerExt {
public: public:
PowerExt(std::shared_ptr<HintManager> hm) PowerExt(std::shared_ptr<DisplayLowPower> dlpw, std::shared_ptr<AdaptiveCpu> acpu)
: mHintManager(hm) {} : mDisplayLowPower(dlpw), mAdaptiveCpu(acpu) {}
ndk::ScopedAStatus setMode(const std::string &mode, bool enabled) override; ndk::ScopedAStatus setMode(const std::string &mode, bool enabled) override;
ndk::ScopedAStatus isModeSupported(const std::string &mode, bool *_aidl_return) override; ndk::ScopedAStatus isModeSupported(const std::string &mode, bool *_aidl_return) override;
ndk::ScopedAStatus setBoost(const std::string &boost, int32_t durationMs) override; ndk::ScopedAStatus setBoost(const std::string &boost, int32_t durationMs) override;
ndk::ScopedAStatus isBoostSupported(const std::string &boost, bool *_aidl_return) override; ndk::ScopedAStatus isBoostSupported(const std::string &boost, bool *_aidl_return) override;
private: private:
std::shared_ptr<HintManager> mHintManager; std::shared_ptr<DisplayLowPower> mDisplayLowPower;
std::shared_ptr<AdaptiveCpu> mAdaptiveCpu;
}; };
} // namespace pixel } // namespace pixel

View file

@ -0,0 +1,596 @@
/*
* Copyright 2021 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 "powerhal-libperfmgr"
#define ATRACE_TAG (ATRACE_TAG_POWER | ATRACE_TAG_HAL)
#include "PowerHintSession.h"
#include <android-base/logging.h>
#include <android-base/parsedouble.h>
#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <perfmgr/AdpfConfig.h>
#include <private/android_filesystem_config.h>
#include <sys/syscall.h>
#include <time.h>
#include <utils/Trace.h>
#include <atomic>
#include "PowerSessionManager.h"
namespace aidl {
namespace google {
namespace hardware {
namespace power {
namespace impl {
namespace pixel {
using ::android::base::StringPrintf;
using ::android::perfmgr::AdpfConfig;
using ::android::perfmgr::HintManager;
using std::chrono::duration_cast;
using std::chrono::nanoseconds;
namespace {
static inline int64_t ns_to_100us(int64_t ns) {
return ns / 100000;
}
static int64_t convertWorkDurationToBoostByPid(std::shared_ptr<AdpfConfig> adpfConfig,
nanoseconds targetDuration,
const std::vector<WorkDuration> &actualDurations,
int64_t *integral_error, int64_t *previous_error,
const std::string &idstr) {
uint64_t samplingWindowP = adpfConfig->mSamplingWindowP;
uint64_t samplingWindowI = adpfConfig->mSamplingWindowI;
uint64_t samplingWindowD = adpfConfig->mSamplingWindowD;
int64_t targetDurationNanos = (int64_t)targetDuration.count();
int64_t length = actualDurations.size();
int64_t p_start =
samplingWindowP == 0 || samplingWindowP > length ? 0 : length - samplingWindowP;
int64_t i_start =
samplingWindowI == 0 || samplingWindowI > length ? 0 : length - samplingWindowI;
int64_t d_start =
samplingWindowD == 0 || samplingWindowD > length ? 0 : length - samplingWindowD;
int64_t dt = ns_to_100us(targetDurationNanos);
int64_t err_sum = 0;
int64_t derivative_sum = 0;
for (int64_t i = std::min({p_start, i_start, d_start}); i < length; i++) {
int64_t actualDurationNanos = actualDurations[i].durationNanos;
if (std::abs(actualDurationNanos) > targetDurationNanos * 20) {
ALOGW("The actual duration is way far from the target (%" PRId64 " >> %" PRId64 ")",
actualDurationNanos, targetDurationNanos);
}
// PID control algorithm
int64_t error = ns_to_100us(actualDurationNanos - targetDurationNanos);
if (i >= d_start) {
derivative_sum += error - (*previous_error);
}
if (i >= p_start) {
err_sum += error;
}
if (i >= i_start) {
*integral_error = *integral_error + error * dt;
*integral_error = std::min(adpfConfig->getPidIHighDivI(), *integral_error);
*integral_error = std::max(adpfConfig->getPidILowDivI(), *integral_error);
}
*previous_error = error;
}
int64_t pOut = static_cast<int64_t>((err_sum > 0 ? adpfConfig->mPidPo : adpfConfig->mPidPu) *
err_sum / (length - p_start));
int64_t iOut = static_cast<int64_t>(adpfConfig->mPidI * (*integral_error));
int64_t dOut =
static_cast<int64_t>((derivative_sum > 0 ? adpfConfig->mPidDo : adpfConfig->mPidDu) *
derivative_sum / dt / (length - d_start));
int64_t output = pOut + iOut + dOut;
if (ATRACE_ENABLED()) {
std::string sz = StringPrintf("adpf.%s-pid.err", idstr.c_str());
ATRACE_INT(sz.c_str(), err_sum / (length - p_start));
sz = StringPrintf("adpf.%s-pid.integral", idstr.c_str());
ATRACE_INT(sz.c_str(), *integral_error);
sz = StringPrintf("adpf.%s-pid.derivative", idstr.c_str());
ATRACE_INT(sz.c_str(), derivative_sum / dt / (length - d_start));
sz = StringPrintf("adpf.%s-pid.pOut", idstr.c_str());
ATRACE_INT(sz.c_str(), pOut);
sz = StringPrintf("adpf.%s-pid.iOut", idstr.c_str());
ATRACE_INT(sz.c_str(), iOut);
sz = StringPrintf("adpf.%s-pid.dOut", idstr.c_str());
ATRACE_INT(sz.c_str(), dOut);
sz = StringPrintf("adpf.%s-pid.output", idstr.c_str());
ATRACE_INT(sz.c_str(), output);
}
return output;
}
} // namespace
PowerHintSession::PowerHintSession(std::shared_ptr<AdaptiveCpu> adaptiveCpu, int32_t tgid,
int32_t uid, const std::vector<int32_t> &threadIds,
int64_t durationNanos)
: mAdaptiveCpu(adaptiveCpu) {
mDescriptor = new AppHintDesc(tgid, uid, threadIds);
mDescriptor->duration = std::chrono::nanoseconds(durationNanos);
mStaleTimerHandler = sp<StaleTimerHandler>(new StaleTimerHandler(this));
mEarlyBoostHandler = sp<EarlyBoostHandler>(new EarlyBoostHandler(this));
mPowerManagerHandler = PowerSessionManager::getInstance();
mLastUpdatedTime.store(std::chrono::steady_clock::now());
mLastStartedTimeNs =
std::chrono::duration_cast<std::chrono::nanoseconds>(
(std::chrono::steady_clock::now() - mDescriptor->duration).time_since_epoch())
.count();
mLastDurationNs = durationNanos;
mWorkPeriodNs = durationNanos;
if (ATRACE_ENABLED()) {
const std::string idstr = getIdString();
std::string sz = StringPrintf("adpf.%s-target", idstr.c_str());
ATRACE_INT(sz.c_str(), (int64_t)mDescriptor->duration.count());
sz = StringPrintf("adpf.%s-active", idstr.c_str());
ATRACE_INT(sz.c_str(), mDescriptor->is_active.load());
}
PowerSessionManager::getInstance()->addPowerSession(this);
// init boost
setSessionUclampMin(HintManager::GetInstance()->GetAdpfProfile()->mUclampMinInit);
ALOGV("PowerHintSession created: %s", mDescriptor->toString().c_str());
}
PowerHintSession::~PowerHintSession() {
close();
ALOGV("PowerHintSession deleted: %s", mDescriptor->toString().c_str());
if (ATRACE_ENABLED()) {
const std::string idstr = getIdString();
std::string sz = StringPrintf("adpf.%s-target", idstr.c_str());
ATRACE_INT(sz.c_str(), 0);
sz = StringPrintf("adpf.%s-actl_last", idstr.c_str());
ATRACE_INT(sz.c_str(), 0);
sz = sz = StringPrintf("adpf.%s-active", idstr.c_str());
ATRACE_INT(sz.c_str(), 0);
}
delete mDescriptor;
}
std::string PowerHintSession::getIdString() const {
std::string idstr = StringPrintf("%" PRId32 "-%" PRId32 "-%" PRIxPTR, mDescriptor->tgid,
mDescriptor->uid, reinterpret_cast<uintptr_t>(this) & 0xffff);
return idstr;
}
bool PowerHintSession::isAppSession() {
// Check if uid is in range reserved for applications
return mDescriptor->uid >= AID_APP_START;
}
void PowerHintSession::updateUniveralBoostMode() {
if (!isAppSession()) {
return;
}
if (ATRACE_ENABLED()) {
const std::string tag = StringPrintf("%s:updateUniveralBoostMode()", getIdString().c_str());
ATRACE_BEGIN(tag.c_str());
}
PowerHintMonitor::getInstance()->getLooper()->sendMessage(mPowerManagerHandler, NULL);
if (ATRACE_ENABLED()) {
ATRACE_END();
}
}
int PowerHintSession::setSessionUclampMin(int32_t min) {
{
std::lock_guard<std::mutex> guard(mSessionLock);
mDescriptor->current_min = min;
}
if (min) {
mStaleTimerHandler->updateTimer();
}
PowerSessionManager::getInstance()->setUclampMin(this, min);
if (ATRACE_ENABLED()) {
const std::string idstr = getIdString();
std::string sz = StringPrintf("adpf.%s-min", idstr.c_str());
ATRACE_INT(sz.c_str(), min);
}
return 0;
}
int PowerHintSession::getUclampMin() {
return mDescriptor->current_min;
}
void PowerHintSession::dumpToStream(std::ostream &stream) {
stream << "ID.Min.Act.Timeout(" << getIdString();
stream << ", " << mDescriptor->current_min;
stream << ", " << mDescriptor->is_active;
stream << ", " << isTimeout() << ")";
}
ndk::ScopedAStatus PowerHintSession::pause() {
if (mSessionClosed) {
ALOGE("Error: session is dead");
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
if (!mDescriptor->is_active.load())
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
// Reset to default uclamp value.
mDescriptor->is_active.store(false);
setStale();
if (ATRACE_ENABLED()) {
const std::string idstr = getIdString();
std::string sz = StringPrintf("adpf.%s-active", idstr.c_str());
ATRACE_INT(sz.c_str(), mDescriptor->is_active.load());
}
updateUniveralBoostMode();
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus PowerHintSession::resume() {
if (mSessionClosed) {
ALOGE("Error: session is dead");
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
if (mDescriptor->is_active.load())
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
mDescriptor->is_active.store(true);
// resume boost
setSessionUclampMin(mDescriptor->current_min);
if (ATRACE_ENABLED()) {
const std::string idstr = getIdString();
std::string sz = StringPrintf("adpf.%s-active", idstr.c_str());
ATRACE_INT(sz.c_str(), mDescriptor->is_active.load());
}
updateUniveralBoostMode();
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus PowerHintSession::close() {
bool sessionClosedExpectedToBe = false;
if (!mSessionClosed.compare_exchange_strong(sessionClosedExpectedToBe, true)) {
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
// Remove the session from PowerSessionManager first to avoid racing.
PowerSessionManager::getInstance()->removePowerSession(this);
setSessionUclampMin(0);
{
std::lock_guard<std::mutex> guard(mSessionLock);
mSessionClosed.store(true);
}
mDescriptor->is_active.store(false);
mEarlyBoostHandler->setSessionDead();
mStaleTimerHandler->setSessionDead();
updateUniveralBoostMode();
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus PowerHintSession::updateTargetWorkDuration(int64_t targetDurationNanos) {
if (mSessionClosed) {
ALOGE("Error: session is dead");
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
if (targetDurationNanos <= 0) {
ALOGE("Error: targetDurationNanos(%" PRId64 ") should bigger than 0", targetDurationNanos);
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
targetDurationNanos =
targetDurationNanos * HintManager::GetInstance()->GetAdpfProfile()->mTargetTimeFactor;
ALOGV("update target duration: %" PRId64 " ns", targetDurationNanos);
mDescriptor->duration = std::chrono::nanoseconds(targetDurationNanos);
if (ATRACE_ENABLED()) {
const std::string idstr = getIdString();
std::string sz = StringPrintf("adpf.%s-target", idstr.c_str());
ATRACE_INT(sz.c_str(), (int64_t)mDescriptor->duration.count());
}
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus PowerHintSession::reportActualWorkDuration(
const std::vector<WorkDuration> &actualDurations) {
if (mSessionClosed) {
ALOGE("Error: session is dead");
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
if (mDescriptor->duration.count() == 0LL) {
ALOGE("Expect to call updateTargetWorkDuration() first.");
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
if (actualDurations.size() == 0) {
ALOGE("Error: duration.size() shouldn't be %zu.", actualDurations.size());
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
if (!mDescriptor->is_active.load()) {
ALOGE("Error: shouldn't report duration during pause state.");
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
std::shared_ptr<AdpfConfig> adpfConfig = HintManager::GetInstance()->GetAdpfProfile();
mDescriptor->update_count++;
bool isFirstFrame = isTimeout();
if (ATRACE_ENABLED()) {
const std::string idstr = getIdString();
std::string sz = StringPrintf("adpf.%s-batch_size", idstr.c_str());
ATRACE_INT(sz.c_str(), actualDurations.size());
sz = StringPrintf("adpf.%s-actl_last", idstr.c_str());
ATRACE_INT(sz.c_str(), actualDurations.back().durationNanos);
sz = StringPrintf("adpf.%s-target", idstr.c_str());
ATRACE_INT(sz.c_str(), (int64_t)mDescriptor->duration.count());
sz = StringPrintf("adpf.%s-hint.count", idstr.c_str());
ATRACE_INT(sz.c_str(), mDescriptor->update_count);
sz = StringPrintf("adpf.%s-hint.overtime", idstr.c_str());
ATRACE_INT(sz.c_str(),
actualDurations.back().durationNanos - mDescriptor->duration.count() > 0);
}
mLastUpdatedTime.store(std::chrono::steady_clock::now());
if (isFirstFrame) {
updateUniveralBoostMode();
}
if (!adpfConfig->mPidOn) {
setSessionUclampMin(adpfConfig->mUclampMinHigh);
return ndk::ScopedAStatus::ok();
}
int64_t output = convertWorkDurationToBoostByPid(
adpfConfig, mDescriptor->duration, actualDurations, &(mDescriptor->integral_error),
&(mDescriptor->previous_error), getIdString());
/* apply to all the threads in the group */
int next_min = std::min(static_cast<int>(adpfConfig->mUclampMinHigh),
mDescriptor->current_min + static_cast<int>(output));
next_min = std::max(static_cast<int>(adpfConfig->mUclampMinLow), next_min);
setSessionUclampMin(next_min);
mStaleTimerHandler->updateTimer(getStaleTime());
if (HintManager::GetInstance()->GetAdpfProfile()->mEarlyBoostOn) {
updateWorkPeriod(actualDurations);
mEarlyBoostHandler->updateTimer(getEarlyBoostTime());
}
mAdaptiveCpu->ReportWorkDurations(actualDurations, mDescriptor->duration);
return ndk::ScopedAStatus::ok();
}
std::string AppHintDesc::toString() const {
std::string out =
StringPrintf("session %" PRIxPTR "\n", reinterpret_cast<uintptr_t>(this) & 0xffff);
const int64_t durationNanos = duration.count();
out.append(StringPrintf(" duration: %" PRId64 " ns\n", durationNanos));
out.append(StringPrintf(" uclamp.min: %d \n", current_min));
out.append(StringPrintf(" uid: %d, tgid: %d\n", uid, tgid));
out.append(" threadIds: [");
bool first = true;
for (int tid : threadIds) {
if (!first) {
out.append(", ");
}
out.append(std::to_string(tid));
first = false;
}
out.append("]\n");
return out;
}
bool PowerHintSession::isActive() {
return mDescriptor->is_active.load();
}
bool PowerHintSession::isTimeout() {
auto now = std::chrono::steady_clock::now();
return now >= getStaleTime();
}
const std::vector<int> &PowerHintSession::getTidList() const {
return mDescriptor->threadIds;
}
void PowerHintSession::setStale() {
// Reset to default uclamp value.
PowerSessionManager::getInstance()->setUclampMin(this, 0);
// Deliver a task to check if all sessions are inactive.
updateUniveralBoostMode();
if (ATRACE_ENABLED()) {
const std::string idstr = getIdString();
std::string sz = StringPrintf("adpf.%s-min", idstr.c_str());
ATRACE_INT(sz.c_str(), 0);
}
}
void PowerHintSession::wakeup() {
std::lock_guard<std::mutex> guard(mSessionLock);
// We only wake up non-paused and stale sessions
if (mSessionClosed || !isActive() || !isTimeout())
return;
if (ATRACE_ENABLED()) {
std::string tag = StringPrintf("wakeup.%s(a:%d,s:%d)", getIdString().c_str(), isActive(),
isTimeout());
ATRACE_NAME(tag.c_str());
}
std::shared_ptr<AdpfConfig> adpfConfig = HintManager::GetInstance()->GetAdpfProfile();
int min = std::max(mDescriptor->current_min, static_cast<int>(adpfConfig->mUclampMinInit));
mDescriptor->current_min = min;
PowerSessionManager::getInstance()->setUclampMinLocked(this, min);
mStaleTimerHandler->updateTimer();
if (ATRACE_ENABLED()) {
const std::string idstr = getIdString();
std::string sz = StringPrintf("adpf.%s-min", idstr.c_str());
ATRACE_INT(sz.c_str(), min);
}
}
void PowerHintSession::updateWorkPeriod(const std::vector<WorkDuration> &actualDurations) {
if (actualDurations.size() == 0)
return;
if (actualDurations.size() >= 2) {
const WorkDuration &last = actualDurations[actualDurations.size() - 2];
mLastStartedTimeNs = last.timeStampNanos - last.durationNanos;
}
const WorkDuration &current = actualDurations.back();
int64_t curr_start = current.timeStampNanos - current.durationNanos;
int64_t period = curr_start - mLastStartedTimeNs;
if (period > 0 && period < mDescriptor->duration.count() * 2) {
// Accounting workload period with moving average for the last 10 workload.
mWorkPeriodNs = 0.9 * mWorkPeriodNs + 0.1 * period;
if (ATRACE_ENABLED()) {
const std::string idstr = getIdString();
std::string sz = StringPrintf("adpf.%s-timer.period", idstr.c_str());
ATRACE_INT(sz.c_str(), mWorkPeriodNs);
}
}
mLastStartedTimeNs = curr_start;
mLastDurationNs = current.durationNanos;
}
time_point<steady_clock> PowerHintSession::getEarlyBoostTime() {
std::shared_ptr<AdpfConfig> adpfConfig = HintManager::GetInstance()->GetAdpfProfile();
int64_t earlyBoostTimeoutNs =
(int64_t)mDescriptor->duration.count() * adpfConfig->mEarlyBoostTimeFactor;
time_point<steady_clock> nextStartTime =
mLastUpdatedTime.load() + nanoseconds(mWorkPeriodNs - mLastDurationNs);
return nextStartTime + nanoseconds(earlyBoostTimeoutNs);
}
time_point<steady_clock> PowerHintSession::getStaleTime() {
return mLastUpdatedTime.load() +
nanoseconds(static_cast<int64_t>(
mDescriptor->duration.count() *
HintManager::GetInstance()->GetAdpfProfile()->mStaleTimeFactor));
}
void PowerHintSession::StaleTimerHandler::updateTimer() {
time_point<steady_clock> staleTime =
std::chrono::steady_clock::now() +
nanoseconds(static_cast<int64_t>(
mSession->mDescriptor->duration.count() *
HintManager::GetInstance()->GetAdpfProfile()->mStaleTimeFactor));
updateTimer(staleTime);
}
void PowerHintSession::StaleTimerHandler::updateTimer(time_point<steady_clock> staleTime) {
mStaleTime.store(staleTime);
{
std::lock_guard<std::mutex> guard(mMessageLock);
PowerHintMonitor::getInstance()->getLooper()->removeMessages(mSession->mStaleTimerHandler);
PowerHintMonitor::getInstance()->getLooper()->sendMessage(mSession->mStaleTimerHandler,
NULL);
}
mIsMonitoring.store(true);
if (ATRACE_ENABLED()) {
const std::string idstr = mSession->getIdString();
std::string sz = StringPrintf("adpf.%s-timer.stale", idstr.c_str());
ATRACE_INT(sz.c_str(), 0);
}
}
void PowerHintSession::StaleTimerHandler::handleMessage(const Message &) {
if (mIsSessionDead) {
return;
}
auto now = std::chrono::steady_clock::now();
int64_t next =
static_cast<int64_t>(duration_cast<nanoseconds>(mStaleTime.load() - now).count());
if (next > 0) {
// Schedule for the stale timeout check.
std::lock_guard<std::mutex> guard(mMessageLock);
PowerHintMonitor::getInstance()->getLooper()->removeMessages(mSession->mStaleTimerHandler);
PowerHintMonitor::getInstance()->getLooper()->sendMessageDelayed(
next, mSession->mStaleTimerHandler, NULL);
} else {
mSession->setStale();
mIsMonitoring.store(false);
if (ATRACE_ENABLED()) {
const std::string idstr = mSession->getIdString();
std::string sz = StringPrintf("adpf.%s-timer.earlyboost", idstr.c_str());
ATRACE_INT(sz.c_str(), 0);
}
}
if (ATRACE_ENABLED()) {
const std::string idstr = mSession->getIdString();
std::string sz = StringPrintf("adpf.%s-timer.stale", idstr.c_str());
ATRACE_INT(sz.c_str(), mIsMonitoring ? 0 : 1);
}
}
void PowerHintSession::StaleTimerHandler::setSessionDead() {
std::lock_guard<std::mutex> guard(mStaleLock);
mIsSessionDead = true;
PowerHintMonitor::getInstance()->getLooper()->removeMessages(mSession->mStaleTimerHandler);
}
void PowerHintSession::EarlyBoostHandler::updateTimer(time_point<steady_clock> boostTime) {
mBoostTime.store(boostTime);
{
std::lock_guard<std::mutex> guard(mMessageLock);
PowerHintMonitor::getInstance()->getLooper()->removeMessages(mSession->mEarlyBoostHandler);
PowerHintMonitor::getInstance()->getLooper()->sendMessage(mSession->mEarlyBoostHandler,
NULL);
}
mIsMonitoring.store(true);
if (ATRACE_ENABLED()) {
const std::string idstr = mSession->getIdString();
std::string sz = StringPrintf("adpf.%s-timer.earlyboost", idstr.c_str());
ATRACE_INT(sz.c_str(), 1);
}
}
void PowerHintSession::EarlyBoostHandler::handleMessage(const Message &) {
std::lock_guard<std::mutex> guard(mBoostLock);
if (mIsSessionDead) {
return;
}
auto now = std::chrono::steady_clock::now();
int64_t next =
static_cast<int64_t>(duration_cast<nanoseconds>(mBoostTime.load() - now).count());
if (next > 0) {
if (ATRACE_ENABLED()) {
const std::string idstr = mSession->getIdString();
std::string sz = StringPrintf("adpf.%s-timer.earlyboost", idstr.c_str());
ATRACE_INT(sz.c_str(), 1);
}
std::lock_guard<std::mutex> guard(mMessageLock);
PowerHintMonitor::getInstance()->getLooper()->removeMessages(mSession->mEarlyBoostHandler);
PowerHintMonitor::getInstance()->getLooper()->sendMessageDelayed(
next, mSession->mEarlyBoostHandler, NULL);
} else {
std::shared_ptr<AdpfConfig> adpfConfig = HintManager::GetInstance()->GetAdpfProfile();
PowerSessionManager::getInstance()->setUclampMin(mSession, adpfConfig->mUclampMinHigh);
mIsMonitoring.store(false);
if (ATRACE_ENABLED()) {
const std::string idstr = mSession->getIdString();
std::string sz = StringPrintf("adpf.%s-min", idstr.c_str());
ATRACE_INT(sz.c_str(), adpfConfig->mUclampMinHigh);
sz = StringPrintf("adpf.%s-timer.earlyboost", idstr.c_str());
ATRACE_INT(sz.c_str(), 2);
}
}
}
void PowerHintSession::EarlyBoostHandler::setSessionDead() {
std::lock_guard<std::mutex> guard(mBoostLock);
mIsSessionDead = true;
PowerHintMonitor::getInstance()->getLooper()->removeMessages(mSession->mEarlyBoostHandler);
}
} // namespace pixel
} // namespace impl
} // namespace power
} // namespace hardware
} // namespace google
} // namespace aidl

View file

@ -0,0 +1,155 @@
/*
* Copyright 2021 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.
*/
#pragma once
#include <aidl/android/hardware/power/BnPowerHintSession.h>
#include <aidl/android/hardware/power/WorkDuration.h>
#include <utils/Looper.h>
#include <utils/Thread.h>
#include <mutex>
#include <unordered_map>
#include "adaptivecpu/AdaptiveCpu.h"
namespace aidl {
namespace google {
namespace hardware {
namespace power {
namespace impl {
namespace pixel {
using aidl::android::hardware::power::BnPowerHintSession;
using aidl::android::hardware::power::WorkDuration;
using ::android::Message;
using ::android::MessageHandler;
using ::android::sp;
using std::chrono::milliseconds;
using std::chrono::nanoseconds;
using std::chrono::steady_clock;
using std::chrono::time_point;
struct AppHintDesc {
AppHintDesc(int32_t tgid, int32_t uid, std::vector<int> threadIds)
: tgid(tgid),
uid(uid),
threadIds(std::move(threadIds)),
duration(0LL),
current_min(0),
is_active(true),
update_count(0),
integral_error(0),
previous_error(0) {}
std::string toString() const;
const int32_t tgid;
const int32_t uid;
const std::vector<int> threadIds;
nanoseconds duration;
int current_min;
// status
std::atomic<bool> is_active;
// pid
uint64_t update_count;
int64_t integral_error;
int64_t previous_error;
};
class PowerHintSession : public BnPowerHintSession {
public:
explicit PowerHintSession(std::shared_ptr<AdaptiveCpu> adaptiveCpu, int32_t tgid, int32_t uid,
const std::vector<int32_t> &threadIds, int64_t durationNanos);
~PowerHintSession();
ndk::ScopedAStatus close() override;
ndk::ScopedAStatus pause() override;
ndk::ScopedAStatus resume() override;
ndk::ScopedAStatus updateTargetWorkDuration(int64_t targetDurationNanos) override;
ndk::ScopedAStatus reportActualWorkDuration(
const std::vector<WorkDuration> &actualDurations) override;
bool isActive();
bool isTimeout();
void wakeup();
void setStale();
// Is this hint session for a user application
bool isAppSession();
const std::vector<int> &getTidList() const;
int getUclampMin();
void dumpToStream(std::ostream &stream);
void updateWorkPeriod(const std::vector<WorkDuration> &actualDurations);
time_point<steady_clock> getEarlyBoostTime();
time_point<steady_clock> getStaleTime();
private:
class StaleTimerHandler : public MessageHandler {
public:
StaleTimerHandler(PowerHintSession *session)
: mSession(session), mIsMonitoring(false), mIsSessionDead(false) {}
void updateTimer();
void updateTimer(time_point<steady_clock> staleTime);
void handleMessage(const Message &message) override;
void setSessionDead();
private:
PowerHintSession *mSession;
std::mutex mStaleLock;
std::mutex mMessageLock;
std::atomic<time_point<steady_clock>> mStaleTime;
std::atomic<bool> mIsMonitoring;
bool mIsSessionDead;
};
class EarlyBoostHandler : public MessageHandler {
public:
EarlyBoostHandler(PowerHintSession *session)
: mSession(session), mIsMonitoring(false), mIsSessionDead(false) {}
void updateTimer(time_point<steady_clock> boostTime);
void handleMessage(const Message &message) override;
void setSessionDead();
private:
PowerHintSession *mSession;
std::mutex mBoostLock;
std::mutex mMessageLock;
std::atomic<time_point<steady_clock>> mBoostTime;
std::atomic<bool> mIsMonitoring;
bool mIsSessionDead;
};
private:
void updateUniveralBoostMode();
int setSessionUclampMin(int32_t min);
std::string getIdString() const;
const std::shared_ptr<AdaptiveCpu> mAdaptiveCpu;
AppHintDesc *mDescriptor = nullptr;
sp<StaleTimerHandler> mStaleTimerHandler;
sp<EarlyBoostHandler> mEarlyBoostHandler;
std::atomic<time_point<steady_clock>> mLastUpdatedTime;
sp<MessageHandler> mPowerManagerHandler;
std::mutex mSessionLock;
std::atomic<bool> mSessionClosed = false;
// These 3 variables are for earlyboost work period estimation.
int64_t mLastStartedTimeNs;
int64_t mLastDurationNs;
int64_t mWorkPeriodNs;
};
} // namespace pixel
} // namespace impl
} // namespace power
} // namespace hardware
} // namespace google
} // namespace aidl

View file

@ -0,0 +1,267 @@
/*
* Copyright 2021 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 "powerhal-libperfmgr"
#define ATRACE_TAG (ATRACE_TAG_POWER | ATRACE_TAG_HAL)
#include "PowerSessionManager.h"
#include <android-base/file.h>
#include <log/log.h>
#include <perfmgr/HintManager.h>
#include <processgroup/processgroup.h>
#include <sys/syscall.h>
#include <utils/Trace.h>
namespace aidl {
namespace google {
namespace hardware {
namespace power {
namespace impl {
namespace pixel {
using ::android::perfmgr::HintManager;
namespace {
/* there is no glibc or bionic wrapper */
struct sched_attr {
__u32 size;
__u32 sched_policy;
__u64 sched_flags;
__s32 sched_nice;
__u32 sched_priority;
__u64 sched_runtime;
__u64 sched_deadline;
__u64 sched_period;
__u32 sched_util_min;
__u32 sched_util_max;
};
static int sched_setattr(int pid, struct sched_attr *attr, unsigned int flags) {
if (!HintManager::GetInstance()->GetAdpfProfile()->mUclampMinOn) {
ALOGV("PowerSessionManager:%s: skip", __func__);
return 0;
}
return syscall(__NR_sched_setattr, pid, attr, flags);
}
static void set_uclamp_min(int tid, int min) {
static constexpr int32_t kMaxUclampValue = 1024;
min = std::max(0, min);
min = std::min(min, kMaxUclampValue);
sched_attr attr = {};
attr.size = sizeof(attr);
attr.sched_flags = (SCHED_FLAG_KEEP_ALL | SCHED_FLAG_UTIL_CLAMP_MIN);
attr.sched_util_min = min;
int ret = sched_setattr(tid, &attr, 0);
if (ret) {
ALOGW("sched_setattr failed for thread %d, err=%d", tid, errno);
}
}
} // namespace
void PowerSessionManager::updateHintMode(const std::string &mode, bool enabled) {
ALOGV("PowerSessionManager::updateHintMode: mode: %s, enabled: %d", mode.c_str(), enabled);
if (enabled && mode.compare(0, 8, "REFRESH_") == 0) {
if (mode.compare("REFRESH_120FPS") == 0) {
mDisplayRefreshRate = 120;
} else if (mode.compare("REFRESH_90FPS") == 0) {
mDisplayRefreshRate = 90;
} else if (mode.compare("REFRESH_60FPS") == 0) {
mDisplayRefreshRate = 60;
}
}
if (HintManager::GetInstance()->GetAdpfProfile()) {
HintManager::GetInstance()->SetAdpfProfile(mode);
}
}
void PowerSessionManager::updateHintBoost(const std::string &boost, int32_t durationMs) {
ATRACE_CALL();
ALOGV("PowerSessionManager::updateHintBoost: boost: %s, durationMs: %d", boost.c_str(),
durationMs);
if (boost.compare("DISPLAY_UPDATE_IMMINENT") == 0) {
PowerHintMonitor::getInstance()->getLooper()->sendMessage(mWakeupHandler, NULL);
}
}
void PowerSessionManager::wakeSessions() {
std::lock_guard<std::mutex> guard(mLock);
for (PowerHintSession *s : mSessions) {
s->wakeup();
}
}
int PowerSessionManager::getDisplayRefreshRate() {
return mDisplayRefreshRate;
}
void PowerSessionManager::addPowerSession(PowerHintSession *session) {
std::lock_guard<std::mutex> guard(mLock);
for (auto t : session->getTidList()) {
mTidSessionListMap[t].insert(session);
if (mTidRefCountMap.find(t) == mTidRefCountMap.end()) {
if (!SetTaskProfiles(t, {"ResetUclampGrp"})) {
ALOGW("Failed to set ResetUclampGrp task profile for tid:%d", t);
} else {
mTidRefCountMap[t] = 1;
}
continue;
}
if (mTidRefCountMap[t] <= 0) {
ALOGE("Error! Unexpected zero/negative RefCount:%d for tid:%d", mTidRefCountMap[t], t);
continue;
}
mTidRefCountMap[t]++;
}
mSessions.insert(session);
}
void PowerSessionManager::removePowerSession(PowerHintSession *session) {
std::lock_guard<std::mutex> guard(mLock);
for (auto t : session->getTidList()) {
if (mTidRefCountMap.find(t) == mTidRefCountMap.end()) {
ALOGE("Unexpected Error! Failed to look up tid:%d in TidRefCountMap", t);
continue;
}
mTidSessionListMap[t].erase(session);
mTidRefCountMap[t]--;
if (mTidRefCountMap[t] <= 0) {
if (!SetTaskProfiles(t, {"NoResetUclampGrp"})) {
ALOGW("Failed to set NoResetUclampGrp task profile for tid:%d", t);
}
mTidRefCountMap.erase(t);
}
}
mSessions.erase(session);
}
void PowerSessionManager::setUclampMin(PowerHintSession *session, int val) {
std::lock_guard<std::mutex> guard(mLock);
setUclampMinLocked(session, val);
}
void PowerSessionManager::setUclampMinLocked(PowerHintSession *session, int val) {
for (auto t : session->getTidList()) {
// Get thex max uclamp.min across sessions which include the tid.
int tidMax = 0;
for (PowerHintSession *s : mTidSessionListMap[t]) {
if (!s->isActive() || s->isTimeout())
continue;
tidMax = std::max(tidMax, s->getUclampMin());
}
set_uclamp_min(t, std::max(val, tidMax));
}
}
std::optional<bool> PowerSessionManager::isAnyAppSessionActive() {
std::lock_guard<std::mutex> guard(mLock);
bool active = false;
for (PowerHintSession *s : mSessions) {
// session active and not stale is actually active.
if (s->isActive() && !s->isTimeout() && s->isAppSession()) {
active = true;
break;
}
}
if (active == mActive) {
return std::nullopt;
} else {
mActive = active;
}
return active;
}
void PowerSessionManager::handleMessage(const Message &) {
auto active = isAnyAppSessionActive();
if (!active.has_value()) {
return;
}
if (active.value()) {
disableSystemTopAppBoost();
} else {
enableSystemTopAppBoost();
}
}
void PowerSessionManager::WakeupHandler::handleMessage(const Message &) {
PowerSessionManager::getInstance()->wakeSessions();
}
void PowerSessionManager::dumpToFd(int fd) {
std::ostringstream dump_buf;
std::lock_guard<std::mutex> guard(mLock);
dump_buf << "========== Begin PowerSessionManager ADPF list ==========\n";
for (PowerHintSession *s : mSessions) {
s->dumpToStream(dump_buf);
dump_buf << " Tid:Ref[";
for (size_t i = 0, len = s->getTidList().size(); i < len; i++) {
int t = s->getTidList()[i];
dump_buf << t << ":" << mTidSessionListMap[t].size();
if (i < len - 1) {
dump_buf << ", ";
}
}
dump_buf << "]\n";
}
dump_buf << "========== End PowerSessionManager ADPF list ==========\n";
if (!::android::base::WriteStringToFd(dump_buf.str(), fd)) {
ALOGE("Failed to dump one of session list to fd:%d", fd);
}
}
void PowerSessionManager::enableSystemTopAppBoost() {
if (HintManager::GetInstance()->IsHintSupported(kDisableBoostHintName)) {
ALOGV("PowerSessionManager::enableSystemTopAppBoost!!");
HintManager::GetInstance()->EndHint(kDisableBoostHintName);
}
}
void PowerSessionManager::disableSystemTopAppBoost() {
if (HintManager::GetInstance()->IsHintSupported(kDisableBoostHintName)) {
ALOGV("PowerSessionManager::disableSystemTopAppBoost!!");
HintManager::GetInstance()->DoHint(kDisableBoostHintName);
}
}
// =========== PowerHintMonitor implementation start from here ===========
void PowerHintMonitor::start() {
if (!isRunning()) {
run("PowerHintMonitor", ::android::PRIORITY_HIGHEST);
}
}
bool PowerHintMonitor::threadLoop() {
while (true) {
mLooper->pollOnce(-1);
}
return true;
}
sp<Looper> PowerHintMonitor::getLooper() {
return mLooper;
}
} // namespace pixel
} // namespace impl
} // namespace power
} // namespace hardware
} // namespace google
} // namespace aidl

View file

@ -0,0 +1,124 @@
/*
* Copyright 2021 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.
*/
#pragma once
#include <android-base/properties.h>
#include <perfmgr/HintManager.h>
#include <utils/Looper.h>
#include <mutex>
#include <optional>
#include <unordered_set>
#include "PowerHintSession.h"
namespace aidl {
namespace google {
namespace hardware {
namespace power {
namespace impl {
namespace pixel {
using ::android::Looper;
using ::android::Message;
using ::android::MessageHandler;
using ::android::Thread;
using ::android::perfmgr::HintManager;
constexpr char kPowerHalAdpfDisableTopAppBoost[] = "vendor.powerhal.adpf.disable.hint";
class PowerSessionManager : public MessageHandler {
public:
// current hint info
void updateHintMode(const std::string &mode, bool enabled);
void updateHintBoost(const std::string &boost, int32_t durationMs);
int getDisplayRefreshRate();
// monitoring session status
void addPowerSession(PowerHintSession *session);
void removePowerSession(PowerHintSession *session);
void setUclampMin(PowerHintSession *session, int min);
void setUclampMinLocked(PowerHintSession *session, int min);
void handleMessage(const Message &message) override;
void dumpToFd(int fd);
// Singleton
static sp<PowerSessionManager> getInstance() {
static sp<PowerSessionManager> instance = new PowerSessionManager();
return instance;
}
private:
class WakeupHandler : public MessageHandler {
public:
WakeupHandler() {}
void handleMessage(const Message &message) override;
};
private:
void wakeSessions();
std::optional<bool> isAnyAppSessionActive();
void disableSystemTopAppBoost();
void enableSystemTopAppBoost();
const std::string kDisableBoostHintName;
std::unordered_set<PowerHintSession *> mSessions; // protected by mLock
std::unordered_map<int, int> mTidRefCountMap; // protected by mLock
std::unordered_map<int, std::unordered_set<PowerHintSession *>> mTidSessionListMap;
sp<WakeupHandler> mWakeupHandler;
bool mActive; // protected by mLock
/**
* mLock to pretect the above data objects opertions.
**/
std::mutex mLock;
int mDisplayRefreshRate;
// Singleton
PowerSessionManager()
: kDisableBoostHintName(::android::base::GetProperty(kPowerHalAdpfDisableTopAppBoost,
"ADPF_DISABLE_TA_BOOST")),
mActive(false),
mDisplayRefreshRate(60) {
mWakeupHandler = sp<WakeupHandler>(new WakeupHandler());
}
PowerSessionManager(PowerSessionManager const &) = delete;
void operator=(PowerSessionManager const &) = delete;
};
class PowerHintMonitor : public Thread {
public:
void start();
bool threadLoop() override;
sp<Looper> getLooper();
// Singleton
static sp<PowerHintMonitor> getInstance() {
static sp<PowerHintMonitor> instance = new PowerHintMonitor();
return instance;
}
PowerHintMonitor(PowerHintMonitor const &) = delete;
void operator=(PowerHintMonitor const &) = delete;
private:
sp<Looper> mLooper;
// Singleton
PowerHintMonitor() : Thread(false), mLooper(new Looper(true)) {}
};
} // namespace pixel
} // namespace impl
} // namespace power
} // namespace hardware
} // namespace google
} // namespace aidl

View file

@ -0,0 +1,32 @@
service vendor.power-hal-aidl /vendor/bin/hw/android.hardware.power-service.pixel-libperfmgr
class hal
user root
group system
priority -20
on late-fs
start vendor.power-hal-aidl
# Restart powerHAL when framework died
on property:init.svc.zygote=restarting && property:vendor.powerhal.state=*
setprop vendor.powerhal.state ""
setprop vendor.powerhal.audio ""
setprop vendor.powerhal.rendering ""
restart vendor.power-hal-aidl
# Clean up after b/163539793 resolved
on property:vendor.powerhal.dalvik.vm.dex2oat-threads=*
setprop dalvik.vm.dex2oat-threads ${vendor.powerhal.dalvik.vm.dex2oat-threads}
setprop dalvik.vm.restore-dex2oat-threads ${vendor.powerhal.dalvik.vm.dex2oat-threads}
on property:vendor.powerhal.dalvik.vm.dex2oat-cpu-set=*
setprop dalvik.vm.dex2oat-cpu-set ${vendor.powerhal.dalvik.vm.dex2oat-cpu-set}
setprop dalvik.vm.restore-dex2oat-cpu-set ${vendor.powerhal.dalvik.vm.dex2oat-cpu-set}
# Restart powerHAL when debug property set
on property:ro.debuggable=1 && property:vendor.powerhal.config.debug=*
restart vendor.power-hal-aidl
on property:persist.vendor.powerhal.config.debug=*
setprop vendor.powerhal.config.debug ${persist.vendor.powerhal.config.debug}

View file

@ -1,6 +1,7 @@
<manifest version="1.0" type="device"> <manifest version="1.0" type="device">
<hal format="aidl"> <hal format="aidl">
<name>android.hardware.power</name> <name>android.hardware.power</name>
<version>3</version>
<fqname>IPower/default</fqname> <fqname>IPower/default</fqname>
</hal> </hal>
</manifest> </manifest>

View file

@ -0,0 +1,5 @@
BOARD_SEPOLICY_DIRS += hardware/google/pixel-sepolicy/power-libperfmgr
# power HAL
PRODUCT_PACKAGES += \
android.hardware.power-service.pixel-libperfmgr

View file

@ -14,43 +14,56 @@
* limitations under the License. * limitations under the License.
*/ */
#define LOG_TAG "android.hardware.power-service.pixel-libperfmgr" #define LOG_TAG "powerhal-libperfmgr"
#include <thread>
#include <android-base/logging.h> #include <android-base/logging.h>
#include <android-base/properties.h> #include <android-base/properties.h>
#include <android/binder_ibinder_platform.h>
#include <android/binder_manager.h> #include <android/binder_manager.h>
#include <android/binder_process.h> #include <android/binder_process.h>
#include <perfmgr/HintManager.h>
#include <thread>
#include "Power.h" #include "Power.h"
#include "PowerExt.h" #include "PowerExt.h"
#include "PowerSessionManager.h"
#include "adaptivecpu/AdaptiveCpu.h"
#include "disp-power/DisplayLowPower.h"
using aidl::google::hardware::power::impl::pixel::AdaptiveCpu;
using aidl::google::hardware::power::impl::pixel::DisplayLowPower;
using aidl::google::hardware::power::impl::pixel::Power; using aidl::google::hardware::power::impl::pixel::Power;
using aidl::google::hardware::power::impl::pixel::PowerExt; using aidl::google::hardware::power::impl::pixel::PowerExt;
using aidl::google::hardware::power::impl::pixel::PowerHintMonitor;
using aidl::google::hardware::power::impl::pixel::PowerSessionManager;
using ::android::perfmgr::HintManager; using ::android::perfmgr::HintManager;
constexpr char kPowerHalConfigPath[] = "/vendor/etc/powerhint.json"; constexpr std::string_view kPowerHalInitProp("vendor.powerhal.init");
constexpr char kPowerHalInitProp[] = "vendor.powerhal.init";
int main() { int main() {
LOG(INFO) << "Pixel Power HAL AIDL Service with Extension is starting.";
// Parse config but do not start the looper // Parse config but do not start the looper
std::shared_ptr<HintManager> hm = HintManager::GetFromJSON(kPowerHalConfigPath, false); std::shared_ptr<HintManager> hm = HintManager::GetInstance();
if (!hm) { if (!hm) {
LOG(FATAL) << "Invalid config: " << kPowerHalConfigPath; LOG(FATAL) << "HintManager Init failed";
} }
std::shared_ptr<DisplayLowPower> dlpw = std::make_shared<DisplayLowPower>();
// single thread // single thread
ABinderProcess_setThreadPoolMaxThreadCount(0); ABinderProcess_setThreadPoolMaxThreadCount(0);
std::shared_ptr<AdaptiveCpu> adaptiveCpu = std::make_shared<AdaptiveCpu>();
// core service // core service
std::shared_ptr<Power> pw = ndk::SharedRefBase::make<Power>(hm); std::shared_ptr<Power> pw = ndk::SharedRefBase::make<Power>(dlpw, adaptiveCpu);
ndk::SpAIBinder pwBinder = pw->asBinder(); ndk::SpAIBinder pwBinder = pw->asBinder();
AIBinder_setMinSchedulerPolicy(pwBinder.get(), SCHED_NORMAL, -20);
// extension service // extension service
std::shared_ptr<PowerExt> pwExt = ndk::SharedRefBase::make<PowerExt>(hm); std::shared_ptr<PowerExt> pwExt = ndk::SharedRefBase::make<PowerExt>(dlpw, adaptiveCpu);
auto pwExtBinder = pwExt->asBinder();
AIBinder_setMinSchedulerPolicy(pwExtBinder.get(), SCHED_NORMAL, -20);
// attach the extension to the same binder we will be registering // attach the extension to the same binder we will be registering
CHECK(STATUS_OK == AIBinder_setExtension(pwBinder.get(), pwExt->asBinder().get())); CHECK(STATUS_OK == AIBinder_setExtension(pwBinder.get(), pwExt->asBinder().get()));
@ -60,9 +73,14 @@ int main() {
CHECK(status == STATUS_OK); CHECK(status == STATUS_OK);
LOG(INFO) << "Pixel Power HAL AIDL Service with Extension is started."; LOG(INFO) << "Pixel Power HAL AIDL Service with Extension is started.";
if (HintManager::GetInstance()->GetAdpfProfile()) {
PowerHintMonitor::getInstance()->start();
}
std::thread initThread([&]() { std::thread initThread([&]() {
::android::base::WaitForProperty(kPowerHalInitProp, "1"); ::android::base::WaitForProperty(kPowerHalInitProp.data(), "1");
hm->Start(); HintManager::GetInstance()->Start();
dlpw->Init();
}); });
initThread.detach(); initThread.detach();

View file

@ -0,0 +1,87 @@
/*
* 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.
*/
#define LOG_TAG "powerhal-libperfmgr"
#include <errno.h>
#include <unistd.h>
#include <cutils/sockets.h>
#include <log/log.h>
#include "DisplayLowPower.h"
namespace aidl {
namespace google {
namespace hardware {
namespace power {
namespace impl {
namespace pixel {
DisplayLowPower::DisplayLowPower() : mFossStatus(false) {}
void DisplayLowPower::Init() {
ConnectPpsDaemon();
}
void DisplayLowPower::SetDisplayLowPower(bool enable) {
SetFoss(enable);
}
void DisplayLowPower::ConnectPpsDaemon() {
constexpr const char kPpsDaemon[] = "pps";
mPpsSocket.reset(
socket_local_client(kPpsDaemon, ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_STREAM));
if (mPpsSocket.get() < 0) {
ALOGW("Connecting to PPS daemon failed (%s)", strerror(errno));
}
}
int DisplayLowPower::SendPpsCommand(const std::string_view cmd) {
if (TEMP_FAILURE_RETRY(write(mPpsSocket.get(), cmd.data(), cmd.size())) < 0) {
ALOGE("Failed to send pps command '%s' over socket (%s)", cmd.data(), strerror(errno));
return -1;
}
return 0;
}
void DisplayLowPower::SetFoss(bool enable) {
if (mPpsSocket.get() < 0 || mFossStatus == enable) {
return;
}
ALOGI("%s foss", (enable) ? "Enable" : "Disable");
std::string_view foss_cmd;
if (enable) {
foss_cmd = "foss:on";
} else {
foss_cmd = "foss:off";
}
if (!SendPpsCommand(foss_cmd)) {
mFossStatus = enable;
}
}
} // namespace pixel
} // namespace impl
} // namespace power
} // namespace hardware
} // namespace google
} // namespace aidl

View file

@ -0,0 +1,51 @@
/*
* 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.
*/
#pragma once
#include <string_view>
#include <android-base/unique_fd.h>
namespace aidl {
namespace google {
namespace hardware {
namespace power {
namespace impl {
namespace pixel {
class DisplayLowPower {
public:
DisplayLowPower();
~DisplayLowPower() {}
void Init();
void SetDisplayLowPower(bool enable);
private:
void ConnectPpsDaemon();
int SendPpsCommand(const std::string_view cmd);
void SetFoss(bool enable);
::android::base::unique_fd mPpsSocket;
bool mFossStatus;
};
} // namespace pixel
} // namespace impl
} // namespace power
} // namespace hardware
} // namespace google
} // namespace aidl

View file

@ -14,44 +14,62 @@
* limitations under the License. * limitations under the License.
*/ */
#define LOG_TAG "android.hardware.power@-service.pixel-libperfmgr" #define LOG_TAG "powerhal-libperfmgr"
#define ATRACE_TAG (ATRACE_TAG_POWER | ATRACE_TAG_HAL) #define ATRACE_TAG (ATRACE_TAG_POWER | ATRACE_TAG_HAL)
#include "InteractionHandler.h"
#include <android-base/properties.h>
#include <fcntl.h> #include <fcntl.h>
#include <perfmgr/HintManager.h>
#include <poll.h> #include <poll.h>
#include <sys/eventfd.h> #include <sys/eventfd.h>
#include <time.h> #include <time.h>
#include <unistd.h> #include <unistd.h>
#include <utils/Log.h> #include <utils/Log.h>
#include <utils/Trace.h> #include <utils/Trace.h>
#include <memory>
#include "InteractionHandler.h" #include <array>
#include <memory>
#define MAX_LENGTH 64 #define MAX_LENGTH 64
#define MSINSEC 1000L #define MSINSEC 1000L
#define USINMS 1000000L #define NSINMS 1000000L
static const std::vector<std::string> fb_idle_patch = {"/sys/class/drm/card0/device/idle_state", namespace aidl {
namespace google {
namespace hardware {
namespace power {
namespace impl {
namespace pixel {
namespace {
static const bool kDisplayIdleSupport =
::android::base::GetBoolProperty("vendor.powerhal.disp.idle_support", true);
static const std::array<const char *, 2> kDispIdlePath = {"/sys/class/drm/card0/device/idle_state",
"/sys/class/graphics/fb0/idle_state"}; "/sys/class/graphics/fb0/idle_state"};
static const uint32_t kWaitMs =
::android::base::GetUintProperty("vendor.powerhal.disp.idle_wait", /*default*/ 100U);
static const uint32_t kMinDurationMs =
::android::base::GetUintProperty("vendor.powerhal.interaction.min", /*default*/ 1400U);
static const uint32_t kMaxDurationMs =
::android::base::GetUintProperty("vendor.powerhal.interaction.max", /*default*/ 5650U);
static const uint32_t kDurationOffsetMs =
::android::base::GetUintProperty("vendor.powerhal.interaction.offset", /*default*/ 650U);
InteractionHandler::InteractionHandler(std::shared_ptr<HintManager> const &hint_manager) static size_t CalcTimespecDiffMs(struct timespec start, struct timespec end) {
: mState(INTERACTION_STATE_UNINITIALIZED), size_t diff_in_ms = 0;
mWaitMs(100), diff_in_ms += (end.tv_sec - start.tv_sec) * MSINSEC;
mMinDurationMs(1400), diff_in_ms += (end.tv_nsec - start.tv_nsec) / NSINMS;
mMaxDurationMs(5650), return diff_in_ms;
mDurationMs(0),
mHintManager(hint_manager) {}
InteractionHandler::~InteractionHandler() {
Exit();
} }
static int fb_idle_open(void) { static int FbIdleOpen(void) {
int fd; int fd;
for (auto &path : fb_idle_patch) { for (const auto &path : kDispIdlePath) {
fd = open(path.c_str(), O_RDONLY); fd = open(path, O_RDONLY);
if (fd >= 0) if (fd >= 0)
return fd; return fd;
} }
@ -59,20 +77,32 @@ static int fb_idle_open(void) {
return -1; return -1;
} }
} // namespace
using ::android::perfmgr::HintManager;
InteractionHandler::InteractionHandler()
: mState(INTERACTION_STATE_UNINITIALIZED), mDurationMs(0) {}
InteractionHandler::~InteractionHandler() {
Exit();
}
bool InteractionHandler::Init() { bool InteractionHandler::Init() {
std::lock_guard<std::mutex> lk(mLock); std::lock_guard<std::mutex> lk(mLock);
if (mState != INTERACTION_STATE_UNINITIALIZED) if (mState != INTERACTION_STATE_UNINITIALIZED)
return true; return true;
mIdleFd = fb_idle_open(); int fd = FbIdleOpen();
if (fd < 0)
return false;
mIdleFd = fd;
mEventFd = eventfd(0, EFD_NONBLOCK); mEventFd = eventfd(0, EFD_NONBLOCK);
if (mEventFd < 0) { if (mEventFd < 0) {
ALOGE("Unable to create event fd (%d)", errno); ALOGE("Unable to create event fd (%d)", errno);
if (mIdleFd >= 0) {
close(mIdleFd); close(mIdleFd);
}
return false; return false;
} }
@ -95,51 +125,44 @@ void InteractionHandler::Exit() {
mThread->join(); mThread->join();
close(mEventFd); close(mEventFd);
if (mIdleFd >= 0) {
close(mIdleFd); close(mIdleFd);
}
} }
void InteractionHandler::PerfLock() { void InteractionHandler::PerfLock() {
ALOGV("%s: acquiring perf lock", __func__); ALOGV("%s: acquiring perf lock", __func__);
if (!mHintManager->DoHint("INTERACTION")) { if (!HintManager::GetInstance()->DoHint("INTERACTION")) {
ALOGE("%s: do hint INTERACTION failed", __func__); ALOGE("%s: do hint INTERACTION failed", __func__);
} }
ATRACE_INT("interaction_lock", 1);
} }
void InteractionHandler::PerfRel() { void InteractionHandler::PerfRel() {
ALOGV("%s: releasing perf lock", __func__); ALOGV("%s: releasing perf lock", __func__);
if (!mHintManager->EndHint("INTERACTION")) { if (!HintManager::GetInstance()->EndHint("INTERACTION")) {
ALOGE("%s: end hint INTERACTION failed", __func__); ALOGE("%s: end hint INTERACTION failed", __func__);
} }
ATRACE_INT("interaction_lock", 0);
}
size_t InteractionHandler::CalcTimespecDiffMs(struct timespec start, struct timespec end) {
size_t diff_in_us = 0;
diff_in_us += (end.tv_sec - start.tv_sec) * MSINSEC;
diff_in_us += (end.tv_nsec - start.tv_nsec) / USINMS;
return diff_in_us;
} }
void InteractionHandler::Acquire(int32_t duration) { void InteractionHandler::Acquire(int32_t duration) {
ATRACE_CALL(); ATRACE_CALL();
std::lock_guard<std::mutex> lk(mLock); std::lock_guard<std::mutex> lk(mLock);
if (mState == INTERACTION_STATE_UNINITIALIZED) {
ALOGW("%s: called while uninitialized", __func__);
return;
}
int inputDuration = duration + 650; int inputDuration = duration + kDurationOffsetMs;
int finalDuration; int finalDuration;
if (inputDuration > mMaxDurationMs) if (inputDuration > kMaxDurationMs)
finalDuration = mMaxDurationMs; finalDuration = kMaxDurationMs;
else if (inputDuration > mMinDurationMs) else if (inputDuration > kMinDurationMs)
finalDuration = inputDuration; finalDuration = inputDuration;
else else
finalDuration = mMinDurationMs; finalDuration = kMinDurationMs;
// Fallback to do boost directly
// 1) override property is set OR
// 2) InteractionHandler not initialized
if (!kDisplayIdleSupport || mState == INTERACTION_STATE_UNINITIALIZED) {
HintManager::GetInstance()->DoHint("INTERACTION", std::chrono::milliseconds(finalDuration));
return;
}
struct timespec cur_timespec; struct timespec cur_timespec;
clock_gettime(CLOCK_MONOTONIC, &cur_timespec); clock_gettime(CLOCK_MONOTONIC, &cur_timespec);
@ -213,18 +236,6 @@ void InteractionHandler::WaitForIdle(int32_t wait_ms, int32_t timeout_ms) {
return; return;
} }
if (mIdleFd < 0) {
ret = poll(pfd, 1, timeout_ms);
if (ret > 0) {
ALOGV("%s: wait for duration aborted", __func__);
return;
} else if (ret < 0) {
ALOGE("%s: Error on waiting for duration (%zd)", __func__, ret);
return;
}
return;
}
ret = pread(mIdleFd, data, sizeof(data), 0); ret = pread(mIdleFd, data, sizeof(data), 0);
if (!ret) { if (!ret) {
ALOGE("%s: Unexpected EOF!", __func__); ALOGE("%s: Unexpected EOF!", __func__);
@ -248,6 +259,7 @@ void InteractionHandler::WaitForIdle(int32_t wait_ms, int32_t timeout_ms) {
} }
void InteractionHandler::Routine() { void InteractionHandler::Routine() {
pthread_setname_np(pthread_self(), "DispIdle");
std::unique_lock<std::mutex> lk(mLock, std::defer_lock); std::unique_lock<std::mutex> lk(mLock, std::defer_lock);
while (true) { while (true) {
@ -258,7 +270,14 @@ void InteractionHandler::Routine() {
mState = INTERACTION_STATE_WAITING; mState = INTERACTION_STATE_WAITING;
lk.unlock(); lk.unlock();
WaitForIdle(mWaitMs, mDurationMs); WaitForIdle(kWaitMs, mDurationMs);
Release(); Release();
} }
} }
} // namespace pixel
} // namespace impl
} // namespace power
} // namespace hardware
} // namespace google
} // namespace aidl

View file

@ -14,8 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
#ifndef POWER_LIBPERFMGR_INTERACTIONHANDLER_H_ #pragma once
#define POWER_LIBPERFMGR_INTERACTIONHANDLER_H_
#include <condition_variable> #include <condition_variable>
#include <memory> #include <memory>
@ -23,11 +22,14 @@
#include <string> #include <string>
#include <thread> #include <thread>
#include <perfmgr/HintManager.h> namespace aidl {
namespace google {
namespace hardware {
namespace power {
namespace impl {
namespace pixel {
using ::android::perfmgr::HintManager; enum InteractionState {
enum interaction_state {
INTERACTION_STATE_UNINITIALIZED, INTERACTION_STATE_UNINITIALIZED,
INTERACTION_STATE_IDLE, INTERACTION_STATE_IDLE,
INTERACTION_STATE_INTERACTION, INTERACTION_STATE_INTERACTION,
@ -36,7 +38,7 @@ enum interaction_state {
class InteractionHandler { class InteractionHandler {
public: public:
InteractionHandler(std::shared_ptr<HintManager> const &hint_manager); InteractionHandler();
~InteractionHandler(); ~InteractionHandler();
bool Init(); bool Init();
void Exit(); void Exit();
@ -51,24 +53,19 @@ class InteractionHandler {
void PerfLock(); void PerfLock();
void PerfRel(); void PerfRel();
size_t CalcTimespecDiffMs(struct timespec start, struct timespec end); enum InteractionState mState;
enum interaction_state mState;
int mIdleFd; int mIdleFd;
int mEventFd; int mEventFd;
int32_t mWaitMs;
int32_t mMinDurationMs;
int32_t mMaxDurationMs;
int32_t mDurationMs; int32_t mDurationMs;
struct timespec mLastTimespec; struct timespec mLastTimespec;
std::unique_ptr<std::thread> mThread; std::unique_ptr<std::thread> mThread;
std::mutex mLock; std::mutex mLock;
std::condition_variable mCond; std::condition_variable mCond;
std::shared_ptr<HintManager> mHintManager;
}; };
#endif // POWER_LIBPERFMGR_INTERACTIONHANDLER_H_ } // namespace pixel
} // namespace impl
} // namespace power
} // namespace hardware
} // namespace google
} // namespace aidl