diff --git a/BoardConfigCommon.mk b/BoardConfigCommon.mk
index 0d191cf8..3c2a5e5d 100644
--- a/BoardConfigCommon.mk
+++ b/BoardConfigCommon.mk
@@ -11,7 +11,6 @@ COMMON_PATH := device/xiaomi/sdm660-common
PRODUCT_USES_QCOM_HARDWARE := true
PRODUCT_BOARD_PLATFORM := sdm660
OVERRIDE_QCOM_HARDWARE_VARIANT := sdm660
-BOARD_VENDOR := xiaomi
# A/B
ifeq ($(ENABLE_AB), true)
diff --git a/configs/perf/perf-profile0.conf b/configs/perf/perf-profile0.conf
new file mode 100644
index 00000000..0c358e14
--- /dev/null
+++ b/configs/perf/perf-profile0.conf
@@ -0,0 +1,2 @@
+[priority] # This profile is reserved for perflock requests
+99
diff --git a/configs/perf/perfconfigstore.xml b/configs/perf/perfconfigstore.xml
new file mode 100644
index 00000000..ee4ea350
--- /dev/null
+++ b/configs/perf/perfconfigstore.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/configs/public.libraries.txt b/configs/public.libraries.txt
index 048e733b..555be3f5 100644
--- a/configs/public.libraries.txt
+++ b/configs/public.libraries.txt
@@ -1,3 +1,4 @@
+libqti-perfd-client.so
libadsprpc.so
libcdsprpc.so
libsdsprpc.so
diff --git a/libqti-perfd-client/Android.bp b/libqti-perfd-client/Android.bp
index ae75b9dd..1c8e2824 100644
--- a/libqti-perfd-client/Android.bp
+++ b/libqti-perfd-client/Android.bp
@@ -1,15 +1,17 @@
-//
-// Copyright (C) 2021-2022 The LineageOS Project
-//
-// SPDX-License-Identifier: Apache-2.0
-//
-
cc_library_shared {
name: "libqti-perfd-client",
- vendor: true,
- srcs: ["client.c"],
+ proprietary: true,
+ defaults: ["hidl_defaults"],
+ srcs: [
+ "client.c",
+ ],
+ cflags: [
+ "-Werror",
+ "-Wextra",
+ "-Wall",
+ ],
shared_libs: [
- "liblog",
"libutils",
+ "liblog",
],
}
diff --git a/libqti-perfd-client/client.c b/libqti-perfd-client/client.c
index 66e75b49..42b3cdcd 100644
--- a/libqti-perfd-client/client.c
+++ b/libqti-perfd-client/client.c
@@ -1,9 +1,3 @@
-/*
- * Copyright (C) 2021 The LineageOS Project
- *
- * SPDX-License-Identifier: Apache-2.0
- */
-
#define LOG_TAG "libqti-perfd-client"
#include
diff --git a/manifest.xml b/manifest.xml
index 40ac7169..c7147361 100755
--- a/manifest.xml
+++ b/manifest.xml
@@ -187,6 +187,11 @@
hwbinder
@1.0::IDspService/dspservice
+
+ vendor.qti.hardware.perf
+ hwbinder
+ @2.2::IPerf/default
+
vendor.qti.hardware.qseecom
hwbinder
diff --git a/power-libperfmgr/Android.bp b/power-libperfmgr/Android.bp
new file mode 100644
index 00000000..78d4029a
--- /dev/null
+++ b/power-libperfmgr/Android.bp
@@ -0,0 +1,117 @@
+//
+// Copyright (C) 2018 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.
+
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+cc_library {
+ name: "libdisppower-xiaomi_sdm660",
+ proprietary: true,
+ srcs: [
+ "disp-power/InteractionHandler.cpp",
+ ],
+ shared_libs: [
+ "libbase",
+ "libcutils",
+ "liblog",
+ "libperfmgr",
+ "libutils",
+ ],
+}
+
+cc_library {
+ name: "libadaptivecpu-xiaomi_sdm660",
+ 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-xiaomi_sdm660",
+ 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-xiaomi_sdm660",
+ "libgmock",
+ "android.hardware.power-V3-ndk",
+ ],
+ shared_libs: [
+ "liblog",
+ "libbase",
+ "libcutils",
+ ],
+ test_suites: ["device-tests"],
+}
+
+cc_binary {
+ name: "android.hardware.power-service.xiaomi_sdm660-libperfmgr",
+ relative_install_path: "hw",
+ init_rc: ["aidl/android.hardware.power-service.xiaomi_sdm660-libperfmgr.rc"],
+ vintf_fragments: ["aidl/android.hardware.power-service.xiaomi_sdm660.xml"],
+ vendor: true,
+ shared_libs: [
+ "android.hardware.power-V3-ndk",
+ "libadaptivecpu-xiaomi_sdm660",
+ "libbase",
+ "libcutils",
+ "liblog",
+ "libutils",
+ "libbinder_ndk",
+ "libdisppower-xiaomi_sdm660",
+ "libperfmgr",
+ "libprocessgroup",
+ "pixel-power-ext-V1-ndk",
+ ],
+ srcs: [
+ "aidl/service.cpp",
+ "aidl/Power.cpp",
+ "aidl/PowerExt.cpp",
+ "aidl/PowerHintSession.cpp",
+ "aidl/PowerSessionManager.cpp",
+ ],
+}
diff --git a/power-libperfmgr/adaptivecpu/AdaptiveCpu.cpp b/power-libperfmgr/adaptivecpu/AdaptiveCpu.cpp
new file mode 100644
index 00000000..0ad96399
--- /dev/null
+++ b/power-libperfmgr/adaptivecpu/AdaptiveCpu.cpp
@@ -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
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+
+#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 &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 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 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(throttleDecision);
+ ATRACE_INT("AdaptiveCpu_throttleDecision", static_cast(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
diff --git a/power-libperfmgr/adaptivecpu/AdaptiveCpu.h b/power-libperfmgr/adaptivecpu/AdaptiveCpu.h
new file mode 100644
index 00000000..4ab83c52
--- /dev/null
+++ b/power-libperfmgr/adaptivecpu/AdaptiveCpu.h
@@ -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
+#include
+
+#include
+#include
+#include
+#include
+
+#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 &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
diff --git a/power-libperfmgr/adaptivecpu/AdaptiveCpuConfig.cpp b/power-libperfmgr/adaptivecpu/AdaptiveCpuConfig.cpp
new file mode 100644
index 00000000..5a8428d1
--- /dev/null
+++ b/power-libperfmgr/adaptivecpu/AdaptiveCpuConfig.cpp
@@ -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
+#include
+#include
+#include
+
+#include
+#include
+#include
+
+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 *output);
+std::string FormatThrottleDecisions(const std::vector &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(kIterationSleepDurationProperty.data(),
+ DEFAULT.iterationSleepDuration.count()));
+ output->iterationSleepDuration =
+ std::max(output->iterationSleepDuration, kIterationSleepDurationMin);
+
+ output->hintTimeout = std::chrono::milliseconds(::android::base::GetUintProperty(
+ kHintTimeoutProperty.data(), DEFAULT.hintTimeout.count()));
+
+ output->randomThrottleDecisionProbability =
+ static_cast(::android::base::GetUintProperty(
+ 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(
+ 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 *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(ThrottleDecision::FIRST) ||
+ throttleDecisionInt > static_cast(ThrottleDecision::LAST)) {
+ LOG(ERROR) << "Failed to parse throttle decision: throttleDecision="
+ << throttleDecisionInt << ", input=" << input;
+ return false;
+ }
+ output->push_back(static_cast(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 &throttleDecisions) {
+ std::stringstream ss;
+ for (size_t i = 0; i < throttleDecisions.size(); i++) {
+ ss << static_cast(throttleDecisions[i]);
+ if (i < throttleDecisions.size() - 1) {
+ ss << ",";
+ }
+ }
+ return ss.str();
+}
+
+} // namespace pixel
+} // namespace impl
+} // namespace power
+} // namespace hardware
+} // namespace google
+} // namespace aidl
diff --git a/power-libperfmgr/adaptivecpu/AdaptiveCpuConfig.h b/power-libperfmgr/adaptivecpu/AdaptiveCpuConfig.h
new file mode 100644
index 00000000..59bef4c9
--- /dev/null
+++ b/power-libperfmgr/adaptivecpu/AdaptiveCpuConfig.h
@@ -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
+#include
+
+#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 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
diff --git a/power-libperfmgr/adaptivecpu/AdaptiveCpuStats.cpp b/power-libperfmgr/adaptivecpu/AdaptiveCpuStats.cpp
new file mode 100644
index 00000000..491683b8
--- /dev/null
+++ b/power-libperfmgr/adaptivecpu/AdaptiveCpuStats.cpp
@@ -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
+
+#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(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(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(numMissedDeadlines) / numDurations
+ << ")\n";
+ totalNumDurations += numDurations;
+ totalNumMissedDeadlines += numMissedDeadlines;
+ }
+ stream << " - Total: " << totalNumMissedDeadlines << " / " << totalNumDurations << " ("
+ << static_cast(totalNumMissedDeadlines) / totalNumDurations << ")\n";
+}
+
+std::string AdaptiveCpuStats::FormatDuration(std::chrono::nanoseconds duration) {
+ double count = static_cast(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
diff --git a/power-libperfmgr/adaptivecpu/AdaptiveCpuStats.h b/power-libperfmgr/adaptivecpu/AdaptiveCpuStats.h
new file mode 100644
index 00000000..0c4a7f9f
--- /dev/null
+++ b/power-libperfmgr/adaptivecpu/AdaptiveCpuStats.h
@@ -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
+
+#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()) {}
+ AdaptiveCpuStats(std::unique_ptr 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 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 mNumThrottles;
+ std::map mThrottleDurations;
+
+ std::map mNumDurations;
+ std::map mNumMissedDeadlines;
+
+ static std::string FormatDuration(std::chrono::nanoseconds duration);
+};
+
+} // namespace pixel
+} // namespace impl
+} // namespace power
+} // namespace hardware
+} // namespace google
+} // namespace aidl
diff --git a/power-libperfmgr/adaptivecpu/CpuFrequencyReader.cpp b/power-libperfmgr/adaptivecpu/CpuFrequencyReader.cpp
new file mode 100644
index 00000000..a15d1e36
--- /dev/null
+++ b/power-libperfmgr/adaptivecpu/CpuFrequencyReader.cpp
@@ -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
+#include
+#include
+
+#include
+#include
+#include
+
+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 *result) {
+ ATRACE_CALL();
+ std::map> 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>
+CpuFrequencyReader::GetPreviousCpuPolicyFrequencies() const {
+ return mPreviousCpuPolicyFrequencies;
+}
+
+bool CpuFrequencyReader::ReadCpuPolicyFrequencies(
+ std::map> *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 timeInStateFile;
+ if (!mFilesystem->ReadFileStream(timeInStatePath.str(), &timeInStateFile)) {
+ return false;
+ }
+
+ std::map 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 *result) const {
+ ATRACE_CALL();
+ std::vector 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
diff --git a/power-libperfmgr/adaptivecpu/CpuFrequencyReader.h b/power-libperfmgr/adaptivecpu/CpuFrequencyReader.h
new file mode 100644
index 00000000..ed7a79c9
--- /dev/null
+++ b/power-libperfmgr/adaptivecpu/CpuFrequencyReader.h
@@ -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
+#include