summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorXin Li <delphij@google.com>2024-01-17 22:14:14 -0800
committerXin Li <delphij@google.com>2024-01-17 22:14:14 -0800
commit21ea176afb312c4119e9a4f283a1b3a4b29d89e0 (patch)
tree1a5aa09851f6371d2f544633b455fa27519821ef
parent7db0ef18a644f82a796b19bba9c02eb68fa9d111 (diff)
parent64724f4a38019ea6ecd291656b4f8bbb3e139ed2 (diff)
downloadpixel-21ea176afb312c4119e9a4f283a1b3a4b29d89e0.tar.gz
Merge Android 24Q1 Release (ab/11220357)
Bug: 319669529 Merged-In: Iab56b7894353927a583e7fd13f702241745390e9 Change-Id: I3fe104d7b6d870e9715af7af7f78ad59506630e8
-rw-r--r--atrace/OWNERS4
-rw-r--r--battery_mitigation/Android.bp1
-rw-r--r--battery_mitigation/BatteryMitigation.cpp11
-rw-r--r--battery_mitigation/BatteryMitigationService.cpp1128
-rw-r--r--battery_mitigation/include/battery_mitigation/BatteryMitigation.h1
-rw-r--r--battery_mitigation/include/battery_mitigation/BatteryMitigationService.h172
-rw-r--r--battery_mitigation/include/battery_mitigation/MitigationConfig.h47
-rw-r--r--battery_mitigation/include/battery_mitigation/uapi/brownout_stats.h27
-rw-r--r--common/init.pixel.rc7
-rw-r--r--common/pixel-common-device.mk5
-rw-r--r--gpu_probe/Android.bp33
-rw-r--r--gpu_probe/gpu_probe.rc9
-rw-r--r--gpu_probe/main.rs34
-rw-r--r--health/ChargerDetect.cpp6
-rw-r--r--misc_writer/include/misc_writer/misc_writer.h9
-rw-r--r--misc_writer/misc_writer.cpp24
-rw-r--r--misc_writer/misc_writer_main.cpp31
-rw-r--r--mm/pixel-mm-gki.rc4
-rw-r--r--pixelstats/Android.bp6
-rw-r--r--pixelstats/BatteryEEPROMReporter.cpp108
-rw-r--r--pixelstats/BrownoutDetectedReporter.cpp99
-rw-r--r--pixelstats/ChargeStatsReporter.cpp20
-rw-r--r--pixelstats/StatsHelper.cpp25
-rw-r--r--pixelstats/SysfsCollector.cpp190
-rw-r--r--pixelstats/TEST_MAPPING (renamed from pixelstats/test/TEST_MAPPING)0
-rw-r--r--pixelstats/UeventListener.cpp92
-rw-r--r--pixelstats/include/pixelstats/BatteryEEPROMReporter.h9
-rw-r--r--pixelstats/include/pixelstats/BrownoutDetectedReporter.h36
-rw-r--r--pixelstats/include/pixelstats/ChargeStatsReporter.h2
-rw-r--r--pixelstats/include/pixelstats/StatsHelper.h2
-rw-r--r--pixelstats/include/pixelstats/SysfsCollector.h14
-rw-r--r--pixelstats/include/pixelstats/UeventListener.h30
-rw-r--r--pixelstats/pixelatoms.proto113
-rw-r--r--power-libperfmgr/Android.bp46
-rw-r--r--power-libperfmgr/aidl/AdpfTypes.h46
-rw-r--r--power-libperfmgr/aidl/AppDescriptorTrace.h93
-rw-r--r--power-libperfmgr/aidl/BackgroundWorker.cpp150
-rw-r--r--power-libperfmgr/aidl/BackgroundWorker.h151
-rw-r--r--power-libperfmgr/aidl/Power.cpp6
-rw-r--r--power-libperfmgr/aidl/PowerExt.cpp7
-rw-r--r--power-libperfmgr/aidl/PowerHintSession.cpp383
-rw-r--r--power-libperfmgr/aidl/PowerHintSession.h99
-rw-r--r--power-libperfmgr/aidl/PowerSessionManager.cpp420
-rw-r--r--power-libperfmgr/aidl/PowerSessionManager.h90
-rw-r--r--power-libperfmgr/aidl/SessionTaskMap.cpp261
-rw-r--r--power-libperfmgr/aidl/SessionTaskMap.h131
-rw-r--r--power-libperfmgr/aidl/SessionValueEntry.cpp48
-rw-r--r--power-libperfmgr/aidl/SessionValueEntry.h55
-rw-r--r--power-libperfmgr/aidl/UClampVoter.cpp130
-rw-r--r--power-libperfmgr/aidl/UClampVoter.h141
-rw-r--r--power-libperfmgr/aidl/android.hardware.power-service.pixel-libperfmgr.rc4
-rw-r--r--power-libperfmgr/aidl/android.hardware.power-service.pixel.xml2
-rw-r--r--power-libperfmgr/aidl/service.cpp6
-rw-r--r--power-libperfmgr/aidl/tests/BackgroundWorkerTest.cpp168
-rw-r--r--power-libperfmgr/aidl/tests/PowerHintSessionTest.cpp183
-rw-r--r--power-libperfmgr/aidl/tests/SessionTaskMapTest.cpp336
-rw-r--r--power-libperfmgr/aidl/tests/UClampVoterTest.cpp209
-rw-r--r--power-libperfmgr/disp-power/InteractionHandler.cpp5
-rw-r--r--power-libperfmgr/libperfmgr/AdpfConfig.cc2
-rw-r--r--power-libperfmgr/libperfmgr/HintManager.cc194
-rw-r--r--power-libperfmgr/libperfmgr/include/perfmgr/AdpfConfig.h9
-rw-r--r--power-libperfmgr/libperfmgr/include/perfmgr/HintManager.h3
-rw-r--r--power-libperfmgr/libperfmgr/tests/HintManagerTest.cc16
-rw-r--r--recovery/Android.bp24
-rw-r--r--recovery/recovery_watch_ui.cpp81
-rw-r--r--thermal/Android.bp47
-rw-r--r--thermal/Thermal.cpp35
-rw-r--r--thermal/android.hardware.thermal-service.pixel.rc2
-rw-r--r--thermal/android.hardware.thermal-service.pixel.xml2
-rw-r--r--thermal/device.mk13
-rw-r--r--thermal/tests/mock_thermal_helper.h4
-rw-r--r--thermal/thermal-helper.cpp266
-rw-r--r--thermal/thermal-helper.h28
-rw-r--r--thermal/utils/power_files.cpp2
-rw-r--r--thermal/utils/powerhal_helper.cpp3
-rw-r--r--thermal/utils/thermal_info.cpp314
-rw-r--r--thermal/utils/thermal_info.h52
-rw-r--r--thermal/utils/thermal_stats_helper.cpp207
-rw-r--r--thermal/utils/thermal_stats_helper.h39
-rw-r--r--thermal/utils/thermal_throttling.cpp68
-rw-r--r--thermal/utils/thermal_throttling.h15
-rw-r--r--thermal/utils/thermal_watcher.cpp2
-rw-r--r--thermal/virtualtemp_estimator/virtualtemp_estimator.cpp158
-rw-r--r--thermal/virtualtemp_estimator/virtualtemp_estimator.h58
-rw-r--r--thermal/virtualtemp_estimator/virtualtemp_estimator_data.h76
-rw-r--r--thermal/virtualtemp_estimator/virtualtemp_estimator_test.cpp416
-rw-r--r--usb/Android.bp15
-rw-r--r--usb/CommonUtils.cpp71
-rw-r--r--usb/include/pixelusb/CommonUtils.h11
-rw-r--r--vibrator/common/StatsBase.cpp20
-rw-r--r--vibrator/common/StatsBase.h4
-rw-r--r--vibrator/cs40l25/Hardware.h4
-rw-r--r--vibrator/cs40l26/Hardware.h7
-rw-r--r--vibrator/cs40l26/Vibrator.cpp34
-rw-r--r--vibrator/cs40l26/Vibrator.h4
-rw-r--r--vibrator/cs40l26/tests/mocks.h1
-rw-r--r--vibrator/cs40l26/tests/test-vibrator.cpp10
97 files changed, 6829 insertions, 957 deletions
diff --git a/atrace/OWNERS b/atrace/OWNERS
index 05e234ae..a7801e6d 100644
--- a/atrace/OWNERS
+++ b/atrace/OWNERS
@@ -1,2 +1,4 @@
wvw@google.com
-namhyung@google.com
+jenhaochen@google.com
+paillon@google.com
+liumartin@google.com
diff --git a/battery_mitigation/Android.bp b/battery_mitigation/Android.bp
index 1a5d6c6e..6b5e1228 100644
--- a/battery_mitigation/Android.bp
+++ b/battery_mitigation/Android.bp
@@ -24,6 +24,7 @@ cc_library {
vendor_available: true,
srcs: [
"BatteryMitigation.cpp",
+ "BatteryMitigationService.cpp",
"MitigationThermalManager.cpp",
],
static_libs: [
diff --git a/battery_mitigation/BatteryMitigation.cpp b/battery_mitigation/BatteryMitigation.cpp
index 6aadaa48..10c944cc 100644
--- a/battery_mitigation/BatteryMitigation.cpp
+++ b/battery_mitigation/BatteryMitigation.cpp
@@ -18,7 +18,6 @@
#include <sstream>
-#define MAX_BROWNOUT_DATA_AGE_MINUTES 5
#define ONE_SECOND_IN_US 1000000
namespace android {
@@ -26,9 +25,11 @@ namespace hardware {
namespace google {
namespace pixel {
+using android::base::ReadFileToString;
+
BatteryMitigation::BatteryMitigation(const struct MitigationConfig::Config &cfg) {
- mThermalMgr = &MitigationThermalManager::getInstance();
- mThermalMgr->updateConfig(cfg);
+ mThermalMgr = &MitigationThermalManager::getInstance();
+ mThermalMgr->updateConfig(cfg);
}
bool BatteryMitigation::isMitigationLogTimeValid(std::chrono::system_clock::time_point startTime,
@@ -36,7 +37,7 @@ bool BatteryMitigation::isMitigationLogTimeValid(std::chrono::system_clock::time
const char *const timestampFormat,
const std::regex pattern) {
std::string logFile;
- if (!android::base::ReadFileToString(logFilePath, &logFile)) {
+ if (!ReadFileToString(logFilePath, &logFile)) {
return false;
}
std::istringstream content(logFile);
@@ -66,7 +67,7 @@ bool BatteryMitigation::isMitigationLogTimeValid(std::chrono::system_clock::time
auto delta = epoch_startTime - epoch_logFileTime;
auto delta_minutes = delta / 60;
- if ((delta_minutes < MAX_BROWNOUT_DATA_AGE_MINUTES) && (delta_minutes >= 0)) {
+ if (delta_minutes >= 0) {
return true;
}
}
diff --git a/battery_mitigation/BatteryMitigationService.cpp b/battery_mitigation/BatteryMitigationService.cpp
new file mode 100644
index 00000000..3357fdfa
--- /dev/null
+++ b/battery_mitigation/BatteryMitigationService.cpp
@@ -0,0 +1,1128 @@
+/*
+ * Copyright (C) 2023 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 <battery_mitigation/BatteryMitigationService.h>
+
+namespace android {
+namespace hardware {
+namespace google {
+namespace pixel {
+
+using android::base::ReadFileToString;
+using android::base::StartsWith;
+using android::hardware::google::pixel::MitigationConfig;
+
+const struct BrownoutStatsCSVFields brownoutStatsCSVFields = {
+ .triggered_time = "triggered_timestamp",
+ .triggered_idx = "triggered_irq",
+ .battery_soc = "battery_soc",
+ .battery_temp = "battery_temp",
+ .battery_cycle = "battery_cycle",
+ .voltage_now = "voltage_now",
+ .current_now = "current_now",
+ .cpu0_freq = "dvfs_channel1",
+ .cpu1_freq = "dvfs_channel2",
+ .cpu2_freq = "dvfs_channel3",
+ .gpu_freq = "dvfs_channel4",
+ .tpu_freq = "dvfs_channel5",
+ .aur_freq = "dvfs_channel6",
+ .odpm_prefix = "odpm_channel_",
+};
+
+BatteryMitigationService::BatteryMitigationService(
+ const struct MitigationConfig::EventThreadConfig &eventThreadCfg)
+ :cfg(eventThreadCfg) {
+ initTotalNumericSysfsPaths();
+ initPmicRelated();
+}
+
+BatteryMitigationService::~BatteryMitigationService() {
+ stopEventThread(threadStop, wakeupEventFd, brownoutEventThread);
+ tearDownBrownoutEventThread();
+ stopEventThread(triggerThreadStop, triggeredStateWakeupEventFd, eventThread);
+ tearDownTriggerEventThread();
+}
+
+bool BatteryMitigationService::isBrownoutStatsBinarySupported() {
+ if (access(cfg.TriggeredIdxPath, F_OK) == 0 &&
+ access(cfg.BrownoutStatsPath, F_OK) == 0) {
+ return true;
+ }
+ return false;
+}
+
+bool readSysfsToInt(const std::string &path, int *val) {
+ std::string file_contents;
+
+ if (!ReadFileToString(path, &file_contents)) {
+ return false;
+ } else if (StartsWith(file_contents, "0x")) {
+ if (sscanf(file_contents.c_str(), "0x%x", val) != 1) {
+ return false;
+ }
+ } else if (sscanf(file_contents.c_str(), "%d", val) != 1) {
+ return false;
+ }
+ return true;
+}
+
+bool readSysfsToDouble(const std::string &path, double *val) {
+ std::string file_contents;
+
+ if (!android::base::ReadFileToString(path, &file_contents)) {
+ return false;
+ } else if (sscanf(file_contents.c_str(), "%lf", val) != 1) {
+ return false;
+ }
+ return true;
+}
+
+int getFilesInDir(const char *directory, std::vector<std::string> *files) {
+ std::string content;
+ struct dirent *entry;
+
+ DIR *dir = opendir(directory);
+ if (dir == NULL)
+ return -1;
+
+ files->clear();
+ while ((entry = readdir(dir)) != NULL)
+ files->push_back(entry->d_name);
+ closedir(dir);
+
+ return 0;
+}
+
+void addNumericSysfsStatPathinDir(
+ std::string numericSysfsStatDir,
+ std::vector<MitigationConfig::numericSysfs> totalNumericSysfsStatPaths) {
+ std::vector<std::string> files;
+ if (getFilesInDir(numericSysfsStatDir.c_str(), &files) < 0) {
+ return;
+ }
+ for (auto &file : files) {
+ std::string fullPath = numericSysfsStatDir + file;
+ totalNumericSysfsStatPaths.push_back({file, fullPath});
+ }
+}
+
+void BatteryMitigationService::initTotalNumericSysfsPaths() {
+ totalNumericSysfsStatPaths.assign(cfg.NumericSysfsStatPaths.begin(),
+ cfg.NumericSysfsStatPaths.end());
+ for (const auto &sysfsStat : cfg.NumericSysfsStatDirs) {
+ addNumericSysfsStatPathinDir(sysfsStat.path.c_str(), totalNumericSysfsStatPaths);
+ }
+
+ /* Append first available path in PlatformSpecific */
+ for (const auto &sysfsStatList : cfg.PlatformSpecific.NumericSysfsStatPaths) {
+ for (const auto &sysfsStatPath : sysfsStatList.paths) {
+ if (access(sysfsStatPath.c_str(), F_OK) == 0) {
+ totalNumericSysfsStatPaths.push_back({sysfsStatList.name, sysfsStatPath});
+ break;
+ }
+ }
+ }
+ for (const auto &sysfsStatList : cfg.PlatformSpecific.NumericSysfsStatDirs) {
+ for (const auto &sysfsStatPath : sysfsStatList.paths) {
+ if (access(sysfsStatPath.c_str(), F_OK) == 0) {
+ addNumericSysfsStatPathinDir(sysfsStatPath, totalNumericSysfsStatPaths);
+ break;
+ }
+ }
+ }
+}
+
+int BatteryMitigationService::readNumericStats(struct BrownoutStatsExtend *brownoutStatsExtend) {
+ int i = 0;
+
+ if (i >= STATS_MAX_SIZE)
+ return 0;
+
+ for (const auto &sysfsStat : totalNumericSysfsStatPaths) {
+ snprintf(brownoutStatsExtend->numericStats[i].name,
+ STAT_NAME_SIZE, "%s", sysfsStat.name.c_str());
+ if (!readSysfsToInt(sysfsStat.path,
+ &brownoutStatsExtend->numericStats[i].value)) {
+ continue;
+ }
+ if (++i == STATS_MAX_SIZE) {
+ LOG(DEBUG) << "STATS_MAX_SIZE not enough for NumericStats";
+ break;
+ }
+ }
+
+ return i;
+}
+
+
+void BatteryMitigationService::startBrownoutEventThread() {
+ if (isBrownoutStatsBinarySupported()) {
+ brownoutEventThread = std::thread(&BatteryMitigationService::BrownoutEventThread, this);
+ eventThread = std::thread(&BatteryMitigationService::TriggerEventThread, this);
+ }
+}
+
+void BatteryMitigationService::stopEventThread(std::atomic_bool &thread_stop, int wakeup_event_fd,
+ std::thread &event_thread) {
+ if (!thread_stop.load()) {
+ thread_stop.store(true);
+ uint64_t flag = 1;
+ /* wakeup epoll_wait */
+ write(wakeup_event_fd, &flag, sizeof(flag));
+
+ if (event_thread.joinable()) {
+ event_thread.join();
+ }
+ }
+}
+
+void BatteryMitigationService::tearDownTriggerEventThread() {
+ triggerThreadStop.store(true);
+ close(triggeredStateWakeupEventFd);
+ close(triggeredStateEpollFd);
+ LOOP_TRIG_STATS(idx) {
+ close(triggeredStateFd[idx]);
+ }
+}
+
+int getMmapAddr(int &fd, const char *const path, size_t memSize, char **addr) {
+ fd = open(path, O_RDWR | O_CREAT, (mode_t) 0644);
+ if (fd < 0) {
+ return fd;
+ }
+ lseek(fd, memSize - 1, SEEK_SET);
+ write(fd, "", 1);
+ *addr = (char *)mmap(NULL, memSize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+ return 0;
+}
+
+int BatteryMitigationService::initTrigFd() {
+ int ret;
+ struct epoll_event trigEvent[MAX_EVENT];
+ struct epoll_event wakeupEvent;
+
+ LOOP_TRIG_STATS(idx) {
+ triggeredStateFd[idx] = open(cfg.triggeredStatePath[idx], O_RDONLY);
+ if (triggeredStateFd[idx] < 0) {
+ for (int i = idx - 1; i >= 0; i--) {
+ close(triggeredStateFd[i]);
+ }
+ return triggeredStateFd[idx];
+ }
+ }
+
+ triggeredStateEpollFd = epoll_create(MAX_EVENT + 1);
+ if (triggeredStateEpollFd < 0) {
+ LOOP_TRIG_STATS(idx) {
+ close(triggeredStateFd[idx]);
+ }
+ return triggeredStateEpollFd;
+ }
+
+ triggeredStateWakeupEventFd = eventfd(0, 0);
+ if (triggeredStateWakeupEventFd < 0) {
+ close(triggeredStateEpollFd);
+ LOOP_TRIG_STATS(idx) {
+ close(triggeredStateFd[idx]);
+ }
+ return triggeredStateWakeupEventFd;
+ }
+
+ LOOP_TRIG_STATS(i) {
+ trigEvent[i] = epoll_event();
+ trigEvent[i].data.fd = triggeredStateFd[i];
+ trigEvent[i].events = EPOLLET;
+ ret = epoll_ctl(triggeredStateEpollFd, EPOLL_CTL_ADD, triggeredStateFd[i], &trigEvent[i]);
+ if (ret < 0) {
+ close(triggeredStateWakeupEventFd);
+ close(triggeredStateEpollFd);
+ LOOP_TRIG_STATS(idx) {
+ close(triggeredStateFd[idx]);
+ }
+ return ret;
+ }
+ }
+
+ wakeupEvent = epoll_event();
+ wakeupEvent.data.fd = triggeredStateWakeupEventFd;
+ wakeupEvent.events = EPOLLIN | EPOLLWAKEUP;
+ ret = epoll_ctl(triggeredStateEpollFd, EPOLL_CTL_ADD, triggeredStateWakeupEventFd,
+ &wakeupEvent);
+ if (ret < 0) {
+ close(triggeredStateWakeupEventFd);
+ close(triggeredStateEpollFd);
+ LOOP_TRIG_STATS(idx) {
+ close(triggeredStateFd[idx]);
+ }
+ return ret;
+ }
+
+ return 0;
+}
+
+void BatteryMitigationService::TriggerEventThread() {
+ int requestedFd;
+ char buf[BUF_SIZE];
+ struct epoll_event events[EPOLL_MAXEVENTS];
+ std::smatch match;
+ if (initTrigFd() != 0) {
+ LOG(DEBUG) << "failed to init Trig FD";
+ tearDownTriggerEventThread();
+ return;
+ }
+
+ LOOP_TRIG_STATS(idx) {
+ read(triggeredStateFd[idx], buf, BUF_SIZE);
+ }
+
+ while (!triggerThreadStop.load()) {
+ requestedFd = epoll_wait(triggeredStateEpollFd, events, EPOLL_MAXEVENTS, -1);
+ if (requestedFd <= 0) {
+ /* ensure epoll_wait can sleep in the next loop */
+ LOOP_TRIG_STATS(idx) {
+ read(triggeredStateFd[idx], buf, BUF_SIZE);
+ }
+ continue;
+ }
+ std::string state;
+ for (int i = 0; i < requestedFd; i++) {
+ /* triggeredStateFd[i]: triggeredState event from kernel */
+ /* triggeredStateWakeupEventFd: wakeup epoll_wait to stop thread properly */
+ LOOP_TRIG_STATS(idx) {
+ if (events[i].data.fd == triggeredStateFd[idx]) {
+ read(triggeredStateFd[idx], buf, BUF_SIZE);
+ if (ReadFileToString(cfg.triggeredStatePath[idx], &state)) {
+ size_t pos = state.find("_");
+ std::string tState = state.substr(0, pos);
+ std::string tModule = state.substr(pos + 1);
+ LOG(INFO) << idx << " triggered, current state: " << tState << ". throttle "
+ << tModule;
+ /* b/299700579 launch throttling on targeted module */
+ }
+ break;
+ }
+ }
+ /* b/299700579 handle wakeupEvent here if we need to do something after this loop */
+ }
+ }
+}
+
+int BatteryMitigationService::initFd() {
+ int ret;
+ struct epoll_event triggeredIdxEvent, wakeupEvent;
+
+ brownoutStatsFd = open(cfg.BrownoutStatsPath, O_RDONLY);
+ if (brownoutStatsFd < 0) {
+ return brownoutStatsFd;
+ }
+
+ storingFd = open(cfg.StoringPath, O_RDWR | O_CREAT | O_TRUNC, (mode_t) 0644);
+ if (storingFd < 0) {
+ return storingFd;
+ }
+
+ triggeredIdxFd = open(cfg.TriggeredIdxPath, O_RDONLY);
+ if (triggeredIdxFd < 0) {
+ return triggeredIdxFd;
+ }
+
+ triggeredIdxEpollFd = epoll_create(2);
+ if (triggeredIdxEpollFd < 0) {
+ return triggeredIdxEpollFd;
+ }
+
+ wakeupEventFd = eventfd(0, 0);
+ if (wakeupEventFd < 0) {
+ return wakeupEventFd;
+ }
+
+ triggeredIdxEvent = epoll_event();
+ triggeredIdxEvent.data.fd = triggeredIdxFd;
+ triggeredIdxEvent.events = EPOLLERR | EPOLLWAKEUP;
+ ret = epoll_ctl(triggeredIdxEpollFd, EPOLL_CTL_ADD, triggeredIdxFd, &triggeredIdxEvent);
+ if (ret < 0) {
+ return ret;
+ }
+
+ wakeupEvent = epoll_event();
+ wakeupEvent.data.fd = wakeupEventFd;
+ wakeupEvent.events = EPOLLIN | EPOLLWAKEUP;
+ ret = epoll_ctl(triggeredIdxEpollFd, EPOLL_CTL_ADD, wakeupEventFd, &wakeupEvent);
+ if (ret < 0) {
+ return ret;
+ }
+
+ return 0;
+}
+
+void BatteryMitigationService::BrownoutEventThread() {
+ int requestedFd;
+ int triggeredIdx;
+ char buf[BUF_SIZE];
+ struct timeval eventReceivedTime;
+ struct timeval statStoredTime;
+ struct epoll_event events[EPOLL_MAXEVENTS];
+ struct BrownoutStatsExtend brownoutStatsExtend;
+ size_t brownoutStatsSize = sizeof(struct brownout_stats);
+ size_t brownoutStatsExtendSize = sizeof(struct BrownoutStatsExtend);
+ bool stopByEvent = false;
+ /* BrownoutEventThread store multiple brownout event (BROWNOUT_EVENT_BUF_SIZE)
+ * and each event contains several dumps (DUMP_TIMES).
+ */
+ int brownoutEventCounter = 0;
+
+ if (initFd() != 0) {
+ LOG(DEBUG) << "failed to init FD";
+ tearDownBrownoutEventThread();
+ return;
+ }
+
+ /* allow epoll_wait sleep in the first loop */
+ read(triggeredIdxFd, buf, BUF_SIZE);
+
+ while (!threadStop.load()) {
+ requestedFd = epoll_wait(triggeredIdxEpollFd, events, EPOLL_MAXEVENTS, -1);
+ if (requestedFd <= 0) {
+ /* ensure epoll_wait can sleep in the next loop */
+ read(triggeredIdxFd, buf, BUF_SIZE);
+ continue;
+ }
+ /* triggeredIdxFd: brownout event from kernel */
+ /* wakeupEventFd: wakeup epoll_wait to stop thread properly */
+ for (int i = 0; i < requestedFd; i++) {
+ if (events[i].data.fd == triggeredIdxFd) {
+ break;
+ } else {
+ stopByEvent = true;
+ }
+ }
+ if (stopByEvent) {
+ break;
+ }
+
+ /* record brownout event idx and received time */
+ gettimeofday(&eventReceivedTime, NULL);
+ lseek(triggeredIdxFd, 0, SEEK_SET);
+ if (read(triggeredIdxFd, buf, BUF_SIZE) == -1) {
+ continue;
+ }
+ triggeredIdx = atoi(buf);
+ if (triggeredIdx >= TRIGGERED_SOURCE_MAX || triggeredIdx < 0) {
+ continue;
+ }
+
+ /* dump brownout related stats */
+ std::string stats;
+ for (int i = 0; i < DUMP_TIMES; i++) {
+ memset(&brownoutStatsExtend, 0 , brownoutStatsExtendSize);
+
+ /* storing by string due the stats msg too complicate */
+ if (ReadFileToString(cfg.FvpStatsPath, &stats)) {
+ snprintf(brownoutStatsExtend.fvpStats, FVP_STATS_SIZE, "%s", stats.c_str());
+ }
+
+ /* storing numericStats */
+ readNumericStats(&brownoutStatsExtend);
+
+ /* storing brownoutStats */
+ lseek(brownoutStatsFd, 0, SEEK_SET);
+ read(brownoutStatsFd, &brownoutStatsExtend.brownoutStats, brownoutStatsSize);
+ gettimeofday(&statStoredTime, NULL);
+ brownoutStatsExtend.dumpTime = statStoredTime;
+ brownoutStatsExtend.eventReceivedTime = eventReceivedTime;
+ brownoutStatsExtend.eventIdx = triggeredIdx;
+
+ /* write to file */
+ lseek(storingFd,
+ brownoutStatsExtendSize * (brownoutEventCounter * DUMP_TIMES + i), SEEK_SET);
+ write(storingFd, &brownoutStatsExtend, brownoutStatsExtendSize);
+ fsync(storingFd);
+ }
+
+ if (++brownoutEventCounter == BROWNOUT_EVENT_BUF_SIZE) {
+ brownoutEventCounter = 0;
+ }
+ }
+}
+
+void readLPFPowerBitResolutions(const char *odpmDir, double *bitResolutions) {
+ char path[BUF_SIZE];
+
+ for (int i = 0; i < METER_CHANNEL_MAX; i++) {
+ snprintf(path, BUF_SIZE, "%s/in_power%d_scale", odpmDir, i);
+ if (!readSysfsToDouble(path, &bitResolutions[i])) {
+ bitResolutions[i] = 0;
+ }
+ }
+}
+
+void readLPFChannelNames(const char *odpmEnabledRailsPath, char **lpfChannelNames) {
+ char *line = NULL;
+ size_t len = 0;
+ ssize_t read;
+
+ FILE *fp = fopen(odpmEnabledRailsPath, "r");
+ if (!fp)
+ return;
+
+ int c = 0;
+ while ((read = getline(&line, &len, fp)) != -1 && read != 0) {
+ lpfChannelNames[c] = (char *)malloc(read);
+ if (lpfChannelNames[c] != nullptr) {
+ snprintf(lpfChannelNames[c], read, "%s", line);
+ }
+ if (++c == METER_CHANNEL_MAX)
+ break;
+ }
+ fclose(fp);
+
+ if (line)
+ free(line);
+}
+
+int getMainPmicID(const std::string &mainPmicNamePath, const std::string &subPmicNamePath) {
+ std::string content;
+ int ret = 0;
+ int mainPmicVer, subPmicVer;
+
+ if (!ReadFileToString(mainPmicNamePath, &content)) {
+ LOG(DEBUG) << "Failed to open " << mainPmicNamePath << " set device0 as main pmic";
+ return ret;
+ }
+ /* ODPM pmic name: s2mpgXX-odpm */
+ mainPmicVer = atoi(content.substr(5, 2).c_str());
+
+ if (!ReadFileToString(subPmicNamePath, &content)) {
+ LOG(DEBUG) << "Failed to open " << subPmicNamePath << " set device0 as main pmic";
+ return ret;
+ }
+ subPmicVer = atoi(content.substr(5, 2).c_str());
+
+ if (mainPmicVer > subPmicVer) {
+ ret = 1;
+ }
+
+ return ret;
+}
+
+void freeLpfChannelNames(char **lpfChannelNames) {
+ for (int c = 0; c < METER_CHANNEL_MAX; c++) {
+ free(lpfChannelNames[c]);
+ }
+}
+
+void BatteryMitigationService::tearDownBrownoutEventThread() {
+ close(triggeredIdxFd);
+ close(brownoutStatsFd);
+ close(triggeredIdxEpollFd);
+ close(wakeupEventFd);
+ close(storingFd);
+ threadStop.store(true);
+ freeLpfChannelNames(mainLpfChannelNames);
+ freeLpfChannelNames(subLpfChannelNames);
+
+}
+
+void BatteryMitigationService::initPmicRelated() {
+ mainPmicID = 0;
+ mainPmicID = getMainPmicID(cfg.PmicCommon[mainPmicID].PmicNamePath,
+ cfg.PmicCommon[subPmicID].PmicNamePath);
+ subPmicID = !mainPmicID;
+
+ /* read odpm resolutions and channel names */
+ readLPFPowerBitResolutions(cfg.PmicCommon[mainPmicID].OdpmDir, mainLpfBitResolutions);
+ readLPFPowerBitResolutions(cfg.PmicCommon[subPmicID].OdpmDir, subLpfBitResolutions);
+ readLPFChannelNames(cfg.PmicCommon[mainPmicID].OdpmEnabledRailsPath, mainLpfChannelNames);
+ readLPFChannelNames(cfg.PmicCommon[subPmicID].OdpmEnabledRailsPath, subLpfChannelNames);
+
+}
+
+void printUTC(FILE *fp, struct timespec time, const char *stat) {
+ if (!fp) {
+ return;
+ }
+ char timeBuff[BUF_SIZE];
+ if (strlen(stat) > 0) {
+ fprintf(fp, "%s: ", stat);
+ }
+ std::strftime(timeBuff, BUF_SIZE, "%Y-%m-%d %H:%M:%S", std::localtime(&time.tv_sec));
+ fprintf(fp, "%s.%09ld",timeBuff, time.tv_nsec);
+}
+
+void printUTC(FILE *fp, timeval time, const char *stat) {
+ if (!fp) {
+ return;
+ }
+ char timeBuff[BUF_SIZE];
+ if (strlen(stat) > 0) {
+ fprintf(fp, "%s: ", stat);
+ }
+ std::strftime(timeBuff, BUF_SIZE, "%Y-%m-%d %H:%M:%S", std::localtime(&time.tv_sec));
+ /* convert usec to nsec */
+ fprintf(fp, "%s.%06ld000",timeBuff, time.tv_usec);
+}
+
+void printODPMInstantDataSummary(FILE *fp, std::vector<odpm_instant_data> &odpmData,
+ double *lpfBitResolutions, char **lpfChannelNames) {
+ if (!fp) {
+ return;
+ }
+ std::vector<struct timespec> validTime;
+ std::vector<OdpmInstantPower> instPower[METER_CHANNEL_MAX];
+ std::vector<OdpmInstantPower> instPowerMax;
+ std::vector<OdpmInstantPower> instPowerMin;
+ std::vector<double> instPowerList;
+ std::vector<double> instPowerStd;
+
+ if (odpmData.size() == 0)
+ return;
+
+ /* initial Max, Min, Sum for sorting */
+ struct timespec curTime = odpmData[0].time;
+ validTime.emplace_back(curTime);
+ for (int c = 0; c < METER_CHANNEL_MAX; c++) {
+ double power = lpfBitResolutions[c] * odpmData[0].value[c];
+ instPower[c].emplace_back((OdpmInstantPower){curTime, power});
+ instPowerMax.emplace_back((OdpmInstantPower){curTime, power});
+ instPowerMin.emplace_back((OdpmInstantPower){curTime, power});
+ instPowerList.emplace_back(power);
+ }
+
+ for (auto lpf = (odpmData.begin() + 1); lpf != odpmData.end(); lpf++) {
+ curTime = lpf->time;
+ /* remove duplicate data by checking the odpm instant data dump time */
+ auto it = std::find_if(validTime.begin(), validTime.end(),
+ [&_ts = curTime] (const struct timespec &ts) ->
+ bool {return _ts.tv_sec == ts.tv_sec &&
+ _ts.tv_nsec == ts.tv_nsec;});
+ if (it == validTime.end()) {
+ validTime.emplace_back(curTime);
+ for (int c = 0; c < METER_CHANNEL_MAX; c++){
+ double power = lpfBitResolutions[c] * lpf->value[c];
+ instPower[c].emplace_back((OdpmInstantPower){curTime, power});
+ instPowerList[c] += power;
+ if (power > instPowerMax[c].value) {
+ instPowerMax[c].value = power;
+ instPowerMax[c].time = curTime;
+ }
+ if (power < instPowerMin[c].value) {
+ instPowerMin[c].value = power;
+ instPowerMin[c].time = curTime;
+ }
+ }
+ }
+ }
+
+ int n = validTime.size();
+ for (int c = 0; c < METER_CHANNEL_MAX; c++) {
+ /* sort instant power by time */
+ std::sort(instPower[c].begin(), instPower[c].end(),
+ [] (const auto &i, const auto &j)
+ {return i.time.tv_sec < j.time.tv_sec ||
+ (i.time.tv_sec == j.time.tv_sec &&
+ i.time.tv_nsec < j.time.tv_nsec);});
+ /* compute std for each channel */
+ double avg = instPowerList[c] / n;
+ double mse = 0;
+ for (int i = 0; i < n; i++) {
+ mse += pow(instPower[c][i].value - avg, 2);
+ }
+ instPowerStd.emplace_back(pow(mse / n, 0.5));
+ }
+
+ /* print Max, Min, Avg, Std */
+ for (int c = 0; c < METER_CHANNEL_MAX; c++) {
+ fprintf(fp, "%s Max: %.2f Min: %.2f Avg: %.2f Std: %.2f\n", lpfChannelNames[c],
+ instPowerMax[c].value,
+ instPowerMin[c].value,
+ instPowerList[c] / n,
+ instPowerStd[c]);
+ }
+ fprintf(fp, "\n");
+
+ /* print time */
+ fprintf(fp, "time ");
+ for (int i = 0; i < n; i++) {
+ printUTC(fp, instPower[0][i].time, "");
+ fprintf(fp, " ");
+ }
+ fprintf(fp, "\n");
+
+ /* print instant power by channel */
+ for (int c = 0; c < METER_CHANNEL_MAX; c++){
+ fprintf(fp, "%s ", lpfChannelNames[c]);
+ for (int i = 0; i < n; i++) {
+ fprintf(fp, "%.2f ", instPower[c][i].value);
+ }
+ fprintf(fp, "\n");
+ }
+
+ fprintf(fp, "\n");
+}
+
+void printLatency(FILE *fp, struct BrownoutStatsExtend *brownoutStatsExtend) {
+ if (!fp) {
+ return;
+ }
+ /* received latency */
+ struct timespec recvLatency;
+ recvLatency.tv_sec = brownoutStatsExtend[0].eventReceivedTime.tv_sec - \
+ brownoutStatsExtend[0].brownoutStats.triggered_time.tv_sec;
+
+ signed long long temp = brownoutStatsExtend[0].eventReceivedTime.tv_usec * 1000;
+ if (temp >= brownoutStatsExtend[0].brownoutStats.triggered_time.tv_nsec)
+ recvLatency.tv_nsec = brownoutStatsExtend[0].eventReceivedTime.tv_usec * 1000 - \
+ brownoutStatsExtend[0].brownoutStats.triggered_time.tv_nsec;
+ else
+ recvLatency.tv_nsec = NSEC_PER_SEC - \
+ brownoutStatsExtend[0].brownoutStats.triggered_time.tv_nsec \
+ + brownoutStatsExtend[0].eventReceivedTime.tv_usec * 1000;
+
+ /* dump latency */
+ struct timespec dumpLatency;
+ dumpLatency.tv_sec = brownoutStatsExtend[0].dumpTime.tv_sec - \
+ brownoutStatsExtend[0].eventReceivedTime.tv_sec;
+
+ temp = brownoutStatsExtend[0].dumpTime.tv_usec;
+ if (temp >= brownoutStatsExtend[0].eventReceivedTime.tv_usec)
+ dumpLatency.tv_nsec = (brownoutStatsExtend[0].dumpTime.tv_usec - \
+ brownoutStatsExtend[0].eventReceivedTime.tv_usec) * 1000;
+ else
+ dumpLatency.tv_nsec = NSEC_PER_SEC - \
+ brownoutStatsExtend[0].eventReceivedTime.tv_usec * 1000 + \
+ brownoutStatsExtend[0].dumpTime.tv_usec * 1000;
+
+ /* total latency */
+ struct timespec totalLatency;
+ totalLatency.tv_sec = brownoutStatsExtend[0].dumpTime.tv_sec - \
+ brownoutStatsExtend[0].brownoutStats.triggered_time.tv_sec;
+ temp = brownoutStatsExtend[0].dumpTime.tv_usec * 1000;
+ if (temp >= brownoutStatsExtend[0].brownoutStats.triggered_time.tv_nsec)
+ totalLatency.tv_nsec = brownoutStatsExtend[0].dumpTime.tv_usec * 1000 - \
+ brownoutStatsExtend[0].brownoutStats.triggered_time.tv_nsec;
+ else
+ totalLatency.tv_nsec = NSEC_PER_SEC - \
+ brownoutStatsExtend[0].brownoutStats.triggered_time.tv_nsec + \
+ brownoutStatsExtend[0].dumpTime.tv_usec * 1000;
+
+ fprintf(fp, "recvLatency %ld.%09ld\n", recvLatency.tv_sec, recvLatency.tv_nsec);
+ fprintf(fp, "dumpLatency %ld.%09ld\n", dumpLatency.tv_sec, dumpLatency.tv_nsec);
+ fprintf(fp, "totalLatency %ld.%09ld\n\n", totalLatency.tv_sec, totalLatency.tv_nsec);
+
+}
+
+void BatteryMitigationService::printBrownoutStatsExtendSummary(
+ FILE *fp, struct BrownoutStatsExtend *brownoutStatsExtend) {
+ if (!fp) {
+ return;
+ }
+ std::vector<odpm_instant_data> odpmData[PMIC_NUM];
+
+ /* print out the triggered_time in first dump */
+ printUTC(fp, brownoutStatsExtend[0].brownoutStats.triggered_time, "triggered_time");
+ fprintf(fp, "\n");
+ fprintf(fp, "triggered_idx: %d\n", brownoutStatsExtend[0].brownoutStats.triggered_idx);
+ printLatency(fp, brownoutStatsExtend);
+
+ /* skip time invalid odpm instant data */
+ for (int i = 0; i < DUMP_TIMES; i++) {
+ for (int d = 0; d < DATA_LOGGING_LEN; d++) {
+ if (brownoutStatsExtend[i].brownoutStats.main_odpm_instant_data[d].time.tv_sec != 0) {
+ odpmData[mainPmicID].emplace_back(brownoutStatsExtend[i].brownoutStats.main_odpm_instant_data[d]);
+ }
+ if (brownoutStatsExtend[i].brownoutStats.sub_odpm_instant_data[d].time.tv_sec != 0) {
+ odpmData[subPmicID].emplace_back(brownoutStatsExtend[i].brownoutStats.sub_odpm_instant_data[d]);
+ }
+ }
+ }
+
+ printODPMInstantDataSummary(fp,
+ odpmData[mainPmicID], mainLpfBitResolutions, mainLpfChannelNames);
+ printODPMInstantDataSummary(fp,
+ odpmData[subPmicID], subLpfBitResolutions, subLpfChannelNames);
+
+}
+
+void printOdpmInstantData(FILE *fp, struct odpm_instant_data odpmInstantData) {
+ if (!fp) {
+ return;
+ }
+ if (odpmInstantData.time.tv_sec == 0 &&
+ odpmInstantData.time.tv_nsec == 0) {
+ return;
+ }
+ printUTC(fp, odpmInstantData.time, "");
+ fprintf(fp, " ");
+ for (int i = 0; i < METER_CHANNEL_MAX; i++){
+ fprintf(fp, "%d ", odpmInstantData.value[i]);
+ }
+ fprintf(fp, "\n");
+}
+
+void parseFvpStats(const char *stats, std::vector<numericStat> &result) {
+ std::string fvpStats(stats);
+ std::string line;
+ std::istringstream iss(fvpStats);
+ size_t pos;
+ while (getline(iss, line, '\n')) {
+ if (line.find("time_ns") == std::string::npos) {
+ if (line.find("cur_freq:") == std::string::npos) {
+ result.emplace_back();
+ snprintf(result.back().name, STAT_NAME_SIZE, "%s", line.c_str());
+ continue;
+ } else if (result.size() > 0 && (pos = line.find(" ")) != std::string::npos) {
+ sscanf(line.substr(pos, line.size()).c_str(), "%d", &result.back().value);
+ }
+ }
+ }
+
+}
+
+void printBrownoutStatsExtendRaw(FILE *fp, struct BrownoutStatsExtend *brownoutStatsExtend) {
+ if (!fp) {
+ return;
+ }
+ printUTC(fp, brownoutStatsExtend->brownoutStats.triggered_time, "triggered_time");
+ fprintf(fp, "\n");
+ fprintf(fp, "triggered_idx: %d\n", brownoutStatsExtend->brownoutStats.triggered_idx);
+
+ fprintf(fp, "main_odpm_instant_data:\n");
+ for (int d = 0; d < DATA_LOGGING_LEN; d++) {
+ printOdpmInstantData(fp, brownoutStatsExtend->brownoutStats.main_odpm_instant_data[d]);
+ }
+ fprintf(fp, "sub_odpm_instant_data:\n");
+ for (int d = 0; d < DATA_LOGGING_LEN; d++) {
+ printOdpmInstantData(fp, brownoutStatsExtend->brownoutStats.sub_odpm_instant_data[d]);
+ }
+ fprintf(fp, "mitigation_state:\n");
+ for (int d = 0; d < DATA_LOGGING_LEN; d++) {
+ fprintf(fp, "%d ", brownoutStatsExtend->brownoutStats.triggered_state[d]);
+ }
+
+ fprintf(fp, "\n");
+ fprintf(fp, "fvp_stats:\n");
+ std::vector<numericStat> fvpStats;
+ parseFvpStats(brownoutStatsExtend->fvpStats, fvpStats);
+ for (const auto &fvpStat : fvpStats) {
+ fprintf(fp, "%s_freq: %d\n", fvpStat.name, fvpStat.value);
+ }
+ for (int i = 0; i < STATS_MAX_SIZE; i++) {
+ if (strlen(brownoutStatsExtend->numericStats[i].name) > 0)
+ fprintf(fp, "%s: %d\n", brownoutStatsExtend->numericStats[i].name,
+ brownoutStatsExtend->numericStats[i].value);
+ }
+ printUTC(fp, brownoutStatsExtend->eventReceivedTime, "eventReceivedTime");
+ fprintf(fp, "\n");
+ printUTC(fp, brownoutStatsExtend->dumpTime, "dumpTime");
+ fprintf(fp, "\n");
+ fprintf(fp, "eventIdx: %d\n", brownoutStatsExtend->eventIdx);
+}
+
+bool getValueFromNumericStats(const char *statName, int *value,
+ struct numericStat *numericStats) {
+ for (int i = 0; i < STATS_MAX_SIZE; i++) {
+ if (strcmp(numericStats[i].name, statName) == 0) {
+ *value = numericStats[i].value;
+ return true;
+ }
+ }
+ return false;
+}
+
+void setMaxNumericStat(const char *statName, int *maxValue,
+ struct numericStat *numericStats) {
+ int statValue;
+ if (getValueFromNumericStats(statName,
+ &statValue,
+ numericStats) &&
+ statValue > *maxValue) {
+ *maxValue = statValue;
+ }
+}
+
+void setMinNumericStat(const char *statName, int *minValue,
+ struct numericStat *numericStats) {
+ int statValue;
+ if (getValueFromNumericStats(statName,
+ &statValue,
+ numericStats) &&
+ statValue < *minValue) {
+ *minValue = statValue;
+ }
+}
+
+/* fvp_stats constins MIF, CL0-2, TPU, AUR */
+void setMinNumericStat(const char *statName, int *minValue,
+ std::vector<numericStat> &fvpStats) {
+ auto it = std::find_if(fvpStats.begin(), fvpStats.end(),
+ [&name = statName] (const struct numericStat &ns) ->
+ bool {return strcmp(ns.name, name) == 0;});
+ if (it != fvpStats.end()) {
+ if (*minValue > (*it).value) {
+ *minValue = (*it).value;
+ }
+ }
+}
+
+void initBrownoutStatsCSVRow(struct BrownoutStatsCSVRow *row) {
+ memset(row, 0, sizeof(struct BrownoutStatsCSVRow));
+ row->min_battery_soc = INT_MAX;
+ row->min_battery_cycle = INT_MAX;
+ row->min_voltage_now = INT_MAX;
+ row->min_cpu0_freq = INT_MAX;
+ row->min_cpu1_freq = INT_MAX;
+ row->min_cpu2_freq = INT_MAX;
+ row->min_gpu_freq = INT_MAX;
+ row->min_tpu_freq = INT_MAX;
+ row->min_aur_freq = INT_MAX;
+}
+
+bool readBrownoutStatsExtend(const char *storingPath,
+ struct BrownoutStatsExtend *brownoutStatsExtends) {
+ int fd = open(storingPath, O_RDONLY);
+ if (fd < 0) {
+ LOG(DEBUG) << "Failed to open " << storingPath;
+ return false;
+ }
+
+ size_t logFileSize = lseek(fd, 0, SEEK_END);
+ char *logFileAddr = (char *) mmap(NULL, logFileSize, PROT_READ, MAP_PRIVATE, fd, 0);
+ close(fd);
+
+ memcpy(brownoutStatsExtends, logFileAddr, logFileSize);
+ munmap(logFileAddr, logFileSize);
+
+ return true;
+}
+
+void BatteryMitigationService::getBrownoutStatsCSVRow(
+ struct BrownoutStatsExtend *brownoutStatsExtendPerEvent,
+ struct BrownoutStatsCSVRow *row) {
+ initBrownoutStatsCSVRow(row);
+ for (int i = 0; i < DUMP_TIMES; i++) {
+ if (i == 0) {
+ /* triggered_time */
+ row->triggered_time = brownoutStatsExtendPerEvent[0].brownoutStats.triggered_time;
+ /* triggered_idx */
+ row->triggered_idx = brownoutStatsExtendPerEvent[0].brownoutStats.triggered_idx;
+ }
+ double power;
+ for (int d = 0; d < DATA_LOGGING_LEN; d++) {
+ for (int c = 0; c < METER_CHANNEL_MAX; c++) {
+ /* max_main_odpm_instant_power */
+ power =
+ brownoutStatsExtendPerEvent[i].brownoutStats.main_odpm_instant_data[d].value[c] *
+ mainLpfBitResolutions[c];
+ if (power > row->max_main_odpm_instant_power[c]) {
+ row->max_main_odpm_instant_power[c] = power;
+ }
+ /* max_sub_odpm_instant_power */
+ power =
+ brownoutStatsExtendPerEvent[i].brownoutStats.sub_odpm_instant_data[d].value[c] *
+ subLpfBitResolutions[c];
+ if (power > row->max_sub_odpm_instant_power[c]) {
+ row->max_sub_odpm_instant_power[c] = power;
+ }
+ }
+ }
+ /* min_battery_soc */
+ setMinNumericStat("battery_soc",
+ &row->min_battery_soc,
+ brownoutStatsExtendPerEvent[i].numericStats);
+ /* max_battery_temp */
+ setMaxNumericStat("battery_temp",
+ &row->max_battery_temp,
+ brownoutStatsExtendPerEvent[i].numericStats);
+ /* min_battery_cycle */
+ setMinNumericStat("battery_cycle",
+ &row->min_battery_cycle,
+ brownoutStatsExtendPerEvent[i].numericStats);
+ /* min_voltage_now */
+ setMinNumericStat("voltage_now",
+ &row->min_voltage_now,
+ brownoutStatsExtendPerEvent[i].numericStats);
+ /* max_current_now */
+ setMaxNumericStat("current_now",
+ &row->max_current_now,
+ brownoutStatsExtendPerEvent[i].numericStats);
+ /* min_cpu0_freq */
+ setMinNumericStat("cpu0_freq",
+ &row->min_cpu0_freq,
+ brownoutStatsExtendPerEvent[i].numericStats);
+ /* min_cpu1_freq */
+ setMinNumericStat("cpu1_freq",
+ &row->min_cpu1_freq,
+ brownoutStatsExtendPerEvent[i].numericStats);
+ /* min_cpu2_freq */
+ setMinNumericStat("cpu2_freq",
+ &row->min_cpu2_freq,
+ brownoutStatsExtendPerEvent[i].numericStats);
+ /* min_gpu_freq */
+ setMinNumericStat("gpu_freq",
+ &row->min_gpu_freq,
+ brownoutStatsExtendPerEvent[i].numericStats);
+
+ std::vector<numericStat> fvpStats;
+ parseFvpStats(brownoutStatsExtendPerEvent[i].fvpStats, fvpStats);
+ /* min_tpu_freq */
+ setMinNumericStat("TPU", &row->min_tpu_freq, fvpStats);
+ /* min_aur_freq */
+ setMinNumericStat("AUR", &row->min_aur_freq, fvpStats);
+ }
+
+}
+
+bool BatteryMitigationService::genLastmealCSV(const char *parsedMealCSVPath) {
+ if (access(cfg.StoringPath, F_OK) != 0) {
+ LOG(DEBUG) << "Failed to access " << cfg.StoringPath;
+ return false;
+ }
+
+ struct BrownoutStatsExtend brownoutStatsExtends[BROWNOUT_EVENT_BUF_SIZE][DUMP_TIMES];
+ if (!readBrownoutStatsExtend(cfg.StoringPath, brownoutStatsExtends[0])) {
+ return false;
+ }
+
+ std::vector<struct BrownoutStatsCSVRow> rows;
+ for (int e = 0; e < BROWNOUT_EVENT_BUF_SIZE; e++) {
+ if (brownoutStatsExtends[e][0].brownoutStats.triggered_time.tv_sec != 0) {
+ rows.emplace_back();
+ getBrownoutStatsCSVRow(brownoutStatsExtends[e], &rows.back());
+ }
+ }
+ /* sort rows by time */
+ std::sort(rows.begin(), rows.end(),
+ [] (const auto &i, const auto &j)
+ {return i.triggered_time.tv_sec < j.triggered_time.tv_sec ||
+ (i.triggered_time.tv_sec == j.triggered_time.tv_sec &&
+ i.triggered_time.tv_nsec < j.triggered_time.tv_nsec);});
+
+ FILE *fp = nullptr;
+ fp = fopen(parsedMealCSVPath, "w");
+ if (!fp)
+ return false;
+
+ /* print csv fields */
+ fprintf(fp, "%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,", brownoutStatsCSVFields.triggered_time,
+ brownoutStatsCSVFields.triggered_idx, brownoutStatsCSVFields.battery_soc,
+ brownoutStatsCSVFields.battery_temp, brownoutStatsCSVFields.battery_cycle,
+ brownoutStatsCSVFields.voltage_now, brownoutStatsCSVFields.current_now,
+ brownoutStatsCSVFields.cpu0_freq, brownoutStatsCSVFields.cpu1_freq,
+ brownoutStatsCSVFields.cpu2_freq, brownoutStatsCSVFields.gpu_freq,
+ brownoutStatsCSVFields.tpu_freq, brownoutStatsCSVFields.aur_freq);
+ for (int c = 1; c <= METER_CHANNEL_MAX; c++) {
+ fprintf(fp, "%s%02d,", brownoutStatsCSVFields.odpm_prefix, c);
+ }
+ for (int c = 1; c <= METER_CHANNEL_MAX; c++) {
+ fprintf(fp, "%s%02d,", brownoutStatsCSVFields.odpm_prefix, c + METER_CHANNEL_MAX);
+ }
+ fprintf(fp, "\n");
+
+ /* print csv rows */
+ for (auto row = rows.begin(); row != rows.end(); row++) {
+ printUTC(fp, row->triggered_time, "");
+ fprintf(fp, ",");
+ fprintf(fp, "%d,", row->triggered_idx);
+ fprintf(fp, "%d,", row->min_battery_soc);
+ fprintf(fp, "%d,", row->max_battery_temp);
+ fprintf(fp, "%d,", row->min_battery_cycle);
+ fprintf(fp, "%d,", row->min_voltage_now);
+ fprintf(fp, "%d,", row->max_current_now);
+ fprintf(fp, "%d,", row->min_cpu0_freq);
+ fprintf(fp, "%d,", row->min_cpu1_freq);
+ fprintf(fp, "%d,", row->min_cpu2_freq);
+ fprintf(fp, "%d,", row->min_gpu_freq);
+ fprintf(fp, "%d,", row->min_tpu_freq);
+ fprintf(fp, "%d,", row->min_aur_freq);
+ for (int c = 0; c < METER_CHANNEL_MAX; c++) {
+ fprintf(fp, "%d,", int(row->max_main_odpm_instant_power[c] * 1000));
+ }
+ for (int c = 0; c < METER_CHANNEL_MAX; c++) {
+ fprintf(fp, "%d,", int(row->max_sub_odpm_instant_power[c] * 1000));
+ }
+ fprintf(fp, "\n");
+ }
+ fclose(fp);
+
+ return true;
+}
+
+bool BatteryMitigationService::isTimeValid(const char *storingPath,
+ std::chrono::system_clock::time_point startTime) {
+ struct BrownoutStatsExtend brownoutStatsExtends[BROWNOUT_EVENT_BUF_SIZE][DUMP_TIMES];
+ if (!readBrownoutStatsExtend(storingPath, brownoutStatsExtends[0])) {
+ return false;
+ }
+ time_t sec =
+ std::chrono::time_point_cast<std::chrono::seconds>(startTime).time_since_epoch().count();
+
+ for (int e = 0; e < BROWNOUT_EVENT_BUF_SIZE; e++) {
+ if (brownoutStatsExtends[e][0].dumpTime.tv_sec == 0 &&
+ brownoutStatsExtends[e][0].dumpTime.tv_usec == 0) {
+ continue;
+ } else if (brownoutStatsExtends[e][0].dumpTime.tv_sec < sec) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool BatteryMitigationService::parseBrownoutStatsExtend(FILE *fp) {
+ if (!fp) {
+ return false;
+ }
+ struct BrownoutStatsExtend brownoutStatsExtends[BROWNOUT_EVENT_BUF_SIZE][DUMP_TIMES];
+ if (!readBrownoutStatsExtend(cfg.StoringPath, brownoutStatsExtends[0])) {
+ return false;
+ }
+
+ for (int e = 0; e < BROWNOUT_EVENT_BUF_SIZE; e++) {
+ if (brownoutStatsExtends[e][0].dumpTime.tv_sec == 0 &&
+ brownoutStatsExtends[e][0].dumpTime.tv_usec == 0) {
+ continue;
+ }
+ printBrownoutStatsExtendSummary(fp, brownoutStatsExtends[e]);
+ fprintf(fp, "=== RAW ===\n");
+ for (int i = 0; i < DUMP_TIMES; i++) {
+ fprintf(fp, "=== Dump %d-%d ===\n", e, i);
+ printBrownoutStatsExtendRaw(fp, &brownoutStatsExtends[e][i]);
+ fprintf(fp, "=============\n\n");
+ }
+ }
+ return true;
+}
+
+bool BatteryMitigationService::genParsedMeal(const char *parsedMealPath) {
+ if (access(cfg.StoringPath, F_OK) != 0) {
+ LOG(DEBUG) << "Failed to access " << cfg.StoringPath;
+ return false;
+ }
+
+ FILE *fp = nullptr;
+ fp = fopen(parsedMealPath, "w");
+ if (!fp || !parseBrownoutStatsExtend(fp)) {
+ return false;
+ }
+ fclose(fp);
+
+ return true;
+}
+
+} // namespace pixel
+} // namespace google
+} // namespace hardware
+} // namespace android
diff --git a/battery_mitigation/include/battery_mitigation/BatteryMitigation.h b/battery_mitigation/include/battery_mitigation/BatteryMitigation.h
index d3ce433d..de22b9af 100644
--- a/battery_mitigation/include/battery_mitigation/BatteryMitigation.h
+++ b/battery_mitigation/include/battery_mitigation/BatteryMitigation.h
@@ -16,6 +16,7 @@
#pragma once
+
#include <utils/RefBase.h>
#include "MitigationThermalManager.h"
diff --git a/battery_mitigation/include/battery_mitigation/BatteryMitigationService.h b/battery_mitigation/include/battery_mitigation/BatteryMitigationService.h
new file mode 100644
index 00000000..9c697140
--- /dev/null
+++ b/battery_mitigation/include/battery_mitigation/BatteryMitigationService.h
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2023 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/chrono_utils.h>
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/parseint.h>
+#include <android-base/properties.h>
+#include <android-base/strings.h>
+#include <errno.h>
+#include <sys/epoll.h>
+#include <sys/eventfd.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/system_properties.h>
+#include <unistd.h>
+#include <utils/RefBase.h>
+
+#include <algorithm>
+#include <cmath>
+#include <map>
+#include <regex>
+#include <thread>
+
+#include "MitigationConfig.h"
+
+#define MIN_SUPPORTED_PLATFORM 2 /* CDT */
+#define MAX_SUPPORTED_PLATFORM 5
+#define NSEC_PER_SEC 1000000000L
+#define BROWNOUT_EVENT_BUF_SIZE 10
+#define DUMP_TIMES 12
+#define EPOLL_MAXEVENTS 12
+#define BUF_SIZE 128
+#define FVP_STATS_SIZE 4096
+#define STAT_NAME_SIZE 48
+#define STATS_MAX_SIZE 64
+#define PMIC_NUM 2
+#define LOOP_TRIG_STATS(idx) for (int idx = 0; idx < MAX_EVENT; idx++)
+
+namespace android {
+namespace hardware {
+namespace google {
+namespace pixel {
+
+using ::android::sp;
+using android::hardware::google::pixel::MitigationConfig;
+
+struct numericStat {
+ char name[STAT_NAME_SIZE];
+ int value;
+};
+
+struct OdpmInstantPower {
+ struct timespec time;
+ double value;
+};
+
+struct BrownoutStatsCSVFields {
+ const char *const triggered_time;
+ const char *const triggered_idx;
+ const char *const battery_soc;
+ const char *const battery_temp;
+ const char *const battery_cycle;
+ const char *const voltage_now;
+ const char *const current_now;
+ const char *const cpu0_freq;
+ const char *const cpu1_freq;
+ const char *const cpu2_freq;
+ const char *const gpu_freq;
+ const char *const tpu_freq;
+ const char *const aur_freq;
+ const char *const odpm_prefix;
+};
+
+struct BrownoutStatsCSVRow {
+ struct timespec triggered_time;
+ int triggered_idx;
+ int min_battery_soc;
+ int max_battery_temp;
+ int min_battery_cycle;
+ int min_voltage_now;
+ int max_current_now;
+ int min_cpu0_freq;
+ int min_cpu1_freq;
+ int min_cpu2_freq;
+ int min_gpu_freq;
+ int min_tpu_freq;
+ int min_aur_freq;
+
+ double max_main_odpm_instant_power[METER_CHANNEL_MAX];
+ double max_sub_odpm_instant_power[METER_CHANNEL_MAX];
+};
+
+struct BrownoutStatsExtend {
+ struct brownout_stats brownoutStats;
+ char fvpStats[FVP_STATS_SIZE];
+ struct numericStat numericStats[STATS_MAX_SIZE];
+ timeval eventReceivedTime;
+ timeval dumpTime;
+ unsigned int eventIdx;
+};
+
+class BatteryMitigationService : public RefBase {
+ public:
+ BatteryMitigationService(const struct MitigationConfig::EventThreadConfig &eventThreadCfg);
+ ~BatteryMitigationService();
+
+ void startBrownoutEventThread();
+ void stopEventThread(std::atomic_bool &thread_stop, int wakeup_event_fd,
+ std::thread &event_thread);
+ bool isBrownoutStatsBinarySupported();
+ bool isPlatformSupported();
+ bool isTimeValid(const char*, std::chrono::system_clock::time_point);
+ bool genParsedMeal(const char*);
+ bool genLastmealCSV(const char*);
+ private:
+ struct MitigationConfig::EventThreadConfig cfg;
+
+ int storingFd;
+ int triggeredStateFd[MAX_EVENT];
+ int triggeredStateEpollFd;
+ int triggeredStateWakeupEventFd;
+ std::thread eventThread;
+ std::atomic_bool triggerThreadStop{false};
+ int brownoutStatsFd;
+ int triggeredIdxFd;
+ int triggeredIdxEpollFd;
+ int wakeupEventFd;
+ std::thread brownoutEventThread;
+ std::atomic_bool threadStop{false};
+
+ int mainPmicID;
+ int subPmicID;
+ double mainLpfBitResolutions[METER_CHANNEL_MAX];
+ double subLpfBitResolutions[METER_CHANNEL_MAX];
+ char *mainLpfChannelNames[METER_CHANNEL_MAX];
+ char *subLpfChannelNames[METER_CHANNEL_MAX];
+ std::vector<MitigationConfig::numericSysfs> totalNumericSysfsStatPaths;
+
+ void BrownoutEventThread();
+ void TriggerEventThread();
+ void initTotalNumericSysfsPaths();
+ void initPmicRelated();
+ int initFd();
+ int initTrigFd();
+ void tearDownBrownoutEventThread();
+ void tearDownTriggerEventThread();
+ int readNumericStats(struct BrownoutStatsExtend*);
+ bool parseBrownoutStatsExtend(FILE *);
+ void printBrownoutStatsExtendSummary(FILE *, struct BrownoutStatsExtend *);
+ void getBrownoutStatsCSVRow(struct BrownoutStatsExtend *, struct BrownoutStatsCSVRow *);
+};
+
+} // namespace pixel
+} // namespace google
+} // namespace hardware
+} // namespace android
diff --git a/battery_mitigation/include/battery_mitigation/MitigationConfig.h b/battery_mitigation/include/battery_mitigation/MitigationConfig.h
index 825e30c6..2062d29d 100644
--- a/battery_mitigation/include/battery_mitigation/MitigationConfig.h
+++ b/battery_mitigation/include/battery_mitigation/MitigationConfig.h
@@ -17,11 +17,22 @@
#ifndef HARDWARE_GOOGLE_PIXEL_BATTERY_MITIGATION_CONFIG_H
#define HARDWARE_GOOGLE_PIXEL_BATTERY_MITIGATION_CONFIG_H
+#include "uapi/brownout_stats.h"
+
namespace android {
namespace hardware {
namespace google {
namespace pixel {
+enum TriggeredEvent {
+ UVLO1,
+ UVLO2,
+ OILO1,
+ OILO2,
+ SMPL,
+ MAX_EVENT,
+};
+
class MitigationConfig {
public:
struct Config {
@@ -32,6 +43,42 @@ class MitigationConfig {
const char *const TimestampFormat;
};
+ struct numericSysfs {
+ std::string name;
+ std::string path;
+ };
+
+ struct numericSysfsList {
+ std::string name;
+ std::vector<std::string> paths;
+ };
+
+ struct platformSpecific {
+ const std::vector<numericSysfsList> NumericSysfsStatPaths;
+ const std::vector<numericSysfsList> NumericSysfsStatDirs;
+ };
+
+ struct pmicCommon {
+ const char *const OdpmDir;
+ const char *const OdpmEnabledRailsPath;
+ const char *const PmicNamePath;
+ };
+
+ struct EventThreadConfig {
+ const std::vector<numericSysfs> NumericSysfsStatPaths;
+ const std::vector<numericSysfs> NumericSysfsStatDirs;
+ const char *const TriggeredIdxPath;
+ const char *const triggeredStatePath[MAX_EVENT];
+ const char *const BrownoutStatsPath;
+ const char *const StoringPath;
+ const char *const ParsedThismealPath;
+ const char *const ParsedLastmealPath;
+ const char *const ParsedLastmealCSVPath;
+ const char *const FvpStatsPath;
+ const std::vector<pmicCommon> PmicCommon;
+ const platformSpecific PlatformSpecific;
+ };
+
MitigationConfig(const struct Config &cfg);
private:
diff --git a/battery_mitigation/include/battery_mitigation/uapi/brownout_stats.h b/battery_mitigation/include/battery_mitigation/uapi/brownout_stats.h
new file mode 100644
index 00000000..c913e40b
--- /dev/null
+++ b/battery_mitigation/include/battery_mitigation/uapi/brownout_stats.h
@@ -0,0 +1,27 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#ifndef __BROWNOUT_STATS_H
+#define __BROWNOUT_STATS_H
+
+#define METER_CHANNEL_MAX 12
+#define DATA_LOGGING_LEN 20
+#define TRIGGERED_SOURCE_MAX 17
+
+struct odpm_instant_data {
+ struct timespec time;
+ unsigned int value[METER_CHANNEL_MAX];
+};
+
+/* Notice: sysfs only allocates a buffer of PAGE_SIZE
+ * so the sizeof brownout_stats should be smaller than that
+ */
+struct brownout_stats {
+ struct timespec triggered_time;
+ unsigned int triggered_idx;
+
+ struct odpm_instant_data main_odpm_instant_data[DATA_LOGGING_LEN];
+ struct odpm_instant_data sub_odpm_instant_data[DATA_LOGGING_LEN];
+ unsigned int triggered_state[DATA_LOGGING_LEN];
+};
+
+#endif /* __BROWNOUT_STATS_H */
diff --git a/common/init.pixel.rc b/common/init.pixel.rc
index 25b14fe6..dc499c3b 100644
--- a/common/init.pixel.rc
+++ b/common/init.pixel.rc
@@ -17,10 +17,3 @@ on property:ota.warm_reset=1
on property:ota.warm_reset=0
write /sys/module/msm_poweroff/parameters/warm_reset 0
-
-on init
- copy_per_line /dev/cpuctl/tasks /dev/cpuctl/system/tasks
-
-# Migrate tasks again in case kernel threads are created during boot
-on property:sys.boot_completed=1
- copy_per_line /dev/cpuctl/tasks /dev/cpuctl/system/tasks
diff --git a/common/pixel-common-device.mk b/common/pixel-common-device.mk
index 5df9cda2..043bc0e0 100644
--- a/common/pixel-common-device.mk
+++ b/common/pixel-common-device.mk
@@ -57,3 +57,8 @@ PRODUCT_PROPERTY_OVERRIDES += \
# Virtual fingerprint HAL
PRODUCT_PACKAGES_DEBUG += com.android.hardware.biometrics.fingerprint.virtual
+# Virtual face HAL
+ifeq ($(RELEASE_AIDL_USE_UNFROZEN), true)
+PRODUCT_PACKAGES_DEBUG += com.android.hardware.biometrics.face.virtual
+endif
+
diff --git a/gpu_probe/Android.bp b/gpu_probe/Android.bp
new file mode 100644
index 00000000..370014c7
--- /dev/null
+++ b/gpu_probe/Android.bp
@@ -0,0 +1,33 @@
+//
+// Copyright (C) 2023 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"],
+}
+
+rust_binary {
+ name: "gpu_probe",
+ srcs: [
+ "main.rs",
+ ],
+ rustlibs: [
+ "liblibloading",
+ "libandroid_logger",
+ "liblog_rust",
+ ],
+ shared_libs: ["libdl"],
+ vendor: true,
+ init_rc: [ "gpu_probe.rc" ],
+}
diff --git a/gpu_probe/gpu_probe.rc b/gpu_probe/gpu_probe.rc
new file mode 100644
index 00000000..6cee644b
--- /dev/null
+++ b/gpu_probe/gpu_probe.rc
@@ -0,0 +1,9 @@
+# Start gpu_probe service for Pixel devices.
+
+service gpu_probe /vendor/bin/gpu_probe
+ class hal
+ oneshot
+ disabled
+ user system
+ capabilities SYS_ADMIN
+ group graphics
diff --git a/gpu_probe/main.rs b/gpu_probe/main.rs
new file mode 100644
index 00000000..906105e0
--- /dev/null
+++ b/gpu_probe/main.rs
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2023 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.
+ */
+
+fn main() {
+ android_logger::init_once(
+ android_logger::Config::default()
+ .with_tag("gpu_probe")
+ .with_min_level(log::Level::Info),
+ );
+
+ log::info!("Starting pixel gpu_probe");
+ std::panic::set_hook(Box::new(|panic_msg| {
+ log::error!("{}", panic_msg);
+ }));
+ unsafe {
+ let gpudataproducer_library =
+ libloading::Library::new("/vendor/lib64/libgpudataproducer.so").unwrap();
+ let start: libloading::Symbol<fn() -> ()> = gpudataproducer_library.get(b"start").unwrap();
+ start();
+ };
+}
diff --git a/health/ChargerDetect.cpp b/health/ChargerDetect.cpp
index 26d926ff..a2615294 100644
--- a/health/ChargerDetect.cpp
+++ b/health/ChargerDetect.cpp
@@ -70,7 +70,7 @@ void ChargerDetect::populateTcpmPsyName(std::string* tcpmPsyName) {
while ((entry = readdir(dir.get()))) {
const char* name = entry->d_name;
- KLOG_INFO(LOG_TAG, "Psy name:%s", name);
+ KLOG_DEBUG(LOG_TAG, "Psy name:%s", name);
if (strstr(name, kTcpmPsyFilter)) {
*tcpmPsyName = name;
}
@@ -119,7 +119,7 @@ void ChargerDetect::onlineUpdate(HealthInfo *health_info) {
if (tcpmPsyName.empty()) {
populateTcpmPsyName(&tcpmPsyName);
- KLOG_INFO(LOG_TAG, "TcpmPsyName:%s\n", tcpmPsyName.c_str());
+ KLOG_DEBUG(LOG_TAG, "TcpmPsyName:%s\n", tcpmPsyName.c_str());
}
if (!getIntField(kUsbOnlinePath)) {
@@ -150,7 +150,7 @@ void ChargerDetect::onlineUpdate(HealthInfo *health_info) {
return;
}
- KLOG_INFO(LOG_TAG, "TcpmPsy Usbtype:%s\n", usbPsyType.c_str());
+ KLOG_DEBUG(LOG_TAG, "TcpmPsy Usbtype:%s\n", usbPsyType.c_str());
return;
}
diff --git a/misc_writer/include/misc_writer/misc_writer.h b/misc_writer/include/misc_writer/misc_writer.h
index ba1b6857..7757640d 100644
--- a/misc_writer/include/misc_writer/misc_writer.h
+++ b/misc_writer/include/misc_writer/misc_writer.h
@@ -40,6 +40,9 @@ enum class MiscWriterActions : int32_t {
kWriteTimeOffset,
kSetMaxRamSize,
kClearMaxRamSize,
+ kWriteTimeRtcOffset,
+ kWriteTimeMinRtc,
+ kSetSotaConfig,
kUnset = -1,
};
@@ -62,6 +65,12 @@ class MiscWriter {
static constexpr uint32_t kMaxRamSizeOffsetInVendorSpace = 192;
static constexpr char kMaxRamSize[] = "max-ram-size=";
static constexpr uint32_t kSotaStateOffsetInVendorSpace = 224;
+ static constexpr uint32_t kRTimeRtcOffsetValOffsetInVendorSpace = 264;
+ static constexpr char kTimeRtcOffset[] = "timertcoffset=";
+ static constexpr uint32_t kRTimeMinRtcValOffsetInVendorSpace = 296;
+ static constexpr char kTimeMinRtc[] = "timeminrtc=";
+ static constexpr uint32_t kFaceauthEvalValOffsetInVendorSpace = 328;
+ static constexpr uint32_t kSotaScheduleShipmodeOffsetInVendorSpace = 360;
// Minimum and maximum valid value for max-ram-size
static constexpr int32_t kRamSizeDefault = -1;
diff --git a/misc_writer/misc_writer.cpp b/misc_writer/misc_writer.cpp
index 216b1883..710c2ffe 100644
--- a/misc_writer/misc_writer.cpp
+++ b/misc_writer/misc_writer.cpp
@@ -93,6 +93,18 @@ bool MiscWriter::PerformAction(std::optional<size_t> override_offset) {
? std::string(kMaxRamSize).append(stringdata_).append("\n")
: std::string(32, 0);
break;
+ case MiscWriterActions::kWriteTimeRtcOffset:
+ offset = override_offset.value_or(kRTimeRtcOffsetValOffsetInVendorSpace);
+ content = std::string(kTimeRtcOffset) + stringdata_;
+ content.resize(32);
+ break;
+ case MiscWriterActions::kWriteTimeMinRtc:
+ offset = override_offset.value_or(kRTimeMinRtcValOffsetInVendorSpace);
+ content = std::string(kTimeMinRtc) + stringdata_;
+ content.resize(32);
+ break;
+ case MiscWriterActions::kSetSotaConfig:
+ goto sota_config;
case MiscWriterActions::kUnset:
LOG(ERROR) << "The misc writer action must be set";
return false;
@@ -104,7 +116,8 @@ bool MiscWriter::PerformAction(std::optional<size_t> override_offset) {
return false;
}
- if (action_ == MiscWriterActions::kSetSotaFlag) {
+sota_config:
+ if (action_ == MiscWriterActions::kSetSotaFlag || action_ == MiscWriterActions::kSetSotaConfig) {
content = ::android::base::GetProperty("persist.vendor.nfc.factoryota.state", "");
if (content.size() != 0 && content.size() <= 40) {
offset = kSotaStateOffsetInVendorSpace;
@@ -114,6 +127,15 @@ bool MiscWriter::PerformAction(std::optional<size_t> override_offset) {
return false;
}
}
+ content = ::android::base::GetProperty("persist.vendor.nfc.factoryota.schedule_shipmode", "");
+ if (content.size() != 0 && content.size() <= 32) {
+ offset = kSotaScheduleShipmodeOffsetInVendorSpace;
+ if (std::string err;
+ !WriteMiscPartitionVendorSpace(content.data(), content.size(), offset, &err)) {
+ LOG(ERROR) << "Failed to write " << content << " at offset " << offset << " : " << err;
+ return false;
+ }
+ }
}
return true;
diff --git a/misc_writer/misc_writer_main.cpp b/misc_writer/misc_writer_main.cpp
index 0af45a91..f3546ef6 100644
--- a/misc_writer/misc_writer_main.cpp
+++ b/misc_writer/misc_writer_main.cpp
@@ -42,6 +42,7 @@ static int Usage(std::string_view name) {
std::cerr << " --clear-dark-theme Clear the dark theme flag\n";
std::cerr << " --set-sota Write the silent OTA flag\n";
std::cerr << " --clear-sota Clear the silent OTA flag\n";
+ std::cerr << " --set-sota-config Set the silent OTA configs\n";
std::cerr << " --set-enable-pkvm Write the enable pKVM flag\n";
std::cerr << " --set-disable-pkvm Write the disable pKVM flag\n";
std::cerr << " --set-wrist-orientation <0-3> Write the wrist orientation flag\n";
@@ -50,6 +51,8 @@ static int Usage(std::string_view name) {
std::cerr << " --set-timeoffset Write the time offset value (tz_time - utc_time)\n";
std::cerr << " --set-max-ram-size <2048-65536> Write the sw limit max ram size in MB\n";
std::cerr << " --set-max-ram-size <-1> Clear the sw limit max ram size\n";
+ std::cerr << " --set-timertcoffset Write the time offset value (utc_time - rtc_time)\n";
+ std::cerr << " --set-minrtc Write the minimum expected rtc value for tilb\n";
std::cerr << "Writes the given hex string to the specified offset in vendor space in /misc "
"partition.\nDefault offset is used for each action unless "
"--override-vendor-space-offset is specified.\n";
@@ -71,6 +74,9 @@ int main(int argc, char** argv) {
{ "set-timeformat", required_argument, nullptr, 0},
{ "set-timeoffset", required_argument, nullptr, 0},
{ "set-max-ram-size", required_argument, nullptr, 0},
+ { "set-timertcoffset", required_argument, nullptr, 0},
+ { "set-minrtc", required_argument, nullptr, 0},
+ { "set-sota-config", no_argument, nullptr, 0 },
{ nullptr, 0, nullptr, 0 },
};
@@ -82,6 +88,7 @@ int main(int argc, char** argv) {
{ "set-enable-pkvm", MiscWriterActions::kSetEnablePkvmFlag },
{ "set-disable-pkvm", MiscWriterActions::kSetDisablePkvmFlag },
{ "clear-wrist-orientation", MiscWriterActions::kClearWristOrientationFlag },
+ { "set-sota-config", MiscWriterActions::kSetSotaConfig },
};
std::unique_ptr<MiscWriter> misc_writer;
@@ -173,6 +180,30 @@ int main(int argc, char** argv) {
misc_writer = std::make_unique<MiscWriter>(MiscWriterActions::kSetMaxRamSize,
std::to_string(max_ram_size));
}
+ } else if (option_name == "set-timertcoffset"s) {
+ long long int timertcoffset = strtoll(optarg, NULL, 10);
+ if (0 == timertcoffset) {
+ LOG(ERROR) << "Failed to parse the timertcoffset:" << optarg;
+ return Usage(argv[0]);
+ }
+ if (misc_writer) {
+ LOG(ERROR) << "Misc writer action has already been set";
+ return Usage(argv[0]);
+ }
+ misc_writer = std::make_unique<MiscWriter>(MiscWriterActions::kWriteTimeRtcOffset,
+ std::to_string(timertcoffset));
+ } else if (option_name == "set-minrtc"s) {
+ long long int minrtc = strtoll(optarg, NULL, 10);
+ if (0 == minrtc) {
+ LOG(ERROR) << "Failed to parse the minrtc:" << optarg;
+ return Usage(argv[0]);
+ }
+ if (misc_writer) {
+ LOG(ERROR) << "Misc writer action has already been set";
+ return Usage(argv[0]);
+ }
+ misc_writer = std::make_unique<MiscWriter>(MiscWriterActions::kWriteTimeMinRtc,
+ std::to_string(minrtc));
} else if (auto iter = action_map.find(option_name); iter != action_map.end()) {
if (misc_writer) {
LOG(ERROR) << "Misc writer action has already been set";
diff --git a/mm/pixel-mm-gki.rc b/mm/pixel-mm-gki.rc
index 52603d5b..694396e4 100644
--- a/mm/pixel-mm-gki.rc
+++ b/mm/pixel-mm-gki.rc
@@ -51,6 +51,10 @@ on property:sys.boot_completed=1
write /sys/kernel/tracing/instances/pixel/events/dmabuf_heap/dma_buf_release/enable 1
write /sys/kernel/tracing/instances/pixel/events/trusty/trusty_dma_buf_put/enable 1
+ # Allow max_usage_kb to be reset by system processes
+ chown system system /sys/kernel/vendor_mm/gcma_heap/trusty:faceauth_rawimage_heap/max_usage_kb
+ chmod 0660 /sys/kernel/vendor_mm/gcma_heap/trusty:faceauth_rawimage_heap/max_usage_kb
+
# turns off tracing right before bugreporting to keep more traces
on property:init.svc.dumpstatez=running
write /sys/kernel/tracing/instances/pixel/tracing_on 0
diff --git a/pixelstats/Android.bp b/pixelstats/Android.bp
index a0e55a36..92fc08af 100644
--- a/pixelstats/Android.bp
+++ b/pixelstats/Android.bp
@@ -131,7 +131,7 @@ cc_library_static {
generated_headers: ["pixelstatsatoms.h"],
export_generated_headers: ["pixelstatsatoms.h"],
shared_libs: [
- "android.frameworks.stats-V1-ndk",
+ "android.frameworks.stats-V2-ndk",
]
}
@@ -167,7 +167,7 @@ cc_library {
"-Werror",
],
shared_libs: [
- "android.frameworks.stats-V1-ndk",
+ "android.frameworks.stats-V2-ndk",
"libbase",
"libbinder_ndk",
"libcutils",
@@ -179,7 +179,7 @@ cc_library {
"pixelatoms-cpp",
],
export_shared_lib_headers: [
- "android.frameworks.stats-V1-ndk",
+ "android.frameworks.stats-V2-ndk",
"pixelatoms-cpp",
],
static_libs: [
diff --git a/pixelstats/BatteryEEPROMReporter.cpp b/pixelstats/BatteryEEPROMReporter.cpp
index d5ed720a..b8060a75 100644
--- a/pixelstats/BatteryEEPROMReporter.cpp
+++ b/pixelstats/BatteryEEPROMReporter.cpp
@@ -38,6 +38,7 @@ using android::hardware::google::pixel::PixelAtoms::BatteryEEPROM;
#define LINESIZE 71
#define LINESIZE_V2 31
+#define LINESIZE_MAX17201_HIST 80
BatteryEEPROMReporter::BatteryEEPROMReporter() {}
@@ -58,7 +59,6 @@ void BatteryEEPROMReporter::checkAndReport(const std::shared_ptr<IStats> &stats_
ALOGE("Unable to read %s - %s", path.c_str(), strerror(errno));
return;
}
- ALOGD("checkAndReport: %s", file_contents.c_str());
int16_t i, num;
struct BatteryHistory hist;
@@ -269,6 +269,112 @@ void BatteryEEPROMReporter::reportEvent(const std::shared_ptr<IStats> &stats_cli
ALOGE("Unable to report BatteryEEPROM to Stats service");
}
+void BatteryEEPROMReporter::checkAndReportGMSR(const std::shared_ptr<IStats> &stats_client,
+ const std::string &path) {
+ struct BatteryHistory gmsr;
+ std::string file_contents;
+ int16_t num;
+
+ if (path.empty())
+ return;
+
+ if (!ReadFileToString(path, &file_contents)) {
+ ALOGE("Unable to read gmsr path: %s - %s", path.c_str(), strerror(errno));
+ return;
+ }
+
+ gmsr.checksum = 0xFFFF;
+ if (path.find("max77779") == std::string::npos) {
+ num = sscanf(file_contents.c_str(), "rcomp0\t:%4" SCNx16 "\ntempco\t:%4" SCNx16
+ "\nfullcaprep\t:%4" SCNx16 "\ncycles\t:%4" SCNx16 "\nfullcapnom\t:%4" SCNx16
+ "\nqresidual00\t:%4" SCNx16 "\nqresidual10\t:%4" SCNx16
+ "\nqresidual20\t:%4" SCNx16 "\nqresidual30\t:%4" SCNx16
+ "\ncv_mixcap\t:%4" SCNx16 "\nhalftime\t:%4" SCNx16,
+ &gmsr.rcomp0, &gmsr.tempco, &gmsr.full_rep, &gmsr.cycle_cnt, &gmsr.full_cap,
+ &gmsr.max_vbatt, &gmsr.min_vbatt, &gmsr.max_ibatt, &gmsr.min_ibatt,
+ &gmsr.esr, &gmsr.rslow);
+ if (num != kNum77759GMSRFields) {
+ ALOGE("Couldn't process 77759GMSR. num=%d\n", num);
+ return;
+ }
+ } else {
+ num = sscanf(file_contents.c_str(), "rcomp0\t:%4" SCNx16 "\ntempco\t:%4" SCNx16
+ "\nfullcaprep\t:%4" SCNx16 "\ncycles\t:%4" SCNx16 "\nfullcapnom\t:%4" SCNx16,
+ &gmsr.rcomp0, &gmsr.tempco, &gmsr.full_rep, &gmsr.cycle_cnt, &gmsr.full_cap);
+ if (num != kNum77779GMSRFields) {
+ ALOGE("Couldn't process 77779GMSR. num=%d\n", num);
+ return;
+ }
+ }
+
+ reportEvent(stats_client, gmsr);
+}
+
+void BatteryEEPROMReporter::checkAndReportMaxfgHistory(const std::shared_ptr<IStats> &stats_client,
+ const std::string &path) {
+ std::string file_contents;
+ int16_t i;
+
+ if (path.empty())
+ return;
+
+ if (!ReadFileToString(path, &file_contents)) {
+ ALOGD("Unable to read maxfg_hist path: %s - %s", path.c_str(), strerror(errno));
+ return;
+ }
+
+ std::string hist_each;
+ const int kHistTotalLen = file_contents.size();
+
+ ALOGD("checkAndReportMaxfgHistory:size=%d\n%s", kHistTotalLen, file_contents.c_str());
+
+ for (i = 0; i < kHistTotalLen; i++) {
+ struct BatteryHistory maxfg_hist;
+ uint16_t nQRTable00, nQRTable10, nQRTable20, nQRTable30, nCycles, nFullCapNom;
+ uint16_t nRComp0, nTempCo, nIAvgEmpty, nFullCapRep, nVoltTemp, nMaxMinCurr, nMaxMinVolt;
+ uint16_t nMaxMinTemp, nSOC, nTimerH;
+ int16_t num;
+ size_t hist_offset = i * LINESIZE_MAX17201_HIST;
+
+ if (hist_offset >= file_contents.size())
+ break;
+
+ hist_each = file_contents.substr(hist_offset, LINESIZE_MAX17201_HIST);
+ num = sscanf(hist_each.c_str(), "%4" SCNx16 "%4" SCNx16 "%4" SCNx16 "%4" SCNx16
+ "%4" SCNx16 "%4" SCNx16 "%4" SCNx16 "%4" SCNx16 "%4" SCNx16 "%4" SCNx16
+ "%4" SCNx16 "%4" SCNx16 "%4" SCNx16 "%4" SCNx16 "%4" SCNx16 "%4" SCNx16,
+ &nQRTable00, &nQRTable10, &nQRTable20, &nQRTable30, &nCycles, &nFullCapNom,
+ &nRComp0, &nTempCo, &nIAvgEmpty, &nFullCapRep, &nVoltTemp, &nMaxMinCurr,
+ &nMaxMinVolt, &nMaxMinTemp, &nSOC, &nTimerH);
+
+ if (num != kNum17201HISTFields) {
+ ALOGE("Couldn't process %s (num=%d)", hist_each.c_str(), num);
+ continue;
+ }
+
+ /* not assign: nQRTable00, nQRTable10, nQRTable20, nQRTable30 */
+ maxfg_hist.reserve = 0xFF;
+ maxfg_hist.tempco = nTempCo;
+ maxfg_hist.rcomp0 = nRComp0;
+ maxfg_hist.full_rep = nFullCapNom;
+ maxfg_hist.full_cap = nFullCapRep;
+ maxfg_hist.cycle_cnt = nCycles * 16 / 100; // LSB: 16%;
+ maxfg_hist.timer_h = (nTimerH * 32 / 10) / 24; // LSB: 3.2 hours
+ maxfg_hist.batt_soc = (nSOC >> 8) & 0x00FF;
+ maxfg_hist.msoc = nSOC & 0x00FF;
+ maxfg_hist.max_ibatt = ((nMaxMinCurr >> 8) & 0x00FF) * 80;
+ maxfg_hist.min_ibatt = (nMaxMinCurr & 0x00FF) * 80 * (-1);
+ maxfg_hist.max_vbatt = ((nMaxMinVolt >> 8) & 0x00FF) * 20;
+ maxfg_hist.min_vbatt = (nMaxMinVolt & 0x00FF) * 20;
+ maxfg_hist.max_temp = (nMaxMinTemp >> 8) & 0x00FF;
+ maxfg_hist.min_temp = nMaxMinTemp & 0x00FF;
+ maxfg_hist.esr = nIAvgEmpty;
+ maxfg_hist.rslow = nVoltTemp;
+
+ reportEvent(stats_client, maxfg_hist);
+ }
+}
+
} // namespace pixel
} // namespace google
} // namespace hardware
diff --git a/pixelstats/BrownoutDetectedReporter.cpp b/pixelstats/BrownoutDetectedReporter.cpp
index e22d2df7..a5f4e39a 100644
--- a/pixelstats/BrownoutDetectedReporter.cpp
+++ b/pixelstats/BrownoutDetectedReporter.cpp
@@ -218,6 +218,92 @@ long BrownoutDetectedReporter::parseTimestamp(std::string timestamp) {
return 0;
}
+int BrownoutDetectedReporter::brownoutReasonCheck(const std::string &brownoutReasonProp) {
+ std::string reason = android::base::GetProperty(brownoutReasonProp.c_str(), "");
+ if (reason.empty()) {
+ // Brownout not found
+ return -1;
+ }
+ auto key = kBrownoutReason.find(reason);
+ if (key == kBrownoutReason.end()) {
+ return -1;
+ }
+ return key->second;
+}
+
+int parseIRQ(const std::string &element) {
+ int idx = atoi(element.c_str());
+ if (idx == SMPL_WARN) {
+ return BrownoutDetected::SMPL_WARN;
+ } else if (idx == UVLO1) {
+ return BrownoutDetected::UVLO1;
+ } else if (idx == UVLO2) {
+ return BrownoutDetected::UVLO2;
+ } else if (idx == BATOILO) {
+ return BrownoutDetected::BATOILO;
+ } else if (idx == BATOILO2) {
+ return BrownoutDetected::BATOILO2;
+ }
+ return -1;
+}
+
+void BrownoutDetectedReporter::logBrownoutCsv(const std::shared_ptr<IStats> &stats_client,
+ const std::string &CsvFilePath,
+ const std::string &brownoutReasonProp) {
+ std::string csvFile;
+ if (!android::base::ReadFileToString(CsvFilePath, &csvFile)) {
+ return;
+ }
+ std::istringstream content(csvFile);
+ std::string line;
+ struct BrownoutDetectedInfo max_value = {};
+ max_value.voltage_now_ = DEFAULT_BATTERY_VOLT;
+ max_value.battery_soc_ = DEFAULT_BATTERY_SOC;
+ max_value.battery_temp_ = DEFAULT_BATTERY_TEMP;
+ std::smatch pattern_match;
+ max_value.brownout_reason_ = brownoutReasonCheck(brownoutReasonProp);
+ if (max_value.brownout_reason_ < 0) {
+ return;
+ }
+ bool isAlreadyUpdated = false;
+ std::vector<std::vector<std::string>> rows;
+ int row_num = 0;
+ while (std::getline(content, line)) {
+ if (std::regex_match(line, pattern_match, kAlreadyUpdatedPattern)) {
+ isAlreadyUpdated = true;
+ break;
+ }
+ row_num++;
+ if (row_num == 1) {
+ continue;
+ }
+ std::vector<std::string> row;
+ std::stringstream ss(line);
+ std::string field;
+ while (getline(ss, field, ',')) {
+ row.push_back(field);
+ }
+
+ max_value.triggered_timestamp_ = parseTimestamp(row[TIMESTAMP_IDX].c_str());
+ max_value.triggered_irq_ = parseIRQ(row[IRQ_IDX]);
+ max_value.battery_soc_ = atoi(row[SOC_IDX].c_str());
+ max_value.battery_temp_ = atoi(row[TEMP_IDX].c_str());
+ max_value.battery_cycle_ = atoi(row[CYCLE_IDX].c_str());
+ max_value.voltage_now_ = atoi(row[VOLTAGE_IDX].c_str());
+ for (int i = 0; i < DVFS_MAX_IDX; i++) {
+ max_value.dvfs_value_[i] = atoi(row[i + DVFS_CHANNEL_0].c_str());
+ }
+ for (int i = 0; i < ODPM_MAX_IDX; i++) {
+ max_value.odpm_value_[i] = atoi(row[i + ODPM_CHANNEL_0].c_str());
+ }
+ }
+ if (!isAlreadyUpdated && max_value.battery_temp_ != DEFAULT_BATTERY_TEMP) {
+ std::string file_content = "LASTMEAL_UPDATED\n" + csvFile;
+ android::base::WriteStringToFile(file_content, CsvFilePath);
+ uploadData(stats_client, max_value);
+ }
+}
+
void BrownoutDetectedReporter::logBrownout(const std::shared_ptr<IStats> &stats_client,
const std::string &logFilePath,
const std::string &brownoutReasonProp) {
@@ -233,17 +319,10 @@ void BrownoutDetectedReporter::logBrownout(const std::shared_ptr<IStats> &stats_
max_value.battery_temp_ = DEFAULT_BATTERY_TEMP;
std::smatch pattern_match;
int odpm_index = 0, dvfs_index = 0;
- std::string reason = android::base::GetProperty(brownoutReasonProp.c_str(), "");
- if (reason.empty()) {
- // Brownout not found
- return;
- }
-
- auto key = kBrownoutReason.find(reason);
- if (key == kBrownoutReason.end()) {
+ max_value.brownout_reason_ = brownoutReasonCheck(brownoutReasonProp);
+ if (max_value.brownout_reason_ < 0) {
return;
}
- max_value.brownout_reason_ = key->second;
bool isAlreadyUpdated = false;
while (std::getline(content, line)) {
if (std::regex_match(line, pattern_match, kAlreadyUpdatedPattern)) {
@@ -277,7 +356,7 @@ void BrownoutDetectedReporter::logBrownout(const std::shared_ptr<IStats> &stats_
max_value.triggered_timestamp_ = parseTimestamp(line.c_str());
continue;
}
- if (updateIfFound(line, kBatterySocPattern, &max_value.battery_soc_, kUpdateMax)) {
+ if (updateIfFound(line, kBatterySocPattern, &max_value.battery_soc_, kUpdateMin)) {
continue;
}
if (updateIfFound(line, kBatteryTempPattern, &max_value.battery_temp_, kUpdateMin)) {
diff --git a/pixelstats/ChargeStatsReporter.cpp b/pixelstats/ChargeStatsReporter.cpp
index 2354acd4..39d91203 100644
--- a/pixelstats/ChargeStatsReporter.cpp
+++ b/pixelstats/ChargeStatsReporter.cpp
@@ -234,12 +234,9 @@ bool ChargeStatsReporter::shouldReportEvent(void) {
void ChargeStatsReporter::checkAndReport(const std::shared_ptr<IStats> &stats_client,
const std::string &path) {
std::string file_contents, line, wfile_contents, wline_at, wline_ac, pca_file_contents,
- pca_line, thermal_file_contents, gcharger_file_contents;
+ pca_line, thermal_file_contents, gcharger_file_contents, gdbatt_file_contents;
std::istringstream ss;
- bool has_wireless = wireless_charge_stats_.CheckWirelessContentsAndAck(&wfile_contents);
- bool has_pca = pca_charge_stats_.CheckPcaContentsAndAck(&pca_file_contents);
- bool has_thermal = checkContentsAndAck(&thermal_file_contents, kThermalChargeMetricsPath);
- bool has_gcharger = checkContentsAndAck(&gcharger_file_contents, kGChargerMetricsPath);
+ bool has_wireless, has_pca, has_thermal, has_gcharger, has_dual_batt;
if (!ReadFileToString(path.c_str(), &file_contents)) {
ALOGE("Unable to read %s - %s", path.c_str(), strerror(errno));
@@ -262,6 +259,7 @@ void ChargeStatsReporter::checkAndReport(const std::shared_ptr<IStats> &stats_cl
return;
}
+ has_pca = pca_charge_stats_.CheckPcaContentsAndAck(&pca_file_contents);
if (has_pca) {
std::istringstream pca_ss;
@@ -269,6 +267,7 @@ void ChargeStatsReporter::checkAndReport(const std::shared_ptr<IStats> &stats_cl
std::getline(pca_ss, pca_line);
}
+ has_wireless = wireless_charge_stats_.CheckWirelessContentsAndAck(&wfile_contents);
if (has_wireless) {
std::istringstream wss;
@@ -287,6 +286,7 @@ void ChargeStatsReporter::checkAndReport(const std::shared_ptr<IStats> &stats_cl
ReportVoltageTierStats(stats_client, line.c_str(), has_wireless, wfile_contents);
}
+ has_thermal = checkContentsAndAck(&thermal_file_contents, kThermalChargeMetricsPath);
if (has_thermal) {
std::istringstream wss;
wss.str(thermal_file_contents);
@@ -295,6 +295,7 @@ void ChargeStatsReporter::checkAndReport(const std::shared_ptr<IStats> &stats_cl
}
}
+ has_gcharger = checkContentsAndAck(&gcharger_file_contents, kGChargerMetricsPath);
if (has_gcharger) {
std::istringstream wss;
wss.str(gcharger_file_contents);
@@ -302,6 +303,15 @@ void ChargeStatsReporter::checkAndReport(const std::shared_ptr<IStats> &stats_cl
ReportVoltageTierStats(stats_client, line.c_str());
}
}
+
+ has_dual_batt = checkContentsAndAck(&gdbatt_file_contents, kGDualBattMetricsPath);
+ if (has_dual_batt) {
+ std::istringstream wss;
+ wss.str(gdbatt_file_contents);
+ while (std::getline(wss, line)) {
+ ReportVoltageTierStats(stats_client, line.c_str());
+ }
+ }
}
bool ChargeStatsReporter::checkContentsAndAck(std::string *file_contents, const std::string &path) {
diff --git a/pixelstats/StatsHelper.cpp b/pixelstats/StatsHelper.cpp
index 1d8d16b4..62af17cb 100644
--- a/pixelstats/StatsHelper.cpp
+++ b/pixelstats/StatsHelper.cpp
@@ -201,6 +201,31 @@ void reportUsbPortOverheat(const std::shared_ptr<IStats> &stats_client,
ALOGE("Unable to report VendorUsbPortOverheat to Stats service");
}
+void reportUsbDataSessionEvent(const std::shared_ptr<IStats> &stats_client,
+ const PixelAtoms::VendorUsbDataSessionEvent &usb_data_event) {
+ // Load values array
+ std::vector<VendorAtomValue> values(4);
+ VendorAtomValue tmp;
+ tmp.set<VendorAtomValue::intValue>(usb_data_event.usb_role());
+ values[0] = tmp;
+ tmp.set<VendorAtomValue::repeatedIntValue>(std::vector<int32_t>(
+ usb_data_event.usb_states().begin(), usb_data_event.usb_states().end()));
+ values[1] = tmp;
+ tmp.set<VendorAtomValue::repeatedLongValue>(std::vector<int64_t>(
+ usb_data_event.elapsed_time_ms().begin(), usb_data_event.elapsed_time_ms().end()));
+ values[2] = tmp;
+ tmp.set<VendorAtomValue::longValue>(usb_data_event.duration_ms());
+ values[3] = tmp;
+
+ // Send vendor atom to IStats HAL
+ VendorAtom event = {.reverseDomainName = "",
+ .atomId = PixelAtoms::Atom::kVendorUsbDataSessionEvent,
+ .values = std::move(values)};
+ const ndk::ScopedAStatus ret = stats_client->reportVendorAtom(event);
+ if (!ret.isOk())
+ ALOGE("Unable to report VendorUsbDataSessionEvent to Stats service");
+}
+
} // namespace pixel
} // namespace google
} // namespace hardware
diff --git a/pixelstats/SysfsCollector.cpp b/pixelstats/SysfsCollector.cpp
index 4460ca1c..bce967ca 100644
--- a/pixelstats/SysfsCollector.cpp
+++ b/pixelstats/SysfsCollector.cpp
@@ -62,7 +62,9 @@ using android::hardware::google::pixel::PixelAtoms::StorageUfsHealth;
using android::hardware::google::pixel::PixelAtoms::StorageUfsResetCount;
using android::hardware::google::pixel::PixelAtoms::ThermalDfsStats;
using android::hardware::google::pixel::PixelAtoms::VendorAudioAdaptedInfoStatsReported;
+using android::hardware::google::pixel::PixelAtoms::VendorAudioBtMediaStatsReported;
using android::hardware::google::pixel::PixelAtoms::VendorAudioHardwareStatsReported;
+using android::hardware::google::pixel::PixelAtoms::VendorAudioOffloadedEffectStatsReported;
using android::hardware::google::pixel::PixelAtoms::VendorAudioPcmStatsReported;
using android::hardware::google::pixel::PixelAtoms::VendorAudioPdmStatsReported;
using android::hardware::google::pixel::PixelAtoms::VendorAudioThirdPartyEffectStatsReported;
@@ -97,6 +99,7 @@ SysfsCollector::SysfsCollector(const struct SysfsPaths &sysfs_paths)
kZramMmStatPath("/sys/block/zram0/mm_stat"),
kZramBdStatPath("/sys/block/zram0/bd_stat"),
kEEPROMPath(sysfs_paths.EEPROMPath),
+ kBrownoutCsvPath(sysfs_paths.BrownoutCsvPath),
kBrownoutLogPath(sysfs_paths.BrownoutLogPath),
kBrownoutReasonProp(sysfs_paths.BrownoutReasonProp),
kPowerMitigationStatsPath(sysfs_paths.MitigationPath),
@@ -123,7 +126,12 @@ SysfsCollector::SysfsCollector(const struct SysfsPaths &sysfs_paths)
kAdaptedInfoDurationPath(sysfs_paths.AdaptedInfoDurationPath),
kPcmLatencyPath(sysfs_paths.PcmLatencyPath),
kPcmCountPath(sysfs_paths.PcmCountPath),
- kTotalCallCountPath(sysfs_paths.TotalCallCountPath) {}
+ kTotalCallCountPath(sysfs_paths.TotalCallCountPath),
+ kOffloadEffectsIdPath(sysfs_paths.OffloadEffectsIdPath),
+ kOffloadEffectsDurationPath(sysfs_paths.OffloadEffectsDurationPath),
+ kBluetoothAudioUsagePath(sysfs_paths.BluetoothAudioUsagePath),
+ kGMSRPath(sysfs_paths.GMSRPath),
+ kMaxfgHistoryPath("/dev/maxfg_history") {}
bool SysfsCollector::ReadFileToInt(const std::string &path, int *val) {
return ReadFileToInt(path.c_str(), val);
@@ -192,10 +200,17 @@ void SysfsCollector::logBatteryChargeCycles(const std::shared_ptr<IStats> &stats
void SysfsCollector::logBatteryEEPROM(const std::shared_ptr<IStats> &stats_client) {
if (kEEPROMPath == nullptr || strlen(kEEPROMPath) == 0) {
ALOGV("Battery EEPROM path not specified");
- return;
+ } else {
+ battery_EEPROM_reporter_.checkAndReport(stats_client, kEEPROMPath);
+ }
+
+ if (kGMSRPath == nullptr || strlen(kGMSRPath) == 0) {
+ ALOGV("Battery GMSR path not specified");
+ } else {
+ battery_EEPROM_reporter_.checkAndReportGMSR(stats_client, kGMSRPath);
}
- battery_EEPROM_reporter_.checkAndReport(stats_client, kEEPROMPath);
+ battery_EEPROM_reporter_.checkAndReportMaxfgHistory(stats_client, kMaxfgHistoryPath);
}
/**
@@ -958,22 +973,23 @@ void SysfsCollector::reportZramMmStat(const std::shared_ptr<IStats> &stats_clien
// The size should be the same as the number of fields in ZramMmStat
std::vector<VendorAtomValue> values(6);
VendorAtomValue tmp;
- tmp.set<VendorAtomValue::intValue>(orig_data_size);
+ tmp.set<VendorAtomValue::longValue>(orig_data_size);
values[ZramMmStat::kOrigDataSizeFieldNumber - kVendorAtomOffset] = tmp;
- tmp.set<VendorAtomValue::intValue>(compr_data_size);
+ tmp.set<VendorAtomValue::longValue>(compr_data_size);
values[ZramMmStat::kComprDataSizeFieldNumber - kVendorAtomOffset] = tmp;
- tmp.set<VendorAtomValue::intValue>(mem_used_total);
+ tmp.set<VendorAtomValue::longValue>(mem_used_total);
values[ZramMmStat::kMemUsedTotalFieldNumber - kVendorAtomOffset] = tmp;
- tmp.set<VendorAtomValue::intValue>(same_pages);
+ tmp.set<VendorAtomValue::longValue>(same_pages);
values[ZramMmStat::kSamePagesFieldNumber - kVendorAtomOffset] = tmp;
- tmp.set<VendorAtomValue::intValue>(huge_pages);
+ tmp.set<VendorAtomValue::longValue>(huge_pages);
values[ZramMmStat::kHugePagesFieldNumber - kVendorAtomOffset] = tmp;
// Skip the first data to avoid a big spike in this accumulated value.
if (prev_huge_pages_since_boot_ == -1)
- tmp.set<VendorAtomValue::intValue>(0);
+ tmp.set<VendorAtomValue::longValue>(0);
else
- tmp.set<VendorAtomValue::intValue>(huge_pages_since_boot - prev_huge_pages_since_boot_);
+ tmp.set<VendorAtomValue::longValue>(huge_pages_since_boot -
+ prev_huge_pages_since_boot_);
values[ZramMmStat::kHugePagesSinceBootFieldNumber - kVendorAtomOffset] = tmp;
prev_huge_pages_since_boot_ = huge_pages_since_boot;
@@ -1012,11 +1028,11 @@ void SysfsCollector::reportZramBdStat(const std::shared_ptr<IStats> &stats_clien
// Load values array
std::vector<VendorAtomValue> values(3);
VendorAtomValue tmp;
- tmp.set<VendorAtomValue::intValue>(bd_count);
+ tmp.set<VendorAtomValue::longValue>(bd_count);
values[ZramBdStat::kBdCountFieldNumber - kVendorAtomOffset] = tmp;
- tmp.set<VendorAtomValue::intValue>(bd_reads);
+ tmp.set<VendorAtomValue::longValue>(bd_reads);
values[ZramBdStat::kBdReadsFieldNumber - kVendorAtomOffset] = tmp;
- tmp.set<VendorAtomValue::intValue>(bd_writes);
+ tmp.set<VendorAtomValue::longValue>(bd_writes);
values[ZramBdStat::kBdWritesFieldNumber - kVendorAtomOffset] = tmp;
// Send vendor atom to IStats HAL
@@ -1523,6 +1539,147 @@ void SysfsCollector::logPcmUsageStats(const std::shared_ptr<IStats> &stats_clien
}
/**
+ * Report audio Offload Effects usage stats duration per day.
+ */
+void SysfsCollector::logOffloadEffectsStats(const std::shared_ptr<IStats> &stats_client) {
+ std::string file_contents;
+ std::vector<int> uuids;
+ std::vector<int> durations;
+
+ if (kOffloadEffectsIdPath == nullptr) {
+ ALOGD("Offload Effects ID Path is not specified");
+ return;
+ }
+
+ if (kOffloadEffectsDurationPath == nullptr) {
+ ALOGD("Offload Effects Duration Path is not specified");
+ return;
+ }
+
+ if (!ReadFileToString(kOffloadEffectsIdPath, &file_contents)) {
+ ALOGD("Unable to read Offload Effect ID path %s", kOffloadEffectsIdPath);
+ } else {
+ std::stringstream file_content_stream(file_contents);
+ int uuid;
+ while (file_content_stream.good() && file_content_stream >> uuid) {
+ uuids.push_back(uuid);
+ }
+ }
+
+ if (!ReadFileToString(kOffloadEffectsDurationPath, &file_contents)) {
+ ALOGD("Unable to read Offload Effect duration path %s", kOffloadEffectsDurationPath);
+ } else {
+ std::stringstream file_content_stream(file_contents);
+ int duration;
+ while (file_content_stream.good() && file_content_stream >> duration) {
+ durations.push_back(duration);
+ }
+ }
+
+ if (durations.size() * 4 != uuids.size()) {
+ ALOGD("ID and duration data does not match: %zu and %zu", durations.size(), uuids.size());
+ return;
+ }
+
+ for (int index = 0; index < durations.size(); index++) {
+ std::vector<VendorAtomValue> values(3);
+ VendorAtomValue tmp;
+ int64_t uuid_msb = ((int64_t)uuids[index * 4] << 32 | uuids[index * 4 + 1]);
+ int64_t uuid_lsb = ((int64_t)uuids[index * 4 + 2] << 32 | uuids[index * 4 + 3]);
+
+ if (uuid_msb == 0 && uuid_lsb == 0) {
+ continue;
+ }
+
+ tmp.set<VendorAtomValue::VendorAtomValue::longValue>(uuid_msb);
+ values[VendorAudioOffloadedEffectStatsReported::kEffectUuidMsbFieldNumber -
+ kVendorAtomOffset] = tmp;
+
+ tmp.set<VendorAtomValue::VendorAtomValue::longValue>(uuid_lsb);
+ values[VendorAudioOffloadedEffectStatsReported::kEffectUuidLsbFieldNumber -
+ kVendorAtomOffset] = tmp;
+
+ tmp.set<VendorAtomValue::intValue>(durations[index]);
+ values[VendorAudioOffloadedEffectStatsReported::kEffectActiveSecondsPerDayFieldNumber -
+ kVendorAtomOffset] = tmp;
+
+ // Send vendor atom to IStats HAL
+ VendorAtom event = {.reverseDomainName = "",
+ .atomId = PixelAtoms::Atom::kVendorAudioOffloadedEffectStatsReported,
+ .values = std::move(values)};
+
+ const ndk::ScopedAStatus ret = stats_client->reportVendorAtom(event);
+ if (!ret.isOk()) {
+ ALOGE("Unable to report VendorAudioOffloadedEffectStatsReported at index %d", index);
+ } else {
+ ALOGD("Reported VendorAudioOffloadedEffectStatsReported successfully at index %d",
+ index);
+ }
+ }
+}
+
+/**
+ * Report bluetooth audio usage stats.
+ * This function will report at most 5 atoms showing different instance stats.
+ */
+void SysfsCollector::logBluetoothAudioUsage(const std::shared_ptr<IStats> &stats_client) {
+ std::string file_contents;
+ std::vector<int> duration_per_codec;
+
+ constexpr int num_codec = 5;
+
+ if (kBluetoothAudioUsagePath == nullptr) {
+ ALOGD("Bluetooth Audio stats path not specified");
+ return;
+ }
+
+ if (!ReadFileToString(kBluetoothAudioUsagePath, &file_contents)) {
+ ALOGD("Unable to read Bluetooth Audio stats path %s", kBluetoothAudioUsagePath);
+ } else {
+ std::stringstream file_content_stream(file_contents);
+ int duration;
+ while (file_content_stream.good() && file_content_stream >> duration) {
+ duration_per_codec.push_back(duration);
+ }
+ }
+
+ if (duration_per_codec.size() != num_codec) {
+ ALOGD("Bluetooth Audio num codec != number of codec. %zu / %d", duration_per_codec.size(),
+ num_codec);
+ return;
+ }
+
+ for (int index = 0; index < num_codec; index++) {
+ std::vector<VendorAtomValue> values(2);
+ VendorAtomValue tmp;
+
+ if (duration_per_codec[index] == 0) {
+ ALOGD("Skipped VendorAudioBtMediaStatsReported at codec:%d", index);
+ continue;
+ }
+
+ tmp.set<VendorAtomValue::intValue>(index);
+ values[VendorAudioBtMediaStatsReported::kBtCodecTypeFieldNumber - kVendorAtomOffset] = tmp;
+
+ tmp.set<VendorAtomValue::intValue>(duration_per_codec[index]);
+ values[VendorAudioBtMediaStatsReported::kActiveSecondsPerDayFieldNumber -
+ kVendorAtomOffset] = tmp;
+
+ // Send vendor atom to IStats HAL
+ VendorAtom event = {.reverseDomainName = "",
+ .atomId = PixelAtoms::Atom::kVendorAudioBtMediaStatsReported,
+ .values = std::move(values)};
+
+ const ndk::ScopedAStatus ret = stats_client->reportVendorAtom(event);
+ if (!ret.isOk())
+ ALOGE("Unable to report VendorAudioBtMediaStatsReported at index %d", index);
+ else
+ ALOGD("Reporting VendorAudioBtMediaStatsReported: codec:%d, duration:%d", index,
+ duration_per_codec[index]);
+ }
+}
+
+/**
* Logs the Resume Latency stats.
*/
void SysfsCollector::logVendorResumeLatencyStats(const std::shared_ptr<IStats> &stats_client) {
@@ -1939,6 +2096,8 @@ void SysfsCollector::logPerDay() {
logWavesStats(stats_client);
logAdaptedInfoStats(stats_client);
logPcmUsageStats(stats_client);
+ logOffloadEffectsStats(stats_client);
+ logBluetoothAudioUsage(stats_client);
}
void SysfsCollector::aggregatePer5Min() {
@@ -1951,7 +2110,10 @@ void SysfsCollector::logBrownout() {
ALOGE("Unable to get AIDL Stats service");
return;
}
- if (kBrownoutLogPath != nullptr && strlen(kBrownoutLogPath) > 0)
+ if (kBrownoutCsvPath != nullptr && strlen(kBrownoutCsvPath) > 0)
+ brownout_detected_reporter_.logBrownoutCsv(stats_client, kBrownoutCsvPath,
+ kBrownoutReasonProp);
+ else if (kBrownoutLogPath != nullptr && strlen(kBrownoutLogPath) > 0)
brownout_detected_reporter_.logBrownout(stats_client, kBrownoutLogPath,
kBrownoutReasonProp);
}
diff --git a/pixelstats/test/TEST_MAPPING b/pixelstats/TEST_MAPPING
index b348cb9c..b348cb9c 100644
--- a/pixelstats/test/TEST_MAPPING
+++ b/pixelstats/TEST_MAPPING
diff --git a/pixelstats/UeventListener.cpp b/pixelstats/UeventListener.cpp
index 8af43a01..6ab91f6f 100644
--- a/pixelstats/UeventListener.cpp
+++ b/pixelstats/UeventListener.cpp
@@ -45,6 +45,7 @@
#include <cutils/uevent.h>
#include <fcntl.h>
#include <hardware/google/pixel/pixelstats/pixelatoms.pb.h>
+#include <linux/thermal.h>
#include <log/log.h>
#include <pixelstats/StatsHelper.h>
#include <pixelstats/UeventListener.h>
@@ -68,6 +69,7 @@ using android::base::ReadFileToString;
using android::base::WriteStringToFile;
using android::hardware::google::pixel::PixelAtoms::GpuEvent;
using android::hardware::google::pixel::PixelAtoms::PdVidPid;
+using android::hardware::google::pixel::PixelAtoms::ThermalSensorAbnormalityDetected;
using android::hardware::google::pixel::PixelAtoms::VendorHardwareFailed;
using android::hardware::google::pixel::PixelAtoms::VendorUsbPortOverheat;
@@ -80,6 +82,8 @@ constexpr int32_t VID_GOOGLE = 0x18d1;
constexpr int32_t PID_OFFSET = 2;
constexpr int32_t PID_LENGTH = 4;
constexpr uint32_t PID_P30 = 0x4f05;
+constexpr const char *THERMAL_ABNORMAL_INFO_EQ = "THERMAL_ABNORMAL_INFO=";
+constexpr const char *THERMAL_ABNORMAL_TYPE_EQ = "THERMAL_ABNORMAL_TYPE=";
bool UeventListener::ReadFileToInt(const std::string &path, int *val) {
return ReadFileToInt(path.c_str(), val);
@@ -300,6 +304,87 @@ void UeventListener::ReportGpuEvent(const std::shared_ptr<IStats> &stats_client,
ALOGE("Unable to report GPU event.");
}
+/**
+ * Report thermal abnormal event.
+ * The data is sent as uevent environment parameters:
+ * 1. THERMAL_ABNORMAL_TYPE={type}
+ * 2. THERMAL_ABNORMAL_INFO=Name:{name},Val:{val}
+ * This atom logs data in 3 potential events:
+ * 1. thermistor or tj temperature reading stuck
+ * 2. thermistor or tj showing very high temperature reading
+ * 3. thermistor or tj showing very low temperature reading
+ */
+void UeventListener::ReportThermalAbnormalEvent(const std::shared_ptr<IStats> &stats_client,
+ const char *devpath,
+ const char *thermal_abnormal_event_type,
+ const char *thermal_abnormal_event_info) {
+ if (!stats_client || !devpath ||
+ strncmp(devpath, "DEVPATH=/module/pixel_metrics",
+ strlen("DEVPATH=/module/pixel_metrics")) ||
+ !thermal_abnormal_event_type || !thermal_abnormal_event_info)
+ return;
+ ALOGD("Thermal Abnormal Type: %s, Thermal Abnormal Info: %s", thermal_abnormal_event_type,
+ thermal_abnormal_event_info);
+ std::vector<std::string> type_msg = android::base::Split(thermal_abnormal_event_type, "=");
+ std::vector<std::string> info_msg = android::base::Split(thermal_abnormal_event_info, "=");
+ if (type_msg.size() != 2 || info_msg.size() != 2) {
+ ALOGE("Invalid msg size for thermal abnormal with type(%zu) and info(%zu)", type_msg.size(),
+ info_msg.size());
+ return;
+ }
+
+ if (type_msg[0] != "THERMAL_ABNORMAL_TYPE" || info_msg[0] != "THERMAL_ABNORMAL_INFO") {
+ ALOGE("Invalid msg prefix for thermal abnormal with type(%s) and info(%s)",
+ type_msg[0].c_str(), info_msg[0].c_str());
+ return;
+ }
+
+ auto abnormality_type = kThermalAbnormalityTypeStrToEnum.find(type_msg[1]);
+ if (abnormality_type == kThermalAbnormalityTypeStrToEnum.end()) {
+ ALOGE("Unknown thermal abnormal event type %s", type_msg[1].c_str());
+ return;
+ }
+
+ std::vector<std::string> info_list = android::base::Split(info_msg[1], ",");
+ if (info_list.size() != 2) {
+ ALOGE("Thermal abnormal info(%s) split size %zu != 2", info_msg[1].c_str(),
+ info_list.size());
+ return;
+ }
+
+ const auto &name_msg = info_list[0], val_msg = info_list[1];
+ if (!android::base::StartsWith(name_msg, "name:") ||
+ !android::base::StartsWith(val_msg, "val:")) {
+ ALOGE("Invalid prefix for thermal abnormal info name(%s), val(%s)", name_msg.c_str(),
+ val_msg.c_str());
+ return;
+ }
+
+ auto name_start_pos = std::strlen("name:");
+ auto name = name_msg.substr(name_start_pos);
+ if (name.length() > THERMAL_NAME_LENGTH) {
+ ALOGE("Invalid sensor name %s with length %zu > %d", name.c_str(), name.length(),
+ THERMAL_NAME_LENGTH);
+ return;
+ }
+
+ auto val_start_pos = std::strlen("val:");
+ auto val_str = val_msg.substr(val_start_pos);
+ int val;
+ if (sscanf(val_str.c_str(), "%d", &val) != 1) {
+ ALOGE("Invalid value for thermal abnormal info: %s", val_str.c_str());
+ return;
+ }
+ ALOGI("Reporting Thermal Abnormal event of type: %s(%d) for %s with val: %d",
+ abnormality_type->first.c_str(), abnormality_type->second, name.c_str(), val);
+ VendorAtom event = {.reverseDomainName = "",
+ .atomId = PixelAtoms::Atom::kThermalSensorAbnormalityDetected,
+ .values = {abnormality_type->second, name, val}};
+ const ndk::ScopedAStatus ret = stats_client->reportVendorAtom(event);
+ if (!ret.isOk())
+ ALOGE("Unable to report Thermal Abnormal event.");
+}
+
bool UeventListener::ProcessUevent() {
char msg[UEVENT_MSG_LEN + 2];
char *cp;
@@ -308,6 +393,7 @@ bool UeventListener::ProcessUevent() {
const char *devpath;
bool collect_partner_id = false;
const char *gpu_event_type = nullptr, *gpu_event_info = nullptr;
+ const char *thermal_abnormal_event_type = nullptr, *thermal_abnormal_event_info = nullptr;
int n;
if (uevent_fd_ < 0) {
@@ -367,6 +453,10 @@ bool UeventListener::ProcessUevent() {
gpu_event_type = cp;
} else if (!strncmp(cp, "GPU_UEVENT_INFO=", strlen("GPU_UEVENT_INFO="))) {
gpu_event_info = cp;
+ } else if (!strncmp(cp, THERMAL_ABNORMAL_TYPE_EQ, strlen(THERMAL_ABNORMAL_TYPE_EQ))) {
+ thermal_abnormal_event_type = cp;
+ } else if (!strncmp(cp, THERMAL_ABNORMAL_INFO_EQ, strlen(THERMAL_ABNORMAL_INFO_EQ))) {
+ thermal_abnormal_event_info = cp;
}
/* advance to after the next \0 */
while (*cp++) {
@@ -387,6 +477,8 @@ bool UeventListener::ProcessUevent() {
ReportTypeCPartnerId(stats_client);
}
ReportGpuEvent(stats_client, driver, gpu_event_type, gpu_event_info);
+ ReportThermalAbnormalEvent(stats_client, devpath, thermal_abnormal_event_type,
+ thermal_abnormal_event_info);
}
if (log_fd_ > 0) {
diff --git a/pixelstats/include/pixelstats/BatteryEEPROMReporter.h b/pixelstats/include/pixelstats/BatteryEEPROMReporter.h
index 13a203b7..41577127 100644
--- a/pixelstats/include/pixelstats/BatteryEEPROMReporter.h
+++ b/pixelstats/include/pixelstats/BatteryEEPROMReporter.h
@@ -45,6 +45,9 @@ class BatteryEEPROMReporter {
public:
BatteryEEPROMReporter();
void checkAndReport(const std::shared_ptr<IStats> &stats_client, const std::string &path);
+ void checkAndReportGMSR(const std::shared_ptr<IStats> &stats_client, const std::string &path);
+ void checkAndReportMaxfgHistory(const std::shared_ptr<IStats> &stats_client,
+ const std::string &path);
private:
// Proto messages are 1-indexed and VendorAtom field numbers start at 2, so
@@ -112,7 +115,7 @@ class BatteryEEPROMReporter {
/* The number of elements in struct BatteryHistory for P20 series */
const int kNumBatteryHistoryFields = 19;
- /* P21 history format */
+ /* P21+ history format */
struct BatteryHistoryExtend {
uint16_t tempco;
uint16_t rcomp0;
@@ -135,6 +138,10 @@ class BatteryEEPROMReporter {
bool checkLogEvent(struct BatteryHistory hist);
void reportEvent(const std::shared_ptr<IStats> &stats_client,
const struct BatteryHistory &hist);
+
+ const int kNum77759GMSRFields = 11;
+ const int kNum77779GMSRFields = 5;
+ const int kNum17201HISTFields = 16;
};
} // namespace pixel
diff --git a/pixelstats/include/pixelstats/BrownoutDetectedReporter.h b/pixelstats/include/pixelstats/BrownoutDetectedReporter.h
index 5f460d9f..d6437761 100644
--- a/pixelstats/include/pixelstats/BrownoutDetectedReporter.h
+++ b/pixelstats/include/pixelstats/BrownoutDetectedReporter.h
@@ -34,6 +34,39 @@ using aidl::android::frameworks::stats::VendorAtomValue;
#define ODPM_MAX_IDX 24
#define DVFS_MAX_IDX 6
+
+enum CsvIdx {
+ TIMESTAMP_IDX,
+ IRQ_IDX,
+ SOC_IDX,
+ TEMP_IDX,
+ CYCLE_IDX,
+ VOLTAGE_IDX,
+ CURRENT_IDX,
+ DVFS_CHANNEL_0 = 7,
+ ODPM_CHANNEL_0 = 12,
+};
+
+enum Irq {
+ SMPL_WARN,
+ OCP_WARN_CPUCL1,
+ OCP_WARN_CPUCL2,
+ SOFT_OCP_WARN_CPUCL1,
+ SOFT_OCP_WARN_CPUCL2,
+ OCP_WARN_TPU,
+ SOFT_OCP_WARN_TPU,
+ OCP_WARN_GPU,
+ SOFT_OCP_WARN_GPU,
+ PMIC_SOC,
+ UVLO1,
+ UVLO2,
+ BATOILO,
+ BATOILO2,
+ PMIC_120C,
+ PMIC_140C,
+ PMIC_OVERHEAT,
+};
+
enum Update { kUpdateMax, kUpdateMin };
/**
@@ -43,6 +76,9 @@ class BrownoutDetectedReporter {
public:
void logBrownout(const std::shared_ptr<IStats> &stats_client, const std::string &logFilePath,
const std::string &brownoutReasonProp);
+ void logBrownoutCsv(const std::shared_ptr<IStats> &stats_client, const std::string &logFilePath,
+ const std::string &brownoutReasonProp);
+ int brownoutReasonCheck(const std::string &brownoutReasonProp);
private:
struct BrownoutDetectedInfo {
diff --git a/pixelstats/include/pixelstats/ChargeStatsReporter.h b/pixelstats/include/pixelstats/ChargeStatsReporter.h
index 62c3abc9..69b6eb9c 100644
--- a/pixelstats/include/pixelstats/ChargeStatsReporter.h
+++ b/pixelstats/include/pixelstats/ChargeStatsReporter.h
@@ -60,6 +60,8 @@ class ChargeStatsReporter {
"/sys/devices/platform/google,charger/thermal_stats";
const std::string kGChargerMetricsPath = "/sys/devices/platform/google,charger/charge_stats";
+
+ const std::string kGDualBattMetricsPath = "/sys/class/power_supply/dualbatt/dbatt_stats";
};
} // namespace pixel
diff --git a/pixelstats/include/pixelstats/StatsHelper.h b/pixelstats/include/pixelstats/StatsHelper.h
index 01395491..f9366771 100644
--- a/pixelstats/include/pixelstats/StatsHelper.h
+++ b/pixelstats/include/pixelstats/StatsHelper.h
@@ -50,6 +50,8 @@ void reportUsbPortOverheat(const std::shared_ptr<IStats> &stats_client,
void reportSpeakerHealthStat(const std::shared_ptr<IStats> &stats_client,
const PixelAtoms::VendorSpeakerStatsReported &speakerHealthStat);
+void reportUsbDataSessionEvent(const std::shared_ptr<IStats> &stats_client,
+ const PixelAtoms::VendorUsbDataSessionEvent &usb_session);
} // namespace pixel
} // namespace google
} // namespace hardware
diff --git a/pixelstats/include/pixelstats/SysfsCollector.h b/pixelstats/include/pixelstats/SysfsCollector.h
index 494acd71..e60842a4 100644
--- a/pixelstats/include/pixelstats/SysfsCollector.h
+++ b/pixelstats/include/pixelstats/SysfsCollector.h
@@ -62,6 +62,7 @@ class SysfsCollector {
const char *const EEPROMPath;
const char *const MitigationPath;
const char *const MitigationDurationPath;
+ const char *const BrownoutCsvPath;
const char *const BrownoutLogPath;
const char *const BrownoutReasonProp;
const char *const SpeakerTemperaturePath;
@@ -87,6 +88,10 @@ class SysfsCollector {
const char *const PcmLatencyPath;
const char *const PcmCountPath;
const char *const TotalCallCountPath;
+ const char *const OffloadEffectsIdPath;
+ const char *const OffloadEffectsDurationPath;
+ const char *const BluetoothAudioUsagePath;
+ const char *const GMSRPath;
};
SysfsCollector(const struct SysfsPaths &paths);
@@ -140,6 +145,9 @@ class SysfsCollector {
void logWavesStats(const std::shared_ptr<IStats> &stats_client);
void logAdaptedInfoStats(const std::shared_ptr<IStats> &stats_client);
void logPcmUsageStats(const std::shared_ptr<IStats> &stats_client);
+ void logOffloadEffectsStats(const std::shared_ptr<IStats> &stats_client);
+ void logBluetoothAudioUsage(const std::shared_ptr<IStats> &stats_client);
+ void logBatteryGMSR(const std::shared_ptr<IStats> &stats_client);
const char *const kSlowioReadCntPath;
const char *const kSlowioWriteCntPath;
@@ -159,6 +167,7 @@ class SysfsCollector {
const char *const kZramMmStatPath;
const char *const kZramBdStatPath;
const char *const kEEPROMPath;
+ const char *const kBrownoutCsvPath;
const char *const kBrownoutLogPath;
const char *const kBrownoutReasonProp;
const char *const kPowerMitigationStatsPath;
@@ -186,6 +195,11 @@ class SysfsCollector {
const char *const kPcmLatencyPath;
const char *const kPcmCountPath;
const char *const kTotalCallCountPath;
+ const char *const kOffloadEffectsIdPath;
+ const char *const kOffloadEffectsDurationPath;
+ const char *const kBluetoothAudioUsagePath;
+ const char *const kGMSRPath;
+ const char *const kMaxfgHistoryPath;
BatteryEEPROMReporter battery_EEPROM_reporter_;
MmMetricsReporter mm_metrics_reporter_;
diff --git a/pixelstats/include/pixelstats/UeventListener.h b/pixelstats/include/pixelstats/UeventListener.h
index 352beb88..6e6f8174 100644
--- a/pixelstats/include/pixelstats/UeventListener.h
+++ b/pixelstats/include/pixelstats/UeventListener.h
@@ -28,7 +28,6 @@ namespace google {
namespace pixel {
using aidl::android::frameworks::stats::IStats;
-
/**
* A class to listen for uevents and report reliability events to
* the PixelStats HAL.
@@ -47,6 +46,7 @@ class UeventListener {
const char *const TypeCPartnerPidPath;
const char *const WirelessChargerPtmcUevent; // Deprecated.
const char *const WirelessChargerPtmcPath; // Deprecated.
+ const char *const GMSRPath;
};
constexpr static const char *const ssoc_details_path =
"/sys/class/power_supply/battery/ssoc_details";
@@ -59,6 +59,7 @@ class UeventListener {
constexpr static const char *const typec_partner_pid_path_default =
"/sys/class/typec/port0-partner/identity/product";
constexpr static const char *const typec_partner_uevent_default = "DEVTYPE=typec_partner";
+ constexpr static const char *const gmsr_path = "";
UeventListener(const std::string audio_uevent, const std::string ssoc_details_path = "",
const std::string overheat_path = overheat_path_default,
@@ -90,7 +91,9 @@ class UeventListener {
void ReportTypeCPartnerId(const std::shared_ptr<IStats> &stats_client);
void ReportGpuEvent(const std::shared_ptr<IStats> &stats_client, const char *driver,
const char *gpu_event_type, const char *gpu_event_info);
-
+ void ReportThermalAbnormalEvent(const std::shared_ptr<IStats> &stats_client,
+ const char *devpath, const char *thermal_abnormal_event_type,
+ const char *thermal_abnormal_event_info);
const std::string kAudioUevent;
const std::string kBatterySSOCPath;
const std::string kUsbPortOverheatPath;
@@ -98,6 +101,7 @@ class UeventListener {
const std::string kTypeCPartnerUevent;
const std::string kTypeCPartnerVidPath;
const std::string kTypeCPartnerPidPath;
+ const std::string kBatteryGMSRPath;
const std::unordered_map<std::string, PixelAtoms::GpuEvent::GpuEventType>
kGpuEventTypeStrToEnum{
@@ -140,6 +144,28 @@ class UeventListener {
{"CSF_RESET_FAILED", PixelAtoms::GpuEvent::GpuEventInfo::
GpuEvent_GpuEventInfo_MALI_CSF_RESET_FAILED}};
+ const std::unordered_map<std::string,
+ PixelAtoms::ThermalSensorAbnormalityDetected::AbnormalityType>
+ kThermalAbnormalityTypeStrToEnum{
+ {"UNKNOWN", PixelAtoms::ThermalSensorAbnormalityDetected::AbnormalityType::
+ ThermalSensorAbnormalityDetected_AbnormalityType_UNKNOWN},
+ {"SENSOR_STUCK",
+ PixelAtoms::ThermalSensorAbnormalityDetected::AbnormalityType::
+ ThermalSensorAbnormalityDetected_AbnormalityType_SENSOR_STUCK},
+ {"EXTREME_HIGH_TEMP",
+ PixelAtoms::ThermalSensorAbnormalityDetected::AbnormalityType::
+ ThermalSensorAbnormalityDetected_AbnormalityType_EXTREME_HIGH_TEMP},
+ {"EXTREME_LOW_TEMP",
+ PixelAtoms::ThermalSensorAbnormalityDetected::AbnormalityType::
+ ThermalSensorAbnormalityDetected_AbnormalityType_EXTREME_LOW_TEMP},
+ {"HIGH_RISING_SPEED",
+ PixelAtoms::ThermalSensorAbnormalityDetected::AbnormalityType::
+ ThermalSensorAbnormalityDetected_AbnormalityType_HIGH_RISING_SPEED},
+ {"TEMP_READ_FAIL",
+ PixelAtoms::ThermalSensorAbnormalityDetected::AbnormalityType::
+ ThermalSensorAbnormalityDetected_AbnormalityType_TEMP_READ_FAIL},
+ };
+
BatteryCapacityReporter battery_capacity_reporter_;
ChargeStatsReporter charge_stats_reporter_;
diff --git a/pixelstats/pixelatoms.proto b/pixelstats/pixelatoms.proto
index 3f5e76f4..c773c2b1 100644
--- a/pixelstats/pixelatoms.proto
+++ b/pixelstats/pixelatoms.proto
@@ -112,6 +112,10 @@ message Atom {
VendorAudioAdaptedInfoStatsReported vendor_audio_adapted_info_stats_reported = 105058;
GpuEvent gpu_event = 105059;
VendorAudioPcmStatsReported vendor_audio_pcm_stats_reported = 105060;
+ VendorUsbDataSessionEvent vendor_usb_data_session_event = 105061;
+ ThermalSensorAbnormalityDetected thermal_sensor_abnormality_detected = 105062;
+ VendorAudioOffloadedEffectStatsReported vendor_audio_offloaded_effect_stats_reported = 105063;
+ VendorAudioBtMediaStatsReported vendor_audio_bt_media_stats_reported = 105064;
}
// AOSP atom ID range ends at 109999
reserved 109997; // reserved for VtsVendorAtomJavaTest test atom
@@ -1514,6 +1518,7 @@ message BrownoutDetected {
SMPL_WARN = 1;
UVLO2 = 2;
BATOILO = 3;
+ BATOILO2 = 4;
}
enum BrownoutReason {
@@ -1990,3 +1995,111 @@ message VendorAudioPcmStatsReported {
/* Total active count of the pcm type per day. */
optional int32 pcm_active_counts_per_day = 4;
}
+
+/**
+ * Keep track of information about a USB data session, which is defined
+ * as the period when a port enters a data role (either host or device) to
+ * when the port exits the data role.
+ */
+message VendorUsbDataSessionEvent {
+ /* Vendor reverse domain name (expecting "com.google.pixel") */
+ optional string reverse_domain_name = 1;
+
+ enum UsbDataRole {
+ USB_ROLE_UNKNOWN = 0;
+ USB_ROLE_DEVICE = 1;
+ USB_ROLE_HOST = 2;
+ }
+ /**
+ * USB device states are key milestones in a USB connection.
+ * For device data role, a typical transition would be like:
+ * not attached -> default -> addressed -> configured.
+ * For host data role, a typical transition would be like
+ * not attached -> powered -> default -> addressed -> configured.
+ */
+ enum UsbDeviceState {
+ USB_STATE_UNKNOWN = 0;
+ USB_STATE_NOT_ATTACHED = 1;
+ USB_STATE_ATTACHED = 2;
+ USB_STATE_POWERED = 3;
+ USB_STATE_DEFAULT = 4;
+ USB_STATE_ADDRESSED = 5;
+ USB_STATE_CONFIGURED = 6;
+ USB_STATE_SUSPENDED = 7;
+ }
+ /* USB data role of the data session. */
+ optional UsbDataRole usb_role = 2;
+ /* Usb device state transitions during the data session. */
+ repeated UsbDeviceState usb_states = 3;
+ /**
+ * Elapsed time from the start of the data session when entering the
+ * state, mapped 1-1 to the usb_states field.
+ */
+ repeated int64 elapsed_time_ms = 4;
+ // Duration of the data session.
+ optional int64 duration_ms = 5;
+}
+
+/*
+ * Logs the thermal sensor abnormal event when detected.
+ * Logged from:
+ * virtual sensors: hardware/google/pixel/thermal/utils/thermal_stats_helper.cpp
+ * thermistors & SoC: hardware/google/pixel/pixelstats/UeventListener.cpp
+ */
+message ThermalSensorAbnormalityDetected {
+ enum AbnormalityType {
+ UNKNOWN = 0;
+ SENSOR_STUCK = 1;
+ EXTREME_HIGH_TEMP = 2;
+ EXTREME_LOW_TEMP = 3;
+ HIGH_RISING_SPEED = 4;
+ TEMP_READ_FAIL = 5;
+ }
+
+ /* Vendor reverse domain name */
+ optional string reverse_domain_name = 1;
+ /* Type of Thermal Sensor Abnormality */
+ optional AbnormalityType type = 2;
+ /* Name of the problematic sensor */
+ optional string sensor = 3;
+ /* Abnormal temp reading of sensor */
+ optional int32 temp = 4;
+}
+
+/**
+ * Logs the reported vendor audio offloaded effects usage stats.
+ */
+message VendorAudioOffloadedEffectStatsReported {
+ /* Vendor reverse domain name */
+ optional string reverse_domain_name = 1;
+
+ /* UUID most significant bit */
+ optional int64 effect_uuid_msb = 2;
+
+ /* UUID least significant bit */
+ optional int64 effect_uuid_lsb = 3;
+
+ /* Active seconds per day. */
+ optional int32 effect_active_seconds_per_day = 4;
+}
+
+/*
+ * Logs the Bluetooth Audio stats.
+ * Two stats are recorded, count and duration (in ms) per features.
+ */
+message VendorAudioBtMediaStatsReported {
+ /* Vendor reverse domain name */
+ optional string reverse_domain_name = 1;
+
+ enum Codec {
+ UNKNOWN = 0;
+ SBC = 1;
+ AAC = 2;
+ OPUS = 3;
+ LC3 = 4;
+ }
+ /* Codec to record. */
+ optional Codec bt_codec_type = 2;
+ /* Total active seconds to record. */
+ optional int32 active_seconds_per_day = 3;
+}
diff --git a/power-libperfmgr/Android.bp b/power-libperfmgr/Android.bp
index 2163954c..9ccd6fdd 100644
--- a/power-libperfmgr/Android.bp
+++ b/power-libperfmgr/Android.bp
@@ -24,6 +24,7 @@ cc_library {
"disp-power/DisplayLowPower.cpp",
"disp-power/InteractionHandler.cpp",
],
+ cpp_std: "gnu++20",
shared_libs: [
"libbase",
"libcutils",
@@ -41,14 +42,49 @@ cc_library_headers {
export_include_dirs: ["hidl"],
}
+cc_test {
+ name: "libadpf_test",
+ defaults: ["android.hardware.power-ndk_static"],
+ proprietary: true,
+ vendor: true,
+ require_root: true,
+ srcs: [
+ "aidl/tests/BackgroundWorkerTest.cpp",
+ "aidl/tests/PowerHintSessionTest.cpp",
+ "aidl/tests/SessionTaskMapTest.cpp",
+ "aidl/tests/UClampVoterTest.cpp",
+ "aidl/BackgroundWorker.cpp",
+ "aidl/PowerHintSession.cpp",
+ "aidl/PowerSessionManager.cpp",
+ "aidl/SessionTaskMap.cpp",
+ "aidl/SessionValueEntry.cpp",
+ "aidl/UClampVoter.cpp",
+ ],
+ cpp_std: "gnu++20",
+ static_libs: [
+ "libgmock",
+ ],
+ shared_libs: [
+ "liblog",
+ "libbase",
+ "libcutils",
+ "libutils",
+ "libperfmgr",
+ "libbinder_ndk",
+ "libprocessgroup",
+ "pixel-power-ext-V1-ndk",
+ ],
+ test_suites: ["device-tests"],
+}
+
cc_binary {
name: "android.hardware.power-service.pixel-libperfmgr",
+ defaults: ["android.hardware.power-ndk_shared"],
relative_install_path: "hw",
init_rc: ["aidl/android.hardware.power-service.pixel-libperfmgr.rc"],
vintf_fragments: ["aidl/android.hardware.power-service.pixel.xml"],
vendor: true,
shared_libs: [
- "android.hardware.power-V4-ndk",
"libbase",
"libcutils",
"liblog",
@@ -58,22 +94,26 @@ cc_binary {
"libperfmgr",
"libprocessgroup",
"pixel-power-ext-V1-ndk",
- "libprotobuf-cpp-full",
],
srcs: [
+ "aidl/BackgroundWorker.cpp",
"aidl/service.cpp",
"aidl/Power.cpp",
"aidl/PowerExt.cpp",
"aidl/PowerHintSession.cpp",
"aidl/PowerSessionManager.cpp",
+ "aidl/UClampVoter.cpp",
+ "aidl/SessionTaskMap.cpp",
+ "aidl/SessionValueEntry.cpp",
],
+ cpp_std: "gnu++20",
}
cc_binary {
name: "sendhint",
+ defaults: ["android.hardware.power-ndk_shared"],
vendor: true,
shared_libs: [
- "android.hardware.power-V3-ndk",
"libbase",
"libcutils",
"liblog",
diff --git a/power-libperfmgr/aidl/AdpfTypes.h b/power-libperfmgr/aidl/AdpfTypes.h
new file mode 100644
index 00000000..bd9dbc9c
--- /dev/null
+++ b/power-libperfmgr/aidl/AdpfTypes.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2023 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 <cstdint>
+
+namespace aidl {
+namespace google {
+namespace hardware {
+namespace power {
+namespace impl {
+namespace pixel {
+
+enum class AdpfErrorCode : int32_t { ERR_OK = 0, ERR_BAD_STATE = -1, ERR_BAD_ARG = -2 };
+
+enum class AdpfHintType : int32_t {
+ ADPF_VOTE_DEFAULT = 1,
+ ADPF_CPU_LOAD_UP = 2,
+ ADPF_CPU_LOAD_RESET = 3,
+ ADPF_CPU_LOAD_RESUME = 4,
+ ADPF_VOTE_POWER_EFFICIENCY = 5
+};
+
+constexpr int kUclampMin{0};
+constexpr int kUclampMax{1024};
+
+} // namespace pixel
+} // namespace impl
+} // namespace power
+} // namespace hardware
+} // namespace google
+} // namespace aidl
diff --git a/power-libperfmgr/aidl/AppDescriptorTrace.h b/power-libperfmgr/aidl/AppDescriptorTrace.h
new file mode 100644
index 00000000..1f092695
--- /dev/null
+++ b/power-libperfmgr/aidl/AppDescriptorTrace.h
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2023 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/SessionMode.h>
+#include <android-base/stringprintf.h>
+
+#include <string>
+
+namespace aidl {
+namespace google {
+namespace hardware {
+namespace power {
+namespace impl {
+namespace pixel {
+
+template <class T>
+constexpr size_t enum_size() {
+ return static_cast<size_t>(*(ndk::enum_range<T>().end() - 1)) + 1;
+}
+
+// The App Hint Descriptor struct manages information necessary
+// to calculate the next uclamp min value from the PID function
+// and is separate so that it can be used as a pointer for
+// easily passing to the pid function
+struct AppDescriptorTrace {
+ AppDescriptorTrace(const std::string &idString) {
+ using ::android::base::StringPrintf;
+ trace_pid_err = StringPrintf("adpf.%s-%s", idString.c_str(), "pid.err");
+ trace_pid_integral = StringPrintf("adpf.%s-%s", idString.c_str(), "pid.integral");
+ trace_pid_derivative = StringPrintf("adpf.%s-%s", idString.c_str(), "pid.derivative");
+ trace_pid_pOut = StringPrintf("adpf.%s-%s", idString.c_str(), "pid.pOut");
+ trace_pid_iOut = StringPrintf("adpf.%s-%s", idString.c_str(), "pid.iOut");
+ trace_pid_dOut = StringPrintf("adpf.%s-%s", idString.c_str(), "pid.dOut");
+ trace_pid_output = StringPrintf("adpf.%s-%s", idString.c_str(), "pid.output");
+ trace_target = StringPrintf("adpf.%s-%s", idString.c_str(), "target");
+ trace_active = StringPrintf("adpf.%s-%s", idString.c_str(), "active");
+ trace_add_threads = StringPrintf("adpf.%s-%s", idString.c_str(), "add_threads");
+ trace_actl_last = StringPrintf("adpf.%s-%s", idString.c_str(), "act_last");
+ trace_min = StringPrintf("adpf.%s-%s", idString.c_str(), "min");
+ trace_batch_size = StringPrintf("adpf.%s-%s", idString.c_str(), "batch_size");
+ trace_hint_count = StringPrintf("adpf.%s-%s", idString.c_str(), "hint_count");
+ trace_hint_overtime = StringPrintf("adpf.%s-%s", idString.c_str(), "hint_overtime");
+ trace_is_first_frame = StringPrintf("adpf.%s-%s", idString.c_str(), "is_first_frame");
+ trace_session_hint = StringPrintf("adpf.%s-%s", idString.c_str(), "session_hint");
+ for (size_t i = 0; i < trace_modes.size(); ++i) {
+ trace_modes[i] = StringPrintf(
+ "adpf.%s-%s_mode", idString.c_str(),
+ toString(static_cast<aidl::android::hardware::power::SessionMode>(i)).c_str());
+ }
+ }
+
+ // Trace values
+ std::string trace_pid_err;
+ std::string trace_pid_integral;
+ std::string trace_pid_derivative;
+ std::string trace_pid_pOut;
+ std::string trace_pid_iOut;
+ std::string trace_pid_dOut;
+ std::string trace_pid_output;
+ std::string trace_target;
+ std::string trace_active;
+ std::string trace_add_threads;
+ std::string trace_actl_last;
+ std::string trace_min;
+ std::string trace_batch_size;
+ std::string trace_hint_count;
+ std::string trace_hint_overtime;
+ std::string trace_is_first_frame;
+ std::string trace_session_hint;
+ std::array<std::string, enum_size<aidl::android::hardware::power::SessionMode>()> trace_modes;
+};
+
+} // namespace pixel
+} // namespace impl
+} // namespace power
+} // namespace hardware
+} // namespace google
+} // namespace aidl
diff --git a/power-libperfmgr/aidl/BackgroundWorker.cpp b/power-libperfmgr/aidl/BackgroundWorker.cpp
new file mode 100644
index 00000000..7e86f70f
--- /dev/null
+++ b/power-libperfmgr/aidl/BackgroundWorker.cpp
@@ -0,0 +1,150 @@
+/*
+ * Copyright 2023 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 "BackgroundWorker.h"
+
+namespace aidl {
+namespace google {
+namespace hardware {
+namespace power {
+namespace impl {
+namespace pixel {
+
+PriorityQueueWorkerPool::PriorityQueueWorkerPool(size_t threadCount,
+ const std::string &threadNamePrefix) {
+ mRunning = true;
+ mThreadPool.reserve(threadCount);
+ for (size_t threadId = 0; threadId < threadCount; ++threadId) {
+ mThreadPool.push_back(std::thread([&, tid = threadId]() { loop(); }));
+
+ if (!threadNamePrefix.empty()) {
+ const std::string fullThreadName = threadNamePrefix + std::to_string(threadId);
+ pthread_setname_np(mThreadPool.back().native_handle(), fullThreadName.c_str());
+ }
+ }
+}
+
+PriorityQueueWorkerPool::~PriorityQueueWorkerPool() {
+ {
+ std::lock_guard<std::mutex> lock(mMutex);
+ mRunning = false;
+ mCv.notify_all();
+ }
+ for (auto &t : mThreadPool) {
+ if (t.joinable()) {
+ t.join();
+ }
+ }
+}
+
+void PriorityQueueWorkerPool::addCallback(int64_t templateQueueWorkerId,
+ std::function<void(int64_t)> callback) {
+ if (!callback) {
+ // Don't add callback if it isn't callable to prevent having to check later
+ return;
+ }
+ std::unique_lock<std::shared_mutex> lock(mSharedMutex);
+ auto itr = mCallbackMap.find(templateQueueWorkerId);
+ if (itr != mCallbackMap.end()) {
+ return;
+ }
+ mCallbackMap[templateQueueWorkerId] = callback;
+}
+
+void PriorityQueueWorkerPool::removeCallback(int64_t templateQueueWorkerId) {
+ std::unique_lock<std::shared_mutex> lock(mSharedMutex);
+ auto itr = mCallbackMap.find(templateQueueWorkerId);
+ if (itr == mCallbackMap.end()) {
+ return;
+ }
+ mCallbackMap.erase(itr);
+}
+
+void PriorityQueueWorkerPool::schedule(int64_t templateQueueWorkerId, int64_t packageId,
+ std::chrono::steady_clock::time_point deadline) {
+ std::unique_lock<std::mutex> lock(mMutex);
+ mPackageQueue.emplace(deadline, templateQueueWorkerId, packageId);
+ mCv.notify_all();
+}
+
+void PriorityQueueWorkerPool::loop() {
+ Package package;
+ while (mRunning) {
+ std::unique_lock<std::mutex> lock(mMutex);
+ // Default to longest wait possible without overflowing if there is
+ // nothing to work on in the queue
+ std::chrono::steady_clock::time_point deadline =
+ std::chrono::steady_clock::time_point::max();
+
+ // Use next item to work on deadline if available
+ if (!mPackageQueue.empty()) {
+ deadline = mPackageQueue.top().deadline;
+ }
+
+ // Wait until signal or deadline
+ mCv.wait_until(lock, deadline, [&]() {
+ // Check if stop running requested, if so return now
+ if (!mRunning)
+ return true;
+
+ // Check if nothing in queue (e.g. spurious wakeup), wait as long as possible again
+ if (mPackageQueue.empty()) {
+ deadline = std::chrono::steady_clock::time_point::max();
+ return false;
+ }
+
+ // Something in queue, use that as next deadline
+ deadline = mPackageQueue.top().deadline;
+ // Check if deadline is in the future still, continue waiting with updated deadline
+ if (deadline > std::chrono::steady_clock::now())
+ return false;
+ // Next work entry's deadline is in the past or exactly now, time to work on it
+ return true;
+ });
+
+ if (!mRunning)
+ break;
+ if (mPackageQueue.empty())
+ continue;
+
+ // Copy work entry from queue and unlock
+ package = mPackageQueue.top();
+ mPackageQueue.pop();
+ lock.unlock();
+
+ // Find callback based on package's callback id
+ {
+ std::shared_lock<std::shared_mutex> lockCb(mSharedMutex);
+ auto callbackItr = mCallbackMap.find(package.templateQueueWorkerId);
+ if (callbackItr == mCallbackMap.end()) {
+ // Callback was removed before package could be worked on, that's ok just ignore
+ continue;
+ }
+ // Exceptions disabled so no need to wrap this
+ callbackItr->second(package.packageId);
+ }
+ }
+}
+
+} // namespace pixel
+} // namespace impl
+} // namespace power
+} // namespace hardware
+} // namespace google
+} // namespace aidl
diff --git a/power-libperfmgr/aidl/BackgroundWorker.h b/power-libperfmgr/aidl/BackgroundWorker.h
new file mode 100644
index 00000000..5d070e12
--- /dev/null
+++ b/power-libperfmgr/aidl/BackgroundWorker.h
@@ -0,0 +1,151 @@
+/*
+ * Copyright 2023 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 <condition_variable>
+#include <functional>
+#include <mutex>
+#include <queue>
+#include <shared_mutex>
+#include <string>
+#include <thread>
+#include <unordered_map>
+
+#include "AdpfTypes.h"
+
+namespace aidl {
+namespace google {
+namespace hardware {
+namespace power {
+namespace impl {
+namespace pixel {
+
+// Background thread processing from priority queue based on time deadline
+// This class isn't meant to be used directly, use TemplatePriorityQueueWorker below
+class PriorityQueueWorkerPool {
+ public:
+ // CTOR
+ // thread count is number of threads to create in thread pool
+ // thread name prefix is use for naming threads to help with debugging
+ PriorityQueueWorkerPool(size_t threadCount, const std::string &threadNamePrefix);
+ // DTOR
+ ~PriorityQueueWorkerPool();
+ // Map callback id to callback function
+ void addCallback(int64_t templateQueueWorkerId, std::function<void(int64_t)> callback);
+ // Unmap callback id with callback function
+ void removeCallback(int64_t templateQueueWorkerId);
+ // Schedule work for specific worker id with package id to be run at time deadline
+ void schedule(int64_t templateQueueWorkerId, int64_t packageId,
+ std::chrono::steady_clock::time_point deadline);
+
+ private:
+ // Thread coordination
+ std::mutex mMutex;
+ bool mRunning;
+ std::condition_variable mCv;
+ std::vector<std::thread> mThreadPool;
+ void loop();
+
+ // Work package with worker id to find correct callback in
+ struct Package {
+ Package() {}
+ Package(std::chrono::steady_clock::time_point pDeadline, int64_t pTemplateQueueWorkerId,
+ int64_t pPackageId)
+ : deadline(pDeadline),
+ templateQueueWorkerId(pTemplateQueueWorkerId),
+ packageId(pPackageId) {}
+ std::chrono::steady_clock::time_point deadline;
+ int64_t templateQueueWorkerId{0};
+ int64_t packageId{0};
+ // Sort time earliest first
+ bool operator<(const Package &p) const { return deadline > p.deadline; }
+ };
+ std::priority_queue<Package> mPackageQueue;
+
+ // Callback management
+ std::shared_mutex mSharedMutex;
+ std::unordered_map<int64_t, std::function<void(int64_t)>> mCallbackMap;
+};
+
+// Generic templated worker for registering a single std::function callback one time
+// and reusing it to reduce memory allocations. Many TemplatePriorityQueueWorkers
+// can make use of the same PriorityQueue worker which enables sharing a thread pool
+// across callbacks of different types. This class is a template to allow for different
+// types of work packages while not requiring virtual calls.
+template <typename PACKAGE>
+class TemplatePriorityQueueWorker {
+ public:
+ // CTOR, callback to run when added work is run, worker to use for adding work to
+ TemplatePriorityQueueWorker(std::function<void(const PACKAGE &)> cb,
+ std::shared_ptr<PriorityQueueWorkerPool> worker)
+ : mCallbackId(reinterpret_cast<std::intptr_t>(this)), mCallback(cb), mWorker(worker) {
+ if (!mCallback) {
+ mCallback = [](const auto &) {};
+ }
+ mWorker->addCallback(mCallbackId, [&](int64_t packageId) { process(packageId); });
+ }
+
+ // DTOR
+ ~TemplatePriorityQueueWorker() { mWorker->removeCallback(mCallbackId); }
+
+ void schedule(const PACKAGE &package,
+ std::chrono::steady_clock::time_point t = std::chrono::steady_clock::now()) {
+ {
+ std::lock_guard<std::mutex> lock(mMutex);
+ ++mPackageIdCounter;
+ mPackages.emplace(mPackageIdCounter, package);
+ }
+ mWorker->schedule(mCallbackId, mPackageIdCounter, t);
+ }
+
+ private:
+ int64_t mCallbackId{0};
+ std::function<void(const PACKAGE &)> mCallback;
+ // Must ensure PriorityQueueWorker does not go out of scope before this class does
+ std::shared_ptr<PriorityQueueWorkerPool> mWorker;
+ mutable std::mutex mMutex;
+ // Counter is used as a unique identifier for work packages
+ int64_t mPackageIdCounter{0};
+ // Want a container that is:
+ // fast to add, fast random find find, fast random removal,
+ // and with reasonable space efficiency
+ std::unordered_map<int64_t, PACKAGE> mPackages;
+
+ void process(int64_t packageId) {
+ PACKAGE package;
+ {
+ std::lock_guard<std::mutex> lock(mMutex);
+ auto itr = mPackages.find(packageId);
+ if (itr == mPackages.end()) {
+ // Work id does not have matching entry, drop it
+ return;
+ }
+
+ package = itr->second;
+ mPackages.erase(itr);
+ }
+ mCallback(package);
+ }
+};
+
+} // namespace pixel
+} // namespace impl
+} // namespace power
+} // namespace hardware
+} // namespace google
+} // namespace aidl
diff --git a/power-libperfmgr/aidl/Power.cpp b/power-libperfmgr/aidl/Power.cpp
index 382a7b77..e15d985d 100644
--- a/power-libperfmgr/aidl/Power.cpp
+++ b/power-libperfmgr/aidl/Power.cpp
@@ -183,6 +183,9 @@ ndk::ScopedAStatus Power::isModeSupported(Mode type, bool *_aidl_return) {
if (type == Mode::LOW_POWER) {
supported = true;
}
+ if (!supported && HintManager::GetInstance()->IsAdpfProfileSupported(toString(type))) {
+ supported = true;
+ }
LOG(INFO) << "Power mode " << toString(type) << " isModeSupported: " << supported;
*_aidl_return = supported;
return ndk::ScopedAStatus::ok();
@@ -231,6 +234,9 @@ ndk::ScopedAStatus Power::setBoost(Boost type, int32_t durationMs) {
ndk::ScopedAStatus Power::isBoostSupported(Boost type, bool *_aidl_return) {
bool supported = HintManager::GetInstance()->IsHintSupported(toString(type));
+ if (!supported && HintManager::GetInstance()->IsAdpfProfileSupported(toString(type))) {
+ supported = true;
+ }
LOG(INFO) << "Power boost " << toString(type) << " isBoostSupported: " << supported;
*_aidl_return = supported;
return ndk::ScopedAStatus::ok();
diff --git a/power-libperfmgr/aidl/PowerExt.cpp b/power-libperfmgr/aidl/PowerExt.cpp
index f6cd084d..bf0a104a 100644
--- a/power-libperfmgr/aidl/PowerExt.cpp
+++ b/power-libperfmgr/aidl/PowerExt.cpp
@@ -57,6 +57,10 @@ ndk::ScopedAStatus PowerExt::setMode(const std::string &mode, bool enabled) {
ndk::ScopedAStatus PowerExt::isModeSupported(const std::string &mode, bool *_aidl_return) {
bool supported = HintManager::GetInstance()->IsHintSupported(mode);
+
+ if (!supported && HintManager::GetInstance()->IsAdpfProfileSupported(mode)) {
+ supported = true;
+ }
LOG(INFO) << "PowerExt mode " << mode << " isModeSupported: " << supported;
*_aidl_return = supported;
return ndk::ScopedAStatus::ok();
@@ -82,6 +86,9 @@ ndk::ScopedAStatus PowerExt::setBoost(const std::string &boost, int32_t duration
ndk::ScopedAStatus PowerExt::isBoostSupported(const std::string &boost, bool *_aidl_return) {
bool supported = HintManager::GetInstance()->IsHintSupported(boost);
+ if (!supported && HintManager::GetInstance()->IsAdpfProfileSupported(boost)) {
+ supported = true;
+ }
LOG(INFO) << "PowerExt boost " << boost << " isBoostSupported: " << supported;
*_aidl_return = supported;
return ndk::ScopedAStatus::ok();
diff --git a/power-libperfmgr/aidl/PowerHintSession.cpp b/power-libperfmgr/aidl/PowerHintSession.cpp
index eadc4cf3..0262d40d 100644
--- a/power-libperfmgr/aidl/PowerHintSession.cpp
+++ b/power-libperfmgr/aidl/PowerHintSession.cpp
@@ -48,6 +48,8 @@ using std::chrono::nanoseconds;
namespace {
+static std::atomic<int64_t> sSessionIDCounter{0};
+
static inline int64_t ns_to_100us(int64_t ns) {
return ns / 100000;
}
@@ -57,7 +59,7 @@ static inline int64_t ns_to_100us(int64_t ns) {
int64_t PowerHintSession::convertWorkDurationToBoostByPid(
const std::vector<WorkDuration> &actualDurations) {
std::shared_ptr<AdpfConfig> adpfConfig = HintManager::GetInstance()->GetAdpfProfile();
- const nanoseconds &targetDuration = mDescriptor->duration;
+ const nanoseconds &targetDuration = mDescriptor->targetNs;
int64_t &integral_error = mDescriptor->integral_error;
int64_t &previous_error = mDescriptor->previous_error;
uint64_t samplingWindowP = adpfConfig->mSamplingWindowP;
@@ -103,52 +105,62 @@ int64_t PowerHintSession::convertWorkDurationToBoostByPid(
derivative_sum / dt / (length - d_start));
int64_t output = pOut + iOut + dOut;
- if (ATRACE_ENABLED()) {
- traceSessionVal("pid.err", err_sum / (length - p_start));
- traceSessionVal("pid.integral", integral_error);
- traceSessionVal("pid.derivative", derivative_sum / dt / (length - d_start));
- traceSessionVal("pid.pOut", pOut);
- traceSessionVal("pid.iOut", iOut);
- traceSessionVal("pid.dOut", dOut);
- traceSessionVal("pid.output", output);
- }
+ ATRACE_INT(mAppDescriptorTrace.trace_pid_err.c_str(), err_sum / (length - p_start));
+ ATRACE_INT(mAppDescriptorTrace.trace_pid_integral.c_str(), integral_error);
+ ATRACE_INT(mAppDescriptorTrace.trace_pid_derivative.c_str(),
+ derivative_sum / dt / (length - d_start));
+ ATRACE_INT(mAppDescriptorTrace.trace_pid_pOut.c_str(), pOut);
+ ATRACE_INT(mAppDescriptorTrace.trace_pid_iOut.c_str(), iOut);
+ ATRACE_INT(mAppDescriptorTrace.trace_pid_dOut.c_str(), dOut);
+ ATRACE_INT(mAppDescriptorTrace.trace_pid_output.c_str(), output);
return output;
}
+AppHintDesc::AppHintDesc(int64_t sessionId, int32_t tgid, int32_t uid,
+ std::chrono::nanoseconds pTargetNs)
+ : sessionId(sessionId),
+ tgid(tgid),
+ uid(uid),
+ targetNs(pTargetNs),
+ pidSetPoint(0),
+ is_active(true),
+ update_count(0),
+ integral_error(0),
+ previous_error(0) {}
+
PowerHintSession::PowerHintSession(int32_t tgid, int32_t uid, const std::vector<int32_t> &threadIds,
- int64_t durationNanos)
- : mStaleTimerHandler(sp<StaleTimerHandler>::make(this)),
- mBoostTimerHandler(sp<BoostTimerHandler>::make(this)) {
- mDescriptor = new AppHintDesc(tgid, uid, threadIds);
- mDescriptor->duration = std::chrono::nanoseconds(durationNanos);
- mIdString = StringPrintf("%" PRId32 "-%" PRId32 "-%" PRIxPTR, mDescriptor->tgid,
- mDescriptor->uid, reinterpret_cast<uintptr_t>(this) & 0xffff);
-
- mPowerManagerHandler = PowerSessionManager::getInstance();
+ int64_t durationNs)
+ : mPSManager(PowerSessionManager::getInstance()),
+ mSessionId(++sSessionIDCounter),
+ mIdString(StringPrintf("%" PRId32 "-%" PRId32 "-%" PRId64, tgid, uid, mSessionId)),
+ mDescriptor(std::make_shared<AppHintDesc>(mSessionId, tgid, uid,
+ std::chrono::nanoseconds(durationNs))),
+ mAppDescriptorTrace(mIdString) {
+ ATRACE_CALL();
+ ATRACE_INT(mAppDescriptorTrace.trace_target.c_str(), mDescriptor->targetNs.count());
+ ATRACE_INT(mAppDescriptorTrace.trace_active.c_str(), mDescriptor->is_active.load());
+
mLastUpdatedTime.store(std::chrono::steady_clock::now());
- if (ATRACE_ENABLED()) {
- traceSessionVal("target", mDescriptor->duration.count());
- traceSessionVal("active", mDescriptor->is_active.load());
- }
- PowerSessionManager::getInstance()->addPowerSession(this);
+ mPSManager->addPowerSession(mIdString, mDescriptor, threadIds);
// init boost
- sendHint(SessionHint::CPU_LOAD_RESET);
+ auto adpfConfig = HintManager::GetInstance()->GetAdpfProfile();
+ mPSManager->voteSet(
+ mSessionId, AdpfHintType::ADPF_CPU_LOAD_RESET, adpfConfig->mUclampMinHigh, kUclampMax,
+ std::chrono::steady_clock::now(),
+ duration_cast<nanoseconds>(mDescriptor->targetNs * adpfConfig->mStaleTimeFactor / 2.0));
+
+ mPSManager->voteSet(mSessionId, AdpfHintType::ADPF_VOTE_DEFAULT, adpfConfig->mUclampMinInit,
+ kUclampMax, std::chrono::steady_clock::now(), mDescriptor->targetNs);
ALOGV("PowerHintSession created: %s", mDescriptor->toString().c_str());
}
PowerHintSession::~PowerHintSession() {
+ ATRACE_CALL();
close();
ALOGV("PowerHintSession deleted: %s", mDescriptor->toString().c_str());
- if (ATRACE_ENABLED()) {
- traceSessionVal("target", 0);
- traceSessionVal("actl_last", 0);
- traceSessionVal("active", 0);
- }
- delete mDescriptor;
-}
-
-void PowerHintSession::traceSessionVal(char const *identifier, int64_t val) const {
- ATRACE_INT(StringPrintf("adpf.%s-%s", mIdString.c_str(), identifier).c_str(), val);
+ ATRACE_INT(mAppDescriptorTrace.trace_target.c_str(), 0);
+ ATRACE_INT(mAppDescriptorTrace.trace_actl_last.c_str(), 0);
+ ATRACE_INT(mAppDescriptorTrace.trace_active.c_str(), 0);
}
bool PowerHintSession::isAppSession() {
@@ -156,18 +168,16 @@ bool PowerHintSession::isAppSession() {
return mDescriptor->uid >= AID_APP_START;
}
-void PowerHintSession::updateUniveralBoostMode() {
- if (!isAppSession()) {
- return;
- }
- if (ATRACE_ENABLED()) {
- const std::string tag = StringPrintf("%s:updateUniveralBoostMode()", mIdString.c_str());
- ATRACE_BEGIN(tag.c_str());
- }
- PowerHintMonitor::getInstance()->getLooper()->sendMessage(mPowerManagerHandler, NULL);
- if (ATRACE_ENABLED()) {
- ATRACE_END();
+void PowerHintSession::updatePidSetPoint(int pidSetPoint, bool updateVote) {
+ mDescriptor->pidSetPoint = pidSetPoint;
+ if (updateVote) {
+ auto adpfConfig = HintManager::GetInstance()->GetAdpfProfile();
+ mPSManager->voteSet(
+ mSessionId, AdpfHintType::ADPF_VOTE_DEFAULT, pidSetPoint, kUclampMax,
+ std::chrono::steady_clock::now(),
+ duration_cast<nanoseconds>(mDescriptor->targetNs * adpfConfig->mStaleTimeFactor));
}
+ ATRACE_INT(mAppDescriptorTrace.trace_min.c_str(), pidSetPoint);
}
void PowerHintSession::tryToSendPowerHint(std::string hint) {
@@ -179,30 +189,10 @@ void PowerHintSession::tryToSendPowerHint(std::string hint) {
}
}
-int PowerHintSession::setSessionUclampMin(int32_t min, bool resetStale) {
- {
- std::lock_guard<std::mutex> guard(mSessionLock);
- mDescriptor->current_min = min;
- }
- if (min != 0 && resetStale) {
- mStaleTimerHandler->updateTimer();
- }
- PowerSessionManager::getInstance()->setUclampMin(this, min);
-
- if (ATRACE_ENABLED()) {
- traceSessionVal("min", min);
- }
- return 0;
-}
-
-int PowerHintSession::getUclampMin() {
- return mDescriptor->current_min;
-}
-
void PowerHintSession::dumpToStream(std::ostream &stream) {
stream << "ID.Min.Act.Timeout(" << mIdString;
- stream << ", " << mDescriptor->current_min;
- stream << ", " << mDescriptor->is_active.load();
+ stream << ", " << mDescriptor->pidSetPoint;
+ stream << ", " << mDescriptor->is_active;
stream << ", " << isTimeout() << ")";
}
@@ -215,12 +205,9 @@ ndk::ScopedAStatus PowerHintSession::pause() {
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
// Reset to default uclamp value.
mDescriptor->is_active.store(false);
- setStale();
- if (ATRACE_ENABLED()) {
- traceSessionVal("active", mDescriptor->is_active.load());
- }
- updateUniveralBoostMode();
- PowerSessionManager::getInstance()->removeThreadsFromPowerSession(this);
+ mPSManager->pause(mSessionId);
+ ATRACE_INT(mAppDescriptorTrace.trace_active.c_str(), false);
+ ATRACE_INT(mAppDescriptorTrace.trace_min.c_str(), 0);
return ndk::ScopedAStatus::ok();
}
@@ -232,13 +219,10 @@ ndk::ScopedAStatus PowerHintSession::resume() {
if (mDescriptor->is_active.load())
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
mDescriptor->is_active.store(true);
- PowerSessionManager::getInstance()->addThreadsFromPowerSession(this);
// resume boost
- setSessionUclampMin(mDescriptor->current_min);
- if (ATRACE_ENABLED()) {
- traceSessionVal("active", mDescriptor->is_active.load());
- }
- updateUniveralBoostMode();
+ mPSManager->resume(mSessionId);
+ ATRACE_INT(mAppDescriptorTrace.trace_active.c_str(), true);
+ ATRACE_INT(mAppDescriptorTrace.trace_min.c_str(), mDescriptor->pidSetPoint);
return ndk::ScopedAStatus::ok();
}
@@ -248,12 +232,9 @@ ndk::ScopedAStatus PowerHintSession::close() {
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
// Remove the session from PowerSessionManager first to avoid racing.
- PowerSessionManager::getInstance()->removePowerSession(this);
- mStaleTimerHandler->setSessionDead();
- mBoostTimerHandler->setSessionDead();
- setSessionUclampMin(0);
+ mPSManager->removePowerSession(mSessionId);
mDescriptor->is_active.store(false);
- updateUniveralBoostMode();
+ ATRACE_INT(mAppDescriptorTrace.trace_min.c_str(), 0);
return ndk::ScopedAStatus::ok();
}
@@ -268,12 +249,11 @@ ndk::ScopedAStatus PowerHintSession::updateTargetWorkDuration(int64_t targetDura
}
targetDurationNanos =
targetDurationNanos * HintManager::GetInstance()->GetAdpfProfile()->mTargetTimeFactor;
- ALOGV("update target duration: %" PRId64 " ns", targetDurationNanos);
- mDescriptor->duration = std::chrono::nanoseconds(targetDurationNanos);
- if (ATRACE_ENABLED()) {
- traceSessionVal("target", mDescriptor->duration.count());
- }
+ mDescriptor->targetNs = std::chrono::nanoseconds(targetDurationNanos);
+ mPSManager->updateTargetWorkDuration(mSessionId, AdpfHintType::ADPF_VOTE_DEFAULT,
+ mDescriptor->targetNs);
+ ATRACE_INT(mAppDescriptorTrace.trace_target.c_str(), targetDurationNanos);
return ndk::ScopedAStatus::ok();
}
@@ -284,53 +264,53 @@ ndk::ScopedAStatus PowerHintSession::reportActualWorkDuration(
ALOGE("Error: session is dead");
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
- if (mDescriptor->duration.count() == 0LL) {
+ if (mDescriptor->targetNs.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());
+ if (actualDurations.empty()) {
+ ALOGE("Error: durations shouldn't be empty.");
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();
+ auto adpfConfig = HintManager::GetInstance()->GetAdpfProfile();
mDescriptor->update_count++;
bool isFirstFrame = isTimeout();
- if (ATRACE_ENABLED()) {
- traceSessionVal("batch_size", actualDurations.size());
- traceSessionVal("actl_last", actualDurations.back().durationNanos);
- traceSessionVal("target", mDescriptor->duration.count());
- traceSessionVal("hint.count", mDescriptor->update_count);
- traceSessionVal("hint.overtime",
- actualDurations.back().durationNanos - mDescriptor->duration.count() > 0);
- }
+ ATRACE_INT(mAppDescriptorTrace.trace_batch_size.c_str(), actualDurations.size());
+ ATRACE_INT(mAppDescriptorTrace.trace_actl_last.c_str(), actualDurations.back().durationNanos);
+ ATRACE_INT(mAppDescriptorTrace.trace_target.c_str(), mDescriptor->targetNs.count());
+ ATRACE_INT(mAppDescriptorTrace.trace_hint_count.c_str(), mDescriptor->update_count);
+ ATRACE_INT(mAppDescriptorTrace.trace_hint_overtime.c_str(),
+ actualDurations.back().durationNanos - mDescriptor->targetNs.count() > 0);
+ ATRACE_INT(mAppDescriptorTrace.trace_is_first_frame.c_str(), (isFirstFrame) ? (1) : (0));
mLastUpdatedTime.store(std::chrono::steady_clock::now());
if (isFirstFrame) {
if (isAppSession()) {
tryToSendPowerHint("ADPF_FIRST_FRAME");
}
- updateUniveralBoostMode();
+
+ mPSManager->updateUniversalBoostMode();
}
- disableTemporaryBoost();
+ mPSManager->disableBoosts(mSessionId);
if (!adpfConfig->mPidOn) {
- setSessionUclampMin(adpfConfig->mUclampMinHigh);
+ updatePidSetPoint(adpfConfig->mUclampMinHigh);
return ndk::ScopedAStatus::ok();
}
int64_t output = convertWorkDurationToBoostByPid(actualDurations);
- /* apply to all the threads in the group */
+ // 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));
+ mDescriptor->pidSetPoint + static_cast<int>(output));
next_min = std::max(static_cast<int>(adpfConfig->mUclampMinLow), next_min);
- setSessionUclampMin(next_min);
+ updatePidSetPoint(next_min);
return ndk::ScopedAStatus::ok();
}
@@ -339,26 +319,38 @@ ndk::ScopedAStatus PowerHintSession::sendHint(SessionHint hint) {
ALOGE("Error: session is dead");
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
- disableTemporaryBoost();
- std::shared_ptr<AdpfConfig> adpfConfig = HintManager::GetInstance()->GetAdpfProfile();
+ if (mDescriptor->targetNs.count() == 0LL) {
+ ALOGE("Expect to call updateTargetWorkDuration() first.");
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+ }
+ auto adpfConfig = HintManager::GetInstance()->GetAdpfProfile();
+
switch (hint) {
case SessionHint::CPU_LOAD_UP:
- mNextUclampMin.store(mDescriptor->current_min);
- mBoostTimerHandler->updateTimer(mDescriptor->duration * 2);
- setSessionUclampMin(adpfConfig->mUclampMinHigh);
+ updatePidSetPoint(mDescriptor->pidSetPoint);
+ mPSManager->voteSet(mSessionId, AdpfHintType::ADPF_CPU_LOAD_UP,
+ adpfConfig->mUclampMinHigh, kUclampMax,
+ std::chrono::steady_clock::now(), mDescriptor->targetNs * 2);
break;
case SessionHint::CPU_LOAD_DOWN:
- setSessionUclampMin(adpfConfig->mUclampMinLow);
+ updatePidSetPoint(adpfConfig->mUclampMinLow);
break;
case SessionHint::CPU_LOAD_RESET:
- mNextUclampMin.store(std::max(adpfConfig->mUclampMinInit,
- static_cast<uint32_t>(mDescriptor->current_min)));
- mBoostTimerHandler->updateTimer(duration_cast<nanoseconds>(
- mDescriptor->duration * adpfConfig->mStaleTimeFactor / 2.0));
- setSessionUclampMin(adpfConfig->mUclampMinHigh);
+ updatePidSetPoint(std::max(adpfConfig->mUclampMinInit,
+ static_cast<uint32_t>(mDescriptor->pidSetPoint)),
+ false);
+ mPSManager->voteSet(mSessionId, AdpfHintType::ADPF_CPU_LOAD_RESET,
+ adpfConfig->mUclampMinHigh, kUclampMax,
+ std::chrono::steady_clock::now(),
+ duration_cast<nanoseconds>(mDescriptor->targetNs *
+ adpfConfig->mStaleTimeFactor / 2.0));
break;
case SessionHint::CPU_LOAD_RESUME:
- setSessionUclampMin(mDescriptor->current_min);
+ mPSManager->voteSet(mSessionId, AdpfHintType::ADPF_CPU_LOAD_RESUME,
+ mDescriptor->pidSetPoint, kUclampMax,
+ std::chrono::steady_clock::now(),
+ duration_cast<nanoseconds>(mDescriptor->targetNs *
+ adpfConfig->mStaleTimeFactor / 2.0));
break;
default:
ALOGE("Error: hint is invalid");
@@ -366,10 +358,28 @@ ndk::ScopedAStatus PowerHintSession::sendHint(SessionHint hint) {
}
tryToSendPowerHint(toString(hint));
mLastUpdatedTime.store(std::chrono::steady_clock::now());
- if (ATRACE_ENABLED()) {
- mLastHintSent = static_cast<int>(hint);
- traceSessionVal("session_hint", static_cast<int>(hint));
+ mLastHintSent = static_cast<int>(hint);
+ ATRACE_INT(mAppDescriptorTrace.trace_session_hint.c_str(), static_cast<int>(hint));
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus PowerHintSession::setMode(SessionMode mode, bool enabled) {
+ if (mSessionClosed) {
+ ALOGE("Error: session is dead");
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+ }
+
+ switch (mode) {
+ case SessionMode::POWER_EFFICIENCY:
+ break;
+ default:
+ ALOGE("Error: mode is invalid");
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
+
+ mModes[static_cast<size_t>(mode)] = enabled;
+ ATRACE_INT(mAppDescriptorTrace.trace_modes[static_cast<size_t>(mode)].c_str(), enabled);
+ mLastUpdatedTime.store(std::chrono::steady_clock::now());
return ndk::ScopedAStatus::ok();
}
@@ -378,38 +388,23 @@ ndk::ScopedAStatus PowerHintSession::setThreads(const std::vector<int32_t> &thre
ALOGE("Error: session is dead");
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
- if (threadIds.size() == 0) {
- LOG(ERROR) << "Error: threadIds.size() shouldn't be " << threadIds.size();
+ if (threadIds.empty()) {
+ ALOGE("Error: threadIds should not be empty");
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
- PowerSessionManager::getInstance()->removeThreadsFromPowerSession(this);
- mDescriptor->threadIds.resize(threadIds.size());
- std::copy(threadIds.begin(), threadIds.end(), back_inserter(mDescriptor->threadIds));
- PowerSessionManager::getInstance()->addThreadsFromPowerSession(this);
+ mPSManager->setThreadsFromPowerSession(mSessionId, threadIds);
// init boost
- setSessionUclampMin(HintManager::GetInstance()->GetAdpfProfile()->mUclampMinInit);
+ updatePidSetPoint(HintManager::GetInstance()->GetAdpfProfile()->mUclampMinInit);
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));
+ std::string out = StringPrintf("session %" PRId64 "\n", sessionId);
+ out.append(
+ StringPrintf(" duration: %" PRId64 " ns\n", static_cast<int64_t>(targetNs.count())));
+ out.append(StringPrintf(" uclamp.min: %d \n", pidSetPoint));
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;
}
@@ -422,105 +417,11 @@ bool PowerHintSession::isTimeout() {
time_point<steady_clock> staleTime =
mLastUpdatedTime.load() +
nanoseconds(static_cast<int64_t>(
- mDescriptor->duration.count() *
+ mDescriptor->targetNs.count() *
HintManager::GetInstance()->GetAdpfProfile()->mStaleTimeFactor));
return now >= staleTime;
}
-const std::vector<int32_t> &PowerHintSession::getTidList() const {
- return mDescriptor->threadIds;
-}
-
-bool PowerHintSession::disableTemporaryBoost() {
- if (ATRACE_ENABLED()) {
- if (mLastHintSent != -1) {
- mLastHintSent = -1;
- traceSessionVal("session_hint", -1);
- }
- }
-
- // replace temporary uclamp_min value with true min
- std::optional<int> trueMin = mNextUclampMin.load();
- if (trueMin.has_value()) {
- std::lock_guard<std::mutex> guard(mSessionLock);
- mDescriptor->current_min = *trueMin;
- mNextUclampMin.store(std::nullopt);
- return true;
- }
-
- return false;
-}
-
-void PowerHintSession::setStale() {
- // Make sure any temporary boost is disabled
- disableTemporaryBoost();
- // Reset to default uclamp value.
- PowerSessionManager::getInstance()->setUclampMin(this, 0);
- // Deliver a task to check if all sessions are inactive.
- updateUniveralBoostMode();
- if (ATRACE_ENABLED()) {
- traceSessionVal("min", 0);
- }
-}
-
-void PowerHintSession::SessionTimerHandler::updateTimer(nanoseconds delay) {
- mTimeout.store(steady_clock::now() + delay);
- {
- std::lock_guard<std::mutex> guard(mMessageLock);
- sp<MessageHandler> selfPtr = sp<MessageHandler>::fromExisting(this);
- PowerHintMonitor::getInstance()->getLooper()->removeMessages(selfPtr);
- PowerHintMonitor::getInstance()->getLooper()->sendMessageDelayed(delay.count(), selfPtr,
- NULL);
- }
- if (ATRACE_ENABLED()) {
- mSession->traceSessionVal(("timer." + mName).c_str(), 0);
- }
-}
-
-void PowerHintSession::SessionTimerHandler::handleMessage(const Message &) {
- std::lock_guard<std::mutex> guard(mClosedLock);
- if (mIsSessionDead) {
- return;
- }
- time_point now = steady_clock::now();
- int64_t next = (mTimeout.load() - now).count();
- if (next > 0) {
- // Schedule for the stale timeout check.
- std::lock_guard<std::mutex> guard(mMessageLock);
- sp<MessageHandler> selfPtr = sp<MessageHandler>::fromExisting(this);
- PowerHintMonitor::getInstance()->getLooper()->removeMessages(selfPtr);
- PowerHintMonitor::getInstance()->getLooper()->sendMessageDelayed(next, selfPtr, NULL);
- } else {
- onTimeout();
- }
- if (ATRACE_ENABLED()) {
- mSession->traceSessionVal(("timer." + mName).c_str(), next > 0 ? 0 : 1);
- }
-}
-
-void PowerHintSession::SessionTimerHandler::setSessionDead() {
- std::lock_guard<std::mutex> guard(mClosedLock);
- mIsSessionDead = true;
- PowerHintMonitor::getInstance()->getLooper()->removeMessages(
- sp<MessageHandler>::fromExisting(this));
-}
-
-void PowerHintSession::StaleTimerHandler::updateTimer() {
- SessionTimerHandler::updateTimer(duration_cast<nanoseconds>(
- mSession->mDescriptor->duration *
- HintManager::GetInstance()->GetAdpfProfile()->mStaleTimeFactor));
-}
-
-void PowerHintSession::StaleTimerHandler::onTimeout() {
- mSession->setStale();
-}
-
-void PowerHintSession::BoostTimerHandler::onTimeout() {
- if (mSession->disableTemporaryBoost()) {
- mSession->setSessionUclampMin(mSession->getUclampMin(), false);
- }
-}
-
} // namespace pixel
} // namespace impl
} // namespace power
diff --git a/power-libperfmgr/aidl/PowerHintSession.h b/power-libperfmgr/aidl/PowerHintSession.h
index e1c2523d..4c304ff2 100644
--- a/power-libperfmgr/aidl/PowerHintSession.h
+++ b/power-libperfmgr/aidl/PowerHintSession.h
@@ -18,13 +18,16 @@
#include <aidl/android/hardware/power/BnPowerHintSession.h>
#include <aidl/android/hardware/power/SessionHint.h>
+#include <aidl/android/hardware/power/SessionMode.h>
#include <aidl/android/hardware/power/WorkDuration.h>
#include <utils/Looper.h>
#include <utils/Thread.h>
-#include <mutex>
+#include <array>
#include <unordered_map>
+#include "AppDescriptorTrace.h"
+
namespace aidl {
namespace google {
namespace hardware {
@@ -34,6 +37,7 @@ namespace pixel {
using aidl::android::hardware::power::BnPowerHintSession;
using aidl::android::hardware::power::SessionHint;
+using aidl::android::hardware::power::SessionMode;
using aidl::android::hardware::power::WorkDuration;
using ::android::Message;
using ::android::MessageHandler;
@@ -43,23 +47,20 @@ using std::chrono::nanoseconds;
using std::chrono::steady_clock;
using std::chrono::time_point;
+class PowerSessionManager;
+
+// The App Hint Descriptor struct manages information necessary
+// to calculate the next uclamp min value from the PID function
+// and is separate so that it can be used as a pointer for
+// easily passing to the pid function
struct AppHintDesc {
- AppHintDesc(int32_t tgid, int32_t uid, std::vector<int32_t> 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) {}
+ AppHintDesc(int64_t sessionId, int32_t tgid, int32_t uid, std::chrono::nanoseconds pTargetNs);
std::string toString() const;
+ int64_t sessionId{0};
const int32_t tgid;
const int32_t uid;
- std::vector<int32_t> threadIds;
- nanoseconds duration;
- int current_min;
+ nanoseconds targetNs;
+ int pidSetPoint;
// status
std::atomic<bool> is_active;
// pid
@@ -68,6 +69,10 @@ struct AppHintDesc {
int64_t previous_error;
};
+// The Power Hint Session is responsible for providing an
+// interface for creating, updating, and closing power hints
+// for a Session. Each sesion that is mapped to multiple
+// threads (or task ids).
class PowerHintSession : public BnPowerHintSession {
public:
explicit PowerHintSession(int32_t tgid, int32_t uid, const std::vector<int32_t> &threadIds,
@@ -80,73 +85,33 @@ class PowerHintSession : public BnPowerHintSession {
ndk::ScopedAStatus reportActualWorkDuration(
const std::vector<WorkDuration> &actualDurations) override;
ndk::ScopedAStatus sendHint(SessionHint hint) override;
+ ndk::ScopedAStatus setMode(SessionMode mode, bool enabled) override;
ndk::ScopedAStatus setThreads(const std::vector<int32_t> &threadIds) override;
bool isActive();
bool isTimeout();
- void setStale();
- // Is this hint session for a user application
+ // Is hint session for a user application
bool isAppSession();
- const std::vector<int> &getTidList() const;
- int getUclampMin();
void dumpToStream(std::ostream &stream);
- // Disable any temporary boost and return to normal operation. It does not
- // reset the actual uclamp value, and relies on the caller to do so, to
- // prevent double-setting. Returns true if it actually disabled an active boost
- bool disableTemporaryBoost();
-
- private:
- class SessionTimerHandler : public MessageHandler {
- public:
- SessionTimerHandler(PowerHintSession *session, std::string name)
- : mSession(session), mIsSessionDead(false), mName(name) {}
- void updateTimer(nanoseconds delay);
- void handleMessage(const Message &message) override;
- void setSessionDead();
- virtual void onTimeout() = 0;
-
- protected:
- PowerHintSession *mSession;
- std::mutex mClosedLock;
- std::mutex mMessageLock;
- std::atomic<time_point<steady_clock>> mTimeout;
- bool mIsSessionDead;
- const std::string mName;
- };
-
- class StaleTimerHandler : public SessionTimerHandler {
- public:
- StaleTimerHandler(PowerHintSession *session) : SessionTimerHandler(session, "stale") {}
- void updateTimer();
- void onTimeout() override;
- };
-
- class BoostTimerHandler : public SessionTimerHandler {
- public:
- BoostTimerHandler(PowerHintSession *session) : SessionTimerHandler(session, "boost") {}
- void onTimeout() override;
- };
-
private:
- void updateUniveralBoostMode();
- int setSessionUclampMin(int32_t min, bool resetStale = true);
void tryToSendPowerHint(std::string hint);
+ void updatePidSetPoint(int pidSetPoint, bool updateVote = true);
int64_t convertWorkDurationToBoostByPid(const std::vector<WorkDuration> &actualDurations);
- void traceSessionVal(char const *identifier, int64_t val) const;
- AppHintDesc *mDescriptor = nullptr;
- sp<StaleTimerHandler> mStaleTimerHandler;
- sp<BoostTimerHandler> mBoostTimerHandler;
+ // Data
+ sp<PowerSessionManager> mPSManager;
+ int64_t mSessionId = 0;
+ std::string mIdString;
+ std::shared_ptr<AppHintDesc> mDescriptor;
+ // Trace strings
+ AppDescriptorTrace mAppDescriptorTrace;
std::atomic<time_point<steady_clock>> mLastUpdatedTime;
- sp<MessageHandler> mPowerManagerHandler;
- std::mutex mSessionLock;
std::atomic<bool> mSessionClosed = false;
- std::string mIdString;
- // Used when setting a temporary boost value to hold the true boost
- std::atomic<std::optional<int>> mNextUclampMin;
- // To cache the status of whether ADPF hints are supported.
+ // Are cpu load change related hints are supported
std::unordered_map<std::string, std::optional<bool>> mSupportedHints;
// Last session hint sent, used for logging
int mLastHintSent = -1;
+ // Use the value of the last enum in enum_range +1 as array size
+ std::array<bool, enum_size<SessionMode>()> mModes{};
};
} // namespace pixel
diff --git a/power-libperfmgr/aidl/PowerSessionManager.cpp b/power-libperfmgr/aidl/PowerSessionManager.cpp
index 2d2aad25..d8e77bd7 100644
--- a/power-libperfmgr/aidl/PowerSessionManager.cpp
+++ b/power-libperfmgr/aidl/PowerSessionManager.cpp
@@ -20,12 +20,16 @@
#include "PowerSessionManager.h"
#include <android-base/file.h>
+#include <android-base/stringprintf.h>
#include <log/log.h>
#include <perfmgr/HintManager.h>
+#include <private/android_filesystem_config.h>
#include <processgroup/processgroup.h>
#include <sys/syscall.h>
#include <utils/Trace.h>
+#include "AdpfTypes.h"
+
namespace aidl {
namespace google {
namespace hardware {
@@ -33,6 +37,7 @@ namespace power {
namespace impl {
namespace pixel {
+using ::android::base::StringPrintf;
using ::android::perfmgr::AdpfConfig;
using ::android::perfmgr::HintManager;
@@ -51,17 +56,10 @@ struct sched_attr {
__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 int set_uclamp_min(int tid, int min) {
+ static constexpr int32_t kMinUclampValue = 0;
static constexpr int32_t kMaxUclampValue = 1024;
- min = std::max(0, min);
+ min = std::max(kMinUclampValue, min);
min = std::min(min, kMaxUclampValue);
sched_attr attr = {};
@@ -70,19 +68,16 @@ static void set_uclamp_min(int tid, int min) {
attr.sched_flags = (SCHED_FLAG_KEEP_ALL | SCHED_FLAG_UTIL_CLAMP_MIN);
attr.sched_util_min = min;
- int ret = sched_setattr(tid, &attr, 0);
+ const int ret = syscall(__NR_sched_setattr, tid, attr, 0);
if (ret) {
- if (errno == ESRCH) {
- ALOGV("sched_setattr failed for thread %d, err=%d", tid, errno);
- } else {
- ALOGW("sched_setattr failed for thread %d, err=%d", tid, errno);
- }
+ ALOGW("sched_setattr failed for thread %d, err=%d", tid, errno);
+ return errno;
}
+ return 0;
}
} // 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;
@@ -107,89 +102,99 @@ int PowerSessionManager::getDisplayRefreshRate() {
return mDisplayRefreshRate;
}
-void PowerSessionManager::addPowerSession(PowerHintSession *session) {
- std::lock_guard<std::mutex> guard(mLock);
- mSessions.insert(session);
- addThreadsFromPowerSessionLocked(session);
-}
+void PowerSessionManager::addPowerSession(const std::string &idString,
+ const std::shared_ptr<AppHintDesc> &sessionDescriptor,
+ const std::vector<int32_t> &threadIds) {
+ if (!sessionDescriptor) {
+ ALOGE("sessionDescriptor is null. PowerSessionManager failed to add power session: %s",
+ idString.c_str());
+ return;
+ }
+ const auto timeNow = std::chrono::steady_clock::now();
+ VoteRange pidVoteRange(false, kUclampMin, kUclampMax, timeNow, sessionDescriptor->targetNs);
+
+ SessionValueEntry sve;
+ sve.tgid = sessionDescriptor->tgid;
+ sve.uid = sessionDescriptor->uid;
+ sve.idString = idString;
+ sve.isActive = sessionDescriptor->is_active;
+ sve.isAppSession = sessionDescriptor->uid >= AID_APP_START;
+ sve.lastUpdatedTime = timeNow;
+ sve.votes = std::make_shared<Votes>();
+ sve.votes->add(
+ static_cast<std::underlying_type_t<AdpfHintType>>(AdpfHintType::ADPF_VOTE_DEFAULT),
+ pidVoteRange);
+
+ bool addedRes = false;
+ {
+ std::lock_guard<std::mutex> lock(mSessionTaskMapMutex);
+ addedRes = mSessionTaskMap.add(sessionDescriptor->sessionId, sve, {});
+ }
+ if (!addedRes) {
+ ALOGE("sessionTaskMap failed to add power session: %" PRId64, sessionDescriptor->sessionId);
+ }
-void PowerSessionManager::removePowerSession(PowerHintSession *session) {
- std::lock_guard<std::mutex> guard(mLock);
- mSessions.erase(session);
- removeThreadsFromPowerSessionLocked(session);
+ setThreadsFromPowerSession(sessionDescriptor->sessionId, threadIds);
}
-void PowerSessionManager::addThreadsFromPowerSession(PowerHintSession *session) {
- std::lock_guard<std::mutex> guard(mLock);
- addThreadsFromPowerSessionLocked(session);
-}
+void PowerSessionManager::removePowerSession(int64_t sessionId) {
+ // To remove a session we also need to undo the effects the session
+ // has on currently enabled votes which means setting vote to inactive
+ // and then forceing a uclamp update to occur
+ forceSessionActive(sessionId, false);
+
+ std::vector<pid_t> addedThreads;
+ std::vector<pid_t> removedThreads;
+
+ {
+ // Wait till end to remove session because it needs to be around for apply U clamp
+ // to work above since applying the uclamp needs a valid session id
+ std::lock_guard<std::mutex> lock(mSessionTaskMapMutex);
+ mSessionTaskMap.replace(sessionId, {}, &addedThreads, &removedThreads);
+ mSessionTaskMap.remove(sessionId);
+ }
-void PowerSessionManager::addThreadsFromPowerSessionLocked(PowerHintSession *session) {
- for (auto t : session->getTidList()) {
- if (mTidSessionListMap[t].empty()) {
- if (!SetTaskProfiles(t, {"ResetUclampGrp"})) {
- ALOGW("Failed to set ResetUclampGrp task profile for tid:%d", t);
- }
+ for (auto tid : removedThreads) {
+ if (!SetTaskProfiles(tid, {"NoResetUclampGrp"})) {
+ ALOGE("Failed to set NoResetUclampGrp task profile for tid:%d", tid);
}
- mTidSessionListMap[t].insert(session);
}
}
-void PowerSessionManager::removeThreadsFromPowerSession(PowerHintSession *session) {
- std::lock_guard<std::mutex> guard(mLock);
- removeThreadsFromPowerSessionLocked(session);
-}
-
-void PowerSessionManager::removeThreadsFromPowerSessionLocked(PowerHintSession *session) {
- for (auto t : session->getTidList()) {
- size_t cnt = mTidSessionListMap[t].erase(session);
- if (cnt != 0 && mTidSessionListMap[t].empty()) {
- if (!SetTaskProfiles(t, {"NoResetUclampGrp"})) {
- ALOGW("Failed to set NoResetUclampGrp task profile for tid:%d", t);
- }
+void PowerSessionManager::setThreadsFromPowerSession(int64_t sessionId,
+ const std::vector<int32_t> &threadIds) {
+ std::vector<pid_t> addedThreads;
+ std::vector<pid_t> removedThreads;
+ forceSessionActive(sessionId, false);
+ {
+ std::lock_guard<std::mutex> lock(mSessionTaskMapMutex);
+ mSessionTaskMap.replace(sessionId, threadIds, &addedThreads, &removedThreads);
+ }
+ for (auto tid : addedThreads) {
+ if (!SetTaskProfiles(tid, {"ResetUclampGrp"})) {
+ ALOGE("Failed to set ResetUclampGrp task profile for tid:%d", tid);
}
}
-}
-
-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());
+ for (auto tid : removedThreads) {
+ if (!SetTaskProfiles(tid, {"NoResetUclampGrp"})) {
+ ALOGE("Failed to set NoResetUclampGrp task profile for tid:%d", tid);
}
- set_uclamp_min(t, std::max(val, tidMax));
}
+ forceSessionActive(sessionId, true);
}
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;
+ bool isAnyAppSessionActive = false;
+ {
+ std::lock_guard<std::mutex> lock(mSessionTaskMapMutex);
+ isAnyAppSessionActive =
+ mSessionTaskMap.isAnyAppSessionActive(std::chrono::steady_clock::now());
}
-
- return active;
+ return isAnyAppSessionActive;
}
-void PowerSessionManager::handleMessage(const Message &) {
- auto active = isAnyAppSessionActive();
+void PowerSessionManager::updateUniversalBoostMode() {
+ const auto active = isAnyAppSessionActive();
if (!active.has_value()) {
return;
}
@@ -202,26 +207,148 @@ void PowerSessionManager::handleMessage(const Message &) {
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";
- }
+ std::lock_guard<std::mutex> lock(mSessionTaskMapMutex);
+ mSessionTaskMap.forEachSessionValTasks(
+ [&](auto /* sessionId */, const auto &sessionVal, const auto &tasks) {
+ sessionVal.dump(dump_buf);
+ dump_buf << " Tid:Ref[";
+
+ size_t tasksLen = tasks.size();
+ for (auto taskId : tasks) {
+ dump_buf << taskId << ":";
+ const auto &sessionIds = mSessionTaskMap.getSessionIds(taskId);
+ if (!sessionIds.empty()) {
+ dump_buf << sessionIds.size();
+ }
+ if (tasksLen > 0) {
+ dump_buf << ", ";
+ --tasksLen;
+ }
+ }
+ 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::pause(int64_t sessionId) {
+ {
+ std::lock_guard<std::mutex> lock(mSessionTaskMapMutex);
+ auto sessValPtr = mSessionTaskMap.findSession(sessionId);
+ if (nullptr == sessValPtr) {
+ ALOGW("Pause failed, session is null %" PRId64, sessionId);
+ return;
+ }
+
+ if (!sessValPtr->isActive) {
+ ALOGW("Sess(%" PRId64 "), cannot pause, already inActive", sessionId);
+ return;
+ }
+ sessValPtr->isActive = false;
+ }
+ applyUclamp(sessionId, std::chrono::steady_clock::now());
+ updateUniversalBoostMode();
+}
+
+void PowerSessionManager::resume(int64_t sessionId) {
+ {
+ std::lock_guard<std::mutex> lock(mSessionTaskMapMutex);
+ auto sessValPtr = mSessionTaskMap.findSession(sessionId);
+ if (nullptr == sessValPtr) {
+ ALOGW("Resume failed, session is null %" PRId64, sessionId);
+ return;
+ }
+
+ if (sessValPtr->isActive) {
+ ALOGW("Sess(%" PRId64 "), cannot resume, already active", sessionId);
+ return;
+ }
+ sessValPtr->isActive = true;
+ }
+ applyUclamp(sessionId, std::chrono::steady_clock::now());
+ updateUniversalBoostMode();
+}
+
+void PowerSessionManager::updateTargetWorkDuration(int64_t sessionId, AdpfHintType voteId,
+ std::chrono::nanoseconds durationNs) {
+ int voteIdInt = static_cast<std::underlying_type_t<AdpfHintType>>(voteId);
+ std::lock_guard<std::mutex> lock(mSessionTaskMapMutex);
+ auto sessValPtr = mSessionTaskMap.findSession(sessionId);
+ if (nullptr == sessValPtr) {
+ ALOGE("Failed to updateTargetWorkDuration, session val is null id: %" PRId64, sessionId);
+ return;
+ }
+
+ sessValPtr->votes->updateDuration(voteIdInt, durationNs);
+ // Note, for now we are not recalculating and applying uclamp because
+ // that maintains behavior from before. In the future we may want to
+ // revisit that decision.
+}
+
+void PowerSessionManager::voteSet(int64_t sessionId, AdpfHintType voteId, int uclampMin,
+ int uclampMax, std::chrono::steady_clock::time_point startTime,
+ std::chrono::nanoseconds durationNs) {
+ const int voteIdInt = static_cast<std::underlying_type_t<AdpfHintType>>(voteId);
+ const auto timeoutDeadline = startTime + durationNs;
+ const VoteRange vr(true, uclampMin, uclampMax, startTime, durationNs);
+ bool scheduleTimeout = false;
+
+ {
+ std::lock_guard<std::mutex> lock(mSessionTaskMapMutex);
+ auto sessValPtr = mSessionTaskMap.findSession(sessionId);
+ if (nullptr == sessValPtr) {
+ // Because of the async nature of some events an event for a session
+ // that has been removed is a possibility so this is a verbose log
+ // instead of a warning or error
+ return;
+ }
+
+ if (!sessValPtr->votes->voteIsActive(voteIdInt)) {
+ scheduleTimeout = true;
+ }
+ if (timeoutDeadline < sessValPtr->votes->voteTimeout(voteIdInt)) {
+ scheduleTimeout = true;
+ }
+ sessValPtr->votes->add(voteIdInt, vr);
+ sessValPtr->lastUpdatedTime = startTime;
+ }
+
+ applyUclamp(sessionId, startTime); // std::chrono::steady_clock::now());
+
+ if (scheduleTimeout) {
+ // Sent event to handle stale-vote/timeout in the future
+ EventSessionTimeout eTimeout;
+ eTimeout.timeStamp = startTime; // eSet.timeStamp;
+ eTimeout.sessionId = sessionId;
+ eTimeout.voteId = voteIdInt;
+ mEventSessionTimeoutWorker.schedule(eTimeout, timeoutDeadline);
+ }
+}
+
+void PowerSessionManager::disableBoosts(int64_t sessionId) {
+ {
+ std::lock_guard<std::mutex> lock(mSessionTaskMapMutex);
+ auto sessValPtr = mSessionTaskMap.findSession(sessionId);
+ if (nullptr == sessValPtr) {
+ // Because of the async nature of some events an event for a session
+ // that has been removed is a possibility so this is a verbose log
+ // instead of a warning or error
+ return;
+ }
+
+ // sessValPtr->disableBoosts();
+ for (auto vid :
+ {AdpfHintType::ADPF_CPU_LOAD_UP, AdpfHintType::ADPF_CPU_LOAD_RESET,
+ AdpfHintType::ADPF_CPU_LOAD_RESUME, AdpfHintType::ADPF_VOTE_POWER_EFFICIENCY}) {
+ auto vint = static_cast<std::underlying_type_t<AdpfHintType>>(vid);
+ sessValPtr->votes->setUseVote(vint, false);
+ }
+ }
+}
+
void PowerSessionManager::enableSystemTopAppBoost() {
if (HintManager::GetInstance()->IsHintSupported(kDisableBoostHintName)) {
ALOGV("PowerSessionManager::enableSystemTopAppBoost!!");
@@ -236,22 +363,105 @@ void PowerSessionManager::disableSystemTopAppBoost() {
}
}
-// =========== PowerHintMonitor implementation start from here ===========
-void PowerHintMonitor::start() {
- if (!isRunning()) {
- run("PowerHintMonitor", ::android::PRIORITY_HIGHEST);
+void PowerSessionManager::handleEvent(const EventSessionTimeout &eventTimeout) {
+ bool recalcUclamp = false;
+ const auto tNow = std::chrono::steady_clock::now();
+ {
+ std::lock_guard<std::mutex> lock(mSessionTaskMapMutex);
+ auto sessValPtr = mSessionTaskMap.findSession(eventTimeout.sessionId);
+ if (nullptr == sessValPtr) {
+ // It is ok for session timeouts to fire after a session has been
+ // removed
+ return;
+ }
+
+ // To minimize the number of events pushed into the queue, we are using
+ // the following logic to make use of a single timeout event which will
+ // requeue itself if the timeout has been changed since it was added to
+ // the work queue. Requeue Logic:
+ // if vote active and vote timeout <= sched time
+ // then deactivate vote and recalc uclamp (near end of function)
+ // if vote active and vote timeout > sched time
+ // then requeue timeout event for new deadline (which is vote timeout)
+ const bool voteIsActive = sessValPtr->votes->voteIsActive(eventTimeout.voteId);
+ const auto voteTimeout = sessValPtr->votes->voteTimeout(eventTimeout.voteId);
+
+ if (voteIsActive) {
+ if (voteTimeout <= tNow) {
+ sessValPtr->votes->setUseVote(eventTimeout.voteId, false);
+ recalcUclamp = true;
+ } else {
+ // Can unlock sooner than we do
+ auto eventTimeout2 = eventTimeout;
+ mEventSessionTimeoutWorker.schedule(eventTimeout2, voteTimeout);
+ }
+ }
+ }
+
+ if (!recalcUclamp) {
+ return;
}
+
+ // It is important to use the correct time here, time now is more reasonable
+ // than trying to use the event's timestamp which will be slightly off given
+ // the background priority queue introduces latency
+ applyUclamp(eventTimeout.sessionId, tNow);
+ updateUniversalBoostMode();
}
-bool PowerHintMonitor::threadLoop() {
- while (true) {
- mLooper->pollOnce(-1);
+void PowerSessionManager::applyUclamp(int64_t sessionId,
+ std::chrono::steady_clock::time_point timePoint) {
+ const bool uclampMinOn = HintManager::GetInstance()->GetAdpfProfile()->mUclampMinOn;
+
+ {
+ std::lock_guard<std::mutex> lock(mSessionTaskMapMutex);
+ auto sessValPtr = mSessionTaskMap.findSession(sessionId);
+ if (nullptr == sessValPtr) {
+ return;
+ }
+
+ if (!uclampMinOn) {
+ ALOGV("PowerSessionManager::set_uclamp_min: skip");
+ } else {
+ auto &threadList = mSessionTaskMap.getTaskIds(sessionId);
+ auto tidIter = threadList.begin();
+ while (tidIter != threadList.end()) {
+ UclampRange uclampRange;
+ mSessionTaskMap.getTaskVoteRange(*tidIter, timePoint, &uclampRange.uclampMin,
+ &uclampRange.uclampMax);
+ int stat = set_uclamp_min(*tidIter, uclampRange.uclampMin);
+ if (stat == ESRCH) {
+ ALOGV("Removing dead thread %d from hint session %s.", *tidIter,
+ sessValPtr->idString.c_str());
+ if (mSessionTaskMap.removeDeadTaskSessionMap(sessionId, *tidIter)) {
+ ALOGV("Removed dead thread-session map.");
+ }
+ tidIter = threadList.erase(tidIter);
+ } else {
+ tidIter++;
+ }
+ }
+ }
+
+ sessValPtr->lastUpdatedTime = timePoint;
}
- return true;
}
-sp<Looper> PowerHintMonitor::getLooper() {
- return mLooper;
+void PowerSessionManager::forceSessionActive(int64_t sessionId, bool isActive) {
+ {
+ std::lock_guard<std::mutex> lock(mSessionTaskMapMutex);
+ auto sessValPtr = mSessionTaskMap.findSession(sessionId);
+ if (nullptr == sessValPtr) {
+ return;
+ }
+ sessValPtr->isActive = isActive;
+ }
+
+ // As currently written, call needs to occur synchronously so as to ensure
+ // that the SessionId remains valid and mapped to the proper threads/tasks
+ // which enables apply u clamp to work correctly
+ applyUclamp(sessionId, std::chrono::steady_clock::now());
+ updateUniversalBoostMode();
}
} // namespace pixel
diff --git a/power-libperfmgr/aidl/PowerSessionManager.h b/power-libperfmgr/aidl/PowerSessionManager.h
index 7c74039a..b55e7475 100644
--- a/power-libperfmgr/aidl/PowerSessionManager.h
+++ b/power-libperfmgr/aidl/PowerSessionManager.h
@@ -24,7 +24,9 @@
#include <optional>
#include <unordered_set>
+#include "BackgroundWorker.h"
#include "PowerHintSession.h"
+#include "SessionTaskMap.h"
namespace aidl {
namespace google {
@@ -35,30 +37,41 @@ 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 {
+class PowerSessionManager : public ::android::RefBase {
public:
- // current hint info
+ // Update the 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 addThreadsFromPowerSession(PowerHintSession *session);
- void addThreadsFromPowerSessionLocked(PowerHintSession *session);
- void removeThreadsFromPowerSession(PowerHintSession *session);
- void removeThreadsFromPowerSessionLocked(PowerHintSession *session);
- void setUclampMin(PowerHintSession *session, int min);
- void setUclampMinLocked(PowerHintSession *session, int min);
- void handleMessage(const Message &message) override;
+ // Add and remove power hint session
+ void addPowerSession(const std::string &idString,
+ const std::shared_ptr<AppHintDesc> &sessionDescriptor,
+ const std::vector<int32_t> &threadIds);
+ void removePowerSession(int64_t sessionId);
+ // Replace current threads in session with threadIds
+ void setThreadsFromPowerSession(int64_t sessionId, const std::vector<int32_t> &threadIds);
+ // Pause and resume power hint session
+ void pause(int64_t sessionId);
+ void resume(int64_t sessionId);
+
+ void updateUniversalBoostMode();
void dumpToFd(int fd);
+ void updateTargetWorkDuration(int64_t sessionId, AdpfHintType voteId,
+ std::chrono::nanoseconds durationNs);
+
+ // Set vote for power hint session
+ void voteSet(int64_t sessionId, AdpfHintType voteId, int uclampMin, int uclampMax,
+ std::chrono::steady_clock::time_point startTime,
+ std::chrono::nanoseconds durationNs);
+
+ void disableBoosts(int64_t sessionId);
+
// Singleton
static sp<PowerSessionManager> getInstance() {
static sp<PowerSessionManager> instance = new PowerSessionManager();
@@ -71,43 +84,38 @@ class PowerSessionManager : public MessageHandler {
void enableSystemTopAppBoost();
const std::string kDisableBoostHintName;
- std::unordered_set<PowerHintSession *> mSessions; // protected by mLock
- std::unordered_map<int, std::unordered_set<PowerHintSession *>> mTidSessionListMap;
- bool mActive; // protected by mLock
- /**
- * mLock to pretect the above data objects opertions.
- **/
- std::mutex mLock;
int mDisplayRefreshRate;
+
+ // Rewrite specific
+ mutable std::mutex mSessionTaskMapMutex;
+ SessionTaskMap mSessionTaskMap;
+ std::shared_ptr<PriorityQueueWorkerPool> mPriorityQueueWorkerPool;
+
+ // Session timeout
+ struct EventSessionTimeout {
+ std::chrono::steady_clock::time_point timeStamp;
+ int64_t sessionId{0};
+ int voteId{0};
+ };
+ void handleEvent(const EventSessionTimeout &e);
+ TemplatePriorityQueueWorker<EventSessionTimeout> mEventSessionTimeoutWorker;
+
+ // Calculate uclamp range
+ void applyUclamp(int64_t sessionId, std::chrono::steady_clock::time_point timePoint);
+ // Force a session active or in-active, helper for other methods
+ void forceSessionActive(int64_t sessionId, bool isActive);
+
// Singleton
PowerSessionManager()
: kDisableBoostHintName(::android::base::GetProperty(kPowerHalAdpfDisableTopAppBoost,
"ADPF_DISABLE_TA_BOOST")),
- mActive(false),
- mDisplayRefreshRate(60) {}
+ mDisplayRefreshRate(60),
+ mPriorityQueueWorkerPool(new PriorityQueueWorkerPool(1, "adpf_handler")),
+ mEventSessionTimeoutWorker([&](auto e) { handleEvent(e); }, mPriorityQueueWorkerPool) {}
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
diff --git a/power-libperfmgr/aidl/SessionTaskMap.cpp b/power-libperfmgr/aidl/SessionTaskMap.cpp
new file mode 100644
index 00000000..62dae33c
--- /dev/null
+++ b/power-libperfmgr/aidl/SessionTaskMap.cpp
@@ -0,0 +1,261 @@
+/*
+ * Copyright 2023 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 "SessionTaskMap.h"
+
+#include <algorithm>
+#include <sstream>
+
+namespace aidl {
+namespace google {
+namespace hardware {
+namespace power {
+namespace impl {
+namespace pixel {
+
+bool SessionTaskMap::add(int64_t sessionId, const SessionValueEntry &sv,
+ const std::vector<pid_t> &taskIds) {
+ if (mSessions.find(sessionId) != mSessions.end()) {
+ return false;
+ }
+
+ auto sessValPtr = std::make_shared<SessionValueEntry>();
+ (*sessValPtr) = sv;
+ sessValPtr->sessionId = sessionId;
+
+ auto &sessEntry = mSessions[sessionId];
+ sessEntry.val = sessValPtr;
+ sessEntry.linkedTasks = taskIds;
+
+ for (auto taskId : taskIds) {
+ mTasks[taskId].push_back(sessValPtr);
+ }
+ return true;
+}
+
+void SessionTaskMap::addVote(int64_t sessionId, int voteId, int uclampMin, int uclampMax,
+ std::chrono::steady_clock::time_point startTime,
+ std::chrono::nanoseconds durationNs) {
+ auto sessItr = mSessions.find(sessionId);
+ if (sessItr == mSessions.end()) {
+ return;
+ }
+
+ sessItr->second.val->votes->add(voteId,
+ VoteRange(true, uclampMin, uclampMax, startTime, durationNs));
+}
+
+std::shared_ptr<SessionValueEntry> SessionTaskMap::findSession(int64_t sessionId) {
+ auto sessItr = mSessions.find(sessionId);
+ if (sessItr == mSessions.end()) {
+ return nullptr;
+ }
+ return sessItr->second.val;
+}
+
+void SessionTaskMap::getTaskVoteRange(pid_t taskId, std::chrono::steady_clock::time_point timeNow,
+ int *uclampMin, int *uclampMax) const {
+ UclampRange uclampRange;
+ auto taskItr = mTasks.find(taskId);
+ if (taskItr == mTasks.end()) {
+ return;
+ }
+
+ for (const auto &sessInTask : taskItr->second) {
+ if (!sessInTask->isActive) {
+ continue;
+ }
+ sessInTask->votes->getUclampRange(&uclampRange, timeNow);
+ }
+ *uclampMin = uclampRange.uclampMin;
+ *uclampMax = uclampRange.uclampMax;
+}
+
+std::vector<int64_t> SessionTaskMap::getSessionIds(pid_t taskId) const {
+ auto itr = mTasks.find(taskId);
+ if (itr == mTasks.end()) {
+ static const std::vector<int64_t> emptySessionIdVec;
+ return emptySessionIdVec;
+ }
+ std::vector<int64_t> res;
+ res.reserve(itr->second.size());
+ for (const auto &i : itr->second) {
+ res.push_back(i->sessionId);
+ }
+ return res;
+}
+
+std::vector<pid_t> &SessionTaskMap::getTaskIds(int64_t sessionId) {
+ auto taskItr = mSessions.find(sessionId);
+ if (taskItr == mSessions.end()) {
+ static std::vector<pid_t> emptyTaskIdVec;
+ return emptyTaskIdVec;
+ }
+ return taskItr->second.linkedTasks;
+}
+
+bool SessionTaskMap::isAnyAppSessionActive(std::chrono::steady_clock::time_point timePoint) const {
+ for (auto &sessionVal : mSessions) {
+ if (!sessionVal.second.val->isAppSession) {
+ continue;
+ }
+ if (!sessionVal.second.val->isActive) {
+ continue;
+ }
+ if (!sessionVal.second.val->votes->allTimedOut(timePoint)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool SessionTaskMap::remove(int64_t sessionId) {
+ auto sessItr = mSessions.find(sessionId);
+ if (sessItr == mSessions.end()) {
+ return false;
+ }
+
+ // For each task id in linked tasks need to remove the corresponding
+ // task to session mapping in the task map
+ for (const auto taskId : sessItr->second.linkedTasks) {
+ // Used linked task ids to cleanup
+ auto taskItr = mTasks.find(taskId);
+ if (taskItr == mTasks.end()) {
+ // Inconsisent state
+ continue;
+ }
+
+ // Now lookup session id in task's set
+ auto taskSessItr =
+ std::find(taskItr->second.begin(), taskItr->second.end(), sessItr->second.val);
+ if (taskSessItr == taskItr->second.end()) {
+ // Should not happen
+ continue;
+ }
+
+ // Remove session id from task map
+ taskItr->second.erase(taskSessItr);
+ if (taskItr->second.empty()) {
+ mTasks.erase(taskItr);
+ }
+ }
+
+ // Now we can safely remove session entirely since there are no more
+ // mappings in task to session id
+ mSessions.erase(sessItr);
+ return true;
+}
+
+bool SessionTaskMap::removeDeadTaskSessionMap(int64_t sessionId, pid_t taskId) {
+ auto sessItr = mSessions.find(sessionId);
+ if (sessItr == mSessions.end()) {
+ return false;
+ }
+
+ auto taskItr = mTasks.find(taskId);
+ if (taskItr == mTasks.end()) {
+ // Inconsisent state
+ return false;
+ }
+
+ // Now lookup session id in task's set
+ auto taskSessItr =
+ std::find(taskItr->second.begin(), taskItr->second.end(), sessItr->second.val);
+ if (taskSessItr == taskItr->second.end()) {
+ // Should not happen
+ return false;
+ }
+
+ // Remove session id from task map
+ taskItr->second.erase(taskSessItr);
+ if (taskItr->second.empty()) {
+ mTasks.erase(taskItr);
+ }
+
+ return true;
+}
+
+bool SessionTaskMap::replace(int64_t sessionId, const std::vector<pid_t> &taskIds,
+ std::vector<pid_t> *addedThreads, std::vector<pid_t> *removedThreads) {
+ auto itr = mSessions.find(sessionId);
+ if (itr == mSessions.end()) {
+ return false;
+ }
+
+ // Make copies of val and threads
+ auto svTmp = itr->second.val;
+ const auto previousTaskIds = itr->second.linkedTasks;
+
+ // Determine newly added threads
+ if (addedThreads) {
+ for (auto tid : taskIds) {
+ auto taskSessItr = mTasks.find(tid);
+ if (taskSessItr == mTasks.end()) {
+ addedThreads->push_back(tid);
+ }
+ }
+ }
+
+ // Remove session from mappings
+ remove(sessionId);
+ // Add session value and task mappings
+ add(sessionId, *svTmp, taskIds);
+
+ // Determine completely removed threads
+ if (removedThreads) {
+ for (auto tid : previousTaskIds) {
+ auto taskSessItr = mTasks.find(tid);
+ if (taskSessItr == mTasks.end()) {
+ removedThreads->push_back(tid);
+ }
+ }
+ }
+
+ return true;
+}
+
+size_t SessionTaskMap::sizeSessions() const {
+ return mSessions.size();
+}
+
+size_t SessionTaskMap::sizeTasks() const {
+ return mTasks.size();
+}
+
+const std::string &SessionTaskMap::idString(int64_t sessionId) const {
+ auto sessItr = mSessions.find(sessionId);
+ if (sessItr == mSessions.end()) {
+ static const std::string emptyString;
+ return emptyString;
+ }
+ return sessItr->second.val->idString;
+}
+
+bool SessionTaskMap::isAppSession(int64_t sessionId) const {
+ auto sessItr = mSessions.find(sessionId);
+ if (sessItr == mSessions.end()) {
+ return false;
+ }
+
+ return sessItr->second.val->isAppSession;
+}
+
+} // namespace pixel
+} // namespace impl
+} // namespace power
+} // namespace hardware
+} // namespace google
+} // namespace aidl
diff --git a/power-libperfmgr/aidl/SessionTaskMap.h b/power-libperfmgr/aidl/SessionTaskMap.h
new file mode 100644
index 00000000..35e7b85a
--- /dev/null
+++ b/power-libperfmgr/aidl/SessionTaskMap.h
@@ -0,0 +1,131 @@
+/*
+ * Copyright 2023 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 <unordered_map>
+#include <vector>
+
+#include "SessionValueEntry.h"
+
+namespace aidl {
+namespace google {
+namespace hardware {
+namespace power {
+namespace impl {
+namespace pixel {
+
+/**
+ * Map session id to a value and link to many task ids
+ * Maintain consistency between mappings
+ * e.g.
+ * Sessions[sid1] -> SessionValueEntry1, [tid1, tid2]
+ * Tasks[tid1] -> [sid1]
+ * Tasks[tid2] -> [sid1]
+ * ...
+ * Sessions[sid2] -> SessionValueEntry2, [tid2, tid3]
+ * Tasks[tid1] -> [sid1]
+ * Tasks[tid2] -> [sid1, sid2]
+ * Tasks[tid3] -> [sid2]
+ */
+class SessionTaskMap {
+ public:
+ // Add a session with associated tasks to mapping
+ bool add(int64_t sessionId, const SessionValueEntry &sv, const std::vector<pid_t> &taskIds);
+
+ // Add a vote to a session
+ void addVote(int64_t sessionId, int voteId, int uclampMin, int uclampMax,
+ std::chrono::steady_clock::time_point startTime,
+ std::chrono::nanoseconds durationNs);
+
+ // Find session id and run callback on session value, linked tasks
+ std::shared_ptr<SessionValueEntry> findSession(int64_t sessionId);
+
+ void getTaskVoteRange(pid_t taskId, std::chrono::steady_clock::time_point timeNow,
+ int *uclampMin, int *uclampmax) const;
+
+ // Find session ids given a task id if it exists
+ std::vector<int64_t> getSessionIds(pid_t taskId) const;
+
+ // Get a vec of tasks associated with a session
+ std::vector<pid_t> &getTaskIds(int64_t sessionId);
+
+ // Return true if any app session is active, false otherwise
+ bool isAnyAppSessionActive(std::chrono::steady_clock::time_point timePoint) const;
+
+ // Remove a session based on session id
+ bool remove(int64_t sessionId);
+
+ // Maintain value of session, remove old task mapping add new
+ bool replace(int64_t sessionId, const std::vector<pid_t> &taskIds,
+ std::vector<pid_t> *addedThreads, std::vector<pid_t> *removedThreads);
+
+ size_t sizeSessions() const;
+
+ size_t sizeTasks() const;
+
+ // Given task id, for each linked-to session id call fn
+ template <typename FN>
+ void forEachSessionInTask(pid_t taskId, FN fn) const {
+ auto taskSessItr = mTasks.find(taskId);
+ if (taskSessItr == mTasks.end()) {
+ return;
+ }
+ for (const auto session : taskSessItr->second) {
+ auto sessionItr = mSessions.find(session->sessionId);
+ if (sessionItr == mSessions.end()) {
+ continue;
+ }
+ fn(sessionItr->first, *(sessionItr->second.val));
+ }
+ }
+
+ // Iterate over all entries in session map and run callback fn
+ // fn takes int64_t session id, session entry val, linked task ids
+ template <typename FN>
+ void forEachSessionValTasks(FN fn) const {
+ for (const auto &e : mSessions) {
+ fn(e.first, *(e.second.val), e.second.linkedTasks);
+ }
+ }
+
+ // Returns string id of session
+ const std::string &idString(int64_t sessionId) const;
+
+ // Returns true if session id is an app session id
+ bool isAppSession(int64_t sessionId) const;
+
+ // Remove dead task-session map entry
+ bool removeDeadTaskSessionMap(int64_t sessionId, pid_t taskId);
+
+ private:
+ // Internal struct to hold per-session data and linked tasks
+ struct ValEntry {
+ std::shared_ptr<SessionValueEntry> val;
+ std::vector<pid_t> linkedTasks;
+ };
+ // Map session id to value
+ std::unordered_map<int64_t, ValEntry> mSessions;
+ // Map task id to set of session ids
+ std::unordered_map<pid_t, std::vector<std::shared_ptr<SessionValueEntry>>> mTasks;
+};
+
+} // namespace pixel
+} // namespace impl
+} // namespace power
+} // namespace hardware
+} // namespace google
+} // namespace aidl
diff --git a/power-libperfmgr/aidl/SessionValueEntry.cpp b/power-libperfmgr/aidl/SessionValueEntry.cpp
new file mode 100644
index 00000000..b420ae8c
--- /dev/null
+++ b/power-libperfmgr/aidl/SessionValueEntry.cpp
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2023 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 "SessionValueEntry.h"
+
+#include <sstream>
+
+namespace aidl {
+namespace google {
+namespace hardware {
+namespace power {
+namespace impl {
+namespace pixel {
+
+std::ostream &SessionValueEntry::dump(std::ostream &os) const {
+ auto timeNow = std::chrono::steady_clock::now();
+ os << "ID.Min.Act(" << idString;
+ if (votes) {
+ UclampRange uclampRange;
+ votes->getUclampRange(&uclampRange, timeNow);
+ os << ", " << uclampRange.uclampMin;
+ os << "-" << uclampRange.uclampMax;
+ } else {
+ os << ", votes nullptr";
+ }
+ os << ", " << isActive;
+ return os;
+}
+
+} // namespace pixel
+} // namespace impl
+} // namespace power
+} // namespace hardware
+} // namespace google
+} // namespace aidl
diff --git a/power-libperfmgr/aidl/SessionValueEntry.h b/power-libperfmgr/aidl/SessionValueEntry.h
new file mode 100644
index 00000000..f43dba48
--- /dev/null
+++ b/power-libperfmgr/aidl/SessionValueEntry.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2023 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 "AdpfTypes.h"
+#include "UClampVoter.h"
+
+namespace aidl {
+namespace google {
+namespace hardware {
+namespace power {
+namespace impl {
+namespace pixel {
+
+// Per-power-session values (equivalent to original PowerHintSession)
+// Responsible for maintaining the state of the power session via attributes
+// Primarily this means actual uclamp value and whether session is active
+// (i.e. whether to include this power session uclmap when setting task uclamp)
+struct SessionValueEntry {
+ int64_t sessionId{0};
+ // Thread group id
+ int64_t tgid{0};
+ uid_t uid{0};
+ std::string idString;
+ bool isActive{true};
+ bool isAppSession{false};
+ std::chrono::steady_clock::time_point lastUpdatedTime;
+ std::shared_ptr<Votes> votes;
+
+ // Write info about power session to ostream for logging and debugging
+ std::ostream &dump(std::ostream &os) const;
+};
+
+} // namespace pixel
+} // namespace impl
+} // namespace power
+} // namespace hardware
+} // namespace google
+} // namespace aidl
diff --git a/power-libperfmgr/aidl/UClampVoter.cpp b/power-libperfmgr/aidl/UClampVoter.cpp
new file mode 100644
index 00000000..251f5410
--- /dev/null
+++ b/power-libperfmgr/aidl/UClampVoter.cpp
@@ -0,0 +1,130 @@
+/*
+ * Copyright 2023 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 "UClampVoter.h"
+
+namespace aidl {
+namespace google {
+namespace hardware {
+namespace power {
+namespace impl {
+namespace pixel {
+
+static void confine(UclampRange *uclampRange, const VoteRange &vr,
+ std::chrono::steady_clock::time_point t) {
+ if (!vr.isTimeInRange(t)) {
+ return;
+ }
+ uclampRange->uclampMin = std::max(uclampRange->uclampMin, vr.uclampMin());
+ uclampRange->uclampMax = std::min(uclampRange->uclampMax, vr.uclampMax());
+}
+
+VoteRange VoteRange::makeMinRange(int uclampMin, std::chrono::steady_clock::time_point startTime,
+ std::chrono::nanoseconds durationNs) {
+ VoteRange v(true, uclampMin, kUclampMax, startTime, durationNs);
+ return v;
+}
+
+std::ostream &operator<<(std::ostream &o, const VoteRange &vr) {
+ o << "[" << vr.uclampMin() << "," << vr.uclampMax() << "]";
+ return o;
+}
+
+Votes::Votes() {}
+
+void Votes::add(int voteId, const VoteRange &v) {
+ mVotes[voteId] = v;
+}
+
+void Votes::updateDuration(int voteId, std::chrono::nanoseconds durationNs) {
+ auto voteItr = mVotes.find(voteId);
+ if (voteItr != mVotes.end()) {
+ voteItr->second.updateDuration(durationNs);
+ }
+}
+
+void Votes::getUclampRange(UclampRange *uclampRange,
+ std::chrono::steady_clock::time_point t) const {
+ if (nullptr == uclampRange) {
+ return;
+ }
+ for (const auto &v : mVotes) {
+ confine(uclampRange, v.second, t);
+ }
+}
+
+bool Votes::anyTimedOut(std::chrono::steady_clock::time_point t) const {
+ for (const auto &v : mVotes) {
+ if (!v.second.isTimeInRange(t)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool Votes::allTimedOut(std::chrono::steady_clock::time_point t) const {
+ for (const auto &v : mVotes) {
+ if (v.second.isTimeInRange(t)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool Votes::remove(int voteId) {
+ auto itr = mVotes.find(voteId);
+ if (itr == mVotes.end()) {
+ return false;
+ }
+ mVotes.erase(itr);
+ return true;
+}
+
+bool Votes::setUseVote(int voteId, bool active) {
+ auto itr = mVotes.find(voteId);
+ if (itr == mVotes.end()) {
+ return false;
+ }
+ itr->second.setActive(active);
+ return true;
+}
+
+size_t Votes::size() const {
+ return mVotes.size();
+}
+
+bool Votes::voteIsActive(int voteId) {
+ auto itr = mVotes.find(voteId);
+ if (itr == mVotes.end()) {
+ return false;
+ }
+ return itr->second.active();
+}
+
+std::chrono::steady_clock::time_point Votes::voteTimeout(int voteId) {
+ auto itr = mVotes.find(voteId);
+ if (itr == mVotes.end()) {
+ return std::chrono::steady_clock::time_point{};
+ }
+ return itr->second.startTime() + itr->second.durationNs();
+}
+
+} // namespace pixel
+} // namespace impl
+} // namespace power
+} // namespace hardware
+} // namespace google
+} // namespace aidl
diff --git a/power-libperfmgr/aidl/UClampVoter.h b/power-libperfmgr/aidl/UClampVoter.h
new file mode 100644
index 00000000..4b9df33a
--- /dev/null
+++ b/power-libperfmgr/aidl/UClampVoter.h
@@ -0,0 +1,141 @@
+/*
+ * Copyright 2023 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 <memory>
+#include <ostream>
+#include <unordered_map>
+
+#include "AdpfTypes.h"
+
+namespace aidl {
+namespace google {
+namespace hardware {
+namespace power {
+namespace impl {
+namespace pixel {
+
+// --------------------------------------------------------
+// Hold a min and max for acceptable uclamp values
+struct UclampRange {
+ int uclampMin{kUclampMin};
+ int uclampMax{kUclampMax};
+};
+
+// --------------------------------------------------------
+// Hold a min max range of acceptable votes, is active status, time duration
+// info, and helper methods for consistent use
+class VoteRange {
+ public:
+ VoteRange() {}
+
+ VoteRange(bool active, int uclampMin, int uclampMax,
+ std::chrono::steady_clock::time_point startTime, std::chrono::nanoseconds durationNs)
+ : mActive(active),
+ mUclampRange({std::min(uclampMin, uclampMax), std::max(uclampMin, uclampMax)}),
+ mStartTime(startTime),
+ mDurationNs(durationNs) {}
+
+ // Returns true if this vote range is active, false if it is not active
+ bool active() const { return mActive; }
+
+ // Returns the utilization clamp minimum
+ int uclampMin() const { return mUclampRange.uclampMin; }
+
+ // Returns the utilization clamp maximum
+ int uclampMax() const { return mUclampRange.uclampMax; }
+
+ // Returns the start time of this vote range
+ std::chrono::steady_clock::time_point startTime() const { return mStartTime; }
+
+ // Returns the duration in nanoseconds of the vote range
+ std::chrono::nanoseconds durationNs() const { return mDurationNs; }
+
+ // Set the is active flag to bool param
+ void setActive(bool active) { mActive = active; }
+
+ // Update the vote duration
+ void updateDuration(std::chrono::nanoseconds durationNs) { mDurationNs = durationNs; }
+
+ // Return true if time point parameter in range of startTime to startTime+duration
+ inline bool isTimeInRange(std::chrono::steady_clock::time_point t) const {
+ return mActive && ((mStartTime <= t) && ((mStartTime + mDurationNs) >= t));
+ }
+
+ // Factory method to make a vote range
+ static VoteRange makeMinRange(int uclampMin, std::chrono::steady_clock::time_point startTime,
+ std::chrono::nanoseconds durationNs);
+
+ private:
+ bool mActive{true};
+ UclampRange mUclampRange;
+ std::chrono::steady_clock::time_point mStartTime{};
+ std::chrono::nanoseconds mDurationNs{};
+};
+
+// Helper for logging
+std::ostream &operator<<(std::ostream &o, const VoteRange &vr);
+
+// --------------------------------------------------------
+// Thread safe collection of votes that can be used to get
+// a clamped range
+class Votes {
+ public:
+ Votes();
+
+ // Add a vote and associate with vote id, overwrites existing vote
+ void add(int voteId, const VoteRange &v);
+
+ // Update the duration of a vote given a vote id
+ void updateDuration(int voteId, std::chrono::nanoseconds durationNs);
+
+ // Given input UclampRange, and a time point now, increase the min and
+ // decrease max if this VoteRange is in range, return UclampRange with
+ // the largest min and the smallest max
+ void getUclampRange(UclampRange *uclampRange, std::chrono::steady_clock::time_point t) const;
+
+ // Return true if any vote has timed out, otherwise return false
+ bool anyTimedOut(std::chrono::steady_clock::time_point t) const;
+
+ // Return true if all votes have timed out, otherwise return false
+ bool allTimedOut(std::chrono::steady_clock::time_point t) const;
+
+ // Remove vote based on vote vote id, return true if remove was successful,
+ // false if remove failed for example no vote with that id exists
+ bool remove(int voteId);
+
+ // Turn on/off vote
+ bool setUseVote(int voteId, bool active);
+
+ // Return number of votes
+ size_t size() const;
+
+ bool voteIsActive(int voteId);
+
+ std::chrono::steady_clock::time_point voteTimeout(int voteId);
+
+ private:
+ std::unordered_map<int, VoteRange> mVotes;
+};
+
+} // namespace pixel
+} // namespace impl
+} // namespace power
+} // namespace hardware
+} // namespace google
+} // namespace aidl
diff --git a/power-libperfmgr/aidl/android.hardware.power-service.pixel-libperfmgr.rc b/power-libperfmgr/aidl/android.hardware.power-service.pixel-libperfmgr.rc
index 5dc9c9e9..b6e7b83e 100644
--- a/power-libperfmgr/aidl/android.hardware.power-service.pixel-libperfmgr.rc
+++ b/power-libperfmgr/aidl/android.hardware.power-service.pixel-libperfmgr.rc
@@ -7,6 +7,10 @@ service vendor.power-hal-aidl /vendor/bin/hw/android.hardware.power-service.pixe
on late-fs
start vendor.power-hal-aidl
+# Unblock thermalHAL under off mode charge
+on charger
+ start vendor.power-hal-aidl
+
# Restart powerHAL when framework died
on property:init.svc.zygote=restarting && property:vendor.powerhal.state=*
setprop vendor.powerhal.state ""
diff --git a/power-libperfmgr/aidl/android.hardware.power-service.pixel.xml b/power-libperfmgr/aidl/android.hardware.power-service.pixel.xml
index f5dd6b95..418fb83d 100644
--- a/power-libperfmgr/aidl/android.hardware.power-service.pixel.xml
+++ b/power-libperfmgr/aidl/android.hardware.power-service.pixel.xml
@@ -1,7 +1,7 @@
<manifest version="1.0" type="device">
<hal format="aidl">
<name>android.hardware.power</name>
- <version>4</version>
+ <version>5</version>
<fqname>IPower/default</fqname>
</hal>
</manifest>
diff --git a/power-libperfmgr/aidl/service.cpp b/power-libperfmgr/aidl/service.cpp
index c3e2dd1d..3d8d6a7c 100644
--- a/power-libperfmgr/aidl/service.cpp
+++ b/power-libperfmgr/aidl/service.cpp
@@ -33,13 +33,13 @@
using aidl::google::hardware::power::impl::pixel::DisplayLowPower;
using aidl::google::hardware::power::impl::pixel::Power;
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;
constexpr std::string_view kPowerHalInitProp("vendor.powerhal.init");
int main() {
+ android::base::SetDefaultTag(LOG_TAG);
// Parse config but do not start the looper
std::shared_ptr<HintManager> hm = HintManager::GetInstance();
if (!hm) {
@@ -69,10 +69,6 @@ int main() {
CHECK(status == STATUS_OK);
LOG(INFO) << "Pixel Power HAL AIDL Service with Extension is started.";
- if (HintManager::GetInstance()->GetAdpfProfile()) {
- PowerHintMonitor::getInstance()->start();
- }
-
std::thread initThread([&]() {
::android::base::WaitForProperty(kPowerHalInitProp.data(), "1");
HintManager::GetInstance()->Start();
diff --git a/power-libperfmgr/aidl/tests/BackgroundWorkerTest.cpp b/power-libperfmgr/aidl/tests/BackgroundWorkerTest.cpp
new file mode 100644
index 00000000..14caea8b
--- /dev/null
+++ b/power-libperfmgr/aidl/tests/BackgroundWorkerTest.cpp
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2023 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 "aidl/BackgroundWorker.h"
+
+namespace aidl {
+namespace google {
+namespace hardware {
+namespace power {
+namespace impl {
+namespace pixel {
+
+using std::literals::chrono_literals::operator""s;
+using std::literals::chrono_literals::operator""ms;
+using std::literals::chrono_literals::operator""ns;
+
+constexpr double kTIMING_TOLERANCE_MS = std::chrono::milliseconds(25).count();
+
+// Use this work to package some work identifier val along with time t of when it was
+// processed to validate that the waiting time is adhered to as closely as possible
+struct work {
+ int64_t val{0};
+ std::chrono::steady_clock::time_point t;
+};
+
+auto getDurationMs(std::chrono::steady_clock::time_point endTime,
+ std::chrono::steady_clock::time_point startTime) {
+ return std::chrono::duration<double, std::milli>(endTime - startTime);
+}
+
+TEST(PriorityQueueWorkerPool, testSingle) {
+ const int pqId = 1;
+ std::condition_variable cv;
+ std::mutex m;
+ std::vector<work> vec;
+ vec.reserve(3);
+
+ auto p = std::make_shared<PriorityQueueWorkerPool>(1, "adpf_");
+ p->addCallback(pqId, [&](int64_t packageId) {
+ std::lock_guard<std::mutex> lock(m);
+ vec.push_back({packageId, std::chrono::steady_clock::now()});
+ cv.notify_all();
+ });
+
+ const auto tNow = std::chrono::steady_clock::now();
+ p->schedule(pqId, 500, tNow + 500ms);
+ p->schedule(pqId, 100, tNow + 100ms);
+ p->schedule(pqId, 300, tNow + 300ms);
+
+ std::unique_lock<std::mutex> lock(m);
+ EXPECT_EQ(0, vec.size());
+ cv.wait_for(lock, 1500ms, [&]() { return vec.size() == 3; });
+
+ EXPECT_EQ(3, vec.size());
+ EXPECT_EQ(100, vec[0].val);
+ EXPECT_NEAR(100, getDurationMs(vec[0].t, tNow).count(), kTIMING_TOLERANCE_MS);
+ EXPECT_EQ(300, vec[1].val);
+ EXPECT_NEAR(300, getDurationMs(vec[1].t, tNow).count(), kTIMING_TOLERANCE_MS);
+ EXPECT_EQ(500, vec[2].val);
+ EXPECT_NEAR(500, getDurationMs(vec[2].t, tNow).count(), kTIMING_TOLERANCE_MS);
+}
+
+TEST(TemplatePriorityQueueWorker, testSingle) {
+ std::condition_variable cv;
+ std::mutex m;
+ std::vector<work> vec;
+ vec.reserve(3);
+
+ auto p = std::make_shared<PriorityQueueWorkerPool>(1, "adpf_");
+ TemplatePriorityQueueWorker<int> worker{
+ [&](int i) {
+ std::lock_guard<std::mutex> lock(m);
+ vec.push_back({i, std::chrono::steady_clock::now()});
+ cv.notify_all();
+ },
+ p};
+
+ // Would be nice to have a pause option for testing
+ const auto tNow = std::chrono::steady_clock::now();
+ worker.schedule(303, tNow + 500ms);
+ worker.schedule(101, tNow + 100ms);
+ worker.schedule(202, tNow + 300ms);
+
+ std::unique_lock<std::mutex> lock(m);
+ EXPECT_EQ(0, vec.size());
+ cv.wait_for(lock, 1500ms, [&]() { return vec.size() == 3; });
+
+ EXPECT_EQ(3, vec.size());
+ EXPECT_EQ(101, vec[0].val);
+ EXPECT_NEAR(100, getDurationMs(vec[0].t, tNow).count(), kTIMING_TOLERANCE_MS);
+ EXPECT_EQ(202, vec[1].val);
+ EXPECT_NEAR(300, getDurationMs(vec[1].t, tNow).count(), kTIMING_TOLERANCE_MS);
+ EXPECT_EQ(303, vec[2].val);
+ EXPECT_NEAR(500, getDurationMs(vec[2].t, tNow).count(), kTIMING_TOLERANCE_MS);
+}
+
+TEST(TemplatePriorityQueueWorker, testDouble) {
+ std::condition_variable cv;
+ std::mutex m;
+ std::vector<work> vec;
+ vec.reserve(6);
+
+ auto p = std::make_shared<PriorityQueueWorkerPool>(1, "adpf_");
+ TemplatePriorityQueueWorker<int> worker1{
+ [&](int i) {
+ std::lock_guard<std::mutex> lock(m);
+ vec.push_back({i, std::chrono::steady_clock::now()});
+ cv.notify_all();
+ },
+ p};
+
+ TemplatePriorityQueueWorker<std::string> worker2{
+ [&](const std::string &s) {
+ std::lock_guard<std::mutex> lock(m);
+ vec.push_back({atoi(s.c_str()), std::chrono::steady_clock::now()});
+ cv.notify_all();
+ },
+ p};
+
+ // Would be nice to have a pause option for testing
+ const auto tNow = std::chrono::steady_clock::now();
+ worker1.schedule(5, tNow + 300ms);
+ worker1.schedule(1, tNow + 100ms);
+ worker1.schedule(3, tNow + 200ms);
+ worker2.schedule("2", tNow + 150ms);
+ worker2.schedule("4", tNow + 250ms);
+ worker2.schedule("6", tNow + 350ms);
+
+ std::unique_lock<std::mutex> lock(m);
+ EXPECT_EQ(0, vec.size());
+ cv.wait_for(lock, 1500ms, [&]() { return vec.size() == 6; });
+
+ EXPECT_EQ(6, vec.size());
+ EXPECT_EQ(1, vec[0].val);
+ EXPECT_NEAR(100, getDurationMs(vec[0].t, tNow).count(), kTIMING_TOLERANCE_MS);
+ EXPECT_EQ(2, vec[1].val);
+ EXPECT_NEAR(150, getDurationMs(vec[1].t, tNow).count(), kTIMING_TOLERANCE_MS);
+ EXPECT_EQ(3, vec[2].val);
+ EXPECT_NEAR(200, getDurationMs(vec[2].t, tNow).count(), kTIMING_TOLERANCE_MS);
+ EXPECT_EQ(4, vec[3].val);
+ EXPECT_NEAR(250, getDurationMs(vec[3].t, tNow).count(), kTIMING_TOLERANCE_MS);
+ EXPECT_EQ(5, vec[4].val);
+ EXPECT_NEAR(300, getDurationMs(vec[4].t, tNow).count(), kTIMING_TOLERANCE_MS);
+ EXPECT_EQ(6, vec[5].val);
+ EXPECT_NEAR(350, getDurationMs(vec[5].t, tNow).count(), kTIMING_TOLERANCE_MS);
+}
+
+} // namespace pixel
+} // namespace impl
+} // namespace power
+} // namespace hardware
+} // namespace google
+} // namespace aidl
diff --git a/power-libperfmgr/aidl/tests/PowerHintSessionTest.cpp b/power-libperfmgr/aidl/tests/PowerHintSessionTest.cpp
new file mode 100644
index 00000000..110889f6
--- /dev/null
+++ b/power-libperfmgr/aidl/tests/PowerHintSessionTest.cpp
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2023 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 <sys/syscall.h>
+
+#include <chrono>
+#include <mutex>
+#include <thread>
+#include <unordered_map>
+#include <vector>
+
+// define private as public to expose the private members for test.
+#define private public
+#include "aidl/PowerHintSession.h"
+#include "aidl/PowerSessionManager.h"
+
+#define gettid() syscall(SYS_gettid)
+
+using std::literals::chrono_literals::operator""ms;
+using std::literals::chrono_literals::operator""ns;
+using std::literals::chrono_literals::operator""s;
+
+namespace aidl {
+namespace google {
+namespace hardware {
+namespace power {
+namespace impl {
+namespace pixel {
+
+class PowerHintSessionTest : public ::testing::Test {
+ public:
+ void SetUp() {
+ // create a list of threads
+ for (int i = 0; i < numOfThreads; i++) {
+ threadIsAlive.emplace_back(true);
+ threadList.emplace_back(std::thread([this, threadInd = i]() {
+ ALOGI("Test thread %d is running.", (int32_t)gettid());
+ {
+ std::lock_guard<std::mutex> lock(m);
+ threadIds[threadInd] = gettid();
+ }
+ while (threadIsAlive[threadInd]) {
+ std::this_thread::sleep_for(50ms);
+ }
+ ALOGI("Test thread %d is closed.", (int32_t)gettid());
+ }));
+ }
+ std::this_thread::sleep_for(50ms);
+
+ // create two hint sessions
+ for (int i = 0; i < numOfThreads; i++) {
+ if (i <= numOfThreads / 2) {
+ session1Threads.emplace_back(threadIds[i]);
+ }
+
+ if (i >= numOfThreads / 2) {
+ session2Threads.emplace_back(threadIds[i]);
+ }
+ }
+
+ sess1 = ndk::SharedRefBase::make<PowerHintSession>(1, 1, session1Threads, 1000000);
+ sess2 = ndk::SharedRefBase::make<PowerHintSession>(2, 2, session2Threads, 1000000);
+ }
+
+ void TearDown() {
+ for (int i = 0; i < numOfThreads; i++) {
+ if (threadIsAlive[i]) {
+ threadIsAlive[i] = false;
+ threadList[i].join();
+ }
+ }
+ threadList.clear();
+ threadIds.clear();
+ threadIsAlive.clear();
+ session1Threads.clear();
+ session2Threads.clear();
+ }
+
+ protected:
+ static const int numOfThreads = 3;
+ std::vector<std::thread> threadList;
+ std::unordered_map<int, int32_t> threadIds;
+ std::vector<bool> threadIsAlive;
+ std::mutex m;
+ std::vector<int32_t> session1Threads;
+ std::vector<int32_t> session2Threads;
+ std::shared_ptr<PowerHintSession> sess1;
+ std::shared_ptr<PowerHintSession> sess2;
+
+ // close the i-th thread in thread list.
+ void closeThread(int i) {
+ if (i < 0 || i >= numOfThreads)
+ return;
+ if (threadIsAlive[i]) {
+ threadIsAlive[i] = false;
+ threadList[i].join();
+ }
+ }
+};
+
+TEST_F(PowerHintSessionTest, removeDeadThread) {
+ ALOGI("Running dead thread test for hint sessions.");
+ auto sessManager = sess1->mPSManager;
+ ASSERT_EQ(2, sessManager->mSessionTaskMap.mSessions.size());
+
+ // The sessions' thread list doesn't change after thread died until the uclamp
+ // min update is triggered.
+ int deadThreadInd = numOfThreads / 2;
+ auto deadThreadID = threadIds[deadThreadInd];
+ closeThread(deadThreadInd);
+ ASSERT_EQ(sessManager->mSessionTaskMap.mSessions[sess1->mSessionId].linkedTasks,
+ session1Threads);
+ ASSERT_EQ(sessManager->mSessionTaskMap.mSessions[sess2->mSessionId].linkedTasks,
+ session2Threads);
+ ASSERT_EQ(sessManager->mSessionTaskMap.mTasks[deadThreadID].size(), 2);
+
+ // Trigger an update of uclamp min.
+ auto tNow = std::chrono::duration_cast<std::chrono::nanoseconds>(
+ std::chrono::high_resolution_clock::now().time_since_epoch())
+ .count();
+ WorkDuration wDur(tNow, 1100000);
+ sess1->reportActualWorkDuration(std::vector<WorkDuration>{wDur});
+ ASSERT_EQ(sessManager->mSessionTaskMap.mTasks[deadThreadID].size(), 1);
+ sess2->reportActualWorkDuration(std::vector<WorkDuration>{wDur});
+ ASSERT_EQ(sessManager->mSessionTaskMap.mTasks.count(deadThreadID), 0);
+ std::erase(session1Threads, deadThreadID);
+ std::erase(session2Threads, deadThreadID);
+ ASSERT_EQ(sessManager->mSessionTaskMap.mSessions[sess1->mSessionId].linkedTasks,
+ session1Threads);
+ ASSERT_EQ(sessManager->mSessionTaskMap.mSessions[sess2->mSessionId].linkedTasks,
+ session2Threads);
+
+ // Close all the threads in session 1.
+ for (int i = 0; i <= numOfThreads / 2; i++) {
+ closeThread(i);
+ }
+ tNow = std::chrono::duration_cast<std::chrono::nanoseconds>(
+ std::chrono::high_resolution_clock::now().time_since_epoch())
+ .count();
+ sess1->reportActualWorkDuration(std::vector<WorkDuration>{wDur});
+ ASSERT_EQ(2, sessManager->mSessionTaskMap.mSessions.size()); // Session still alive
+ ASSERT_EQ(sessManager->mSessionTaskMap.mSessions[sess1->mSessionId].linkedTasks.size(), 0);
+}
+
+TEST_F(PowerHintSessionTest, setThreads) {
+ auto sessManager = sess1->mPSManager;
+ ASSERT_EQ(2, sessManager->mSessionTaskMap.mSessions.size());
+
+ ASSERT_EQ(sessManager->mSessionTaskMap.mSessions[sess1->mSessionId].linkedTasks,
+ session1Threads);
+
+ std::vector<int32_t> newSess1Threads;
+ for (auto tid : threadIds) {
+ newSess1Threads.emplace_back(tid.second);
+ }
+ sess1->setThreads(newSess1Threads);
+ ASSERT_EQ(sessManager->mSessionTaskMap.mSessions[sess1->mSessionId].linkedTasks,
+ newSess1Threads);
+
+ sess1->close();
+ sess2->close();
+}
+
+} // namespace pixel
+} // namespace impl
+} // namespace power
+} // namespace hardware
+} // namespace google
+} // namespace aidl
diff --git a/power-libperfmgr/aidl/tests/SessionTaskMapTest.cpp b/power-libperfmgr/aidl/tests/SessionTaskMapTest.cpp
new file mode 100644
index 00000000..7f7dab3f
--- /dev/null
+++ b/power-libperfmgr/aidl/tests/SessionTaskMapTest.cpp
@@ -0,0 +1,336 @@
+/*
+ * Copyright (C) 2023 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 "aidl/SessionTaskMap.h"
+
+using std::literals::chrono_literals::operator""ms;
+using std::literals::chrono_literals::operator""ns;
+
+namespace aidl {
+namespace google {
+namespace hardware {
+namespace power {
+namespace impl {
+namespace pixel {
+
+SessionValueEntry makeSession(int tg) {
+ SessionValueEntry sv;
+ sv.tgid = tg;
+ sv.uid = tg + 1;
+ sv.idString = "Sess" + std::to_string(tg);
+ sv.isActive = true;
+ sv.isAppSession = false;
+ sv.lastUpdatedTime = std::chrono::steady_clock::now();
+ sv.votes = std::make_shared<Votes>();
+ return sv;
+}
+
+// Get all sessions associated with taskId
+std::vector<int64_t> getSessions(int taskId, const SessionTaskMap &m) {
+ std::vector<int64_t> sessionIds;
+ m.forEachSessionInTask(
+ taskId, [&](int sessionId, const auto & /*sve*/) { sessionIds.push_back(sessionId); });
+ std::sort(sessionIds.begin(), sessionIds.end());
+ return sessionIds;
+}
+
+// Get all tasks associated with sessionId
+std::vector<int> getTasks(int64_t sessionId, const SessionTaskMap &m) {
+ std::vector<int> tasks;
+ m.forEachSessionValTasks([&](int64_t sessId, const auto & /*sve*/, const auto &linkedTasks) {
+ if (sessId != sessionId)
+ return;
+ tasks.insert(std::end(tasks), std::begin(linkedTasks), std::end(linkedTasks));
+ });
+ std::sort(tasks.begin(), tasks.end());
+ return tasks;
+}
+
+// Tests ...
+TEST(SessionTaskMapTest, add) {
+ SessionTaskMap m;
+ EXPECT_TRUE(m.add(1, makeSession(1000), {10, 20, 30}));
+ EXPECT_TRUE(m.add(2, makeSession(2000), {40, 50}));
+ EXPECT_TRUE(m.add(3, makeSession(2000), {60}));
+ EXPECT_FALSE(m.add(3, makeSession(2000), {70}));
+}
+
+TEST(SessionTaskMapTest, threeWayMappingSessions) {
+ SessionTaskMap m;
+ m.add(1, makeSession(1000), {10, 20, 30});
+ m.add(2, makeSession(2000), {40, 50, 60});
+ m.add(3, makeSession(3000), {50});
+
+ // Check three tasks map properly to sessions
+ EXPECT_EQ(std::vector<int64_t>({1}), getSessions(10, m));
+ EXPECT_EQ(std::vector<int64_t>({1}), getSessions(20, m));
+ EXPECT_EQ(std::vector<int64_t>({1}), getSessions(30, m));
+ EXPECT_EQ(std::vector<int64_t>({2}), getSessions(40, m));
+ EXPECT_EQ(std::vector<int64_t>({2, 3}), getSessions(50, m));
+ EXPECT_EQ(std::vector<int64_t>({2}), getSessions(60, m));
+}
+
+TEST(SessionTaskMapTest, threeWayMappingTasks) {
+ SessionTaskMap m;
+ m.add(1, makeSession(1000), {10, 20, 30});
+ m.add(2, makeSession(2000), {40, 50, 60});
+ m.add(3, makeSession(3000), {50});
+
+ // Check three sessions map properly to tasks
+ EXPECT_EQ(std::vector<int>({10, 20, 30}), getTasks(1, m));
+ EXPECT_EQ(std::vector<int>({40, 50, 60}), getTasks(2, m));
+ EXPECT_EQ(std::vector<int>({50}), getTasks(3, m));
+}
+
+TEST(SessionTaskMapTest, removeNonExisting) {
+ SessionTaskMap m;
+ EXPECT_FALSE(m.remove(1));
+}
+
+TEST(SessionTaskMapTest, removeMappingSessions) {
+ SessionTaskMap m;
+ m.add(1, makeSession(1000), {10, 20, 30});
+ m.add(2, makeSession(2000), {40, 50, 60});
+ m.add(3, makeSession(3000), {50});
+
+ // remove
+ EXPECT_TRUE(m.remove(2));
+
+ // Check that remaining tasks map correctly to sessions
+ EXPECT_EQ(std::vector<int64_t>({1}), getSessions(10, m));
+ EXPECT_EQ(std::vector<int64_t>({1}), getSessions(20, m));
+ EXPECT_EQ(std::vector<int64_t>({1}), getSessions(30, m));
+ EXPECT_EQ(std::vector<int64_t>({}), getSessions(40, m));
+ EXPECT_EQ(std::vector<int64_t>({3}), getSessions(50, m));
+}
+
+TEST(SessionTaskMapTest, removeMappingTasks) {
+ SessionTaskMap m;
+ EXPECT_FALSE(m.remove(1));
+
+ m.add(1, makeSession(1000), {10, 20, 30});
+ m.add(2, makeSession(2000), {40, 50, 60});
+ m.add(3, makeSession(3000), {50});
+
+ // remove
+ EXPECT_TRUE(m.remove(2));
+ EXPECT_FALSE(m.remove(2));
+
+ // Check that remaining tasks map correctly to sessions
+ EXPECT_EQ(std::vector<int>({10, 20, 30}), getTasks(1, m));
+ EXPECT_EQ(std::vector<int>({}), getTasks(2, m));
+ EXPECT_EQ(std::vector<int>({50}), getTasks(3, m));
+}
+
+TEST(SessionTaskMapTest, findEmpty) {
+ SessionTaskMap m;
+ EXPECT_EQ(nullptr, m.findSession(1));
+}
+
+TEST(SessionTaskMapTest, findSessionExists) {
+ SessionTaskMap m;
+ EXPECT_TRUE(m.add(1, makeSession(1000), {}));
+ EXPECT_NE(nullptr, m.findSession(1));
+}
+
+TEST(SessionTaskMapTest, findSessionEmptyExistsEmpty) {
+ SessionTaskMap m;
+ EXPECT_EQ(nullptr, m.findSession(1));
+ EXPECT_TRUE(m.add(1, makeSession(1000), {}));
+ EXPECT_NE(nullptr, m.findSession(1));
+ EXPECT_TRUE(m.remove(1));
+ EXPECT_EQ(nullptr, m.findSession(1));
+}
+
+TEST(SessionTaskMapTest, sizeTasks) {
+ SessionTaskMap m;
+ EXPECT_EQ(0, m.sizeTasks());
+ EXPECT_TRUE(m.add(1, makeSession(1000), {10, 20, 30}));
+ EXPECT_TRUE(m.add(2, makeSession(2000), {40, 50, 60}));
+ EXPECT_EQ(6, m.sizeTasks());
+}
+
+TEST(SessionTaskMapTest, sizeSessions) {
+ SessionTaskMap m;
+ EXPECT_EQ(0, m.sizeSessions());
+ EXPECT_TRUE(m.add(1, makeSession(1000), {10, 20, 30}));
+ EXPECT_TRUE(m.add(2, makeSession(2000), {40, 50, 60}));
+ EXPECT_EQ(2, m.sizeSessions());
+}
+
+TEST(SessionTaskMapTest, replace) {
+ SessionTaskMap m;
+
+ // Add three sessions where sessions 2 and 3 have shared threads
+ EXPECT_TRUE(m.add(1, makeSession(1000), {10, 20, 30}));
+ EXPECT_TRUE(m.add(2, makeSession(2000), {20}));
+
+ std::vector<pid_t> addedThreads;
+ std::vector<pid_t> removedThreads;
+
+ m.replace(1, {10, 40}, &addedThreads, &removedThreads);
+ EXPECT_EQ(1, addedThreads.size());
+ EXPECT_EQ(40, addedThreads[0]);
+
+ EXPECT_EQ(1, removedThreads.size());
+ EXPECT_EQ(30, removedThreads[0]);
+}
+
+TEST(SessionTaskMapTest, remove) {
+ SessionTaskMap m;
+ auto tNow = std::chrono::steady_clock::now();
+ const int64_t sessionId = 1;
+ SessionValueEntry sve;
+ sve.isAppSession = true;
+ sve.votes = std::make_shared<Votes>();
+ sve.votes->add(sessionId, VoteRange::makeMinRange(123, tNow, 400ms));
+ m.add(sessionId, sve, {10, 20, 30});
+ EXPECT_TRUE(m.isAnyAppSessionActive(tNow));
+ EXPECT_TRUE(m.remove(sessionId));
+ EXPECT_FALSE(m.isAnyAppSessionActive(tNow));
+}
+
+TEST(SessionTaskMapTest, isAnyAppActive) {
+ SessionTaskMap m;
+ auto tNow = std::chrono::steady_clock::now();
+
+ EXPECT_FALSE(m.isAnyAppSessionActive(tNow));
+
+ const int sessionId = 1000;
+ SessionValueEntry sv;
+ sv.isActive = true;
+ sv.isAppSession = true;
+ sv.lastUpdatedTime = tNow;
+ sv.votes = std::make_shared<Votes>();
+ sv.votes->add(1, VoteRange::makeMinRange(123, tNow, 400ms));
+
+ EXPECT_TRUE(m.add(sessionId, sv, {10, 20, 30}));
+ EXPECT_TRUE(m.isAnyAppSessionActive(tNow));
+ EXPECT_FALSE(m.isAnyAppSessionActive(tNow + 500ms));
+}
+
+int getVoteMin(const SessionTaskMap &m, int64_t taskId, std::chrono::steady_clock::time_point t) {
+ int uclampMin;
+ int uclampMax;
+ m.getTaskVoteRange(taskId, t, &uclampMin, &uclampMax);
+ return uclampMin;
+}
+
+TEST(SessionTaskMapTest, votesEdgeCaseOverlap) {
+ SessionTaskMap m;
+
+ // Sess 1: 10
+ EXPECT_TRUE(m.add(1, makeSession(1000), {10}));
+ const auto t0 = std::chrono::steady_clock::now();
+ const int voteMax = 1000;
+
+ // Session Vote UClamp [Time start----------------Time End] Delta
+ // 1 1 111 [20----60] 40
+ // 1 2 122 [60-85] 25
+ // 1 3 133 [60--90] 30
+ m.addVote(1, 1, 111, voteMax, t0 + 20ns, 40ns);
+ m.addVote(1, 2, 122, voteMax, t0 + 60ns, 25ns);
+ m.addVote(1, 3, 133, voteMax, t0 + 60ns, 30ns);
+
+ // Before any votes active
+ EXPECT_EQ(0, getVoteMin(m, 10, t0 + 0ns));
+ // First vote active
+ EXPECT_EQ(111, getVoteMin(m, 10, t0 + 20ns));
+ // In middle of first vote
+ EXPECT_EQ(111, getVoteMin(m, 10, t0 + 35ns));
+ // First, second, and third votes active
+ EXPECT_EQ(133, getVoteMin(m, 10, t0 + 60ns));
+ // Second and third votes are being used
+ EXPECT_EQ(133, getVoteMin(m, 10, t0 + 61ns));
+ // Third vote is being used
+ EXPECT_EQ(133, getVoteMin(m, 10, t0 + 86ns));
+ // No votes active
+ EXPECT_EQ(0, getVoteMin(m, 10, t0 + 91ns));
+}
+
+TEST(SessionTaskMapTest, votesEdgeCaseNoOverlap) {
+ SessionTaskMap m;
+ // Sess 2: 30
+ EXPECT_TRUE(m.add(2, makeSession(2000), {20}));
+ const auto t0 = std::chrono::steady_clock::now();
+ const int voteMax = 1000;
+
+ // Session Vote UClamp [Time start----------------Time End] Delta
+ // 2 1 211 [30-55] 25
+ // 2 2 222 [100-135] 35
+ // 2 3 233 [140-180] 40
+ m.addVote(2, 1, 211, voteMax, t0 + 30ns, 25ns);
+ m.addVote(2, 2, 222, voteMax, t0 + 100ns, 35ns);
+ m.addVote(2, 3, 233, voteMax, t0 + 140ns, 40ns);
+
+ // No votes active yet
+ EXPECT_EQ(0, getVoteMin(m, 20, t0 + 0ns));
+ // First vote active
+ EXPECT_EQ(211, getVoteMin(m, 20, t0 + 30ns));
+ // Second vote active
+ EXPECT_EQ(222, getVoteMin(m, 20, t0 + 100ns));
+ // Third vote active
+ EXPECT_EQ(233, getVoteMin(m, 20, t0 + 140ns));
+ // No votes active
+ EXPECT_EQ(0, getVoteMin(m, 20, t0 + 181ns));
+}
+
+TEST(SessionTaskMapTest, TwoSessionsOneInactive) {
+ const auto tNow = std::chrono::steady_clock::now();
+ SessionTaskMap m;
+
+ {
+ SessionValueEntry sv;
+ sv.isActive = true;
+ sv.isAppSession = true;
+ sv.lastUpdatedTime = tNow;
+ sv.votes = std::make_shared<Votes>();
+ sv.votes->add(11, VoteRange::makeMinRange(111, tNow, 400ms));
+ EXPECT_TRUE(m.add(1001, sv, {10, 20, 30}));
+ }
+
+ {
+ SessionValueEntry sv;
+ sv.isActive = true;
+ sv.isAppSession = true;
+ sv.lastUpdatedTime = tNow;
+ sv.votes = std::make_shared<Votes>();
+ sv.votes->add(22, VoteRange::makeMinRange(222, tNow, 400ms));
+ EXPECT_TRUE(m.add(2001, sv, {10, 20, 30}));
+ }
+
+ UclampRange uclampRange;
+ m.getTaskVoteRange(10, tNow + 10ns, &uclampRange.uclampMin, &uclampRange.uclampMax);
+ EXPECT_EQ(222, uclampRange.uclampMin);
+
+ auto sessItr = m.findSession(2001);
+ EXPECT_NE(nullptr, sessItr);
+ sessItr->isActive = false;
+
+ uclampRange.uclampMin = 0;
+ uclampRange.uclampMax = 1024;
+ m.getTaskVoteRange(10, tNow + 10ns, &uclampRange.uclampMin, &uclampRange.uclampMax);
+ EXPECT_EQ(111, uclampRange.uclampMin);
+}
+
+} // namespace pixel
+} // namespace impl
+} // namespace power
+} // namespace hardware
+} // namespace google
+} // namespace aidl
diff --git a/power-libperfmgr/aidl/tests/UClampVoterTest.cpp b/power-libperfmgr/aidl/tests/UClampVoterTest.cpp
new file mode 100644
index 00000000..53418a4e
--- /dev/null
+++ b/power-libperfmgr/aidl/tests/UClampVoterTest.cpp
@@ -0,0 +1,209 @@
+/*
+ * Copyright (C) 2023 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 "aidl/UClampVoter.h"
+
+namespace aidl {
+namespace google {
+namespace hardware {
+namespace power {
+namespace impl {
+namespace pixel {
+
+using std::literals::chrono_literals::operator""s;
+using std::literals::chrono_literals::operator""ms;
+using std::literals::chrono_literals::operator""ns;
+
+TEST(VoteRange, active) {
+ auto tNow = std::chrono::steady_clock::now();
+ VoteRange vr(true, 101, 202, tNow, 200ms);
+ EXPECT_TRUE(vr.active());
+}
+
+TEST(VoteRange, inActive) {
+ auto tNow = std::chrono::steady_clock::now();
+ VoteRange vr(false, 101, 202, tNow, 200ms);
+ EXPECT_FALSE(vr.active());
+}
+
+TEST(VoteRange, makeMinRange) {
+ auto tNow = std::chrono::steady_clock::now();
+ auto vr = VoteRange::makeMinRange(123, tNow, 210ms);
+ EXPECT_EQ(123, vr.uclampMin());
+ EXPECT_EQ(tNow, vr.startTime());
+ EXPECT_EQ(210ms, vr.durationNs());
+}
+
+TEST(VoteRange, isTimeInRange) {
+ const auto tNow = std::chrono::steady_clock::now();
+ auto voteRange = VoteRange::makeMinRange(234, tNow, 250ms);
+ EXPECT_EQ(234, voteRange.uclampMin());
+ EXPECT_FALSE(voteRange.isTimeInRange(tNow - 1ns));
+ EXPECT_TRUE(voteRange.isTimeInRange(tNow));
+ EXPECT_TRUE(voteRange.isTimeInRange(tNow + 1ns));
+ EXPECT_TRUE(voteRange.isTimeInRange(tNow + 250ms));
+ EXPECT_FALSE(voteRange.isTimeInRange(tNow + 250ms + 1ns));
+}
+
+TEST(VoteRange, isTimeInRangeInActive) {
+ const auto tNow = std::chrono::steady_clock::now();
+ auto voteRange = VoteRange::makeMinRange(345, tNow, 250ms);
+ voteRange.setActive(false);
+ EXPECT_FALSE(voteRange.active());
+ // Still reports 345 as the min even if inactive
+ EXPECT_EQ(345, voteRange.uclampMin());
+ EXPECT_FALSE(voteRange.isTimeInRange(tNow));
+ EXPECT_FALSE(voteRange.isTimeInRange(tNow + 1ns));
+ EXPECT_FALSE(voteRange.isTimeInRange(tNow + 250ms));
+ EXPECT_FALSE(voteRange.isTimeInRange(tNow + 250ms + 1ns));
+}
+
+TEST(VoteRange, getUclampRange) {
+ const auto tNow = std::chrono::steady_clock::now();
+ const auto tNext = tNow + 1s;
+ const auto tEnd1 = tNow + 4000000001ns;
+ const auto tPrev = tNow - 1s;
+
+ const auto voteFirst = VoteRange::makeMinRange(11, tNow, 4000000000ns);
+ EXPECT_FALSE(voteFirst.isTimeInRange(tPrev));
+ EXPECT_TRUE(voteFirst.isTimeInRange(tNext));
+ EXPECT_FALSE(voteFirst.isTimeInRange(tEnd1));
+
+ Votes votes;
+ votes.add(1, voteFirst);
+ UclampRange ur;
+
+ votes.getUclampRange(&ur, tNext);
+ EXPECT_EQ(11, ur.uclampMin);
+ EXPECT_EQ(kUclampMax, ur.uclampMax);
+}
+
+TEST(UclampVoter, simple) {
+ const auto tNow = std::chrono::steady_clock::now();
+
+ auto votes = std::make_shared<Votes>();
+ EXPECT_EQ(0, votes->size());
+
+ votes->add(1, VoteRange::makeMinRange(11, tNow, 4s));
+ EXPECT_EQ(1, votes->size());
+
+ votes->add(2, VoteRange::makeMinRange(22, tNow, 1s));
+ EXPECT_EQ(2, votes->size());
+
+ UclampRange ur1;
+ votes->getUclampRange(&ur1, tNow);
+ EXPECT_EQ(22, ur1.uclampMin);
+ EXPECT_EQ(kUclampMax, ur1.uclampMax);
+
+ UclampRange ur2;
+ votes->getUclampRange(&ur2, tNow + 2s);
+ EXPECT_EQ(11, ur2.uclampMin);
+ EXPECT_EQ(kUclampMax, ur2.uclampMax);
+
+ UclampRange ur3;
+ votes->getUclampRange(&ur3, tNow + 5s);
+ EXPECT_EQ(0, ur3.uclampMin);
+ EXPECT_EQ(1024, ur3.uclampMax);
+
+ EXPECT_FALSE(votes->allTimedOut(tNow + 200ns));
+ EXPECT_TRUE(votes->allTimedOut(tNow + 5s));
+
+ EXPECT_TRUE(votes->setUseVote(2, false));
+ EXPECT_TRUE(votes->anyTimedOut(tNow + 5s));
+ EXPECT_TRUE(votes->setUseVote(2, true));
+
+ EXPECT_FALSE(votes->anyTimedOut(tNow + 200ns));
+}
+
+TEST(UclampVoter, overwrite) {
+ const auto tNow = std::chrono::steady_clock::now();
+
+ auto votes = std::make_shared<Votes>();
+ EXPECT_EQ(0, votes->size());
+
+ votes->add(1, VoteRange::makeMinRange(11, tNow, 4s));
+ EXPECT_EQ(1, votes->size());
+
+ votes->add(2, VoteRange::makeMinRange(22, tNow, 2s));
+ EXPECT_EQ(2, votes->size());
+
+ UclampRange ucr1;
+ votes->getUclampRange(&ucr1, tNow + 1s);
+ EXPECT_EQ(22, ucr1.uclampMin);
+
+ votes->add(1, VoteRange::makeMinRange(32, tNow, 5s));
+ UclampRange ucr2;
+ votes->getUclampRange(&ucr2, tNow + 1s);
+ EXPECT_EQ(32, ucr2.uclampMin);
+}
+
+TEST(UclampVoter, updateDuration) {
+ const auto tNow = std::chrono::steady_clock::now();
+
+ auto votes = std::make_shared<Votes>();
+ EXPECT_EQ(0, votes->size());
+
+ votes->add(1, VoteRange::makeMinRange(11, tNow, 4s));
+ votes->add(2, VoteRange::makeMinRange(22, tNow, 2s));
+ EXPECT_EQ(2, votes->size());
+
+ EXPECT_TRUE(votes->allTimedOut(tNow + 7s));
+ votes->updateDuration(1, 8s);
+ EXPECT_FALSE(votes->allTimedOut(tNow + 7s));
+ votes->updateDuration(5, 10s);
+ EXPECT_TRUE(votes->allTimedOut(tNow + 9s));
+}
+
+TEST(UclampVoter, loadVoteTest) {
+ const int defaultVoteId = 1;
+ const int loadVoteId = 2;
+ const int uclampMinDefault = 50;
+ const int uclampMinInit = 162;
+ const int uclampMinHigh = 450;
+ const auto tNow = std::chrono::steady_clock::now();
+ UclampRange ucr;
+ auto votes = std::make_shared<Votes>();
+
+ // Default: min = 50 (original)
+ votes->add(defaultVoteId, VoteRange::makeMinRange(uclampMinDefault, tNow, 400ms));
+ votes->getUclampRange(&ucr, tNow + 100ms);
+ EXPECT_EQ(uclampMinDefault, ucr.uclampMin);
+
+ // Default: min = UclampMinInit
+ votes->add(defaultVoteId, VoteRange::makeMinRange(uclampMinInit, tNow, 400ns));
+ // Load: min = uclampMinHigh
+ votes->add(loadVoteId, VoteRange::makeMinRange(uclampMinHigh, tNow, 250ns));
+
+ // Check that load is enabled
+ ucr.uclampMin = 0;
+ votes->getUclampRange(&ucr, tNow + 100ns);
+ EXPECT_EQ(uclampMinHigh, ucr.uclampMin);
+
+ // Timeout or restore after 1st frame
+ // Expect to get 162.
+ ucr.uclampMin = 0;
+ votes->getUclampRange(&ucr, tNow + 350ns);
+ EXPECT_EQ(uclampMinInit, ucr.uclampMin);
+}
+
+} // namespace pixel
+} // namespace impl
+} // namespace power
+} // namespace hardware
+} // namespace google
+} // namespace aidl
diff --git a/power-libperfmgr/disp-power/InteractionHandler.cpp b/power-libperfmgr/disp-power/InteractionHandler.cpp
index d2357a6a..9cb7f1ea 100644
--- a/power-libperfmgr/disp-power/InteractionHandler.cpp
+++ b/power-libperfmgr/disp-power/InteractionHandler.cpp
@@ -48,8 +48,9 @@ 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"};
+static const std::array<const char *, 3> kDispIdlePath = {"/sys/class/drm/card0/device/idle_state",
+ "/sys/class/graphics/fb0/idle_state",
+ "/sys/class/graphics/fb0/device/idle_state"};
static const uint32_t kWaitMs =
::android::base::GetUintProperty("vendor.powerhal.disp.idle_wait", /*default*/ 100U);
static const uint32_t kMinDurationMs =
diff --git a/power-libperfmgr/libperfmgr/AdpfConfig.cc b/power-libperfmgr/libperfmgr/AdpfConfig.cc
index 36e52091..c017965e 100644
--- a/power-libperfmgr/libperfmgr/AdpfConfig.cc
+++ b/power-libperfmgr/libperfmgr/AdpfConfig.cc
@@ -56,8 +56,6 @@ void AdpfConfig::dumpToFd(int fd) {
dump_buf << "UclampMin_High: " << mUclampMinHigh << "\n";
dump_buf << "UclampMin_Low: " << mUclampMinLow << "\n";
dump_buf << "ReportingRateLimitNs: " << mReportingRateLimitNs << "\n";
- dump_buf << "EarlyBoost_On: " << mEarlyBoostOn << "\n";
- dump_buf << "EarlyBoost_TimeFactor: " << mEarlyBoostTimeFactor << "\n";
dump_buf << "TargetTimeFactor: " << mTargetTimeFactor << "\n";
dump_buf << "StaleTimeFactor: " << mStaleTimeFactor << "\n";
if (!android::base::WriteStringToFd(dump_buf.str(), fd)) {
diff --git a/power-libperfmgr/libperfmgr/HintManager.cc b/power-libperfmgr/libperfmgr/HintManager.cc
index dd313ef5..bc6b10df 100644
--- a/power-libperfmgr/libperfmgr/HintManager.cc
+++ b/power-libperfmgr/libperfmgr/HintManager.cc
@@ -58,7 +58,7 @@ bool HintManager::ValidateHint(const std::string& hint_type) const {
bool HintManager::IsHintSupported(const std::string& hint_type) const {
if (actions_.find(hint_type) == actions_.end()) {
- LOG(INFO) << "Hint type not present in actions: " << hint_type;
+ LOG(DEBUG) << "Hint type not present in actions: " << hint_type;
return false;
}
return true;
@@ -644,6 +644,16 @@ std::unordered_map<std::string, Hint> HintManager::ParseActions(
return actions_parsed;
}
+#define ADPF_PARSE(VARIABLE, ENTRY, TYPE) \
+ static_assert(std::is_same<decltype(adpfs[i][ENTRY].as##TYPE()), decltype(VARIABLE)>::value, \
+ "Parser type mismatch"); \
+ if (adpfs[i][ENTRY].empty() || !adpfs[i][ENTRY].is##TYPE()) { \
+ LOG(ERROR) << "Failed to read AdpfConfig[" << name << "][" ENTRY "]'s Values"; \
+ adpfs_parsed.clear(); \
+ return adpfs_parsed; \
+ } \
+ VARIABLE = adpfs[i][ENTRY].as##TYPE()
+
std::vector<std::shared_ptr<AdpfConfig>> HintManager::ParseAdpfConfigs(
const std::string &json_doc) {
// function starts
@@ -665,8 +675,6 @@ std::vector<std::shared_ptr<AdpfConfig>> HintManager::ParseAdpfConfigs(
uint64_t samplingWindowD;
double staleTimeFactor;
uint64_t reportingRate;
- bool earlyBoostOn;
- double earlyBoostTimeFactor;
double targetTimeFactor;
std::vector<std::shared_ptr<AdpfConfig>> adpfs_parsed;
std::set<std::string> name_parsed;
@@ -695,162 +703,31 @@ std::vector<std::shared_ptr<AdpfConfig>> HintManager::ParseAdpfConfigs(
return adpfs_parsed;
}
- if (adpfs[i]["PID_On"].empty() || !adpfs[i]["PID_On"].isBool()) {
- LOG(ERROR) << "Failed to read AdpfConfig[" << name << "][PID_On]'s Values";
- adpfs_parsed.clear();
- return adpfs_parsed;
- }
- pidOn = adpfs[i]["PID_On"].asBool();
-
- if (adpfs[i]["PID_Po"].empty() || !adpfs[i]["PID_Po"].isDouble()) {
- LOG(ERROR) << "Failed to read AdpfConfig[" << name << "][PID_Po]'s Values";
- adpfs_parsed.clear();
- return adpfs_parsed;
- }
- pidPOver = adpfs[i]["PID_Po"].asDouble();
-
- if (adpfs[i]["PID_Pu"].empty() || !adpfs[i]["PID_Pu"].isDouble()) {
- LOG(ERROR) << "Failed to read AdpfConfig[" << name << "][PID_Pu]'s Values";
- adpfs_parsed.clear();
- return adpfs_parsed;
- }
- pidPUnder = adpfs[i]["PID_Pu"].asDouble();
-
- if (adpfs[i]["PID_I"].empty() || !adpfs[i]["PID_I"].isDouble()) {
- LOG(ERROR) << "Failed to read AdpfConfig[" << name << "][PID_I]'s Values";
- adpfs_parsed.clear();
- return adpfs_parsed;
- }
- pidI = adpfs[i]["PID_I"].asDouble();
-
- if (adpfs[i]["PID_I_Init"].empty() || !adpfs[i]["PID_I_Init"].isInt64()) {
- LOG(ERROR) << "Failed to read AdpfConfig[" << name << "][PID_I_Init]'s Values";
- adpfs_parsed.clear();
- return adpfs_parsed;
- }
- pidIInit = adpfs[i]["PID_I_Init"].asInt64();
-
- if (adpfs[i]["PID_I_High"].empty() || !adpfs[i]["PID_I_High"].isInt64()) {
- LOG(ERROR) << "Failed to read AdpfConfig[" << name << "][PID_I_High]'s Values";
- adpfs_parsed.clear();
- return adpfs_parsed;
- }
- pidIHighLimit = adpfs[i]["PID_I_High"].asInt64();
-
- if (adpfs[i]["PID_I_Low"].empty() || !adpfs[i]["PID_I_Low"].isInt64()) {
- LOG(ERROR) << "Failed to read AdpfConfig[" << name << "][PID_I_Low]'s Values";
- adpfs_parsed.clear();
- return adpfs_parsed;
- }
- pidILowLimit = adpfs[i]["PID_I_Low"].asInt64();
-
- if (adpfs[i]["PID_Do"].empty() || !adpfs[i]["PID_Do"].isDouble()) {
- LOG(ERROR) << "Failed to read AdpfConfig[" << name << "][PID_Do]'s Values";
- adpfs_parsed.clear();
- return adpfs_parsed;
- }
- pidDOver = adpfs[i]["PID_Do"].asDouble();
-
- if (adpfs[i]["PID_Du"].empty() || !adpfs[i]["PID_Du"].isDouble()) {
- LOG(ERROR) << "Failed to read AdpfConfig[" << name << "][PID_Du]'s Values";
- adpfs_parsed.clear();
- return adpfs_parsed;
- }
- pidDUnder = adpfs[i]["PID_Du"].asDouble();
-
- if (adpfs[i]["UclampMin_On"].empty() || !adpfs[i]["UclampMin_On"].isBool()) {
- LOG(ERROR) << "Failed to read AdpfConfig[" << name << "][UclampMin_On]'s Values";
- adpfs_parsed.clear();
- return adpfs_parsed;
- }
- adpfUclamp = adpfs[i]["UclampMin_On"].asBool();
-
- if (adpfs[i]["UclampMin_Init"].empty() || !adpfs[i]["UclampMin_Init"].isInt()) {
- LOG(ERROR) << "Failed to read AdpfConfig[" << name << "][UclampMin_Init]'s Values";
- adpfs_parsed.clear();
- return adpfs_parsed;
- }
- uclampMinInit = adpfs[i]["UclampMin_Init"].asInt();
-
- if (adpfs[i]["UclampMin_High"].empty() || !adpfs[i]["UclampMin_High"].isUInt()) {
- LOG(ERROR) << "Failed to read AdpfConfig[" << name << "][UclampMin_High]'s Values";
- adpfs_parsed.clear();
- return adpfs_parsed;
- }
- uclampMinHighLimit = adpfs[i]["UclampMin_High"].asUInt();
-
- if (adpfs[i]["UclampMin_Low"].empty() || !adpfs[i]["UclampMin_Low"].isUInt()) {
- LOG(ERROR) << "Failed to read AdpfConfig[" << name << "][UclampMin_Low]'s Values";
- adpfs_parsed.clear();
- return adpfs_parsed;
- }
- uclampMinLowLimit = adpfs[i]["UclampMin_Low"].asUInt();
-
- if (adpfs[i]["SamplingWindow_P"].empty() || !adpfs[i]["SamplingWindow_P"].isUInt64()) {
- LOG(ERROR) << "Failed to read AdpfConfig[" << name << "][SamplingWindow_P]'s Values";
- adpfs_parsed.clear();
- return adpfs_parsed;
- }
- samplingWindowP = adpfs[i]["SamplingWindow_P"].asUInt64();
-
- if (adpfs[i]["SamplingWindow_I"].empty() || !adpfs[i]["SamplingWindow_I"].isUInt64()) {
- LOG(ERROR) << "Failed to read AdpfConfig[" << name << "][SamplingWindow_I]'s Values";
- adpfs_parsed.clear();
- return adpfs_parsed;
- }
- samplingWindowI = adpfs[i]["SamplingWindow_I"].asUInt64();
-
- if (adpfs[i]["SamplingWindow_D"].empty() || !adpfs[i]["SamplingWindow_D"].isUInt64()) {
- LOG(ERROR) << "Failed to read AdpfConfig[" << name << "][SamplingWindow_D]'s Values";
- adpfs_parsed.clear();
- return adpfs_parsed;
- }
- samplingWindowD = adpfs[i]["SamplingWindow_D"].asUInt64();
-
- if (adpfs[i]["StaleTimeFactor"].empty() || !adpfs[i]["StaleTimeFactor"].isUInt64()) {
- LOG(ERROR) << "Failed to read AdpfConfig[" << name << "][StaleTimeFactor]'s Values";
- adpfs_parsed.clear();
- return adpfs_parsed;
- }
- staleTimeFactor = adpfs[i]["StaleTimeFactor"].asDouble();
-
- if (adpfs[i]["ReportingRateLimitNs"].empty() ||
- !adpfs[i]["ReportingRateLimitNs"].isInt64()) {
- LOG(ERROR) << "Failed to read AdpfConfig[" << name
- << "][ReportingRateLimitNs]'s Values";
- adpfs_parsed.clear();
- return adpfs_parsed;
- }
- reportingRate = adpfs[i]["ReportingRateLimitNs"].asInt64();
-
- if (adpfs[i]["EarlyBoost_On"].empty() || !adpfs[i]["EarlyBoost_On"].isBool()) {
- LOG(ERROR) << "Failed to read AdpfConfig[" << name << "][EarlyBoost_On]'s Values";
- adpfs_parsed.clear();
- return adpfs_parsed;
- }
- earlyBoostOn = adpfs[i]["EarlyBoost_On"].asBool();
-
- if (adpfs[i]["EarlyBoost_TimeFactor"].empty() ||
- !adpfs[i]["EarlyBoost_TimeFactor"].isDouble()) {
- LOG(ERROR) << "Failed to read AdpfConfig[" << name
- << "][EarlyBoost_TimeFactor]'s Values";
- adpfs_parsed.clear();
- return adpfs_parsed;
- }
- earlyBoostTimeFactor = adpfs[i]["EarlyBoost_TimeFactor"].asDouble();
-
- if (adpfs[i]["TargetTimeFactor"].empty() || !adpfs[i]["TargetTimeFactor"].isDouble()) {
- LOG(ERROR) << "Failed to read AdpfConfig[" << name << "][TargetTimeFactor]'s Values";
- adpfs_parsed.clear();
- return adpfs_parsed;
- }
- targetTimeFactor = adpfs[i]["TargetTimeFactor"].asDouble();
+ ADPF_PARSE(pidOn, "PID_On", Bool);
+ ADPF_PARSE(pidPOver, "PID_Po", Double);
+ ADPF_PARSE(pidPUnder, "PID_Pu", Double);
+ ADPF_PARSE(pidI, "PID_I", Double);
+ ADPF_PARSE(pidIInit, "PID_I_Init", Int64);
+ ADPF_PARSE(pidIHighLimit, "PID_I_High", Int64);
+ ADPF_PARSE(pidILowLimit, "PID_I_Low", Int64);
+ ADPF_PARSE(pidDOver, "PID_Do", Double);
+ ADPF_PARSE(pidDUnder, "PID_Du", Double);
+ ADPF_PARSE(adpfUclamp, "UclampMin_On", Bool);
+ ADPF_PARSE(uclampMinInit, "UclampMin_Init", UInt);
+ ADPF_PARSE(uclampMinHighLimit, "UclampMin_High", UInt);
+ ADPF_PARSE(uclampMinLowLimit, "UclampMin_Low", UInt);
+ ADPF_PARSE(samplingWindowP, "SamplingWindow_P", UInt64);
+ ADPF_PARSE(samplingWindowI, "SamplingWindow_I", UInt64);
+ ADPF_PARSE(samplingWindowD, "SamplingWindow_D", UInt64);
+ ADPF_PARSE(staleTimeFactor, "StaleTimeFactor", Double);
+ ADPF_PARSE(reportingRate, "ReportingRateLimitNs", UInt64);
+ ADPF_PARSE(targetTimeFactor, "TargetTimeFactor", Double);
adpfs_parsed.emplace_back(std::make_shared<AdpfConfig>(
name, pidOn, pidPOver, pidPUnder, pidI, pidIInit, pidIHighLimit, pidILowLimit,
pidDOver, pidDUnder, adpfUclamp, uclampMinInit, uclampMinHighLimit,
uclampMinLowLimit, samplingWindowP, samplingWindowI, samplingWindowD, reportingRate,
- earlyBoostOn, earlyBoostTimeFactor, targetTimeFactor, staleTimeFactor));
+ targetTimeFactor, staleTimeFactor));
}
LOG(INFO) << adpfs_parsed.size() << " AdpfConfigs parsed successfully";
return adpfs_parsed;
@@ -872,5 +749,14 @@ bool HintManager::SetAdpfProfile(const std::string &profile_name) {
return false;
}
+bool HintManager::IsAdpfProfileSupported(const std::string &profile_name) const {
+ for (std::size_t i = 0; i < adpfs_.size(); ++i) {
+ if (adpfs_[i]->mName == profile_name) {
+ return true;
+ }
+ }
+ return false;
+}
+
} // namespace perfmgr
} // namespace android
diff --git a/power-libperfmgr/libperfmgr/include/perfmgr/AdpfConfig.h b/power-libperfmgr/libperfmgr/include/perfmgr/AdpfConfig.h
index 7df1bfe7..deab85af 100644
--- a/power-libperfmgr/libperfmgr/include/perfmgr/AdpfConfig.h
+++ b/power-libperfmgr/libperfmgr/include/perfmgr/AdpfConfig.h
@@ -43,9 +43,6 @@ struct AdpfConfig {
uint64_t mSamplingWindowI;
uint64_t mSamplingWindowD;
int64_t mReportingRateLimitNs;
- int64_t mFreezeDurationNs;
- bool mEarlyBoostOn;
- double mEarlyBoostTimeFactor;
double mTargetTimeFactor;
// Stale control
double mStaleTimeFactor;
@@ -59,8 +56,8 @@ struct AdpfConfig {
int64_t pidIInit, int64_t pidIHigh, int64_t pidILow, double pidDo, double pidDu,
bool uclampMinOn, uint32_t uclampMinInit, uint32_t uclampMinHigh,
uint32_t uclampMinLow, uint64_t samplingWindowP, uint64_t samplingWindowI,
- uint64_t samplingWindowD, int64_t reportingRateLimitNs, bool earlyBoostOn,
- double earlyBoostTimeFactor, double targetTimeFactor, double staleTimeFactor)
+ uint64_t samplingWindowD, int64_t reportingRateLimitNs, double targetTimeFactor,
+ double staleTimeFactor)
: mName(std::move(name)),
mPidOn(pidOn),
mPidPo(pidPo),
@@ -79,8 +76,6 @@ struct AdpfConfig {
mSamplingWindowI(samplingWindowI),
mSamplingWindowD(samplingWindowD),
mReportingRateLimitNs(reportingRateLimitNs),
- mEarlyBoostOn(earlyBoostOn),
- mEarlyBoostTimeFactor(earlyBoostTimeFactor),
mTargetTimeFactor(targetTimeFactor),
mStaleTimeFactor(staleTimeFactor) {}
};
diff --git a/power-libperfmgr/libperfmgr/include/perfmgr/HintManager.h b/power-libperfmgr/libperfmgr/include/perfmgr/HintManager.h
index 13211a18..34726f5e 100644
--- a/power-libperfmgr/libperfmgr/include/perfmgr/HintManager.h
+++ b/power-libperfmgr/libperfmgr/include/perfmgr/HintManager.h
@@ -125,6 +125,9 @@ class HintManager {
// get current ADPF.
std::shared_ptr<AdpfConfig> GetAdpfProfile() const;
+ // Query if given AdpfProfile supported.
+ bool IsAdpfProfileSupported(const std::string &name) const;
+
// Static method to construct HintManager from the JSON config file.
static std::unique_ptr<HintManager> GetFromJSON(
const std::string& config_path, bool start = true);
diff --git a/power-libperfmgr/libperfmgr/tests/HintManagerTest.cc b/power-libperfmgr/libperfmgr/tests/HintManagerTest.cc
index 979e7dc0..f323e46e 100644
--- a/power-libperfmgr/libperfmgr/tests/HintManagerTest.cc
+++ b/power-libperfmgr/libperfmgr/tests/HintManagerTest.cc
@@ -803,10 +803,6 @@ TEST_F(HintManagerTest, ParseAdpfConfigsTest) {
EXPECT_EQ(157U, adpfs[1]->mUclampMinLow);
EXPECT_EQ(166666660LL, adpfs[0]->mReportingRateLimitNs);
EXPECT_EQ(83333330LL, adpfs[1]->mReportingRateLimitNs);
- EXPECT_EQ(false, adpfs[0]->mEarlyBoostOn);
- EXPECT_EQ(true, adpfs[1]->mEarlyBoostOn);
- EXPECT_EQ(0.8, adpfs[0]->mEarlyBoostTimeFactor);
- EXPECT_EQ(1.2, adpfs[1]->mEarlyBoostTimeFactor);
EXPECT_EQ(1.0, adpfs[0]->mTargetTimeFactor);
EXPECT_EQ(1.4, adpfs[1]->mTargetTimeFactor);
EXPECT_EQ(10.0, adpfs[0]->mStaleTimeFactor);
@@ -851,5 +847,17 @@ TEST_F(HintManagerTest, GetFromJSONAdpfConfigTest) {
EXPECT_EQ("REFRESH_120FPS", hm->GetAdpfProfile()->mName);
}
+TEST_F(HintManagerTest, IsAdpfProfileSupported) {
+ TemporaryFile json_file;
+ ASSERT_TRUE(android::base::WriteStringToFile(json_doc_, json_file.path)) << strerror(errno);
+ std::unique_ptr<HintManager> hm = HintManager::GetFromJSON(json_file.path, false);
+ EXPECT_NE(nullptr, hm.get());
+
+ // Check if given AdpfProfile supported
+ EXPECT_FALSE(hm->IsAdpfProfileSupported("NoSuchProfile"));
+ EXPECT_TRUE(hm->IsAdpfProfileSupported("REFRESH_60FPS"));
+ EXPECT_TRUE(hm->IsAdpfProfileSupported("REFRESH_120FPS"));
+}
+
} // namespace perfmgr
} // namespace android
diff --git a/recovery/Android.bp b/recovery/Android.bp
index 4c7e0c7b..cd3526b2 100644
--- a/recovery/Android.bp
+++ b/recovery/Android.bp
@@ -27,3 +27,27 @@ cc_library_static {
"librecovery_ui",
],
}
+
+cc_library_static {
+ name: "librecovery_ui_pixel_watch",
+ owner: "google",
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ "-pedantic",
+ ],
+ srcs: [
+ "recovery_watch_ui.cpp",
+ ],
+
+ whole_static_libs: [
+ "libmisc_writer",
+ "libbootloader_message",
+ ],
+
+ shared_libs: [
+ "libbase",
+ "librecovery_ui",
+ ],
+}
diff --git a/recovery/recovery_watch_ui.cpp b/recovery/recovery_watch_ui.cpp
new file mode 100644
index 00000000..39a09522
--- /dev/null
+++ b/recovery/recovery_watch_ui.cpp
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2023 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 <dlfcn.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <string>
+#include <string_view>
+#include <vector>
+
+#include <android-base/endian.h>
+#include <android-base/logging.h>
+#include <android-base/strings.h>
+#include <misc_writer/misc_writer.h>
+#include <recovery_ui/device.h>
+#include <recovery_ui/wear_ui.h>
+
+namespace android {
+namespace hardware {
+namespace google {
+namespace pixel {
+
+namespace {
+
+// Provision Silent OTA(SOTA) flag while reason is "enable-sota"
+bool ProvisionSilentOtaFlag(const std::string& reason) {
+ if (android::base::StartsWith(reason, MiscWriter::kSotaFlag)) {
+ MiscWriter misc_writer(MiscWriterActions::kSetSotaFlag);
+ if (!misc_writer.PerformAction()) {
+ LOG(ERROR) << "Failed to set the silent ota flag";
+ return false;
+ }
+ LOG(INFO) << "Silent ota flag set successful";
+ }
+ return true;
+}
+
+} // namespace
+
+class PixelWatchDevice : public ::Device {
+ public:
+ explicit PixelWatchDevice(::WearRecoveryUI* const ui) : ::Device(ui) {}
+
+ /** Hook to wipe user data not stored in /data */
+ bool PostWipeData() override {
+ // Try to do everything but report a failure if anything wasn't successful
+ bool totalSuccess = false;
+
+ // Additional behavior along with wiping data
+ auto reason = GetReason();
+ CHECK(reason.has_value());
+ if (!ProvisionSilentOtaFlag(reason.value())) {
+ totalSuccess = false;
+ }
+
+ return totalSuccess;
+ }
+};
+
+} // namespace pixel
+} // namespace google
+} // namespace hardware
+} // namespace android
+
+Device *make_device() {
+ return new ::android::hardware::google::pixel::PixelWatchDevice(new ::WearRecoveryUI);
+}
diff --git a/thermal/Android.bp b/thermal/Android.bp
index 61403b31..afd3c811 100644
--- a/thermal/Android.bp
+++ b/thermal/Android.bp
@@ -15,6 +15,7 @@ cc_binary {
"utils/powerhal_helper.cpp",
"utils/thermal_stats_helper.cpp",
"utils/thermal_watcher.cpp",
+ "virtualtemp_estimator/virtualtemp_estimator.cpp",
],
vendor: true,
relative_install_path: "hw",
@@ -31,9 +32,9 @@ cc_binary {
"libutils",
"libnl",
"libbinder_ndk",
- "android.frameworks.stats-V1-ndk",
+ "android.frameworks.stats-V2-ndk",
"android.hardware.power-V1-ndk",
- "android.hardware.thermal-V1-ndk",
+ "android.hardware.thermal-V2-ndk",
"pixel-power-ext-V1-ndk",
"pixelatoms-cpp",
],
@@ -41,7 +42,7 @@ cc_binary {
"libpixelstats",
],
export_shared_lib_headers: [
- "android.frameworks.stats-V1-ndk",
+ "android.frameworks.stats-V2-ndk",
"pixelatoms-cpp",
],
cflags: [
@@ -79,6 +80,7 @@ cc_test {
"utils/thermal_watcher.cpp",
"tests/mock_thermal_helper.cpp",
"tests/thermal_looper_test.cpp",
+ "virtualtemp_estimator/virtualtemp_estimator.cpp",
],
shared_libs: [
"libbase",
@@ -88,9 +90,9 @@ cc_test {
"libnl",
"liblog",
"libbinder_ndk",
- "android.frameworks.stats-V1-ndk",
+ "android.frameworks.stats-V2-ndk",
"android.hardware.power-V1-ndk",
- "android.hardware.thermal-V1-ndk",
+ "android.hardware.thermal-V2-ndk",
"pixel-power-ext-V1-ndk",
"pixelatoms-cpp",
],
@@ -119,3 +121,38 @@ sh_binary {
"pixel-thermal-symlinks.rc",
],
}
+
+
+cc_binary {
+ name: "virtualtemp_estimator_test",
+ srcs: [
+ "virtualtemp_estimator/virtualtemp_estimator.cpp",
+ "virtualtemp_estimator/virtualtemp_estimator_test.cpp"
+ ],
+ shared_libs: [
+ "libc",
+ "liblog",
+ "libcutils",
+ "libbinder",
+ "libhidlbase",
+ "libutils",
+ "libjsoncpp",],
+ vendor: true,
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wextra",
+ "-Wunused",
+ ],
+ tidy: true,
+ tidy_checks: [
+ "android-*",
+ "cert-*",
+ "clang-analyzer-security*",
+ ],
+ tidy_checks_as_errors: [
+ "android-*",
+ "clang-analyzer-security*",
+ "cert-*",
+ ],
+}
diff --git a/thermal/Thermal.cpp b/thermal/Thermal.cpp
index 4b55ab96..13cdcaa9 100644
--- a/thermal/Thermal.cpp
+++ b/thermal/Thermal.cpp
@@ -646,15 +646,17 @@ void Thermal::dumpThermalData(int fd) {
}
}
{
- dump_buf << "getEmulTemperatures:" << std::endl;
+ dump_buf << "getEmulSettings:" << std::endl;
for (const auto &sensor_status_pair : sensor_status_map) {
- if (sensor_status_pair.second.emul_setting == nullptr) {
+ if (sensor_status_pair.second.override_status.emul_temp == nullptr) {
continue;
}
- dump_buf << " Name: " << sensor_status_pair.first
- << " EmulTemp: " << sensor_status_pair.second.emul_setting->emul_temp
+ dump_buf << " Name: " << sensor_status_pair.first << " EmulTemp: "
+ << sensor_status_pair.second.override_status.emul_temp->temp
<< " EmulSeverity: "
- << sensor_status_pair.second.emul_setting->emul_severity << std::endl;
+ << sensor_status_pair.second.override_status.emul_temp->severity
+ << " maxThrottling: " << std::boolalpha
+ << sensor_status_pair.second.override_status.max_throttling << std::endl;
}
}
{
@@ -774,17 +776,18 @@ binder_status_t Thermal::dump(int fd, const char **args, uint32_t numArgs) {
return STATUS_OK;
}
- if (std::string(args[0]) == "emul_temp") {
- return (numArgs != 3 ||
- !thermal_helper_->emulTemp(std::string(args[1]), std::strtod(args[2], nullptr)))
- ? STATUS_BAD_VALUE
- : STATUS_OK;
- } else if (std::string(args[0]) == "emul_severity") {
- return (numArgs != 3 ||
- !thermal_helper_->emulSeverity(std::string(args[1]),
- static_cast<int>(std::strtol(args[2], nullptr, 10))))
- ? STATUS_BAD_VALUE
- : STATUS_OK;
+ if (std::string(args[0]) == "emul_temp" && numArgs >= 3) {
+ return thermal_helper_->emulTemp(
+ std::string(args[1]), std::atof(args[2]),
+ numArgs == 3 ? false : std::string(args[3]) == "max_throttling")
+ ? STATUS_OK
+ : STATUS_BAD_VALUE;
+ } else if (std::string(args[0]) == "emul_severity" && numArgs >= 3) {
+ return thermal_helper_->emulSeverity(
+ std::string(args[1]), std::atof(args[2]),
+ numArgs == 3 ? false : std::string(args[3]) == "max_throttling")
+ ? STATUS_OK
+ : STATUS_BAD_VALUE;
} else if (std::string(args[0]) == "emul_clear") {
return (numArgs != 2 || !thermal_helper_->emulClear(std::string(args[1])))
? STATUS_BAD_VALUE
diff --git a/thermal/android.hardware.thermal-service.pixel.rc b/thermal/android.hardware.thermal-service.pixel.rc
index f9f823b8..a1c89fcc 100644
--- a/thermal/android.hardware.thermal-service.pixel.rc
+++ b/thermal/android.hardware.thermal-service.pixel.rc
@@ -10,5 +10,5 @@ service vendor.thermal-hal /vendor/bin/hw/android.hardware.thermal-service.pixel
class hal
user system
group system
- priority -20
+ priority -10
disabled
diff --git a/thermal/android.hardware.thermal-service.pixel.xml b/thermal/android.hardware.thermal-service.pixel.xml
index bdee7446..08dc68ca 100644
--- a/thermal/android.hardware.thermal-service.pixel.xml
+++ b/thermal/android.hardware.thermal-service.pixel.xml
@@ -1,7 +1,7 @@
<manifest version="1.0" type="device">
<hal format="aidl">
<name>android.hardware.thermal</name>
- <version>1</version>
+ <version>2</version>
<fqname>IThermal/default</fqname>
</hal>
</manifest>
diff --git a/thermal/device.mk b/thermal/device.mk
deleted file mode 100644
index 1d71d312..00000000
--- a/thermal/device.mk
+++ /dev/null
@@ -1,13 +0,0 @@
-PRODUCT_PACKAGES += \
- android.hardware.thermal-service.pixel
-
-# Thermal utils
-PRODUCT_PACKAGES += \
- thermal_symlinks
-
-ifneq (,$(filter userdebug eng, $(TARGET_BUILD_VARIANT)))
-PRODUCT_PACKAGES += \
- thermal_logd
-endif
-
-BOARD_SEPOLICY_DIRS += hardware/google/pixel-sepolicy/thermal
diff --git a/thermal/tests/mock_thermal_helper.h b/thermal/tests/mock_thermal_helper.h
index 43ff3286..c706461b 100644
--- a/thermal/tests/mock_thermal_helper.h
+++ b/thermal/tests/mock_thermal_helper.h
@@ -34,8 +34,8 @@ class MockThermalHelper : public ThermalHelper {
(bool, TemperatureType, std::vector<TemperatureThreshold> *), (const, override));
MOCK_METHOD(bool, fillCurrentCoolingDevices, (bool, CoolingType, std::vector<CoolingDevice> *),
(const, override));
- MOCK_METHOD(bool, emulTemp, (std::string_view, const float), (override));
- MOCK_METHOD(bool, emulSeverity, (std::string_view, const int), (override));
+ MOCK_METHOD(bool, emulTemp, (std::string_view, const float, const bool), (override));
+ MOCK_METHOD(bool, emulSeverity, (std::string_view, const int, const bool), (override));
MOCK_METHOD(bool, emulClear, (std::string_view), (override));
MOCK_METHOD(bool, isInitializedOk, (), (const, override));
MOCK_METHOD(bool, readTemperature,
diff --git a/thermal/thermal-helper.cpp b/thermal/thermal-helper.cpp
index dc4a599c..61de8bcc 100644
--- a/thermal/thermal-helper.cpp
+++ b/thermal/thermal-helper.cpp
@@ -157,9 +157,11 @@ ThermalHelperImpl::ThermalHelperImpl(const NotificationCallback &cb)
ret = false;
}
- if (!thermal_stats_helper_.initializeStats(config, sensor_info_map_,
- cooling_device_info_map_)) {
- LOG(FATAL) << "Failed to initialize thermal stats";
+ if (ret) {
+ if (!thermal_stats_helper_.initializeStats(config, sensor_info_map_,
+ cooling_device_info_map_)) {
+ LOG(FATAL) << "Failed to initialize thermal stats";
+ }
}
for (auto &name_status_pair : sensor_info_map_) {
@@ -169,7 +171,7 @@ ThermalHelperImpl::ThermalHelperImpl(const NotificationCallback &cb)
.prev_cold_severity = ThrottlingSeverity::NONE,
.last_update_time = boot_clock::time_point::min(),
.thermal_cached = {NAN, boot_clock::time_point::min()},
- .emul_setting = nullptr,
+ .override_status = {nullptr, false, false},
};
if (name_status_pair.second.throttling_info != nullptr) {
@@ -286,42 +288,82 @@ bool getThermalZoneTypeById(int tz_id, std::string *type) {
return true;
}
-bool ThermalHelperImpl::emulTemp(std::string_view target_sensor, const float value) {
- LOG(INFO) << "Set " << target_sensor.data() << " emul_temp "
- << "to " << value;
+void ThermalHelperImpl::checkUpdateSensorForEmul(std::string_view target_sensor,
+ const bool max_throttling) {
+ // Force update all the sensors which are related to the target emul sensor
+ for (auto &[sensor_name, sensor_info] : sensor_info_map_) {
+ if (sensor_info.virtual_sensor_info == nullptr || !sensor_info.is_watch) {
+ continue;
+ }
+
+ const auto &linked_sensors = sensor_info.virtual_sensor_info->linked_sensors;
+ if (std::find(linked_sensors.begin(), linked_sensors.end(), target_sensor) ==
+ linked_sensors.end()) {
+ continue;
+ }
+
+ auto &sensor_status = sensor_status_map_.at(sensor_name.data());
+ sensor_status.override_status.max_throttling = max_throttling;
+ sensor_status.override_status.pending_update = true;
+
+ checkUpdateSensorForEmul(sensor_name, max_throttling);
+ }
+}
+
+bool ThermalHelperImpl::emulTemp(std::string_view target_sensor, const float temp,
+ const bool max_throttling) {
+ LOG(INFO) << "Set " << target_sensor.data() << " emul_temp: " << temp
+ << " max_throttling: " << max_throttling;
std::lock_guard<std::shared_mutex> _lock(sensor_status_map_mutex_);
+
// Check the target sensor is valid
if (!sensor_status_map_.count(target_sensor.data())) {
LOG(ERROR) << "Cannot find target emul sensor: " << target_sensor.data();
return false;
}
- sensor_status_map_.at(target_sensor.data())
- .emul_setting.reset(new EmulSetting{value, -1, true});
+ auto &sensor_status = sensor_status_map_.at(target_sensor.data());
+
+ sensor_status.override_status.emul_temp.reset(new EmulTemp{temp, -1});
+ sensor_status.override_status.max_throttling = max_throttling;
+ sensor_status.override_status.pending_update = true;
+
+ checkUpdateSensorForEmul(target_sensor.data(), max_throttling);
thermal_watcher_->wake();
return true;
}
-bool ThermalHelperImpl::emulSeverity(std::string_view target_sensor, const int severity) {
- LOG(INFO) << "Set " << target_sensor.data() << " emul_severity "
- << "to " << severity;
+bool ThermalHelperImpl::emulSeverity(std::string_view target_sensor, const int severity,
+ const bool max_throttling) {
+ LOG(INFO) << "Set " << target_sensor.data() << " emul_severity: " << severity
+ << " max_throttling: " << max_throttling;
std::lock_guard<std::shared_mutex> _lock(sensor_status_map_mutex_);
// Check the target sensor is valid
- if (!sensor_status_map_.count(target_sensor.data())) {
+ if (!sensor_status_map_.count(target_sensor.data()) ||
+ !sensor_info_map_.count(target_sensor.data())) {
LOG(ERROR) << "Cannot find target emul sensor: " << target_sensor.data();
return false;
}
+ const auto &sensor_info = sensor_info_map_.at(target_sensor.data());
+
// Check the emul severity is valid
if (severity > static_cast<int>(kThrottlingSeverityCount)) {
LOG(ERROR) << "Invalid emul severity value " << severity;
return false;
}
- sensor_status_map_.at(target_sensor.data())
- .emul_setting.reset(new EmulSetting{NAN, severity, true});
+ const auto temp = sensor_info.hot_thresholds[severity] / sensor_info.multiplier;
+
+ auto &sensor_status = sensor_status_map_.at(target_sensor.data());
+
+ sensor_status.override_status.emul_temp.reset(new EmulTemp{temp, severity});
+ sensor_status.override_status.max_throttling = max_throttling;
+ sensor_status.override_status.pending_update = true;
+
+ checkUpdateSensorForEmul(target_sensor.data(), max_throttling);
thermal_watcher_->wake();
return true;
@@ -332,19 +374,22 @@ bool ThermalHelperImpl::emulClear(std::string_view target_sensor) {
std::lock_guard<std::shared_mutex> _lock(sensor_status_map_mutex_);
if (target_sensor == "all") {
- for (auto &sensor_status : sensor_status_map_) {
- if (sensor_status.second.emul_setting != nullptr) {
- sensor_status.second.emul_setting.reset(new EmulSetting{NAN, -1, true});
- }
+ for (auto &[sensor_name, sensor_status] : sensor_status_map_) {
+ sensor_status.override_status = {
+ .emul_temp = nullptr, .max_throttling = false, .pending_update = true};
+ checkUpdateSensorForEmul(sensor_name, false);
}
- } else if (sensor_status_map_.count(target_sensor.data()) &&
- sensor_status_map_.at(target_sensor.data()).emul_setting != nullptr) {
- sensor_status_map_.at(target_sensor.data())
- .emul_setting.reset(new EmulSetting{NAN, -1, true});
+ } else if (sensor_status_map_.count(target_sensor.data())) {
+ auto &sensor_status = sensor_status_map_.at(target_sensor.data());
+ sensor_status.override_status = {
+ .emul_temp = nullptr, .max_throttling = false, .pending_update = true};
+ checkUpdateSensorForEmul(target_sensor.data(), false);
} else {
LOG(ERROR) << "Cannot find target emul sensor: " << target_sensor.data();
return false;
}
+
+ thermal_watcher_->wake();
return true;
}
@@ -377,8 +422,15 @@ bool ThermalHelperImpl::readTemperature(
std::map<std::string, float> sensor_log_map;
auto &sensor_status = sensor_status_map_.at(sensor_name.data());
- if (!readThermalSensor(sensor_name, &temp, force_no_cache, &sensor_log_map) ||
- std::isnan(temp)) {
+ if (!readThermalSensor(sensor_name, &temp, force_no_cache, &sensor_log_map)) {
+ LOG(ERROR) << "Failed to read thermal sensor " << sensor_name.data();
+ thermal_stats_helper_.reportThermalAbnormality(
+ ThermalSensorAbnormalityDetected::TEMP_READ_FAIL, sensor_name, std::nullopt);
+ return false;
+ }
+
+ if (std::isnan(temp)) {
+ LOG(INFO) << "Sensor " << sensor_name.data() << " temperature is nan.";
return false;
}
@@ -407,10 +459,11 @@ bool ThermalHelperImpl::readTemperature(
*throttling_status = status;
}
- if (sensor_status.emul_setting != nullptr && sensor_status.emul_setting->emul_severity >= 0) {
+ if (sensor_status.override_status.emul_temp != nullptr &&
+ sensor_status.override_status.emul_temp->severity >= 0) {
std::shared_lock<std::shared_mutex> _lock(sensor_status_map_mutex_);
out->throttlingStatus =
- static_cast<ThrottlingSeverity>(sensor_status.emul_setting->emul_severity);
+ static_cast<ThrottlingSeverity>(sensor_status.override_status.emul_temp->severity);
} else {
out->throttlingStatus =
static_cast<size_t>(status.first) > static_cast<size_t>(status.second)
@@ -853,12 +906,55 @@ bool ThermalHelperImpl::readDataByType(std::string_view sensor_data, float *read
return true;
}
+float ThermalHelperImpl::runVirtualTempEstimator(std::string_view sensor_name,
+ std::map<std::string, float> *sensor_log_map) {
+ std::vector<float> model_inputs;
+ float estimated_vt = NAN;
+ constexpr int kCelsius2mC = 1000;
+
+ ATRACE_NAME(StringPrintf("ThermalHelper::runVirtualTempEstimator - %s", sensor_name.data())
+ .c_str());
+ if (!(sensor_info_map_.count(sensor_name.data()) &&
+ sensor_status_map_.count(sensor_name.data()))) {
+ LOG(ERROR) << sensor_name << " not part of sensor_info_map_ or sensor_status_map_";
+ return NAN;
+ }
+
+ const auto &sensor_info = sensor_info_map_.at(sensor_name.data());
+ if (!sensor_info.virtual_sensor_info->vt_estimator) {
+ LOG(ERROR) << "vt_estimator not valid for " << sensor_name;
+ return NAN;
+ }
+
+ model_inputs.reserve(sensor_info.virtual_sensor_info->linked_sensors.size());
+
+ for (size_t i = 0; i < sensor_info.virtual_sensor_info->linked_sensors.size(); i++) {
+ std::string linked_sensor = sensor_info.virtual_sensor_info->linked_sensors[i];
+
+ if ((*sensor_log_map).count(linked_sensor.data())) {
+ float value = (*sensor_log_map)[linked_sensor.data()];
+ model_inputs.push_back(value / kCelsius2mC);
+ } else {
+ LOG(ERROR) << "failed to read sensor: " << linked_sensor;
+ return NAN;
+ }
+ }
+
+ ::thermal::vtestimator::VtEstimatorStatus ret =
+ sensor_info.virtual_sensor_info->vt_estimator->Estimate(model_inputs, &estimated_vt);
+ if (ret != ::thermal::vtestimator::kVtEstimatorOk) {
+ LOG(ERROR) << "Failed to run estimator (ret: " << ret << ") for " << sensor_name;
+ return NAN;
+ }
+
+ return (estimated_vt * kCelsius2mC);
+}
+
constexpr int kTranTimeoutParam = 2;
bool ThermalHelperImpl::readThermalSensor(std::string_view sensor_name, float *temp,
const bool force_no_cache,
std::map<std::string, float> *sensor_log_map) {
- float temp_val = 0.0;
std::string file_reading;
boot_clock::time_point now = boot_clock::now();
@@ -873,9 +969,8 @@ bool ThermalHelperImpl::readThermalSensor(std::string_view sensor_name, float *t
{
std::shared_lock<std::shared_mutex> _lock(sensor_status_map_mutex_);
- if (sensor_status.emul_setting != nullptr &&
- !isnan(sensor_status.emul_setting->emul_temp)) {
- *temp = sensor_status.emul_setting->emul_temp;
+ if (sensor_status.override_status.emul_temp != nullptr) {
+ *temp = sensor_status.override_status.emul_temp->temp;
return true;
}
}
@@ -903,57 +998,75 @@ bool ThermalHelperImpl::readThermalSensor(std::string_view sensor_name, float *t
}
*temp = std::stof(::android::base::Trim(file_reading));
} else {
- for (size_t i = 0; i < sensor_info.virtual_sensor_info->linked_sensors.size(); i++) {
- float sensor_reading = 0.0;
- // Get the sensor reading data
- if (!readDataByType(sensor_info.virtual_sensor_info->linked_sensors[i], &sensor_reading,
+ const auto &linked_sensors_size = sensor_info.virtual_sensor_info->linked_sensors.size();
+ std::vector<float> sensor_readings(linked_sensors_size, 0.0);
+
+ // Calculate temperature of each of the linked sensor
+ for (size_t i = 0; i < linked_sensors_size; i++) {
+ if (!readDataByType(sensor_info.virtual_sensor_info->linked_sensors[i],
+ &sensor_readings[i],
sensor_info.virtual_sensor_info->linked_sensors_type[i],
force_no_cache, sensor_log_map)) {
LOG(ERROR) << "Failed to read " << sensor_name.data() << "'s linked sensor "
<< sensor_info.virtual_sensor_info->linked_sensors[i];
return false;
}
-
- float coefficient = 0.0;
- if (!readDataByType(sensor_info.virtual_sensor_info->coefficients[i], &coefficient,
- sensor_info.virtual_sensor_info->coefficients_type[i],
- force_no_cache, sensor_log_map)) {
- LOG(ERROR) << "Failed to read " << sensor_name.data() << "'s coefficient "
- << sensor_info.virtual_sensor_info->coefficients[i];
- return false;
- }
-
- if (std::isnan(sensor_reading) || std::isnan(coefficient)) {
+ if (std::isnan(sensor_readings[i])) {
LOG(INFO) << sensor_name << " data is under collecting";
return true;
}
+ }
- switch (sensor_info.virtual_sensor_info->formula) {
- case FormulaOption::COUNT_THRESHOLD:
- if ((coefficient < 0 && sensor_reading < -coefficient) ||
- (coefficient >= 0 && sensor_reading >= coefficient))
- temp_val += 1;
- break;
- case FormulaOption::WEIGHTED_AVG:
- temp_val += sensor_reading * coefficient;
- break;
- case FormulaOption::MAXIMUM:
- if (i == 0)
- temp_val = std::numeric_limits<float>::lowest();
- if (sensor_reading * coefficient > temp_val)
- temp_val = sensor_reading * coefficient;
- break;
- case FormulaOption::MINIMUM:
- if (i == 0)
- temp_val = std::numeric_limits<float>::max();
- if (sensor_reading * coefficient < temp_val)
- temp_val = sensor_reading * coefficient;
- break;
- default:
- break;
+ if (sensor_info.virtual_sensor_info->formula == FormulaOption::USE_ML_MODEL) {
+ *temp = runVirtualTempEstimator(sensor_name, sensor_log_map);
+
+ if (std::isnan(*temp)) {
+ LOG(ERROR) << "VirtualEstimator returned NAN for " << sensor_name;
+ return false;
+ }
+ } else {
+ float temp_val = 0.0;
+ for (size_t i = 0; i < linked_sensors_size; i++) {
+ float coefficient = 0.0;
+ if (!readDataByType(sensor_info.virtual_sensor_info->coefficients[i], &coefficient,
+ sensor_info.virtual_sensor_info->coefficients_type[i],
+ force_no_cache, sensor_log_map)) {
+ LOG(ERROR) << "Failed to read " << sensor_name.data() << "'s coefficient "
+ << sensor_info.virtual_sensor_info->coefficients[i];
+ return false;
+ }
+ if (std::isnan(coefficient)) {
+ LOG(INFO) << sensor_name << " data is under collecting";
+ return true;
+ }
+ switch (sensor_info.virtual_sensor_info->formula) {
+ case FormulaOption::COUNT_THRESHOLD:
+ if ((coefficient < 0 && sensor_readings[i] < -coefficient) ||
+ (coefficient >= 0 && sensor_readings[i] >= coefficient))
+ temp_val += 1;
+ break;
+ case FormulaOption::WEIGHTED_AVG:
+ temp_val += sensor_readings[i] * coefficient;
+ break;
+ case FormulaOption::MAXIMUM:
+ if (i == 0)
+ temp_val = std::numeric_limits<float>::lowest();
+ if (sensor_readings[i] * coefficient > temp_val)
+ temp_val = sensor_readings[i] * coefficient;
+ break;
+ case FormulaOption::MINIMUM:
+ if (i == 0)
+ temp_val = std::numeric_limits<float>::max();
+ if (sensor_readings[i] * coefficient < temp_val)
+ temp_val = sensor_readings[i] * coefficient;
+ break;
+ default:
+ LOG(ERROR) << "Unknown formula type for sensor " << sensor_name.data();
+ return false;
+ }
}
+ *temp = (temp_val + sensor_info.virtual_sensor_info->offset);
}
- *temp = (temp_val + sensor_info.virtual_sensor_info->offset);
}
if (!isnan(sensor_info.step_ratio) && !isnan(sensor_status.thermal_cached.temp) &&
@@ -993,6 +1106,7 @@ std::chrono::milliseconds ThermalHelperImpl::thermalWatcherCallbackFunc(
TemperatureThreshold threshold;
SensorStatus &sensor_status = name_status_pair.second;
const SensorInfo &sensor_info = sensor_info_map_.at(name_status_pair.first);
+ bool max_throttling = false;
// Only handle the sensors in allow list
if (!sensor_info.is_watch) {
@@ -1046,12 +1160,10 @@ std::chrono::milliseconds ThermalHelperImpl::thermalWatcherCallbackFunc(
}
{
std::lock_guard<std::shared_mutex> _lock(sensor_status_map_mutex_);
- if (sensor_status.emul_setting != nullptr &&
- sensor_status.emul_setting->pending_update) {
- force_update = true;
- sensor_status.emul_setting->pending_update = false;
- LOG(INFO) << "Update " << name_status_pair.first.data()
- << " right away with emul setting";
+ max_throttling = sensor_status.override_status.max_throttling;
+ if (sensor_status.override_status.pending_update) {
+ force_update = sensor_status.override_status.pending_update;
+ sensor_status.override_status.pending_update = false;
}
}
LOG(VERBOSE) << "sensor " << name_status_pair.first
@@ -1110,7 +1222,7 @@ std::chrono::milliseconds ThermalHelperImpl::thermalWatcherCallbackFunc(
// update thermal throttling request
thermal_throttling_.thermalThrottlingUpdate(
temp, sensor_info, sensor_status.severity, time_elapsed_ms,
- power_files_.GetPowerStatusMap(), cooling_device_info_map_);
+ power_files_.GetPowerStatusMap(), cooling_device_info_map_, max_throttling);
}
thermal_throttling_.computeCoolingDevicesRequest(
diff --git a/thermal/thermal-helper.h b/thermal/thermal-helper.h
index 6ae13c43..489b792a 100644
--- a/thermal/thermal-helper.h
+++ b/thermal/thermal-helper.h
@@ -55,9 +55,14 @@ struct ThermalSample {
boot_clock::time_point timestamp;
};
-struct EmulSetting {
- float emul_temp;
- int emul_severity;
+struct EmulTemp {
+ float temp;
+ int severity;
+};
+
+struct OverrideStatus {
+ std::unique_ptr<EmulTemp> emul_temp;
+ bool max_throttling;
bool pending_update;
};
@@ -67,7 +72,7 @@ struct SensorStatus {
ThrottlingSeverity prev_cold_severity;
boot_clock::time_point last_update_time;
ThermalSample thermal_cached;
- std::unique_ptr<EmulSetting> emul_setting;
+ OverrideStatus override_status;
};
class ThermalHelper {
@@ -79,8 +84,10 @@ class ThermalHelper {
std::vector<TemperatureThreshold> *thresholds) const = 0;
virtual bool fillCurrentCoolingDevices(bool filterType, CoolingType type,
std::vector<CoolingDevice> *coolingdevices) const = 0;
- virtual bool emulTemp(std::string_view target_sensor, const float temp) = 0;
- virtual bool emulSeverity(std::string_view target_sensor, const int severity) = 0;
+ virtual bool emulTemp(std::string_view target_sensor, const float temp,
+ const bool max_throttling) = 0;
+ virtual bool emulSeverity(std::string_view target_sensor, const int severity,
+ const bool max_throttling) = 0;
virtual bool emulClear(std::string_view target_sensor) = 0;
virtual bool isInitializedOk() const = 0;
virtual bool readTemperature(
@@ -117,8 +124,10 @@ class ThermalHelperImpl : public ThermalHelper {
std::vector<TemperatureThreshold> *thresholds) const override;
bool fillCurrentCoolingDevices(bool filterType, CoolingType type,
std::vector<CoolingDevice> *coolingdevices) const override;
- bool emulTemp(std::string_view target_sensor, const float temp) override;
- bool emulSeverity(std::string_view target_sensor, const int severity) override;
+ bool emulTemp(std::string_view target_sensor, const float temp,
+ const bool max_throttling) override;
+ bool emulSeverity(std::string_view target_sensor, const int severity,
+ const bool max_throttling) override;
bool emulClear(std::string_view target_sensor) override;
// Disallow copy and assign.
@@ -203,10 +212,13 @@ class ThermalHelperImpl : public ThermalHelper {
// Read temperature data according to thermal sensor's info
bool readThermalSensor(std::string_view sensor_name, float *temp, const bool force_sysfs,
std::map<std::string, float> *sensor_log_map);
+ float runVirtualTempEstimator(std::string_view sensor_name,
+ std::map<std::string, float> *sensor_log_map);
void updateCoolingDevices(const std::vector<std::string> &cooling_devices_to_update);
// Check the max CDEV state for cdev_ceiling
void maxCoolingRequestCheck(
std::unordered_map<std::string, BindedCdevInfo> *binded_cdev_info_map);
+ void checkUpdateSensorForEmul(std::string_view target_sensor, const bool max_throttling);
sp<ThermalWatcher> thermal_watcher_;
PowerFiles power_files_;
ThermalFiles thermal_sensors_;
diff --git a/thermal/utils/power_files.cpp b/thermal/utils/power_files.cpp
index 767f434b..2e4a77ff 100644
--- a/thermal/utils/power_files.cpp
+++ b/thermal/utils/power_files.cpp
@@ -125,8 +125,8 @@ bool PowerFiles::registerPowerRailsToWatch(const Json::Value &config) {
if (power_history.size()) {
power_status_map_[power_rail_info_pair.first] = {
- .power_history = power_history,
.last_update_time = boot_clock::time_point::min(),
+ .power_history = power_history,
.last_updated_avg_power = NAN,
};
} else {
diff --git a/thermal/utils/powerhal_helper.cpp b/thermal/utils/powerhal_helper.cpp
index f8ac7d0b..8b633908 100644
--- a/thermal/utils/powerhal_helper.cpp
+++ b/thermal/utils/powerhal_helper.cpp
@@ -58,7 +58,8 @@ bool PowerHalService::connect() {
}
const std::string kInstance = std::string(IPower::descriptor) + "/default";
- ndk::SpAIBinder power_binder = ndk::SpAIBinder(AServiceManager_getService(kInstance.c_str()));
+ ndk::SpAIBinder power_binder =
+ ndk::SpAIBinder(AServiceManager_waitForService(kInstance.c_str()));
ndk::SpAIBinder ext_power_binder;
if (power_binder.get() == nullptr) {
diff --git a/thermal/utils/thermal_info.cpp b/thermal/utils/thermal_info.cpp
index c5535c5f..d03b6d87 100644
--- a/thermal/utils/thermal_info.cpp
+++ b/thermal/utils/thermal_info.cpp
@@ -129,8 +129,75 @@ bool getFloatFromJsonValues(const Json::Value &values, ThrottlingArray *out, boo
*out = ret;
return true;
}
+
+bool getTempRangeInfoFromJsonValues(const Json::Value &values, TempRangeInfo *temp_range_info) {
+ if (values.size() != 2) {
+ LOG(ERROR) << "Temp Range Values size: " << values.size() << "is invalid.";
+ return false;
+ }
+
+ float min_temp = getFloatFromValue(values[0]);
+ float max_temp = getFloatFromValue(values[1]);
+
+ if (std::isnan(min_temp) || std::isnan(max_temp)) {
+ LOG(ERROR) << "Illegal temp range: thresholds not defined properly " << min_temp << " : "
+ << max_temp;
+ return false;
+ }
+
+ if (min_temp > max_temp) {
+ LOG(ERROR) << "Illegal temp range: temp_min_threshold(" << min_temp
+ << ") > temp_max_threshold(" << max_temp << ")";
+ return false;
+ }
+ temp_range_info->min_temp_threshold = min_temp;
+ temp_range_info->max_temp_threshold = max_temp;
+ LOG(INFO) << "Temp Range Info: " << temp_range_info->min_temp_threshold
+ << " <= t <= " << temp_range_info->max_temp_threshold;
+ return true;
+}
+
+bool getTempStuckInfoFromJsonValue(const Json::Value &values, TempStuckInfo *temp_stuck_info) {
+ if (values["MinStuckDuration"].empty()) {
+ LOG(ERROR) << "Minimum stuck duration not present.";
+ return false;
+ }
+ int min_stuck_duration_int = getIntFromValue(values["MinStuckDuration"]);
+ if (min_stuck_duration_int <= 0) {
+ LOG(ERROR) << "Invalid Minimum stuck duration " << min_stuck_duration_int;
+ return false;
+ }
+
+ if (values["MinPollingCount"].empty()) {
+ LOG(ERROR) << "Minimum polling count not present.";
+ return false;
+ }
+ int min_polling_count = getIntFromValue(values["MinPollingCount"]);
+ if (min_polling_count <= 0) {
+ LOG(ERROR) << "Invalid Minimum stuck duration " << min_polling_count;
+ return false;
+ }
+ temp_stuck_info->min_stuck_duration = std::chrono::milliseconds(min_stuck_duration_int);
+ temp_stuck_info->min_polling_count = min_polling_count;
+ LOG(INFO) << "Temp Stuck Info: polling_count=" << temp_stuck_info->min_polling_count
+ << " stuck_duration=" << temp_stuck_info->min_stuck_duration.count();
+ return true;
+}
} // namespace
+std::ostream &operator<<(std::ostream &stream, const SensorFusionType &sensor_fusion_type) {
+ switch (sensor_fusion_type) {
+ case SensorFusionType::SENSOR:
+ return stream << "SENSOR";
+ case SensorFusionType::ODPM:
+ return stream << "ODPM";
+ case SensorFusionType::CONSTANT:
+ return stream << "CONSTANT";
+ default:
+ return stream << "UNDEFINED";
+ }
+}
+
bool ParseThermalConfig(std::string_view config_path, Json::Value *config) {
std::string json_doc;
if (!::android::base::ReadFileToString(config_path.data(), &json_doc)) {
@@ -165,6 +232,8 @@ bool ParseVirtualSensorInfo(const std::string_view name, const Json::Value &sens
std::vector<std::string> coefficients;
std::vector<SensorFusionType> coefficients_type;
FormulaOption formula = FormulaOption::COUNT_THRESHOLD;
+ std::string vt_estimator_model_file;
+ std::unique_ptr<::thermal::vtestimator::VirtualTempEstimator> vt_estimator;
Json::Value values = sensor["Combination"];
if (values.size()) {
@@ -178,6 +247,21 @@ bool ParseVirtualSensorInfo(const std::string_view name, const Json::Value &sens
return false;
}
+ if (sensor["Formula"].asString().compare("COUNT_THRESHOLD") == 0) {
+ formula = FormulaOption::COUNT_THRESHOLD;
+ } else if (sensor["Formula"].asString().compare("WEIGHTED_AVG") == 0) {
+ formula = FormulaOption::WEIGHTED_AVG;
+ } else if (sensor["Formula"].asString().compare("MAXIMUM") == 0) {
+ formula = FormulaOption::MAXIMUM;
+ } else if (sensor["Formula"].asString().compare("MINIMUM") == 0) {
+ formula = FormulaOption::MINIMUM;
+ } else if (sensor["Formula"].asString().compare("USE_ML_MODEL") == 0) {
+ formula = FormulaOption::USE_ML_MODEL;
+ } else {
+ LOG(ERROR) << "Sensor[" << name << "]'s Formula is invalid";
+ return false;
+ }
+
values = sensor["CombinationType"];
if (!values.size()) {
linked_sensors_type.reserve(linked_sensors.size());
@@ -216,7 +300,8 @@ bool ParseVirtualSensorInfo(const std::string_view name, const Json::Value &sens
LOG(ERROR) << "Sensor[" << name << "] has no Coefficient setting";
return false;
}
- if (linked_sensors.size() != coefficients.size()) {
+ if ((linked_sensors.size() != coefficients.size()) &&
+ (formula != FormulaOption::USE_ML_MODEL)) {
LOG(ERROR) << "Sensor[" << name << "] has invalid Coefficient size";
return false;
}
@@ -280,21 +365,43 @@ bool ParseVirtualSensorInfo(const std::string_view name, const Json::Value &sens
}
}
- if (sensor["Formula"].asString().compare("COUNT_THRESHOLD") == 0) {
- formula = FormulaOption::COUNT_THRESHOLD;
- } else if (sensor["Formula"].asString().compare("WEIGHTED_AVG") == 0) {
- formula = FormulaOption::WEIGHTED_AVG;
- } else if (sensor["Formula"].asString().compare("MAXIMUM") == 0) {
- formula = FormulaOption::MAXIMUM;
- } else if (sensor["Formula"].asString().compare("MINIMUM") == 0) {
- formula = FormulaOption::MINIMUM;
- } else {
- LOG(ERROR) << "Sensor[" << name << "]'s Formula is invalid";
- return false;
+ if (formula == FormulaOption::USE_ML_MODEL) {
+ if (sensor["ModelPath"].empty()) {
+ LOG(ERROR) << "Sensor[" << name << "] has no ModelPath";
+ return false;
+ }
+
+ if (!linked_sensors.size()) {
+ LOG(ERROR) << "Sensor[" << name << "] uses USE_ML_MODEL and has zero linked_sensors";
+ return false;
+ }
+
+ vt_estimator = std::make_unique<::thermal::vtestimator::VirtualTempEstimator>(
+ linked_sensors.size());
+ if (!vt_estimator) {
+ LOG(ERROR) << "Failed to create vt estimator for Sensor[" << name
+ << "] with linked sensor size : " << linked_sensors.size();
+ return false;
+ }
+
+ vt_estimator_model_file = "vendor/etc/" + sensor["ModelPath"].asString();
+
+ ::thermal::vtestimator::VtEstimatorStatus ret =
+ vt_estimator->Initialize(vt_estimator_model_file.c_str());
+ if (ret != ::thermal::vtestimator::kVtEstimatorOk) {
+ LOG(ERROR) << "Failed to initialize vt estimator for Sensor[" << name
+ << "] with ModelPath: " << vt_estimator_model_file
+ << " with ret code : " << ret;
+ return false;
+ }
+
+ LOG(INFO) << "Successfully created vt_estimator for Sensor[" << name
+ << "] with input samples: " << linked_sensors.size();
}
- virtual_sensor_info->reset(new VirtualSensorInfo{linked_sensors, linked_sensors_type,
- coefficients, coefficients_type, offset,
- trigger_sensors, formula});
+
+ virtual_sensor_info->reset(new VirtualSensorInfo{
+ linked_sensors, linked_sensors_type, coefficients, coefficients_type, offset,
+ trigger_sensors, formula, vt_estimator_model_file, std::move(vt_estimator)});
return true;
}
@@ -434,14 +541,14 @@ bool ParseBindedCdevInfo(const Json::Value &values,
.limit_info = limit_info,
.power_thresholds = power_thresholds,
.release_logic = release_logic,
- .high_power_check = high_power_check,
- .throttling_with_power_link = throttling_with_power_link,
.cdev_weight_for_pid = cdev_weight_for_pid,
.cdev_ceiling = cdev_ceiling,
.max_release_step = max_release_step,
.max_throttle_step = max_throttle_step,
.cdev_floor_with_power_link = cdev_floor_with_power_link,
.power_rail = power_rail,
+ .high_power_check = high_power_check,
+ .throttling_with_power_link = throttling_with_power_link,
.enabled = enabled,
};
}
@@ -1023,14 +1130,6 @@ bool ParsePowerRailInfo(const Json::Value &config,
return false;
}
- std::string rail;
- if (power_rails[i]["Rail"].empty()) {
- rail = name;
- } else {
- rail = power_rails[i]["Rail"].asString();
- }
- LOG(INFO) << "PowerRail[" << i << "]'s Rail: " << rail;
-
std::vector<std::string> linked_power_rails;
std::vector<float> coefficient;
float offset = 0;
@@ -1124,7 +1223,6 @@ bool ParsePowerRailInfo(const Json::Value &config,
}
(*power_rails_parsed)[name] = {
- .rail = rail,
.power_sample_count = power_sample_count,
.power_sample_delay = power_sample_delay,
.virtual_power_rail_info = std::move(virtual_power_rail_info),
@@ -1232,44 +1330,180 @@ bool ParseStatsInfo(const Json::Value &stats_config,
return true;
}
-bool ParseStatsConfig(const Json::Value &config,
- const std::unordered_map<std::string, SensorInfo> &sensor_info_map_,
- const std::unordered_map<std::string, CdevInfo> &cooling_device_info_map_,
- StatsConfig *stats_config_parsed) {
- Json::Value stats_config = config["Stats"];
+bool ParseSensorAbnormalStatsConfig(
+ const Json::Value &abnormal_stats_config,
+ const std::unordered_map<std::string, SensorInfo> &sensor_info_map_,
+ AbnormalStatsInfo *abnormal_stats_info_parsed) {
+ if (abnormal_stats_config.empty()) {
+ LOG(INFO) << "No sensors abnormality monitoring info present.";
+ return true;
+ }
+
+ Json::Value values;
+
+ std::optional<TempRangeInfo> default_temp_range_info;
+ std::vector<AbnormalStatsInfo::SensorsTempRangeInfo> sensors_temp_range_infos;
+ Json::Value outlier_temp_config = abnormal_stats_config["Outlier"];
+ if (outlier_temp_config) {
+ LOG(INFO) << "Start to parse outlier temp config.";
+
+ if (outlier_temp_config["Default"]) {
+ LOG(INFO) << "Start to parse defaultTempRange.";
+ if (!getTempRangeInfoFromJsonValues(outlier_temp_config["Default"],
+ &default_temp_range_info.value())) {
+ LOG(ERROR) << "Failed to parse default temp range config.";
+ return false;
+ }
+ }
+
+ Json::Value configs = outlier_temp_config["Configs"];
+ if (configs) {
+ std::unordered_set<std::string> sensors_parsed;
+ for (Json::Value::ArrayIndex i = 0; i < configs.size(); i++) {
+ LOG(INFO) << "Start to parse temp range config[" << i << "]";
+ AbnormalStatsInfo::SensorsTempRangeInfo sensors_temp_range_info;
+ values = configs[i]["Monitor"];
+ if (!values.size()) {
+ LOG(ERROR) << "Invalid config no sensor list present for outlier temp "
+ "config.";
+ return false;
+ }
+ for (Json::Value::ArrayIndex j = 0; j < values.size(); j++) {
+ const std::string &sensor = values[j].asString();
+ if (!sensor_info_map_.count(sensor)) {
+ LOG(ERROR) << "Unknown sensor " << sensor;
+ return false;
+ }
+ auto result = sensors_parsed.insert(sensor);
+ if (!result.second) {
+ LOG(ERROR) << "Duplicate Sensor Temp Range Config: " << sensor;
+ return false;
+ }
+ LOG(INFO) << "Monitored sensor [" << j << "]: " << sensor;
+ sensors_temp_range_info.sensors.push_back(sensor);
+ }
+ if (!getTempRangeInfoFromJsonValues(configs[i]["TempRange"],
+ &sensors_temp_range_info.temp_range_info)) {
+ LOG(ERROR) << "Failed to parse temp range config.";
+ return false;
+ }
+ sensors_temp_range_infos.push_back(sensors_temp_range_info);
+ }
+ }
+ }
+ std::optional<TempStuckInfo> default_temp_stuck_info;
+ std::vector<AbnormalStatsInfo::SensorsTempStuckInfo> sensors_temp_stuck_infos;
+ Json::Value stuck_temp_config = abnormal_stats_config["Stuck"];
+ if (stuck_temp_config) {
+ LOG(INFO) << "Start to parse stuck temp config.";
+
+ if (stuck_temp_config["Default"]) {
+ LOG(INFO) << "Start to parse defaultTempStuck.";
+ if (!getTempStuckInfoFromJsonValue(stuck_temp_config["Default"],
+ &default_temp_stuck_info.value())) {
+ LOG(ERROR) << "Failed to parse default temp stuck config.";
+ return false;
+ }
+ }
+
+ Json::Value configs = stuck_temp_config["Configs"];
+ if (configs) {
+ std::unordered_set<std::string> sensors_parsed;
+ for (Json::Value::ArrayIndex i = 0; i < configs.size(); i++) {
+ LOG(INFO) << "Start to parse temp stuck config[" << i << "]";
+ AbnormalStatsInfo::SensorsTempStuckInfo sensor_temp_stuck_info;
+ values = configs[i]["Monitor"];
+ if (!values.size()) {
+ LOG(ERROR) << "Invalid config no sensor list present for stuck temp "
+ "config.";
+ return false;
+ }
+ for (Json::Value::ArrayIndex j = 0; j < values.size(); j++) {
+ const std::string &sensor = values[j].asString();
+ if (!sensor_info_map_.count(sensor)) {
+ LOG(ERROR) << "Unknown sensor " << sensor;
+ return false;
+ }
+ auto result = sensors_parsed.insert(sensor);
+ if (!result.second) {
+ LOG(ERROR) << "Duplicate Sensor Temp Stuck Config: " << sensor;
+ return false;
+ }
+ LOG(INFO) << "Monitored sensor [" << j << "]: " << sensor;
+ sensor_temp_stuck_info.sensors.push_back(sensor);
+ }
+ if (!getTempStuckInfoFromJsonValue(configs[i]["TempStuck"],
+ &sensor_temp_stuck_info.temp_stuck_info)) {
+ LOG(ERROR) << "Failed to parse temp stuck config.";
+ return false;
+ }
+ sensors_temp_stuck_infos.push_back(sensor_temp_stuck_info);
+ }
+ }
+ }
+ *abnormal_stats_info_parsed = {
+ .default_temp_range_info = default_temp_range_info,
+ .sensors_temp_range_infos = sensors_temp_range_infos,
+ .default_temp_stuck_info = default_temp_stuck_info,
+ .sensors_temp_stuck_infos = sensors_temp_stuck_infos,
+ };
+ return true;
+}
+bool ParseSensorStatsConfig(const Json::Value &config,
+ const std::unordered_map<std::string, SensorInfo> &sensor_info_map_,
+ StatsInfo<float> *sensor_stats_info_parsed,
+ AbnormalStatsInfo *abnormal_stats_info_parsed) {
+ Json::Value stats_config = config["Stats"];
if (stats_config.empty()) {
LOG(INFO) << "No Stats Config present.";
return true;
}
-
+ // Parse cooling device user vote
+ Json::Value sensor_config = stats_config["Sensors"];
+ if (sensor_config.empty()) {
+ LOG(INFO) << "No Sensor Stats Config present.";
+ return true;
+ }
LOG(INFO) << "Parse Stats Config for Sensor Temp.";
// Parse sensor stats config
- if (!ParseStatsInfo(stats_config["Sensors"], sensor_info_map_,
- &stats_config_parsed->sensor_stats_info,
+ if (!ParseStatsInfo(stats_config["Sensors"], sensor_info_map_, sensor_stats_info_parsed,
std::numeric_limits<float>::lowest())) {
LOG(ERROR) << "Failed to parse sensor temp stats info.";
- stats_config_parsed->clear();
+ sensor_stats_info_parsed->clear();
+ return false;
+ }
+ if (!ParseSensorAbnormalStatsConfig(sensor_config["Abnormality"], sensor_info_map_,
+ abnormal_stats_info_parsed)) {
+ LOG(ERROR) << "Failed to parse sensor abnormal stats config.";
return false;
}
+ return true;
+}
+bool ParseCoolingDeviceStatsConfig(
+ const Json::Value &config,
+ const std::unordered_map<std::string, CdevInfo> &cooling_device_info_map_,
+ StatsInfo<int> *cooling_device_request_info_parsed) {
+ Json::Value stats_config = config["Stats"];
+ if (stats_config.empty()) {
+ LOG(INFO) << "No Stats Config present.";
+ return true;
+ }
// Parse cooling device user vote
if (stats_config["CoolingDevices"].empty()) {
LOG(INFO) << "No cooling device stats present.";
return true;
}
-
LOG(INFO) << "Parse Stats Config for Sensor CDev Request.";
if (!ParseStatsInfo(stats_config["CoolingDevices"]["RecordVotePerSensor"],
- cooling_device_info_map_, &stats_config_parsed->cooling_device_request_info,
- -1)) {
+ cooling_device_info_map_, cooling_device_request_info_parsed, -1)) {
LOG(ERROR) << "Failed to parse cooling device user vote stats info.";
- stats_config_parsed->clear();
+ cooling_device_request_info_parsed->clear();
return false;
}
return true;
}
-
} // namespace implementation
} // namespace thermal
} // namespace hardware
diff --git a/thermal/utils/thermal_info.h b/thermal/utils/thermal_info.h
index 12997f26..843424e6 100644
--- a/thermal/utils/thermal_info.h
+++ b/thermal/utils/thermal_info.h
@@ -27,6 +27,8 @@
#include <unordered_set>
#include <variant>
+#include "virtualtemp_estimator/virtualtemp_estimator.h"
+
namespace aidl {
namespace android {
namespace hardware {
@@ -48,11 +50,12 @@ constexpr int kMaxPowerLogPerLine = 6;
constexpr int kMaxStatsResidencyCount = 20;
constexpr int kMaxStatsThresholdCount = kMaxStatsResidencyCount - 1;
-enum FormulaOption : uint32_t {
+enum class FormulaOption : uint32_t {
COUNT_THRESHOLD = 0,
WEIGHTED_AVG,
MAXIMUM,
MINIMUM,
+ USE_ML_MODEL
};
template <typename T>
@@ -93,12 +96,40 @@ struct StatsConfig {
}
};
-enum SensorFusionType : uint32_t {
+struct TempRangeInfo {
+ int max_temp_threshold;
+ int min_temp_threshold;
+};
+
+struct TempStuckInfo {
+ int min_polling_count;
+ std::chrono::milliseconds min_stuck_duration;
+};
+
+struct AbnormalStatsInfo {
+ struct SensorsTempRangeInfo {
+ std::vector<std::string> sensors;
+ TempRangeInfo temp_range_info;
+ };
+ struct SensorsTempStuckInfo {
+ std::vector<std::string> sensors;
+ TempStuckInfo temp_stuck_info;
+ };
+
+ std::optional<TempRangeInfo> default_temp_range_info;
+ std::vector<SensorsTempRangeInfo> sensors_temp_range_infos;
+ std::optional<TempStuckInfo> default_temp_stuck_info;
+ std::vector<SensorsTempStuckInfo> sensors_temp_stuck_infos;
+};
+
+enum class SensorFusionType : uint32_t {
SENSOR = 0,
ODPM,
CONSTANT,
};
+std::ostream &operator<<(std::ostream &os, const SensorFusionType &sensor_fusion_type);
+
struct VirtualSensorInfo {
std::vector<std::string> linked_sensors;
std::vector<SensorFusionType> linked_sensors_type;
@@ -108,6 +139,8 @@ struct VirtualSensorInfo {
float offset;
std::vector<std::string> trigger_sensors;
FormulaOption formula;
+ std::string vt_estimator_model_file;
+ std::unique_ptr<::thermal::vtestimator::VirtualTempEstimator> vt_estimator;
};
struct VirtualPowerRailInfo {
@@ -118,7 +151,7 @@ struct VirtualPowerRailInfo {
};
// The method when the ODPM power is lower than threshold
-enum ReleaseLogic : uint32_t {
+enum class ReleaseLogic : uint32_t {
INCREASE = 0, // Increase throttling by step
DECREASE, // Decrease throttling by step
STEPWISE, // Support both increase and decrease logix
@@ -195,7 +228,6 @@ struct CdevInfo {
};
struct PowerRailInfo {
- std::string rail;
int power_sample_count;
std::chrono::milliseconds power_sample_delay;
std::unique_ptr<VirtualPowerRailInfo> virtual_power_rail_info;
@@ -208,10 +240,14 @@ bool ParseCoolingDevice(const Json::Value &config,
std::unordered_map<std::string, CdevInfo> *cooling_device_parsed);
bool ParsePowerRailInfo(const Json::Value &config,
std::unordered_map<std::string, PowerRailInfo> *power_rail_parsed);
-bool ParseStatsConfig(const Json::Value &config,
- const std::unordered_map<std::string, SensorInfo> &sensor_info_map_,
- const std::unordered_map<std::string, CdevInfo> &cooling_device_info_map_,
- StatsConfig *stats_config);
+bool ParseSensorStatsConfig(const Json::Value &config,
+ const std::unordered_map<std::string, SensorInfo> &sensor_info_map_,
+ StatsInfo<float> *sensor_stats_info_parsed,
+ AbnormalStatsInfo *abnormal_stats_info_parsed);
+bool ParseCoolingDeviceStatsConfig(
+ const Json::Value &config,
+ const std::unordered_map<std::string, CdevInfo> &cooling_device_info_map_,
+ StatsInfo<int> *cooling_device_request_info_parsed);
} // namespace implementation
} // namespace thermal
} // namespace hardware
diff --git a/thermal/utils/thermal_stats_helper.cpp b/thermal/utils/thermal_stats_helper.cpp
index bbd99542..d4571d90 100644
--- a/thermal/utils/thermal_stats_helper.cpp
+++ b/thermal/utils/thermal_stats_helper.cpp
@@ -18,7 +18,6 @@
#include <android-base/logging.h>
#include <android/binder_manager.h>
-#include <hardware/google/pixel/pixelstats/pixelatoms.pb.h>
#include <algorithm>
#include <numeric>
@@ -75,26 +74,49 @@ int calculateThresholdBucket(const std::vector<T> &thresholds, T value) {
return bucket;
}
+void resetCurrentTempStatus(CurrTempStatus *curr_temp_status, float new_temp) {
+ curr_temp_status->temp = new_temp;
+ curr_temp_status->start_time = boot_clock::now();
+ curr_temp_status->repeat_count = 1;
+}
+
} // namespace
bool ThermalStatsHelper::initializeStats(
const Json::Value &config,
const std::unordered_map<std::string, SensorInfo> &sensor_info_map_,
const std::unordered_map<std::string, CdevInfo> &cooling_device_info_map_) {
- StatsConfig stats_config;
- if (!ParseStatsConfig(config, sensor_info_map_, cooling_device_info_map_, &stats_config)) {
- LOG(ERROR) << "Failed to parse stats config";
+ StatsInfo<float> sensor_stats_info;
+ AbnormalStatsInfo abnormal_stats_info;
+ if (!ParseSensorStatsConfig(config, sensor_info_map_, &sensor_stats_info,
+ &abnormal_stats_info)) {
+ LOG(ERROR) << "Failed to parse sensor stats config";
+ return false;
+ }
+ StatsInfo<int> cooling_device_request_info;
+ if (!ParseCoolingDeviceStatsConfig(config, cooling_device_info_map_,
+ &cooling_device_request_info)) {
+ LOG(ERROR) << "Failed to parse cooling device stats config";
+ return false;
+ }
+ if (!initializeSensorTempStats(sensor_stats_info, sensor_info_map_)) {
+ LOG(ERROR) << "Failed to initialize sensor temp stats";
return false;
}
- bool is_initialized_ =
- initializeSensorTempStats(stats_config.sensor_stats_info, sensor_info_map_) &&
- initializeSensorCdevRequestStats(stats_config.cooling_device_request_info,
- sensor_info_map_, cooling_device_info_map_);
- if (is_initialized_) {
- last_total_stats_report_time = boot_clock::now();
- LOG(INFO) << "Thermal Stats Initialized Successfully";
+ if (!initializeSensorCdevRequestStats(cooling_device_request_info, sensor_info_map_,
+ cooling_device_info_map_)) {
+ LOG(ERROR) << "Failed to initialize sensor cooling device request stats";
+ return false;
+ }
+ if (!initializeSensorAbnormalityStats(abnormal_stats_info, sensor_info_map_)) {
+ LOG(ERROR) << "Failed to initialize sensor abnormal stats";
+ return false;
}
- return is_initialized_;
+
+ last_total_stats_report_time = boot_clock::now();
+ abnormal_stats_reported_per_update_interval = 0;
+ LOG(INFO) << "Thermal Stats Initialized Successfully";
+ return true;
}
bool ThermalStatsHelper::initializeSensorCdevRequestStats(
@@ -161,7 +183,8 @@ bool ThermalStatsHelper::initializeSensorCdevRequestStats(
bool ThermalStatsHelper::initializeSensorTempStats(
const StatsInfo<float> &sensor_stats_info,
const std::unordered_map<std::string, SensorInfo> &sensor_info_map_) {
- std::unique_lock<std::shared_mutex> _lock(sensor_temp_stats_map_mutex_);
+ std::unique_lock<std::shared_mutex> _lock(sensor_stats_mutex_);
+ auto &temp_stats_map_ = sensor_stats.temp_stats_map_;
const int severity_time_in_state_size = kThrottlingSeverityCount;
for (const auto &[sensor, sensor_info] : sensor_info_map_) {
// Record by severity
@@ -169,7 +192,7 @@ bool ThermalStatsHelper::initializeSensorTempStats(
isRecordByDefaultThreshold(
sensor_stats_info.record_by_default_threshold_all_or_name_set_, sensor)) {
// number of buckets = number of severity
- sensor_temp_stats_map_[sensor].stats_by_default_threshold =
+ temp_stats_map_[sensor].stats_by_default_threshold =
StatsRecord(severity_time_in_state_size);
LOG(INFO) << "Sensor temp stats on basis of severity initialized for [" << sensor
<< "]";
@@ -178,8 +201,7 @@ bool ThermalStatsHelper::initializeSensorTempStats(
// Record by custom threshold
if (sensor_stats_info.record_by_threshold.count(sensor)) {
for (const auto &threshold_list : sensor_stats_info.record_by_threshold.at(sensor)) {
- sensor_temp_stats_map_[sensor].stats_by_custom_threshold.emplace_back(
- threshold_list);
+ temp_stats_map_[sensor].stats_by_custom_threshold.emplace_back(threshold_list);
LOG(INFO) << "Sensor temp stats on basis of threshold initialized for [" << sensor
<< "]";
}
@@ -188,6 +210,54 @@ bool ThermalStatsHelper::initializeSensorTempStats(
return true;
}
+bool ThermalStatsHelper::initializeSensorAbnormalityStats(
+ const AbnormalStatsInfo &abnormal_stats_info,
+ const std::unordered_map<std::string, SensorInfo> &sensor_info_map_) {
+ std::unique_lock<std::shared_mutex> _lock(sensor_stats_mutex_);
+ auto &temp_range_info_map_ = sensor_stats.temp_range_info_map_;
+ for (const auto &sensors_temp_range_info : abnormal_stats_info.sensors_temp_range_infos) {
+ const auto &temp_range_info_ptr =
+ std::make_shared<TempRangeInfo>(sensors_temp_range_info.temp_range_info);
+ for (const auto &sensor : sensors_temp_range_info.sensors) {
+ temp_range_info_map_[sensor] = temp_range_info_ptr;
+ }
+ }
+ auto &temp_stuck_info_map_ = sensor_stats.temp_stuck_info_map_;
+ for (const auto &sensors_temp_stuck_info : abnormal_stats_info.sensors_temp_stuck_infos) {
+ const auto &temp_stuck_info_ptr =
+ std::make_shared<TempStuckInfo>(sensors_temp_stuck_info.temp_stuck_info);
+ for (const auto &sensor : sensors_temp_stuck_info.sensors) {
+ temp_stuck_info_map_[sensor] = temp_stuck_info_ptr;
+ }
+ }
+ const auto &default_temp_range_info_ptr =
+ abnormal_stats_info.default_temp_range_info
+ ? std::make_shared<TempRangeInfo>(
+ abnormal_stats_info.default_temp_range_info.value())
+ : nullptr;
+ const auto &default_temp_stuck_info_ptr =
+ abnormal_stats_info.default_temp_stuck_info
+ ? std::make_shared<TempStuckInfo>(
+ abnormal_stats_info.default_temp_stuck_info.value())
+ : nullptr;
+ for (const auto &sensor_info : sensor_info_map_) {
+ const auto &sensor = sensor_info.first;
+ if (default_temp_range_info_ptr && !temp_range_info_map_.count(sensor))
+ temp_range_info_map_[sensor] = default_temp_range_info_ptr;
+ if (default_temp_stuck_info_ptr && !temp_stuck_info_map_.count(sensor))
+ temp_stuck_info_map_[sensor] = default_temp_stuck_info_ptr;
+ }
+
+ for (const auto &sensor_temp_stuck_info : temp_stuck_info_map_) {
+ sensor_stats.curr_temp_status_map_[sensor_temp_stuck_info.first] = {
+ .temp = std::numeric_limits<float>::min(),
+ .start_time = boot_clock::time_point::min(),
+ .repeat_count = 0,
+ };
+ }
+ return true;
+}
+
void ThermalStatsHelper::updateStatsRecord(StatsRecord *stats_record, int new_state) {
const auto now = boot_clock::now();
const auto cur_state_duration = std::chrono::duration_cast<std::chrono::milliseconds>(
@@ -231,11 +301,13 @@ void ThermalStatsHelper::updateSensorCdevRequestStats(std::string_view sensor,
void ThermalStatsHelper::updateSensorTempStatsByThreshold(std::string_view sensor,
float temperature) {
- std::unique_lock<std::shared_mutex> _lock(sensor_temp_stats_map_mutex_);
- if (!sensor_temp_stats_map_.count(sensor.data())) {
+ std::unique_lock<std::shared_mutex> _lock(sensor_stats_mutex_);
+ verifySensorAbnormality(sensor, temperature);
+ auto &temp_stats_map_ = sensor_stats.temp_stats_map_;
+ if (!temp_stats_map_.count(sensor.data())) {
return;
}
- auto &sensor_temp_stats = sensor_temp_stats_map_[sensor.data()];
+ auto &sensor_temp_stats = temp_stats_map_[sensor.data()];
for (auto &stats_by_threshold : sensor_temp_stats.stats_by_custom_threshold) {
int value = calculateThresholdBucket(stats_by_threshold.thresholds, temperature);
if (value != stats_by_threshold.stats_record.cur_state) {
@@ -256,11 +328,11 @@ void ThermalStatsHelper::updateSensorTempStatsByThreshold(std::string_view senso
void ThermalStatsHelper::updateSensorTempStatsBySeverity(std::string_view sensor,
const ThrottlingSeverity &severity) {
- std::unique_lock<std::shared_mutex> _lock(sensor_temp_stats_map_mutex_);
- if (sensor_temp_stats_map_.count(sensor.data()) &&
- sensor_temp_stats_map_[sensor.data()].stats_by_default_threshold.has_value()) {
- auto &stats_record =
- sensor_temp_stats_map_[sensor.data()].stats_by_default_threshold.value();
+ std::unique_lock<std::shared_mutex> _lock(sensor_stats_mutex_);
+ auto &temp_stats_map_ = sensor_stats.temp_stats_map_;
+ if (temp_stats_map_.count(sensor.data()) &&
+ temp_stats_map_[sensor.data()].stats_by_default_threshold.has_value()) {
+ auto &stats_record = temp_stats_map_[sensor.data()].stats_by_default_threshold.value();
int value = static_cast<int>(severity);
if (value != stats_record.cur_state) {
LOG(VERBOSE) << "Updating sensor stats for sensor: " << sensor.data()
@@ -270,6 +342,52 @@ void ThermalStatsHelper::updateSensorTempStatsBySeverity(std::string_view sensor
}
}
+void ThermalStatsHelper::verifySensorAbnormality(std::string_view sensor, float temp) {
+ LOG(VERBOSE) << "Verify sensor abnormality for " << sensor << " with temp " << temp;
+ if (sensor_stats.temp_range_info_map_.count(sensor.data())) {
+ const auto &temp_range_info = sensor_stats.temp_range_info_map_[sensor.data()];
+ if (temp < temp_range_info->min_temp_threshold) {
+ LOG(ERROR) << "Outlier Temperature Detected, sensor: " << sensor.data()
+ << " temp: " << temp << " < " << temp_range_info->min_temp_threshold;
+ reportThermalAbnormality(ThermalSensorAbnormalityDetected::EXTREME_LOW_TEMP, sensor,
+ std::round(temp));
+ } else if (temp > temp_range_info->max_temp_threshold) {
+ LOG(ERROR) << "Outlier Temperature Detected, sensor: " << sensor.data()
+ << " temp: " << temp << " > " << temp_range_info->max_temp_threshold;
+ reportThermalAbnormality(ThermalSensorAbnormalityDetected::EXTREME_HIGH_TEMP, sensor,
+ std::round(temp));
+ }
+ }
+ if (sensor_stats.temp_stuck_info_map_.count(sensor.data())) {
+ const auto &temp_stuck_info = sensor_stats.temp_stuck_info_map_[sensor.data()];
+ auto &curr_temp_status = sensor_stats.curr_temp_status_map_[sensor.data()];
+ LOG(VERBOSE) << "Current Temp Status: temp=" << curr_temp_status.temp
+ << " repeat_count=" << curr_temp_status.repeat_count
+ << " start_time=" << curr_temp_status.start_time.time_since_epoch().count();
+ if (std::fabs(curr_temp_status.temp - temp) <= kPrecisionThreshold) {
+ curr_temp_status.repeat_count++;
+ if (temp_stuck_info->min_polling_count <= curr_temp_status.repeat_count) {
+ auto time_elapsed_ms = std::chrono::duration_cast<std::chrono::milliseconds>(
+ boot_clock::now() - curr_temp_status.start_time);
+ if (temp_stuck_info->min_stuck_duration <= time_elapsed_ms) {
+ LOG(ERROR) << "Stuck Temperature Detected, sensor: " << sensor.data()
+ << " temp: " << temp << " repeated "
+ << temp_stuck_info->min_polling_count << " times for "
+ << time_elapsed_ms.count() << "ms";
+ if (reportThermalAbnormality(ThermalSensorAbnormalityDetected::SENSOR_STUCK,
+ sensor, std::round(temp))) {
+ // reset current status to verify for sensor stuck with start time as
+ // current polling
+ resetCurrentTempStatus(&curr_temp_status, temp);
+ }
+ }
+ }
+ } else {
+ resetCurrentTempStatus(&curr_temp_status, temp);
+ }
+ }
+}
+
int ThermalStatsHelper::reportStats() {
const auto curTime = boot_clock::now();
const auto since_last_total_stats_update_ms =
@@ -290,13 +408,14 @@ int ThermalStatsHelper::reportStats() {
int count_failed_reporting =
reportAllSensorTempStats(stats_client) + reportAllSensorCdevRequestStats(stats_client);
last_total_stats_report_time = curTime;
+ abnormal_stats_reported_per_update_interval = 0;
return count_failed_reporting;
}
int ThermalStatsHelper::reportAllSensorTempStats(const std::shared_ptr<IStats> &stats_client) {
int count_failed_reporting = 0;
- std::unique_lock<std::shared_mutex> _lock(sensor_temp_stats_map_mutex_);
- for (auto &[sensor, temp_stats] : sensor_temp_stats_map_) {
+ std::unique_lock<std::shared_mutex> _lock(sensor_stats_mutex_);
+ for (auto &[sensor, temp_stats] : sensor_stats.temp_stats_map_) {
for (size_t threshold_set_idx = 0;
threshold_set_idx < temp_stats.stats_by_custom_threshold.size(); threshold_set_idx++) {
auto &stats_by_threshold = temp_stats.stats_by_custom_threshold[threshold_set_idx];
@@ -445,6 +564,40 @@ std::vector<int64_t> ThermalStatsHelper::processStatsRecordForReporting(StatsRec
return stats_residency;
}
+bool ThermalStatsHelper::reportThermalAbnormality(
+ const ThermalSensorAbnormalityDetected::AbnormalityType &type, std::string_view name,
+ std::optional<int> reading) {
+ const auto value_str = reading.has_value() ? std::to_string(reading.value()) : "undefined";
+ if (abnormal_stats_reported_per_update_interval >= kMaxAbnormalLoggingPerUpdateInterval) {
+ LOG(ERROR) << "Thermal abnormal atom logging rate limited for " << name.data()
+ << " with value " << value_str;
+ return true;
+ }
+ const std::shared_ptr<IStats> stats_client = getStatsService();
+ if (!stats_client) {
+ LOG(ERROR) << "Unable to get AIDL Stats service";
+ return false;
+ }
+ std::vector<VendorAtomValue> values(3);
+ values[ThermalSensorAbnormalityDetected::kTypeFieldNumber - kVendorAtomOffset] =
+ VendorAtomValue::make<VendorAtomValue::intValue>(type);
+ values[ThermalSensorAbnormalityDetected::kSensorFieldNumber - kVendorAtomOffset] =
+ VendorAtomValue::make<VendorAtomValue::stringValue>(name);
+ if (reading.has_value()) {
+ values[ThermalSensorAbnormalityDetected::kTempFieldNumber - kVendorAtomOffset] =
+ VendorAtomValue::make<VendorAtomValue::intValue>(reading.value());
+ }
+ if (!reportAtom(stats_client, PixelAtoms::Atom::kThermalSensorAbnormalityDetected,
+ std::move(values))) {
+ LOG(ERROR) << "Failed to log thermal abnormal atom for " << name.data() << " with value "
+ << value_str;
+ return false;
+ }
+ LOG(INFO) << "Thermal abnormality reported for " << name.data() << " with value " << value_str;
+ abnormal_stats_reported_per_update_interval++;
+ return true;
+}
+
bool ThermalStatsHelper::reportAtom(const std::shared_ptr<IStats> &stats_client,
const int32_t &atom_id, std::vector<VendorAtomValue> &&values) {
LOG(VERBOSE) << "Reporting thermal stats for atom_id " << atom_id;
@@ -467,7 +620,7 @@ StatsRecord ThermalStatsHelper::restoreStatsRecordOnFailure(
}
std::unordered_map<std::string, SensorTempStats> ThermalStatsHelper::GetSensorTempStatsSnapshot() {
- auto sensor_temp_stats_snapshot = sensor_temp_stats_map_;
+ auto sensor_temp_stats_snapshot = sensor_stats.temp_stats_map_;
for (auto &sensor_temp_stats_pair : sensor_temp_stats_snapshot) {
for (auto &temp_stats : sensor_temp_stats_pair.second.stats_by_custom_threshold) {
// update the last unclosed entry and start new record with same state
diff --git a/thermal/utils/thermal_stats_helper.h b/thermal/utils/thermal_stats_helper.h
index ef2f811f..fde9e9c7 100644
--- a/thermal/utils/thermal_stats_helper.h
+++ b/thermal/utils/thermal_stats_helper.h
@@ -19,6 +19,7 @@
#include <aidl/android/frameworks/stats/IStats.h>
#include <aidl/android/hardware/thermal/Temperature.h>
#include <android-base/chrono_utils.h>
+#include <hardware/google/pixel/pixelstats/pixelatoms.pb.h>
#include <chrono>
#include <shared_mutex>
@@ -37,10 +38,18 @@ namespace implementation {
using aidl::android::frameworks::stats::IStats;
using aidl::android::frameworks::stats::VendorAtomValue;
using ::android::base::boot_clock;
+using ::android::hardware::google::pixel::PixelAtoms::ThermalSensorAbnormalityDetected;
using std::chrono::system_clock;
using SystemTimePoint = std::chrono::time_point<std::chrono::system_clock>;
+// Number of abnormal atoms to be logged per kUpdateIntervalMs
+constexpr int kMaxAbnormalLoggingPerUpdateInterval = 20;
constexpr int kMaxStatsReportingFailCount = 3;
+// Proto messages are 1-indexed and VendorAtom field numbers start at 2, so
+// store everything in the values array at the index of the field number
+// -2.
+constexpr int kVendorAtomOffset = 2;
+constexpr float kPrecisionThreshold = 1e-4;
struct StatsRecord {
int cur_state; /* temperature / cdev state at current time */
@@ -98,6 +107,23 @@ struct SensorTempStats : ThermalStats<float> {
SystemTimePoint min_temp_timestamp = SystemTimePoint::min();
};
+struct CurrTempStatus {
+ float temp;
+ boot_clock::time_point start_time;
+ int repeat_count;
+};
+
+struct SensorStats {
+ // Temperature residency stats for each sensor being watched
+ std::unordered_map<std::string, SensorTempStats> temp_stats_map_;
+ // Min, Max Temp threshold info for each sensor being monitored
+ std::unordered_map<std::string, std::shared_ptr<TempRangeInfo>> temp_range_info_map_;
+ // Temperature Stuck info for each sensor being monitored
+ std::unordered_map<std::string, std::shared_ptr<TempStuckInfo>> temp_stuck_info_map_;
+ // Current temperature status for each sensor being monitored for stuck
+ std::unordered_map<std::string, CurrTempStatus> curr_temp_status_map_;
+};
+
class ThermalStatsHelper {
public:
ThermalStatsHelper() = default;
@@ -122,6 +148,8 @@ class ThermalStatsHelper {
* >0, count represents the number of stats failed to report.
*/
int reportStats();
+ bool reportThermalAbnormality(const ThermalSensorAbnormalityDetected::AbnormalityType &type,
+ std::string_view name, std::optional<int> reading);
// Get a snapshot of Thermal Stats Sensor Map till that point in time
std::unordered_map<std::string, SensorTempStats> GetSensorTempStatsSnapshot();
// Get a snapshot of Thermal Stats Sensor Map till that point in time
@@ -132,10 +160,9 @@ class ThermalStatsHelper {
static constexpr std::chrono::milliseconds kUpdateIntervalMs =
std::chrono::duration_cast<std::chrono::milliseconds>(24h);
boot_clock::time_point last_total_stats_report_time = boot_clock::time_point::min();
-
- mutable std::shared_mutex sensor_temp_stats_map_mutex_;
- // Temperature stats for each sensor being watched
- std::unordered_map<std::string, SensorTempStats> sensor_temp_stats_map_;
+ int abnormal_stats_reported_per_update_interval = 0;
+ mutable std::shared_mutex sensor_stats_mutex_;
+ SensorStats sensor_stats;
mutable std::shared_mutex sensor_cdev_request_stats_map_mutex_;
// userVote request stat for the sensor to the corresponding cdev (sensor -> cdev ->
// StatsRecord)
@@ -149,7 +176,11 @@ class ThermalStatsHelper {
const StatsInfo<int> &request_stats_info,
const std::unordered_map<std::string, SensorInfo> &sensor_info_map_,
const std::unordered_map<std::string, CdevInfo> &cooling_device_info_map_);
+ bool initializeSensorAbnormalityStats(
+ const AbnormalStatsInfo &abnormal_stats_info,
+ const std::unordered_map<std::string, SensorInfo> &sensor_info_map_);
void updateStatsRecord(StatsRecord *stats_record, int new_state);
+ void verifySensorAbnormality(std::string_view sensor, float temperature);
int reportAllSensorTempStats(const std::shared_ptr<IStats> &stats_client);
bool reportSensorTempStats(const std::shared_ptr<IStats> &stats_client, std::string_view sensor,
const SensorTempStats &sensor_temp_stats, StatsRecord *stats_record);
diff --git a/thermal/utils/thermal_throttling.cpp b/thermal/utils/thermal_throttling.cpp
index 57dca169..295f76f6 100644
--- a/thermal/utils/thermal_throttling.cpp
+++ b/thermal/utils/thermal_throttling.cpp
@@ -187,7 +187,8 @@ bool ThermalThrottling::registerThermalThrottling(
// return power budget based on PID algo
float ThermalThrottling::updatePowerBudget(const Temperature &temp, const SensorInfo &sensor_info,
std::chrono::milliseconds time_elapsed_ms,
- ThrottlingSeverity curr_severity) {
+ ThrottlingSeverity curr_severity,
+ const bool max_throttling) {
float p = 0, d = 0;
float power_budget = std::numeric_limits<float>::max();
bool target_changed = false;
@@ -210,6 +211,11 @@ float ThermalThrottling::updatePowerBudget(const Temperature &temp, const Sensor
// Compute PID
float err = sensor_info.hot_thresholds[target_state] - temp.value;
+
+ if (max_throttling && err <= 0) {
+ return sensor_info.throttling_info->min_alloc_power[target_state];
+ }
+
p = err * (err < 0 ? sensor_info.throttling_info->k_po[target_state]
: sensor_info.throttling_info->k_pu[target_state]);
@@ -317,7 +323,8 @@ bool ThermalThrottling::allocatePowerToCdev(
const Temperature &temp, const SensorInfo &sensor_info,
const ThrottlingSeverity curr_severity, const std::chrono::milliseconds time_elapsed_ms,
const std::unordered_map<std::string, PowerStatus> &power_status_map,
- const std::unordered_map<std::string, CdevInfo> &cooling_device_info_map) {
+ const std::unordered_map<std::string, CdevInfo> &cooling_device_info_map,
+ const bool max_throttling) {
float total_weight = 0;
float last_updated_avg_power = NAN;
float allocated_power = 0;
@@ -329,7 +336,8 @@ bool ThermalThrottling::allocatePowerToCdev(
std::string log_buf;
std::unique_lock<std::shared_mutex> _lock(thermal_throttling_status_map_mutex_);
- auto total_power_budget = updatePowerBudget(temp, sensor_info, time_elapsed_ms, curr_severity);
+ auto total_power_budget =
+ updatePowerBudget(temp, sensor_info, time_elapsed_ms, curr_severity, max_throttling);
const auto &profile = thermal_throttling_status_map_[temp.name].profile;
if (sensor_info.throttling_info->excluded_power_info_map.size()) {
@@ -466,30 +474,34 @@ bool ThermalThrottling::allocatePowerToCdev(
thermal_throttling_status_map_[temp.name].pid_cdev_request_map.at(
binded_cdev_info_pair.first);
- if (binded_cdev_info_pair.second.max_release_step !=
- std::numeric_limits<int>::max() &&
- (power_data_invalid || cdev_power_adjustment > 0)) {
- if (!power_data_invalid && curr_cdev_vote < max_cdev_vote) {
- cdev_power_budget = cdev_info.state2power[curr_cdev_vote];
- LOG(VERBOSE) << temp.name << "'s " << binded_cdev_info_pair.first
- << " vote: " << curr_cdev_vote
- << " is lower than max cdev vote: " << max_cdev_vote;
- } else {
- const auto target_state = std::max(
- curr_cdev_vote - binded_cdev_info_pair.second.max_release_step, 0);
- cdev_power_budget =
- std::min(cdev_power_budget, cdev_info.state2power[target_state]);
+ if (!max_throttling) {
+ if (binded_cdev_info_pair.second.max_release_step !=
+ std::numeric_limits<int>::max() &&
+ (power_data_invalid || cdev_power_adjustment > 0)) {
+ if (!power_data_invalid && curr_cdev_vote < max_cdev_vote) {
+ cdev_power_budget = cdev_info.state2power[curr_cdev_vote];
+ LOG(VERBOSE) << temp.name << "'s " << binded_cdev_info_pair.first
+ << " vote: " << curr_cdev_vote
+ << " is lower than max cdev vote: " << max_cdev_vote;
+ } else {
+ const auto target_state = std::max(
+ curr_cdev_vote - binded_cdev_info_pair.second.max_release_step,
+ 0);
+ cdev_power_budget = std::min(cdev_power_budget,
+ cdev_info.state2power[target_state]);
+ }
}
- }
- if (binded_cdev_info_pair.second.max_throttle_step !=
- std::numeric_limits<int>::max() &&
- (power_data_invalid || cdev_power_adjustment < 0)) {
- const auto target_state = std::min(
- curr_cdev_vote + binded_cdev_info_pair.second.max_throttle_step,
- cdev_info.max_state);
- cdev_power_budget =
- std::max(cdev_power_budget, cdev_info.state2power[target_state]);
+ if (binded_cdev_info_pair.second.max_throttle_step !=
+ std::numeric_limits<int>::max() &&
+ (power_data_invalid || cdev_power_adjustment < 0)) {
+ const auto target_state = std::min(
+ curr_cdev_vote + binded_cdev_info_pair.second.max_throttle_step,
+ binded_cdev_info_pair.second
+ .cdev_ceiling[static_cast<size_t>(curr_severity)]);
+ cdev_power_budget =
+ std::max(cdev_power_budget, cdev_info.state2power[target_state]);
+ }
}
thermal_throttling_status_map_[temp.name].pid_power_budget_map.at(
@@ -671,7 +683,8 @@ void ThermalThrottling::thermalThrottlingUpdate(
const Temperature &temp, const SensorInfo &sensor_info,
const ThrottlingSeverity curr_severity, const std::chrono::milliseconds time_elapsed_ms,
const std::unordered_map<std::string, PowerStatus> &power_status_map,
- const std::unordered_map<std::string, CdevInfo> &cooling_device_info_map) {
+ const std::unordered_map<std::string, CdevInfo> &cooling_device_info_map,
+ const bool max_throttling) {
if (!thermal_throttling_status_map_.count(temp.name)) {
return;
}
@@ -682,7 +695,7 @@ void ThermalThrottling::thermalThrottlingUpdate(
if (thermal_throttling_status_map_[temp.name].pid_power_budget_map.size()) {
if (!allocatePowerToCdev(temp, sensor_info, curr_severity, time_elapsed_ms,
- power_status_map, cooling_device_info_map)) {
+ power_status_map, cooling_device_info_map, max_throttling)) {
LOG(ERROR) << "Sensor " << temp.name << " PID request cdev failed";
// Clear the CDEV request if the power budget is failed to be allocated
for (auto &pid_cdev_request_pair :
@@ -773,6 +786,7 @@ void ThermalThrottling::computeCoolingDevicesRequest(
}
request_state = std::min(request_state, cdev_ceiling);
if (cdev_request_pair.second != request_state) {
+ ATRACE_INT((atrace_prefix + std::string("-final_request")).c_str(), request_state);
if (updateCdevMaxRequestAndNotifyIfChange(cdev_name, cdev_request_pair.second,
request_state)) {
cooling_devices_to_update->emplace_back(cdev_name);
diff --git a/thermal/utils/thermal_throttling.h b/thermal/utils/thermal_throttling.h
index 76c41e0f..cac7f8dc 100644
--- a/thermal/utils/thermal_throttling.h
+++ b/thermal/utils/thermal_throttling.h
@@ -62,17 +62,12 @@ class ThermalThrottling {
ThermalThrottling(const ThermalThrottling &) = delete;
void operator=(const ThermalThrottling &) = delete;
- // Check if the thermal throttling profile need to be switched
- void parseProfileProperty(std::string_view sensor_name, const SensorInfo &sensor_info);
// Clear throttling data
void clearThrottlingData(std::string_view sensor_name, const SensorInfo &sensor_info);
// Register map for throttling algo
bool registerThermalThrottling(
std::string_view sensor_name, const std::shared_ptr<ThrottlingInfo> &throttling_info,
const std::unordered_map<std::string, CdevInfo> &cooling_device_info_map);
- // Register map for throttling release algo
- bool registerThrottlingReleaseToWatch(std::string_view sensor_name, std::string_view cdev_name,
- const BindedCdevInfo &binded_cdev_info);
// Get throttling status map
const std::unordered_map<std::string, ThermalThrottlingStatus> &GetThermalThrottlingStatusMap()
const {
@@ -84,7 +79,8 @@ class ThermalThrottling {
const Temperature &temp, const SensorInfo &sensor_info,
const ThrottlingSeverity curr_severity, const std::chrono::milliseconds time_elapsed_ms,
const std::unordered_map<std::string, PowerStatus> &power_status_map,
- const std::unordered_map<std::string, CdevInfo> &cooling_device_info_map);
+ const std::unordered_map<std::string, CdevInfo> &cooling_device_info_map,
+ const bool max_throttling = false);
// Compute the throttling target from all the sensors' request
void computeCoolingDevicesRequest(std::string_view sensor_name, const SensorInfo &sensor_info,
@@ -95,10 +91,12 @@ class ThermalThrottling {
bool getCdevMaxRequest(std::string_view cdev_name, int *max_state);
private:
+ // Check if the thermal throttling profile need to be switched
+ void parseProfileProperty(std::string_view sensor_name, const SensorInfo &sensor_info);
// PID algo - get the total power budget
float updatePowerBudget(const Temperature &temp, const SensorInfo &sensor_info,
std::chrono::milliseconds time_elapsed_ms,
- ThrottlingSeverity curr_severity);
+ ThrottlingSeverity curr_severity, const bool max_throttling);
// PID algo - return the power number from excluded power rail list
float computeExcludedPower(const SensorInfo &sensor_info,
@@ -111,7 +109,8 @@ class ThermalThrottling {
const Temperature &temp, const SensorInfo &sensor_info,
const ThrottlingSeverity curr_severity, const std::chrono::milliseconds time_elapsed_ms,
const std::unordered_map<std::string, PowerStatus> &power_status_map,
- const std::unordered_map<std::string, CdevInfo> &cooling_device_info_map);
+ const std::unordered_map<std::string, CdevInfo> &cooling_device_info_map,
+ const bool max_throttling);
// PID algo - map the target throttling state according to the power budget
void updateCdevRequestByPower(
std::string sensor_name,
diff --git a/thermal/utils/thermal_watcher.cpp b/thermal/utils/thermal_watcher.cpp
index d8bc92e1..f8ca2c2b 100644
--- a/thermal/utils/thermal_watcher.cpp
+++ b/thermal/utils/thermal_watcher.cpp
@@ -401,7 +401,7 @@ void ThermalWatcher::registerFilesToWatchNl(const std::set<std::string> &sensors
bool ThermalWatcher::startWatchingDeviceFiles() {
if (cb_) {
- auto ret = this->run("FileWatcherThread", ::android::PRIORITY_HIGHEST);
+ auto ret = this->run("FileWatcherThread", -10);
if (ret != ::android::NO_ERROR) {
LOG(ERROR) << "ThermalWatcherThread start fail";
return false;
diff --git a/thermal/virtualtemp_estimator/virtualtemp_estimator.cpp b/thermal/virtualtemp_estimator/virtualtemp_estimator.cpp
new file mode 100644
index 00000000..2dc2185c
--- /dev/null
+++ b/thermal/virtualtemp_estimator/virtualtemp_estimator.cpp
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2023 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 "virtualtemp_estimator.h"
+
+#include <android-base/logging.h>
+#include <dlfcn.h>
+
+#include <vector>
+
+namespace thermal {
+namespace vtestimator {
+
+void VirtualTempEstimator::LoadTFLiteWrapper() {
+ if (!data_) {
+ LOG(ERROR) << "data_ is nullptr during LoadTFLiteWrapper";
+ return;
+ }
+
+ std::unique_lock<std::mutex> lock(data_->tflite_methods.mutex_);
+
+ void *mLibHandle = dlopen("/vendor/lib64/libthermal_tflite_wrapper.so", 0);
+ if (mLibHandle == nullptr) {
+ LOG(ERROR) << "Could not load libthermal_tflite_wrapper library with error: " << dlerror();
+ return;
+ }
+
+ data_->tflite_methods.create =
+ reinterpret_cast<tflitewrapper_create>(dlsym(mLibHandle, "Create"));
+ if (!data_->tflite_methods.create) {
+ LOG(ERROR) << "Could not link and cast tflitewrapper_create with error: " << dlerror();
+ }
+
+ data_->tflite_methods.init = reinterpret_cast<tflitewrapper_init>(dlsym(mLibHandle, "Init"));
+ if (!data_->tflite_methods.init) {
+ LOG(ERROR) << "Could not link and cast tflitewrapper_init with error: " << dlerror();
+ }
+
+ data_->tflite_methods.invoke =
+ reinterpret_cast<tflitewrapper_invoke>(dlsym(mLibHandle, "Invoke"));
+ if (!data_->tflite_methods.invoke) {
+ LOG(ERROR) << "Could not link and cast tflitewrapper_invoke with error: " << dlerror();
+ }
+
+ data_->tflite_methods.destroy =
+ reinterpret_cast<tflitewrapper_destroy>(dlsym(mLibHandle, "Destroy"));
+ if (!data_->tflite_methods.destroy) {
+ LOG(ERROR) << "Could not link and cast tflitewrapper_destroy with error: " << dlerror();
+ }
+}
+
+VirtualTempEstimator::VirtualTempEstimator(size_t num_input_samples) {
+ data_ = std::make_unique<VirtualTempEstimatorTFLiteData>(num_input_samples);
+ LoadTFLiteWrapper();
+}
+
+VirtualTempEstimator::~VirtualTempEstimator() {
+ LOG(INFO) << "VirtualTempEstimator destructor";
+}
+
+VtEstimatorStatus VirtualTempEstimator::Initialize(const char *model_path) {
+ LOG(INFO) << "Initialize VirtualTempEstimator\n";
+
+ if (!data_) {
+ LOG(ERROR) << "data_ is nullptr during Initialize\n";
+ return kVtEstimatorInitFailed;
+ }
+
+ std::unique_lock<std::mutex> lock(data_->tflite_methods.mutex_);
+
+ if (!model_path) {
+ LOG(ERROR) << "Invalid model_path:" << model_path;
+ return kVtEstimatorInvalidArgs;
+ }
+
+ if (!data_->input_buffer || !data_->input_buffer_size) {
+ LOG(ERROR) << "Invalid data_ members " << model_path
+ << " input_buffer: " << data_->input_buffer
+ << " input_buffer_size: " << data_->input_buffer_size;
+ return kVtEstimatorInitFailed;
+ }
+
+ if (!data_->tflite_methods.create || !data_->tflite_methods.init ||
+ !data_->tflite_methods.invoke || !data_->tflite_methods.destroy) {
+ LOG(ERROR) << "Invalid tflite methods";
+ return kVtEstimatorInitFailed;
+ }
+
+ data_->tflite_wrapper = data_->tflite_methods.create(kNumInputTensors, kNumOutputTensors);
+ if (!data_->tflite_wrapper) {
+ LOG(ERROR) << "Failed to create tflite wrapper";
+ return kVtEstimatorInitFailed;
+ }
+
+ int ret = data_->tflite_methods.init(data_->tflite_wrapper, model_path);
+ if (ret) {
+ LOG(ERROR) << "Failed to Init tflite_wrapper for " << model_path << " (ret: )" << ret
+ << ")";
+ return kVtEstimatorInitFailed;
+ }
+
+ data_->is_initialized = true;
+ data_->model_path = model_path;
+
+ LOG(INFO) << "Successfully initialized VirtualTempEstimator for " << model_path;
+ return kVtEstimatorOk;
+}
+
+VtEstimatorStatus VirtualTempEstimator::Estimate(const std::vector<float> &thermistors,
+ float *output) {
+ if (!data_) {
+ LOG(ERROR) << "data_ is nullptr during Estimate\n";
+ return kVtEstimatorInitFailed;
+ }
+
+ std::unique_lock<std::mutex> lock(data_->tflite_methods.mutex_);
+
+ if (!data_->is_initialized) {
+ LOG(ERROR) << "data_ not initialized for " << data_->model_path;
+ return kVtEstimatorInitFailed;
+ }
+
+ if ((thermistors.size() != data_->input_buffer_size) || (!output)) {
+ LOG(ERROR) << "Invalid args for " << data_->model_path
+ << " thermistors.size(): " << thermistors.size()
+ << " input_buffer_size: " << data_->input_buffer_size << " output: " << output;
+ return kVtEstimatorInvalidArgs;
+ }
+
+ // copy input data into input tensors
+ for (size_t i = 0; i < data_->input_buffer_size; ++i) {
+ data_->input_buffer[i] = thermistors[i];
+ }
+
+ int ret = data_->tflite_methods.invoke(data_->tflite_wrapper, data_->input_buffer,
+ data_->input_buffer_size, output, 1);
+ if (ret) {
+ LOG(ERROR) << "Failed to Invoke for " << data_->model_path << " (ret: " << ret << ")";
+ return kVtEstimatorInvokeFailed;
+ }
+
+ return kVtEstimatorOk;
+}
+
+} // namespace vtestimator
+} // namespace thermal
diff --git a/thermal/virtualtemp_estimator/virtualtemp_estimator.h b/thermal/virtualtemp_estimator/virtualtemp_estimator.h
new file mode 100644
index 00000000..0ae37f9e
--- /dev/null
+++ b/thermal/virtualtemp_estimator/virtualtemp_estimator.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2023 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 <vector>
+
+#include "virtualtemp_estimator_data.h"
+
+namespace thermal {
+namespace vtestimator {
+
+enum VtEstimatorStatus {
+ kVtEstimatorOk = 0,
+ kVtEstimatorInvalidArgs = 1,
+ kVtEstimatorInitFailed = 2,
+ kVtEstimatorInvokeFailed = 3,
+ kVtEstimatorUnSupported = 4,
+};
+
+// Class to estimate virtual temperature based on a model
+class VirtualTempEstimator {
+ public:
+ // Implicit copy-move headers.
+ VirtualTempEstimator(const VirtualTempEstimator &) = delete;
+ VirtualTempEstimator(VirtualTempEstimator &&) = default;
+ VirtualTempEstimator &operator=(const VirtualTempEstimator &) = delete;
+ VirtualTempEstimator &operator=(VirtualTempEstimator &&) = default;
+
+ VirtualTempEstimator(size_t num_input_samples);
+ ~VirtualTempEstimator();
+
+ // Initializes the model provided by model_path.
+ VtEstimatorStatus Initialize(const char *model_path);
+
+ // Performs the inference on the loaded VT model.
+ // Output of the inference is returned in output argument
+ VtEstimatorStatus Estimate(const std::vector<float> &thermistors, float *output);
+
+ private:
+ void LoadTFLiteWrapper();
+ std::unique_ptr<VirtualTempEstimatorTFLiteData> data_;
+};
+
+} // namespace vtestimator
+} // namespace thermal
diff --git a/thermal/virtualtemp_estimator/virtualtemp_estimator_data.h b/thermal/virtualtemp_estimator/virtualtemp_estimator_data.h
new file mode 100644
index 00000000..935c753b
--- /dev/null
+++ b/thermal/virtualtemp_estimator/virtualtemp_estimator_data.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2023 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 <cstddef>
+#include <mutex>
+#include <string>
+
+#pragma once
+
+namespace thermal {
+namespace vtestimator {
+
+// Current version only supports single input/output tensors
+constexpr int kNumInputTensors = 1;
+constexpr int kNumOutputTensors = 1;
+
+typedef void *(*tflitewrapper_create)(int num_input_tensors, int num_output_tensors);
+typedef bool (*tflitewrapper_init)(void *handle, const char *model_path);
+typedef bool (*tflitewrapper_invoke)(void *handle, float *input_samples, int num_input_samples,
+ float *output_samples, int num_output_samples);
+typedef void (*tflitewrapper_destroy)(void *handle);
+
+struct TFLiteWrapperMethods {
+ tflitewrapper_create create;
+ tflitewrapper_init init;
+ tflitewrapper_invoke invoke;
+ tflitewrapper_destroy destroy;
+ mutable std::mutex mutex_;
+};
+
+struct VirtualTempEstimatorTFLiteData {
+ VirtualTempEstimatorTFLiteData(size_t num_input_samples) {
+ input_buffer = new float[num_input_samples];
+ input_buffer_size = num_input_samples;
+ is_initialized = false;
+ tflite_wrapper = nullptr;
+
+ tflite_methods.create = nullptr;
+ tflite_methods.init = nullptr;
+ tflite_methods.invoke = nullptr;
+ tflite_methods.destroy = nullptr;
+ }
+
+ void *tflite_wrapper;
+ float *input_buffer;
+ size_t input_buffer_size;
+ std::string model_path;
+ TFLiteWrapperMethods tflite_methods;
+ bool is_initialized;
+
+ ~VirtualTempEstimatorTFLiteData() {
+ if (tflite_wrapper && tflite_methods.destroy) {
+ tflite_methods.destroy(tflite_wrapper);
+ }
+
+ if (input_buffer) {
+ delete input_buffer;
+ }
+ }
+};
+
+} // namespace vtestimator
+} // namespace thermal
diff --git a/thermal/virtualtemp_estimator/virtualtemp_estimator_test.cpp b/thermal/virtualtemp_estimator/virtualtemp_estimator_test.cpp
new file mode 100644
index 00000000..fde99977
--- /dev/null
+++ b/thermal/virtualtemp_estimator/virtualtemp_estimator_test.cpp
@@ -0,0 +1,416 @@
+// Copyright (C) 2023 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.
+
+/**
+ *@file virtualtemp_estimator_test.cc
+ * Test application to verify virtualtemp estimator
+ *
+ */
+// Test application to run and verify virtualtemp estimator interface unit tests
+
+#include "virtualtemp_estimator.h"
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/parsedouble.h>
+#include <android-base/properties.h>
+#include <android-base/strings.h>
+#include <cutils/properties.h>
+#include <cutils/trace.h>
+#include <json/reader.h>
+#include <json/value.h>
+#include <json/writer.h>
+#include <log/log.h>
+#include <malloc.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+
+#include <climits>
+#include <fstream>
+#include <iostream>
+
+constexpr std::string_view kDefaultModel("/vendor/etc/vt_estimation_model.tflite");
+constexpr std::string_view kConfigProperty("vendor.thermal.config");
+constexpr std::string_view kConfigDefaultFileName("thermal_info_config.json");
+constexpr int kmillion = 1000000;
+constexpr int klog_interval_usec = 10 * kmillion;
+
+static inline unsigned long get_elapsed_time_usec(struct timeval start, struct timeval end) {
+ unsigned long elapsed_time = (end.tv_sec - start.tv_sec) * kmillion;
+ elapsed_time += (end.tv_usec - start.tv_usec);
+
+ return elapsed_time;
+}
+
+static std::vector<std::string> get_input_combination(std::string_view thermal_config_path) {
+ std::vector<std::string> result;
+ std::string json_doc;
+ if (!android::base::ReadFileToString(thermal_config_path.data(), &json_doc)) {
+ std::cout << "Failed to read JSON config from " << thermal_config_path.data();
+ return result;
+ }
+
+ Json::Value root;
+ Json::CharReaderBuilder reader_builder;
+ std::unique_ptr<Json::CharReader> reader(reader_builder.newCharReader());
+ std::string errorMessage;
+
+ if (!reader->parse(&*json_doc.begin(), &*json_doc.end(), &root, &errorMessage)) {
+ std::cout << "Failed to parse JSON config: " << errorMessage;
+ return result;
+ }
+
+ Json::Value sensors = root["Sensors"];
+ std::cout << "Sensors size: " << sensors.size() << std::endl;
+
+ for (Json::Value::ArrayIndex i = 0; i < sensors.size(); ++i) {
+ const std::string &name = sensors[i]["Name"].asString();
+ if (name == "VIRTUAL-SKIN-MODEL") {
+ Json::Value values = sensors[i]["Combination"];
+ if (values.size() == 0) {
+ return result;
+ }
+
+ std::cout << "Combination : [";
+ for (Json::Value::ArrayIndex j = 0; j < values.size(); ++j) {
+ result.push_back(values[j].asString());
+ std::cout << result.back() << ", ";
+ }
+ std::cout << "]" << std::endl;
+ }
+ }
+
+ return result;
+}
+
+static int run_random_input_inference(std::string_view model_path,
+ std::string_view thermal_config_path, int min_inference_count,
+ int inference_delay_sec) {
+ float output;
+ unsigned long prev_log_time = 0;
+ thermal::vtestimator::VtEstimatorStatus ret;
+ std::vector<std::string> input_combination = get_input_combination(thermal_config_path.data());
+ int input_size = input_combination.size();
+ thermal::vtestimator::VirtualTempEstimator vt_estimator_(input_size);
+
+ std::cout << "Initialize estimator\n";
+ ret = vt_estimator_.Initialize(model_path.data());
+ if (ret != thermal::vtestimator::kVtEstimatorOk) {
+ std::cout << "Failed to Initialize estimator (ret: " << ret << ")\n";
+ return -1;
+ }
+
+ struct timeval start_loop_time;
+ int inference_count = 0;
+ unsigned long max_inference_time = 0, min_inference_time = ULONG_MAX;
+ unsigned long sum_inference_time = 0;
+ float avg_inference_time = 0;
+ std::vector<unsigned long> inference_times;
+
+ gettimeofday(&start_loop_time, nullptr);
+ do {
+ struct timeval begin, end;
+ std::vector<float> thermistors;
+
+ // preparing random inputs with starting temperature between 20C to 40C
+ int r = 20 + std::rand() % 20;
+ for (int i = 0; i < input_size; ++i) {
+ thermistors.push_back(r + i);
+ }
+
+ gettimeofday(&begin, nullptr);
+ ret = vt_estimator_.Estimate(thermistors, &output);
+ gettimeofday(&end, nullptr);
+ if (ret != thermal::vtestimator::kVtEstimatorOk) {
+ std::cout << "Failed to run estimator (ret: " << ret << ")\n";
+ return -1;
+ }
+
+ unsigned long inference_time_usec = get_elapsed_time_usec(begin, end);
+
+ inference_count++;
+ max_inference_time = std::max(max_inference_time, inference_time_usec);
+ min_inference_time = std::min(min_inference_time, inference_time_usec);
+ sum_inference_time += inference_time_usec;
+ avg_inference_time = sum_inference_time / inference_count;
+ inference_times.push_back(inference_time_usec);
+
+ unsigned long elapsed_time = get_elapsed_time_usec(start_loop_time, end);
+ if (elapsed_time - prev_log_time >= klog_interval_usec) {
+ std::cout << "elapsed_time_sec: " << elapsed_time / kmillion
+ << " inference_count: " << inference_count
+ << " min_inference_time: " << min_inference_time
+ << " max_inference_time: " << max_inference_time
+ << " avg_inference_time: " << avg_inference_time << std::endl;
+ prev_log_time = elapsed_time;
+ }
+
+ if (inference_delay_sec)
+ sleep(inference_delay_sec);
+ } while (inference_count < min_inference_count);
+
+ std::cout << "\n\ntotal inference count: " << inference_count << std::endl;
+ std::cout << "total inference time: " << sum_inference_time << std::endl;
+ std::cout << "avg_inference_time: " << avg_inference_time << std::endl;
+ std::cout << "min_inference_time: " << min_inference_time << std::endl;
+ std::cout << "max_inference_time: " << max_inference_time << std::endl;
+
+ std::sort(inference_times.begin(), inference_times.end());
+ std::cout << "\n\n";
+ std::cout << "p50: " << inference_times[inference_count * 0.5] << std::endl;
+ std::cout << "p90: " << inference_times[inference_count * 0.9] << std::endl;
+
+ return 0;
+}
+
+static int run_single_inference(std::string_view model_path, char *input) {
+ if (!input) {
+ std::cout << "input is nullptr" << std::endl;
+ return -1;
+ }
+
+ std::vector<float> thermistors;
+ char *ip = input;
+ char *saveptr;
+
+ std::cout << "Parsing thermistors from input string: ";
+ ip = strtok_r(ip, " ", &saveptr);
+ while (ip) {
+ float thermistor_value;
+
+ if (sscanf(ip, "%f", &thermistor_value) != 1) {
+ std::cout << "inputs parsing failed";
+ }
+
+ std::cout << thermistor_value << " ";
+ thermistors.push_back(thermistor_value);
+
+ ip = strtok_r(NULL, " ", &saveptr);
+ }
+ std::cout << std::endl;
+ std::cout << "thermistors.size(): " << thermistors.size() << std::endl;
+
+ float output;
+ thermal::vtestimator::VtEstimatorStatus ret;
+ thermal::vtestimator::VirtualTempEstimator vt_estimator_(thermistors.size());
+
+ std::cout << "Initialize estimator\n";
+ ret = vt_estimator_.Initialize(model_path.data());
+ if (ret != thermal::vtestimator::kVtEstimatorOk) {
+ std::cout << "Failed to Initialize estimator (ret: " << ret << ")\n";
+ return -1;
+ }
+
+ std::cout << "run estimator\n";
+ ret = vt_estimator_.Estimate(thermistors, &output);
+ if (ret != thermal::vtestimator::kVtEstimatorOk) {
+ std::cout << "Failed to run estimator (ret: " << ret << ")\n";
+ return -1;
+ }
+
+ std::cout << "output: " << output << std::endl;
+ return 0;
+}
+
+static int run_batch_process(std::string_view model_path, std::string_view thermal_config_path,
+ const char *input_file, const char *output_file) {
+ if (!input_file || !output_file) {
+ std::cout << "input and output files required for batch process\n";
+ return -1;
+ }
+
+ std::cout << "get_input_combination(): ";
+ std::vector<std::string> input_combination = get_input_combination(thermal_config_path.data());
+ if (input_combination.size() == 0) {
+ LOG(ERROR) << "Invalid input_combination";
+ return -1;
+ }
+
+ thermal::vtestimator::VtEstimatorStatus ret;
+ thermal::vtestimator::VirtualTempEstimator vt_estimator_(input_combination.size());
+
+ std::cout << "Initialize estimator\n";
+ ret = vt_estimator_.Initialize(model_path.data());
+ if (ret != thermal::vtestimator::kVtEstimatorOk) {
+ std::cout << "Failed to Initialize estimator (ret: " << ret << ")\n";
+ return -1;
+ }
+
+ std::string json_doc;
+ if (!android::base::ReadFileToString(input_file, &json_doc)) {
+ LOG(ERROR) << "Failed to read JSON config from " << input_file;
+ return -1;
+ }
+ Json::Value root;
+ Json::CharReaderBuilder reader_builder;
+ std::unique_ptr<Json::CharReader> reader(reader_builder.newCharReader());
+ std::string errorMessage;
+
+ if (!reader->parse(&*json_doc.begin(), &*json_doc.end(), &root, &errorMessage)) {
+ LOG(ERROR) << "Failed to parse JSON config: " << errorMessage;
+ return -1;
+ }
+
+ std::cout << "Number of testcases " << root.size() << std::endl;
+
+ for (auto const &testcase_name : root.getMemberNames()) {
+ if (testcase_name == "Metadata") {
+ continue;
+ }
+
+ Json::Value testcase = root[testcase_name];
+ Json::Value model_vt_outputs;
+ int loop_count = testcase[input_combination[0]].size();
+
+ std::cout << "tc: " << testcase_name << " count: " << loop_count << std::endl;
+ for (int i = 0; i < loop_count; ++i) {
+ std::vector<float> model_inputs;
+ float model_output;
+ int num_inputs = input_combination.size();
+
+ for (int j = 0; j < num_inputs; ++j) {
+ std::string input_name = input_combination[j];
+ std::string value_str = testcase[input_name][std::to_string(i)].asString();
+
+ std::cout << "tc[" << testcase_name << "] entry[" << i << "] input[" << input_name
+ << "] value_str[" << value_str << "]\n";
+
+ float value;
+ if (android::base::ParseFloat(value_str, &value) == false) {
+ std::cout << "Failed to parse value_str : " << value_str << " to float\n";
+ }
+
+ model_inputs.push_back(value);
+ }
+
+ ret = vt_estimator_.Estimate(model_inputs, &model_output);
+ if (ret != thermal::vtestimator::kVtEstimatorOk) {
+ std::cout << "Failed to run estimator (ret: " << ret << ")\n";
+ return -1;
+ }
+
+ model_vt_outputs[std::to_string(i)] = std::to_string(model_output);
+ }
+
+ testcase["model_vt"] = model_vt_outputs;
+ root[testcase_name] = testcase;
+ std::cout << "completed testcase_name: " << testcase_name << std::endl;
+ }
+
+ Json::StreamWriterBuilder writer_builder;
+ writer_builder["indentation"] = "";
+ std::unique_ptr<Json::StreamWriter> writer(writer_builder.newStreamWriter());
+ std::ofstream output_stream(output_file, std::ofstream::out);
+ writer->write(root, &output_stream);
+
+ return 0;
+}
+
+void print_usage() {
+ std::string message = "usage: \n";
+ message += "-m : input mode (";
+ message += "0: single inference ";
+ message += "1: json input file ";
+ message += "2: generate random inputs) \n";
+ message += "-p : path to model file \n";
+ message += "-t : path to thermal config file \n";
+ message += "-i : input samples (mode 0), path to input file (mode 1) \n";
+ message += "-o : output file (mode 1) \n";
+ message += "-d : delay between inferences in seconds (mode 2) \n";
+ message += "-c : inference count (mode 2)";
+
+ std::cout << message << std::endl;
+}
+
+int main(int argc, char *argv[]) {
+ int c, mode = -1;
+ char *input = nullptr, *output = nullptr;
+ std::string model_path, thermal_config_path;
+ int min_inference_count = -1;
+ int inference_delay_sec = 0;
+
+ while ((c = getopt(argc, argv, "hm:p:i:c:o:d:t:")) != -1) switch (c) {
+ case 'm':
+ mode = atoi(optarg);
+ std::cout << "mode: " << mode << std::endl;
+ break;
+ case 'p':
+ model_path = optarg;
+ std::cout << "model_path: " << model_path << std::endl;
+ break;
+ case 't':
+ thermal_config_path = optarg;
+ std::cout << "thermal_config_path: " << thermal_config_path << std::endl;
+ break;
+ case 'i':
+ input = optarg;
+ std::cout << "input: " << input << std::endl;
+ break;
+ case 'o':
+ output = optarg;
+ std::cout << "output: " << output << std::endl;
+ break;
+ case 'c':
+ min_inference_count = atoi(optarg);
+ std::cout << "min_inference_count: " << min_inference_count << std::endl;
+ break;
+ case 'd':
+ inference_delay_sec = atoi(optarg);
+ std::cout << "inference_delay_sec : " << inference_delay_sec << std::endl;
+ break;
+ case 'h':
+ print_usage();
+ return 0;
+ default:
+ std::cout << "unsupported option " << c << std::endl;
+ abort();
+ }
+
+ if (model_path.empty()) {
+ model_path = kDefaultModel;
+ std::cout << "Using default model_path: " << model_path << std::endl;
+ }
+
+ if (thermal_config_path.empty()) {
+ thermal_config_path =
+ "/vendor/etc/" +
+ android::base::GetProperty(kConfigProperty.data(), kConfigDefaultFileName.data());
+ std::cout << "Using default thermal config: " << thermal_config_path << std::endl;
+ }
+
+ int ret = -1;
+ switch (mode) {
+ case 0:
+ ret = run_single_inference(model_path, input);
+ break;
+ case 1:
+ ret = run_batch_process(model_path, thermal_config_path, input, output);
+ break;
+ case 2:
+ ret = run_random_input_inference(model_path, thermal_config_path, min_inference_count,
+ inference_delay_sec);
+ break;
+ default:
+ std::cout << "unsupported mode" << std::endl;
+ print_usage();
+ break;
+ }
+
+ std::cout << "Exiting" << std::endl;
+ fflush(stdout);
+
+ return ret;
+}
diff --git a/usb/Android.bp b/usb/Android.bp
index 6fec9965..1e05ef2d 100644
--- a/usb/Android.bp
+++ b/usb/Android.bp
@@ -20,7 +20,7 @@ package {
cc_library_static {
name: "libpixelusb",
- vendor_available: true,
+ vendor: true,
export_include_dirs: [
"hidl/include",
"include",
@@ -45,12 +45,17 @@ cc_library_static {
"libhidlbase",
"libutils",
"libbinder_ndk",
+ "pixelatoms-cpp",
"android.hardware.usb.gadget@1.0",
"android.hardware.thermal@1.0",
"android.hardware.thermal@2.0",
"android.hardware.thermal-V1-ndk"
],
+ export_shared_lib_headers: [
+ "pixelatoms-cpp",
+ ],
+
static_libs: [
"libthermalutils",
]
@@ -58,7 +63,7 @@ cc_library_static {
cc_library_static {
name: "libpixelusb-aidl",
- vendor_available: true,
+ vendor: true,
export_include_dirs: [
"aidl/include",
"include",
@@ -81,12 +86,17 @@ cc_library_static {
"libbinder",
"libcutils",
"libutils",
+ "pixelatoms-cpp",
"android.hardware.usb.gadget-V1-ndk",
"android.hardware.thermal@1.0",
"android.hardware.thermal@2.0",
"android.hardware.thermal-V1-ndk"
],
+ export_shared_lib_headers: [
+ "pixelatoms-cpp",
+ ],
+
static_libs: [
"libthermalutils",
]
@@ -94,6 +104,7 @@ cc_library_static {
cc_fuzz {
name: "libpixelusb_gadgetutils_fuzzer",
+ vendor: true,
srcs:[
"UsbGadgetUtils_fuzz.cpp"
diff --git a/usb/CommonUtils.cpp b/usb/CommonUtils.cpp
index 3a396d7e..6e0f08c9 100644
--- a/usb/CommonUtils.cpp
+++ b/usb/CommonUtils.cpp
@@ -35,12 +35,37 @@ namespace google {
namespace pixel {
namespace usb {
+// Android metrics requires number of elements in any repeated field cannot exceed 127 elements
+constexpr int kWestworldRepeatedFieldSizeLimit = 127;
+
using ::android::base::GetProperty;
using ::android::base::SetProperty;
using ::android::base::WriteStringToFile;
using ::std::chrono::microseconds;
using ::std::chrono::steady_clock;
using ::std::literals::chrono_literals::operator""ms;
+using android::hardware::google::pixel::PixelAtoms::VendorUsbDataSessionEvent;
+using android::hardware::google::pixel::PixelAtoms::
+ VendorUsbDataSessionEvent_UsbDataRole_USB_ROLE_DEVICE;
+using android::hardware::google::pixel::PixelAtoms::
+ VendorUsbDataSessionEvent_UsbDataRole_USB_ROLE_HOST;
+using android::hardware::google::pixel::PixelAtoms::VendorUsbDataSessionEvent_UsbDeviceState;
+using android::hardware::google::pixel::PixelAtoms::
+ VendorUsbDataSessionEvent_UsbDeviceState_USB_STATE_ADDRESSED;
+using android::hardware::google::pixel::PixelAtoms::
+ VendorUsbDataSessionEvent_UsbDeviceState_USB_STATE_ATTACHED;
+using android::hardware::google::pixel::PixelAtoms::
+ VendorUsbDataSessionEvent_UsbDeviceState_USB_STATE_CONFIGURED;
+using android::hardware::google::pixel::PixelAtoms::
+ VendorUsbDataSessionEvent_UsbDeviceState_USB_STATE_DEFAULT;
+using android::hardware::google::pixel::PixelAtoms::
+ VendorUsbDataSessionEvent_UsbDeviceState_USB_STATE_NOT_ATTACHED;
+using android::hardware::google::pixel::PixelAtoms::
+ VendorUsbDataSessionEvent_UsbDeviceState_USB_STATE_POWERED;
+using android::hardware::google::pixel::PixelAtoms::
+ VendorUsbDataSessionEvent_UsbDeviceState_USB_STATE_SUSPENDED;
+using android::hardware::google::pixel::PixelAtoms::
+ VendorUsbDataSessionEvent_UsbDeviceState_USB_STATE_UNKNOWN;
int addEpollFd(const base::unique_fd &epfd, const base::unique_fd &fd) {
struct epoll_event event;
@@ -154,6 +179,52 @@ bool resetGadgetCommon() {
return true;
}
+static VendorUsbDataSessionEvent_UsbDeviceState stringToUsbDeviceStateProto(
+ const std::string &state) {
+ if (state == "not attached\n") {
+ return VendorUsbDataSessionEvent_UsbDeviceState_USB_STATE_NOT_ATTACHED;
+ } else if (state == "attached\n") {
+ return VendorUsbDataSessionEvent_UsbDeviceState_USB_STATE_ATTACHED;
+ } else if (state == "powered\n") {
+ return VendorUsbDataSessionEvent_UsbDeviceState_USB_STATE_POWERED;
+ } else if (state == "default\n") {
+ return VendorUsbDataSessionEvent_UsbDeviceState_USB_STATE_DEFAULT;
+ } else if (state == "addressed\n") {
+ return VendorUsbDataSessionEvent_UsbDeviceState_USB_STATE_ADDRESSED;
+ } else if (state == "configured\n") {
+ return VendorUsbDataSessionEvent_UsbDeviceState_USB_STATE_CONFIGURED;
+ } else if (state == "suspended\n") {
+ return VendorUsbDataSessionEvent_UsbDeviceState_USB_STATE_SUSPENDED;
+ } else {
+ return VendorUsbDataSessionEvent_UsbDeviceState_USB_STATE_UNKNOWN;
+ }
+}
+
+void BuildVendorUsbDataSessionEvent(bool is_host, boot_clock::time_point currentTime,
+ boot_clock::time_point startTime,
+ std::vector<std::string> *states,
+ std::vector<boot_clock::time_point> *timestamps,
+ VendorUsbDataSessionEvent *event) {
+ if (is_host) {
+ event->set_usb_role(VendorUsbDataSessionEvent_UsbDataRole_USB_ROLE_HOST);
+ } else {
+ event->set_usb_role(VendorUsbDataSessionEvent_UsbDataRole_USB_ROLE_DEVICE);
+ }
+
+ for (int i = 0; i < states->size() && i < kWestworldRepeatedFieldSizeLimit; i++) {
+ event->add_usb_states(stringToUsbDeviceStateProto(states->at(i)));
+ }
+
+ for (int i = 0; i < timestamps->size() && i < kWestworldRepeatedFieldSizeLimit; i++) {
+ event->add_elapsed_time_ms(
+ std::chrono::duration_cast<std::chrono::milliseconds>(timestamps->at(i) - startTime)
+ .count());
+ }
+
+ event->set_duration_ms(
+ std::chrono::duration_cast<std::chrono::milliseconds>(currentTime - startTime).count());
+}
+
} // namespace usb
} // namespace pixel
} // namespace google
diff --git a/usb/include/pixelusb/CommonUtils.h b/usb/include/pixelusb/CommonUtils.h
index 91244f81..dbd59c63 100644
--- a/usb/include/pixelusb/CommonUtils.h
+++ b/usb/include/pixelusb/CommonUtils.h
@@ -17,7 +17,9 @@
#ifndef HARDWARE_GOOGLE_PIXEL_USB_UTILSCOMMON_H_
#define HARDWARE_GOOGLE_PIXEL_USB_UTILSCOMMON_H_
+#include <android-base/chrono_utils.h>
#include <android-base/unique_fd.h>
+#include <hardware/google/pixel/pixelstats/pixelatoms.pb.h>
#include <string>
@@ -57,6 +59,9 @@ constexpr char kUvcEnabled[] = "ro.usb.uvc.enabled";
#define FUNCTION_PATH CONFIG_PATH FUNCTION_NAME
#define RNDIS_PATH FUNCTIONS_PATH "gsi.rndis"
+using ::android::base::boot_clock;
+using android::hardware::google::pixel::PixelAtoms::VendorUsbDataSessionEvent;
+
// Adds the given fd to the epollfd(epfd).
int addEpollFd(const ::android::base::unique_fd &epfd, const ::android::base::unique_fd &fd);
// Extracts vendor functions from the vendor init properties.
@@ -69,6 +74,12 @@ int linkFunction(const char *function, int index);
bool setVidPidCommon(const char *vid, const char *pid);
// Pulls down USB gadget. Returns true on success, false on failure
bool resetGadgetCommon();
+void BuildVendorUsbDataSessionEvent(bool is_host, boot_clock::time_point currentTime,
+ boot_clock::time_point startTime,
+ std::vector<std::string> *states,
+ std::vector<boot_clock::time_point> *timestamps,
+ VendorUsbDataSessionEvent *event);
+
} // namespace usb
} // namespace pixel
} // namespace google
diff --git a/vibrator/common/StatsBase.cpp b/vibrator/common/StatsBase.cpp
index 68b8a2dc..a0402b4e 100644
--- a/vibrator/common/StatsBase.cpp
+++ b/vibrator/common/StatsBase.cpp
@@ -129,21 +129,22 @@ void StatsBase::uploadDiagnostics() {
uploadErrorAtoms();
}
-void StatsBase::waitForStatsService() const {
+std::shared_ptr<IStats> StatsBase::waitForStatsService() const {
STATS_TRACE("waitForStatsService()");
if (!AServiceManager_isDeclared(kStatsInstanceName.c_str())) {
ALOGE("IStats service '%s' is not registered.", kStatsInstanceName.c_str());
- return;
+ return nullptr;
}
ALOGI("Waiting for IStats service '%s' to come up.", kStatsInstanceName.c_str());
- const std::shared_ptr<IStats> statsClient = IStats::fromBinder(
+ std::shared_ptr<IStats> client = IStats::fromBinder(
ndk::SpAIBinder(AServiceManager_waitForService(kStatsInstanceName.c_str())));
- if (!statsClient) {
+ if (!client) {
ALOGE("Failed to get IStats service '%s'.", kStatsInstanceName.c_str());
- return;
+ return nullptr;
}
ALOGI("IStats service online.");
+ return client;
}
void StatsBase::runReporterThread() {
@@ -152,8 +153,6 @@ void StatsBase::runReporterThread() {
auto nextUpload = clock::now() + UPLOAD_INTERVAL;
auto status = std::cv_status::no_timeout;
- waitForStatsService();
-
while (!mTerminateReporterThread) {
drainAtomQueue();
{
@@ -178,15 +177,14 @@ void StatsBase::drainAtomQueue() {
std::swap(mAtomQueue, tempQueue);
}
- std::shared_ptr<IStats> statsClient = IStats::fromBinder(
- ndk::SpAIBinder(AServiceManager_waitForService(kStatsInstanceName.c_str())));
- if (!statsClient) {
+ std::shared_ptr<IStats> client = waitForStatsService();
+ if (!client) {
ALOGE("Failed to get IStats service. Atoms are dropped.");
return;
}
for (const VendorAtom &atom : tempQueue) {
- reportVendorAtom(statsClient, atom);
+ reportVendorAtom(client, atom);
}
}
diff --git a/vibrator/common/StatsBase.h b/vibrator/common/StatsBase.h
index 2e16220a..2b0d9867 100644
--- a/vibrator/common/StatsBase.h
+++ b/vibrator/common/StatsBase.h
@@ -31,6 +31,7 @@ namespace frameworks {
namespace stats {
class VendorAtom;
+class IStats;
} // namespace stats
} // namespace frameworks
@@ -45,6 +46,7 @@ namespace vibrator {
class StatsBase {
public:
using VendorAtom = ::aidl::android::frameworks::stats::VendorAtom;
+ using IStats = ::aidl::android::frameworks::stats::IStats;
StatsBase(const std::string &instance);
~StatsBase();
@@ -65,7 +67,7 @@ class StatsBase {
void runReporterThread();
void reportVendorAtomAsync(const VendorAtom &atom);
void uploadDiagnostics();
- void waitForStatsService() const;
+ std::shared_ptr<IStats> waitForStatsService() const;
void drainAtomQueue();
void uploadPlaycountAtoms();
diff --git a/vibrator/cs40l25/Hardware.h b/vibrator/cs40l25/Hardware.h
index e2222798..7f06141b 100644
--- a/vibrator/cs40l25/Hardware.h
+++ b/vibrator/cs40l25/Hardware.h
@@ -198,9 +198,7 @@ class HwCal : public Vibrator::HwCal, private HwCalBase {
return true;
}
bool isChirpEnabled() override {
- bool value;
- getProperty("chirp.enabled", &value, false);
- return value;
+ return utils::getProperty("persist.vendor.vibrator.hal.chirp.enabled", false);
}
void debug(int fd) override { HwCalBase::debug(fd); }
};
diff --git a/vibrator/cs40l26/Hardware.h b/vibrator/cs40l26/Hardware.h
index 50e2cb66..12ddb59b 100644
--- a/vibrator/cs40l26/Hardware.h
+++ b/vibrator/cs40l26/Hardware.h
@@ -297,6 +297,9 @@ class HwApi : public Vibrator::HwApi, private HwApiBase {
*haptic_pcm = NULL;
return false;
}
+ bool isPassthroughI2sHapticSupported() override {
+ return utils::getProperty("ro.vendor.vibrator.hal.passthrough_i2s_supported", false);
+ }
bool uploadOwtEffect(const uint8_t *owtData, const uint32_t numBytes, struct ff_effect *effect,
uint32_t *outEffectIndex, int *status) override {
ATRACE_NAME(__func__);
@@ -516,9 +519,7 @@ class HwCal : public Vibrator::HwCal, private HwCalBase {
return true;
}
bool isChirpEnabled() override {
- bool value;
- getProperty("chirp.enabled", &value, false);
- return value;
+ return utils::getProperty("persist.vendor.vibrator.hal.chirp.enabled", false);
}
bool getSupportedPrimitives(uint32_t *value) override {
return getProperty("supported_primitives", value, (uint32_t)0);
diff --git a/vibrator/cs40l26/Vibrator.cpp b/vibrator/cs40l26/Vibrator.cpp
index 8c303ffd..c2fd73a5 100644
--- a/vibrator/cs40l26/Vibrator.cpp
+++ b/vibrator/cs40l26/Vibrator.cpp
@@ -496,6 +496,8 @@ Vibrator::Vibrator(std::unique_ptr<HwApi> hwapi, std::unique_ptr<HwCal> hwcal,
mHwApi->setF0CompEnable(mHwCal->isF0CompEnabled());
mHwApi->setRedcCompEnable(mHwCal->isRedcCompEnabled());
+ mHasPassthroughHapticDevice = mHwApi->isPassthroughI2sHapticSupported();
+
mIsUnderExternalControl = false;
mIsChirpEnabled = mHwCal->isChirpEnabled();
@@ -534,7 +536,7 @@ ndk::ScopedAStatus Vibrator::getCapabilities(int32_t *_aidl_return) {
int32_t ret = IVibrator::CAP_ON_CALLBACK | IVibrator::CAP_PERFORM_CALLBACK |
IVibrator::CAP_AMPLITUDE_CONTROL | IVibrator::CAP_GET_RESONANT_FREQUENCY |
IVibrator::CAP_GET_Q_FACTOR;
- if (hasHapticAlsaDevice()) {
+ if (mHasPassthroughHapticDevice || hasHapticAlsaDevice()) {
ret |= IVibrator::CAP_EXTERNAL_CONTROL;
} else {
mStatsApi->logError(kAlsaFailError);
@@ -671,16 +673,21 @@ ndk::ScopedAStatus Vibrator::setExternalControl(bool enabled) {
ATRACE_NAME("Vibrator::setExternalControl");
setGlobalAmplitude(enabled);
- if (mHasHapticAlsaDevice || mConfigHapticAlsaDeviceDone || hasHapticAlsaDevice()) {
- if (!mHwApi->setHapticPcmAmp(&mHapticPcm, enabled, mCard, mDevice)) {
- mStatsApi->logError(kHwApiError);
- ALOGE("Failed to %s haptic pcm device: %d", (enabled ? "enable" : "disable"), mDevice);
+ if (!mHasPassthroughHapticDevice) {
+ if (mHasHapticAlsaDevice || mConfigHapticAlsaDeviceDone ||
+ hasHapticAlsaDevice()) {
+ if (!mHwApi->setHapticPcmAmp(&mHapticPcm, enabled, mCard,
+ mDevice)) {
+ mStatsApi->logError(kHwApiError);
+ ALOGE("Failed to %s haptic pcm device: %d",
+ (enabled ? "enable" : "disable"), mDevice);
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+ }
+ } else {
+ mStatsApi->logError(kAlsaFailError);
+ ALOGE("No haptics ALSA device");
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
- } else {
- mStatsApi->logError(kAlsaFailError);
- ALOGE("No haptics ALSA device");
- return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
mIsUnderExternalControl = enabled;
@@ -1569,6 +1576,15 @@ binder_status_t Vibrator::dump(int fd, const char **args, uint32_t numArgs) {
dprintf(fd, " cs40l26-calib.bin: %s\n", ver.c_str());
verFile.close();
}
+ verFile.open("/vendor/firmware/cs40l26-dvl.bin", verBinFileMode);
+ if (verFile.is_open()) {
+ verFile.seekg(36);
+ getline(verFile, ver);
+ ver = ver.substr(0, ver.find('\0') + 1);
+ ver = ver.substr(ver.rfind('\\') + 1);
+ dprintf(fd, " cs40l26-dvl.bin: %s\n", ver.c_str());
+ verFile.close();
+ }
dprintf(fd, "\n");
diff --git a/vibrator/cs40l26/Vibrator.h b/vibrator/cs40l26/Vibrator.h
index c4a992ff..b2b75150 100644
--- a/vibrator/cs40l26/Vibrator.h
+++ b/vibrator/cs40l26/Vibrator.h
@@ -100,6 +100,9 @@ class Vibrator : public BnVibrator {
// Set haptics PCM amplifier before triggering audio haptics feature
virtual bool setHapticPcmAmp(struct pcm **haptic_pcm, bool enable, int card,
int device) = 0;
+ // Checks to see if the passthrough i2s haptics feature is supported by
+ // the target device.
+ virtual bool isPassthroughI2sHapticSupported() = 0;
// Set OWT waveform for compose or compose PWLE request
virtual bool uploadOwtEffect(const uint8_t *owtData, const uint32_t numBytes,
struct ff_effect *effect, uint32_t *outEffectIndex,
@@ -256,6 +259,7 @@ class Vibrator : public BnVibrator {
int mCard;
int mDevice;
bool mHasHapticAlsaDevice{false};
+ bool mHasPassthroughHapticDevice;
bool mIsUnderExternalControl;
float mLongEffectScale = 1.0;
bool mIsChirpEnabled;
diff --git a/vibrator/cs40l26/tests/mocks.h b/vibrator/cs40l26/tests/mocks.h
index 53c9a045..0837938c 100644
--- a/vibrator/cs40l26/tests/mocks.h
+++ b/vibrator/cs40l26/tests/mocks.h
@@ -45,6 +45,7 @@ class MockApi : public ::aidl::android::hardware::vibrator::Vibrator::HwApi {
MOCK_METHOD2(setFFPlay, bool(int8_t index, bool value));
MOCK_METHOD2(getHapticAlsaDevice, bool(int *card, int *device));
MOCK_METHOD4(setHapticPcmAmp, bool(struct pcm **haptic_pcm, bool enable, int card, int device));
+ MOCK_METHOD0(isPassthroughI2sHapticSupported, bool());
MOCK_METHOD5(uploadOwtEffect,
bool(const uint8_t *owtData, const uint32_t numBytes, struct ff_effect *effect,
uint32_t *outEffectIndex, int *status));
diff --git a/vibrator/cs40l26/tests/test-vibrator.cpp b/vibrator/cs40l26/tests/test-vibrator.cpp
index 254a6bdb..0b150955 100644
--- a/vibrator/cs40l26/tests/test-vibrator.cpp
+++ b/vibrator/cs40l26/tests/test-vibrator.cpp
@@ -303,6 +303,7 @@ class VibratorTest : public Test {
EXPECT_CALL(*mMockApi, setMinOnOffInterval(_)).Times(times);
EXPECT_CALL(*mMockApi, getHapticAlsaDevice(_, _)).Times(times);
EXPECT_CALL(*mMockApi, setHapticPcmAmp(_, _, _, _)).Times(times);
+ EXPECT_CALL(*mMockApi, isPassthroughI2sHapticSupported()).Times(times);
EXPECT_CALL(*mMockApi, enableDbc()).Times(times);
EXPECT_CALL(*mMockApi, debug(_)).Times(times);
@@ -339,9 +340,11 @@ TEST_F(VibratorTest, Constructor) {
std::unique_ptr<MockApi> mockapi;
std::unique_ptr<MockCal> mockcal;
std::unique_ptr<MockStats> mockstats;
- std::string f0Val = std::to_string(std::rand());
- std::string redcVal = std::to_string(std::rand());
- std::string qVal = std::to_string(std::rand());
+ int min_val = 0xC8000;
+ int max_val = 0x7FC000;
+ std::string f0Val = std::to_string(std::rand() % (max_val - min_val + 1) + min_val);
+ std::string redcVal = std::to_string(std::rand() % (max_val - min_val + 1) + min_val);
+ std::string qVal = std::to_string(std::rand() % (max_val - min_val + 1) + min_val);
uint32_t calVer;
uint32_t supportedPrimitivesBits = 0x0;
Expectation volGet;
@@ -384,6 +387,7 @@ TEST_F(VibratorTest, Constructor) {
EXPECT_CALL(*mMockCal, isRedcCompEnabled()).WillOnce(Return(true));
EXPECT_CALL(*mMockApi, setRedcCompEnable(true)).WillOnce(Return(true));
+ EXPECT_CALL(*mMockApi, isPassthroughI2sHapticSupported()).WillOnce(Return(false));
EXPECT_CALL(*mMockCal, isChirpEnabled()).WillOnce(Return(true));
EXPECT_CALL(*mMockCal, getSupportedPrimitives(_))
.InSequence(supportedPrimitivesSeq)