summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorXin Li <delphij@google.com>2024-06-13 10:50:18 -0700
committerXin Li <delphij@google.com>2024-06-13 10:50:18 -0700
commita47a38edeaff82199dbc6023c0cf318dc5f0f02c (patch)
treeed8c025e9e71f45fde5a8fc28a5ab4c9e2d7e130
parentd5f7bc349d001fe5eca44f9ae30faa6a88643def (diff)
parent8b12fb16bdbd43b8916101b283fa6c4ecb772a38 (diff)
downloadpixel-master.tar.gz
Merge Android 14 QPR3 to AOSP mainHEADmastermain
Bug: 346855327 Merged-In: I5efa37f3ff793f07556c5a9bf9610db51e09bda8 Change-Id: I9a0cab92196064fb471ec8799273f5dbc9713393
-rw-r--r--battery_mitigation/Android.bp47
-rw-r--r--battery_mitigation/BatteryMitigation.cpp85
-rw-r--r--battery_mitigation/BatteryMitigationService.cpp1128
-rw-r--r--battery_mitigation/MitigationThermalManager.cpp189
-rw-r--r--battery_mitigation/OWNERS2
-rw-r--r--battery_mitigation/include/battery_mitigation/BatteryMitigationService.h172
-rw-r--r--battery_mitigation/include/battery_mitigation/MitigationConfig.h97
-rw-r--r--battery_mitigation/include/battery_mitigation/MitigationThermalManager.h121
-rw-r--r--battery_mitigation/include/battery_mitigation/uapi/brownout_stats.h27
-rw-r--r--kernel_headers/Android.bp6
-rw-r--r--misc_writer/include/misc_writer/misc_writer.h8
-rw-r--r--misc_writer/misc_writer.cpp10
-rw-r--r--misc_writer/misc_writer_main.cpp28
-rw-r--r--mm/pixel-mm-gki.rc4
-rw-r--r--pixelstats/Android.bp107
-rw-r--r--pixelstats/BatteryEEPROMReporter.cpp26
-rw-r--r--pixelstats/BatteryFGReporter.cpp300
-rw-r--r--pixelstats/BrownoutDetectedReporter.cpp1
-rw-r--r--pixelstats/DisplayStatsReporter.cpp216
-rw-r--r--pixelstats/SysfsCollector.cpp25
-rw-r--r--pixelstats/UeventListener.cpp24
-rw-r--r--pixelstats/include/pixelstats/BatteryEEPROMReporter.h2
-rw-r--r--pixelstats/include/pixelstats/BatteryFGReporter.h82
-rw-r--r--pixelstats/include/pixelstats/DisplayStatsReporter.h77
-rw-r--r--pixelstats/include/pixelstats/SysfsCollector.h10
-rw-r--r--pixelstats/include/pixelstats/UeventListener.h38
-rw-r--r--pixelstats/pixelatoms.proto146
-rw-r--r--power-libperfmgr/Android.bp9
-rw-r--r--power-libperfmgr/aidl/AdpfTypes.h6
-rw-r--r--power-libperfmgr/aidl/GpuCapacityNode.cpp128
-rw-r--r--power-libperfmgr/aidl/GpuCapacityNode.h90
-rw-r--r--power-libperfmgr/aidl/PhysicalQuantityTypes.h107
-rw-r--r--power-libperfmgr/aidl/Power.cpp94
-rw-r--r--power-libperfmgr/aidl/Power.h19
-rw-r--r--power-libperfmgr/aidl/PowerHintSession.cpp78
-rw-r--r--power-libperfmgr/aidl/PowerHintSession.h20
-rw-r--r--power-libperfmgr/aidl/PowerSessionManager.cpp11
-rw-r--r--power-libperfmgr/aidl/SessionTaskMap.cpp2
-rw-r--r--power-libperfmgr/aidl/UClampVoter.cpp136
-rw-r--r--power-libperfmgr/aidl/UClampVoter.h60
-rw-r--r--power-libperfmgr/aidl/tests/GpuCapacityNodeTest.cpp276
-rw-r--r--power-libperfmgr/aidl/tests/PhysicalQuantityTypeTest.cpp83
-rw-r--r--power-libperfmgr/aidl/tests/PowerHintSessionTest.cpp7
-rw-r--r--power-libperfmgr/aidl/tests/SessionTaskMapTest.cpp8
-rw-r--r--power-libperfmgr/aidl/tests/UClampVoterTest.cpp184
-rw-r--r--powerstats/aidl/android/vendor/powerstats/IPixelStateResidencyProvider.aidl5
-rw-r--r--powerstats/dataproviders/PixelStateResidencyDataProvider.cpp15
-rw-r--r--powerstats/include/dataproviders/PixelStateResidencyDataProvider.h12
-rw-r--r--presubmit_tests/schemas/Android.bp17
-rw-r--r--presubmit_tests/schemas/powerhint-schema.proto64
-rw-r--r--thermal/Thermal.cpp66
-rw-r--r--thermal/Thermal.h27
-rw-r--r--thermal/thermal-helper.cpp21
-rw-r--r--thermal/utils/power_files.cpp30
-rw-r--r--thermal/utils/thermal_info.cpp85
-rw-r--r--thermal/utils/thermal_info.h3
-rw-r--r--thermal/virtualtemp_estimator/virtualtemp_estimator.cpp305
-rw-r--r--thermal/virtualtemp_estimator/virtualtemp_estimator.h61
-rw-r--r--thermal/virtualtemp_estimator/virtualtemp_estimator_data.h60
-rw-r--r--thermal/virtualtemp_estimator/virtualtemp_estimator_test.cpp69
-rw-r--r--usb/Android.bp14
-rw-r--r--usb/I2cHelper.cpp113
-rw-r--r--usb/include/pixelusb/I2cHelper.h (renamed from battery_mitigation/include/battery_mitigation/BatteryMitigation.h)28
-rw-r--r--vibrator/common/Android.bp7
-rw-r--r--vibrator/cs40l26/Android.bp32
-rw-r--r--vibrator/cs40l26/DspMemChunk.cpp316
-rw-r--r--vibrator/cs40l26/DspMemChunk.h108
-rw-r--r--vibrator/cs40l26/Hardware.h5
-rw-r--r--vibrator/cs40l26/Trace.cpp256
-rw-r--r--vibrator/cs40l26/Trace.h231
-rw-r--r--vibrator/cs40l26/Vibrator.cpp415
-rw-r--r--vibrator/cs40l26/VibratorFlags.aconfig10
-rw-r--r--vibrator/cs40l26/tests/Android.bp1
-rw-r--r--vibrator/cs40l26/tests/test-vibrator.cpp2
74 files changed, 4098 insertions, 2576 deletions
diff --git a/battery_mitigation/Android.bp b/battery_mitigation/Android.bp
deleted file mode 100644
index 6b5e1228..00000000
--- a/battery_mitigation/Android.bp
+++ /dev/null
@@ -1,47 +0,0 @@
-//
-// Copyright (C) 2022 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
-package {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-cc_library {
- name: "libpixelmitigation",
- export_include_dirs: ["include"],
- vendor_available: true,
- srcs: [
- "BatteryMitigation.cpp",
- "BatteryMitigationService.cpp",
- "MitigationThermalManager.cpp",
- ],
- static_libs: [
- "libmath",
- ],
-
- shared_libs: [
- "libbase",
- "libbinder_ndk",
- "libcutils",
- "libhardware",
- "liblog",
- "libutils",
- "android.hardware.thermal-V1-ndk"
- ],
- cflags: [
- "-Wall",
- "-Werror",
- ],
-}
diff --git a/battery_mitigation/BatteryMitigation.cpp b/battery_mitigation/BatteryMitigation.cpp
deleted file mode 100644
index 10c944cc..00000000
--- a/battery_mitigation/BatteryMitigation.cpp
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <battery_mitigation/BatteryMitigation.h>
-
-#include <sstream>
-
-#define ONE_SECOND_IN_US 1000000
-
-namespace android {
-namespace hardware {
-namespace google {
-namespace pixel {
-
-using android::base::ReadFileToString;
-
-BatteryMitigation::BatteryMitigation(const struct MitigationConfig::Config &cfg) {
- mThermalMgr = &MitigationThermalManager::getInstance();
- mThermalMgr->updateConfig(cfg);
-}
-
-bool BatteryMitigation::isMitigationLogTimeValid(std::chrono::system_clock::time_point startTime,
- const char *const logFilePath,
- const char *const timestampFormat,
- const std::regex pattern) {
- std::string logFile;
- if (!ReadFileToString(logFilePath, &logFile)) {
- return false;
- }
- std::istringstream content(logFile);
- std::string line;
- int counter = 0;
- std::smatch pattern_match;
- while (std::getline(content, line)) {
- if (std::regex_match(line, pattern_match, pattern)) {
- std::tm triggeredTimestamp = {};
- std::istringstream ss(pattern_match.str());
- ss >> std::get_time(&triggeredTimestamp, timestampFormat);
- auto logFileTime = std::chrono::system_clock::from_time_t(mktime(&triggeredTimestamp));
- auto epoch_logFileTime = logFileTime.time_since_epoch().count() / ONE_SECOND_IN_US;
-
- // Convert start time to same format
- auto time_sec = std::chrono::system_clock::to_time_t(startTime);
- struct tm start_tm;
- std::stringstream oss;
- localtime_r(&time_sec, &start_tm);
- oss << std::put_time(&start_tm, timestampFormat) << std::flush;
- std::tm startTimestamp = {};
- std::istringstream st(oss.str());
- st >> std::get_time(&startTimestamp, timestampFormat);
- auto start = std::chrono::system_clock::from_time_t(mktime(&startTimestamp));
- auto epoch_startTime = start.time_since_epoch().count() / ONE_SECOND_IN_US;
-
- auto delta = epoch_startTime - epoch_logFileTime;
- auto delta_minutes = delta / 60;
-
- if (delta_minutes >= 0) {
- return true;
- }
- }
- counter += 1;
- if (counter > 5) {
- break;
- }
- }
- return false;
-}
-
-} // namespace pixel
-} // namespace google
-} // namespace hardware
-} // namespace android
diff --git a/battery_mitigation/BatteryMitigationService.cpp b/battery_mitigation/BatteryMitigationService.cpp
deleted file mode 100644
index 3357fdfa..00000000
--- a/battery_mitigation/BatteryMitigationService.cpp
+++ /dev/null
@@ -1,1128 +0,0 @@
-/*
- * 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/MitigationThermalManager.cpp b/battery_mitigation/MitigationThermalManager.cpp
deleted file mode 100644
index 42e6e3cb..00000000
--- a/battery_mitigation/MitigationThermalManager.cpp
+++ /dev/null
@@ -1,189 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#define LOG_TAG "mitigation-logger"
-
-#include <android-base/chrono_utils.h>
-#include <android-base/file.h>
-#include <android-base/logging.h>
-#include <android/binder_manager.h>
-#include <android/binder_process.h>
-#include <battery_mitigation/MitigationThermalManager.h>
-#include <errno.h>
-#include <utils/Log.h>
-
-#include <chrono>
-#include <ctime>
-#include <iomanip>
-#include <iostream>
-#include <string>
-#include <thread>
-
-#define NUM_OF_SAMPLES 20
-#define CAPTURE_INTERVAL_S 2 /* 2 seconds between new capture */
-
-namespace android {
-namespace hardware {
-namespace google {
-namespace pixel {
-
-MitigationThermalManager &MitigationThermalManager::getInstance() {
- static MitigationThermalManager mitigationThermalManager;
- return mitigationThermalManager;
-}
-
-void MitigationThermalManager::remove() {
- const std::lock_guard<std::mutex> lock(thermal_hal_mutex_);
- if (!thermal) {
- return;
- }
- if (callback) {
- auto ret = thermal->unregisterThermalChangedCallback(callback);
- if (!ret.isOk()) {
- LOG(ERROR) << "Failed to release thermal callback! " << ret.getMessage();
- }
- }
-}
-
-MitigationThermalManager::MitigationThermalManager() {
- if (!ABinderProcess_isThreadPoolStarted()) {
- // if no thread pool then the thermal callback will not work, so abort
- LOG(ERROR) << "The user of MitigationThermalManager did not start a thread pool!";
- return;
- }
- if (!connectThermalHal()) {
- remove();
- }
-}
-
-MitigationThermalManager::~MitigationThermalManager() {
- remove();
-}
-
-void MitigationThermalManager::updateConfig(const struct MitigationConfig::Config &cfg) {
- kLogFilePath = std::string(cfg.LogFilePath);
- kSystemPath = cfg.SystemPath;
- kSystemName = cfg.SystemName;
- kFilteredZones = cfg.FilteredZones;
- kTimestampFormat = cfg.TimestampFormat;
-}
-
-bool MitigationThermalManager::connectThermalHal() {
- const std::string thermal_instance_name =
- std::string(::aidl::android::hardware::thermal::IThermal::descriptor) + "/default";
- std::shared_ptr<aidl::android::hardware::thermal::IThermal> thermal_aidl_service;
- const std::lock_guard<std::mutex> lock(thermal_hal_mutex_);
- if (AServiceManager_isDeclared(thermal_instance_name.c_str())) {
- thermal = ::aidl::android::hardware::thermal::IThermal::fromBinder(
- ndk::SpAIBinder(AServiceManager_waitForService(thermal_instance_name.c_str())));
- lastCapturedTime = ::android::base::boot_clock::now();
- return registerCallback();
- } else {
- LOG(ERROR) << "Thermal AIDL service is not declared";
- }
- return false;
-}
-
-bool MitigationThermalManager::isMitigationTemperature(const Temperature &temperature) {
- if (std::find(kFilteredZones.begin(), kFilteredZones.end(), temperature.name) !=
- kFilteredZones.end()) {
- return true;
- }
- return false;
-}
-
-void MitigationThermalManager::thermalCb(const Temperature &temperature) {
- if ((temperature.throttlingStatus == ThrottlingSeverity::NONE) ||
- (!isMitigationTemperature(temperature))) {
- return;
- }
- auto currentTime = ::android::base::boot_clock::now();
- auto delta =
- std::chrono::duration_cast<std::chrono::seconds>(currentTime - lastCapturedTime);
- if (delta.count() < CAPTURE_INTERVAL_S) {
- /* Do not log if delta is within 2 seconds */
- return;
- }
- int flag = O_WRONLY | O_CREAT | O_NOFOLLOW | O_CLOEXEC | O_APPEND | O_TRUNC;
- android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(kLogFilePath.c_str(), flag, 0644)));
- lastCapturedTime = currentTime;
- std::stringstream oss;
- oss << temperature.name << " triggered at " << temperature.value << std::endl << std::flush;
- android::base::WriteStringToFd(oss.str(), fd);
- fsync(fd);
-
- for (int i = 0; i < NUM_OF_SAMPLES; i++) {
- auto now = std::chrono::system_clock::now();
- auto time_sec = std::chrono::system_clock::to_time_t(now);
- auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()) -
- std::chrono::duration_cast<std::chrono::seconds>(now.time_since_epoch());
- struct tm now_tm;
- localtime_r(&time_sec, &now_tm);
- oss << std::put_time(&now_tm, kTimestampFormat.c_str()) << "." << std::setw(3)
- << std::setfill('0') << ms.count() << std::endl
- << std::flush;
- android::base::WriteStringToFd(oss.str(), fd);
- fsync(fd);
- oss.str("");
- /* log System info */
- for (int j = 0; j < kSystemName.size(); j++) {
- std::string value;
- bool result = android::base::ReadFileToString(kSystemPath[j], &value);
- if (!result) {
- LOG(ERROR) << "Could not read: " << kSystemName[j];
- }
- android::base::WriteStringToFd(kSystemName[j] + ":" + value, fd);
- }
- }
- fsync(fd);
-}
-
-bool MitigationThermalManager::registerCallback() {
- if (!thermal) {
- LOG(ERROR) << "Unable to connect Thermal AIDL service";
- return false;
- }
- // Create thermal callback recipient object.
- if (callback == nullptr) {
- callback =
- ndk::SharedRefBase::make<ThermalCallback>([this](const Temperature &temperature) {
- std::lock_guard api_lock(thermal_callback_mutex_);
- thermalCb(temperature);
- });
- }
- // Register thermal callback to thermal hal to cover all. Cannot register twice.
- auto ret_callback = thermal->registerThermalChangedCallback(callback);
- if (!ret_callback.isOk()) {
- LOG(ERROR) << "Failed to register thermal callback! " << ret_callback.getMessage();
- return false;
- }
-
- // Create AIDL thermal death recipient object.
- if (aidlDeathRecipient.get() == nullptr) {
- aidlDeathRecipient = ndk::ScopedAIBinder_DeathRecipient(
- AIBinder_DeathRecipient_new(onThermalAidlBinderDied));
- }
- auto linked = AIBinder_linkToDeath(thermal->asBinder().get(), aidlDeathRecipient.get(), this);
- if (linked != STATUS_OK) {
- // should we continue if the death recipient is not registered?
- LOG(ERROR) << "Failed to register AIDL thermal death notification";
- }
- return true;
-}
-
-} // namespace pixel
-} // namespace google
-} // namespace hardware
-} // namespace android
diff --git a/battery_mitigation/OWNERS b/battery_mitigation/OWNERS
deleted file mode 100644
index 363efac2..00000000
--- a/battery_mitigation/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-geolee@google.com
-wvw@google.com
diff --git a/battery_mitigation/include/battery_mitigation/BatteryMitigationService.h b/battery_mitigation/include/battery_mitigation/BatteryMitigationService.h
deleted file mode 100644
index 9c697140..00000000
--- a/battery_mitigation/include/battery_mitigation/BatteryMitigationService.h
+++ /dev/null
@@ -1,172 +0,0 @@
-/*
- * 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
deleted file mode 100644
index 2062d29d..00000000
--- a/battery_mitigation/include/battery_mitigation/MitigationConfig.h
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#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 {
- const std::vector<std::string> SystemPath;
- const std::vector<std::string> FilteredZones;
- const std::vector<std::string> SystemName;
- const char *const LogFilePath;
- 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:
- const std::vector<std::string> kSystemPath;
- const std::vector<std::string> kFilteredZones;
- const std::vector<std::string> kSystemName;
- const char *const kLogFilePath;
- const char *const kTimestampFormat;
-};
-
-} // namespace pixel
-} // namespace google
-} // namespace hardware
-} // namespace android
-
-#endif // HARDWARE_GOOGLE_PIXEL_BATTERY_MITIGATION_CONFIG_H
diff --git a/battery_mitigation/include/battery_mitigation/MitigationThermalManager.h b/battery_mitigation/include/battery_mitigation/MitigationThermalManager.h
deleted file mode 100644
index 5b5a3f0a..00000000
--- a/battery_mitigation/include/battery_mitigation/MitigationThermalManager.h
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#ifndef MITIGATION_THERMAL_MANAGER_H_
-#define MITIGATION_THERMAL_MANAGER_H_
-
-#include <aidl/android/hardware/thermal/BnThermalChangedCallback.h>
-#include <aidl/android/hardware/thermal/IThermal.h>
-#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 <android/binder_auto_utils.h>
-#include <unistd.h>
-#include <utils/Mutex.h>
-
-#include <fstream>
-#include <iostream>
-#include <regex>
-
-#include "MitigationConfig.h"
-
-namespace android {
-namespace hardware {
-namespace google {
-namespace pixel {
-
-using ::aidl::android::hardware::thermal::BnThermalChangedCallback;
-using ::aidl::android::hardware::thermal::IThermal;
-using ::aidl::android::hardware::thermal::Temperature;
-using ::aidl::android::hardware::thermal::TemperatureType;
-using ::aidl::android::hardware::thermal::ThrottlingSeverity;
-using android::hardware::google::pixel::MitigationConfig;
-
-class MitigationThermalManager {
- public:
- static MitigationThermalManager &getInstance();
-
- // delete copy and move constructors and assign operators
- MitigationThermalManager(MitigationThermalManager const &) = delete;
- MitigationThermalManager(MitigationThermalManager &&) = delete;
- MitigationThermalManager &operator=(MitigationThermalManager const &) = delete;
- MitigationThermalManager &operator=(MitigationThermalManager &&) = delete;
-
- private:
- // ThermalCallback implements the HIDL thermal changed callback
- // interface, IThermalChangedCallback.
- void thermalCb(const Temperature &temperature);
- android::base::boot_clock::time_point lastCapturedTime;
-
- class ThermalCallback : public BnThermalChangedCallback {
- public:
- ThermalCallback(std::function<void(const Temperature &)> notify_function)
- : notifyFunction(notify_function) {}
-
- // Callback function. thermal service will call this.
- ndk::ScopedAStatus notifyThrottling(const Temperature &temperature) override {
- if ((temperature.type == TemperatureType::BCL_VOLTAGE) ||
- (temperature.type == TemperatureType::BCL_CURRENT)) {
- notifyFunction(temperature);
- }
- return ndk::ScopedAStatus::ok();
- }
-
- private:
- std::function<void(const Temperature &)> notifyFunction;
- };
-
- static void onThermalAidlBinderDied(void *) {
- LOG(ERROR) << "Thermal AIDL service died, trying to reconnect";
- MitigationThermalManager::getInstance().connectThermalHal();
- }
-
- public:
- MitigationThermalManager();
- ~MitigationThermalManager();
- bool connectThermalHal();
- bool isMitigationTemperature(const Temperature &temperature);
- bool registerCallback();
- void remove();
- void updateConfig(const struct MitigationConfig::Config &cfg);
-
-
- private:
- std::mutex thermal_callback_mutex_;
- std::mutex thermal_hal_mutex_;
- // Thermal hal interface.
- std::shared_ptr<IThermal> thermal;
- // Thermal hal callback object.
- std::shared_ptr<ThermalCallback> callback;
- // Receiver when AIDL thermal hal restart.
- ndk::ScopedAIBinder_DeathRecipient aidlDeathRecipient;
- std::vector<std::string> kSystemPath;
- std::vector<std::string> kFilteredZones;
- std::vector<std::string> kSystemName;
- std::string kLogFilePath;
- std::string kTimestampFormat;
-};
-
-} // namespace pixel
-} // namespace google
-} // namespace hardware
-} // namespace android
-#endif
diff --git a/battery_mitigation/include/battery_mitigation/uapi/brownout_stats.h b/battery_mitigation/include/battery_mitigation/uapi/brownout_stats.h
deleted file mode 100644
index c913e40b..00000000
--- a/battery_mitigation/include/battery_mitigation/uapi/brownout_stats.h
+++ /dev/null
@@ -1,27 +0,0 @@
-/* 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/kernel_headers/Android.bp b/kernel_headers/Android.bp
index e59f4aee..308ce8a4 100644
--- a/kernel_headers/Android.bp
+++ b/kernel_headers/Android.bp
@@ -13,3 +13,9 @@ kernel_headers {
vendor: true,
recovery_available: true
}
+
+kernel_headers {
+ name: "qti_ipa_kernel_headers",
+ vendor: true,
+ recovery_available: true
+}
diff --git a/misc_writer/include/misc_writer/misc_writer.h b/misc_writer/include/misc_writer/misc_writer.h
index 7757640d..c61543f1 100644
--- a/misc_writer/include/misc_writer/misc_writer.h
+++ b/misc_writer/include/misc_writer/misc_writer.h
@@ -43,12 +43,15 @@ enum class MiscWriterActions : int32_t {
kWriteTimeRtcOffset,
kWriteTimeMinRtc,
kSetSotaConfig,
+ kWriteDstTransition,
+ kWriteDstOffset,
kUnset = -1,
};
class MiscWriter {
public:
+ // sync with bootloader's abl bootloader_message.h
static constexpr uint32_t kThemeFlagOffsetInVendorSpace = 0;
static constexpr char kDarkThemeFlag[] = "theme-dark";
static constexpr uint32_t kSotaFlagOffsetInVendorSpace = 32;
@@ -71,6 +74,11 @@ class MiscWriter {
static constexpr char kTimeMinRtc[] = "timeminrtc=";
static constexpr uint32_t kFaceauthEvalValOffsetInVendorSpace = 328;
static constexpr uint32_t kSotaScheduleShipmodeOffsetInVendorSpace = 360;
+ static constexpr uint32_t kDstTransitionOffsetInVendorSpace = 392;
+ static constexpr char kDstTransition[] = "dst-transition=";
+ static constexpr uint32_t kDstOffsetOffsetInVendorSpace = 424;
+ static constexpr char kDstOffset[] = "dst-offset=";
+ // Next available space = 456
// 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 710c2ffe..b7ca147e 100644
--- a/misc_writer/misc_writer.cpp
+++ b/misc_writer/misc_writer.cpp
@@ -105,6 +105,16 @@ bool MiscWriter::PerformAction(std::optional<size_t> override_offset) {
break;
case MiscWriterActions::kSetSotaConfig:
goto sota_config;
+ case MiscWriterActions::kWriteDstTransition:
+ offset = override_offset.value_or(kDstTransitionOffsetInVendorSpace);
+ content = std::string(kDstTransition) + stringdata_;
+ content.resize(32);
+ break;
+ case MiscWriterActions::kWriteDstOffset:
+ offset = override_offset.value_or(kDstOffsetOffsetInVendorSpace);
+ content = std::string(kDstOffset) + stringdata_;
+ content.resize(32);
+ break;
case MiscWriterActions::kUnset:
LOG(ERROR) << "The misc writer action must be set";
return false;
diff --git a/misc_writer/misc_writer_main.cpp b/misc_writer/misc_writer_main.cpp
index f3546ef6..aafb348d 100644
--- a/misc_writer/misc_writer_main.cpp
+++ b/misc_writer/misc_writer_main.cpp
@@ -53,6 +53,8 @@ static int Usage(std::string_view name) {
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 << " --set-dsttransition Write the next dst transition in the current timezone\n";
+ std::cerr << " --set-dstoffset Write the time offset during the next dst transition\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";
@@ -77,6 +79,8 @@ int main(int argc, char** argv) {
{ "set-timertcoffset", required_argument, nullptr, 0},
{ "set-minrtc", required_argument, nullptr, 0},
{ "set-sota-config", no_argument, nullptr, 0 },
+ { "set-dsttransition", required_argument, nullptr, 0},
+ { "set-dstoffset", required_argument, nullptr, 0 },
{ nullptr, 0, nullptr, 0 },
};
@@ -210,6 +214,30 @@ int main(int argc, char** argv) {
return Usage(argv[0]);
}
misc_writer = std::make_unique<MiscWriter>(iter->second);
+ } else if (option_name == "set-dsttransition"s) {
+ long long int dst_transition = strtoll(optarg, NULL, 10);
+ if (0 == dst_transition) {
+ LOG(ERROR) << "Failed to parse the dst transition:" << 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::kWriteDstTransition,
+ std::to_string(dst_transition));
+ } else if (option_name == "set-dstoffset"s) {
+ int dst_offset;
+ if (!android::base::ParseInt(optarg, &dst_offset)) {
+ LOG(ERROR) << "Failed to parse the dst offset: " << 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::kWriteDstOffset,
+ std::to_string(dst_offset));
} else {
LOG(FATAL) << "Unreachable path, option_name: " << option_name;
}
diff --git a/mm/pixel-mm-gki.rc b/mm/pixel-mm-gki.rc
index 694396e4..9f6fd2da 100644
--- a/mm/pixel-mm-gki.rc
+++ b/mm/pixel-mm-gki.rc
@@ -16,8 +16,8 @@ on init
# memory reserve tuning
write /proc/sys/vm/min_free_kbytes 27386
write /proc/sys/vm/lowmem_reserve_ratio "0 0 0"
- # khugepaged tuning
- write /sys/kernel/mm/transparent_hugepage/khugepaged/scan_sleep_millisecs 60000
+ # disable khugepaged
+ write /sys/kernel/mm/transparent_hugepage/enabled "never"
# Property from experiments - server config
on property:persist.device_config.vendor_system_native_boot.zram_size=*
diff --git a/pixelstats/Android.bp b/pixelstats/Android.bp
index 92fc08af..66640316 100644
--- a/pixelstats/Android.bp
+++ b/pixelstats/Android.bp
@@ -103,7 +103,7 @@ genrule {
"pixelatoms.proto",
":libstats_atom_options_protos",
":libprotobuf-internal-protos",
- ]
+ ],
}
genrule {
@@ -121,7 +121,7 @@ genrule {
"pixelatoms.proto",
":libstats_atom_options_protos",
":libprotobuf-internal-protos",
- ]
+ ],
}
cc_library_static {
@@ -132,61 +132,62 @@ cc_library_static {
export_generated_headers: ["pixelstatsatoms.h"],
shared_libs: [
"android.frameworks.stats-V2-ndk",
- ]
+ ],
}
cc_library {
- name: "libpixelstats",
- vendor: true,
- export_include_dirs: ["include"],
+ name: "libpixelstats",
+ vendor: true,
+ export_include_dirs: ["include"],
- tidy_disabled_srcs: [
- "MmMetricsReporter.cpp", // b/215238264
- ],
- srcs: [
- "BatteryCapacityReporter.cpp",
- "BatteryEEPROMReporter.cpp",
- "BatteryHealthReporter.cpp",
- "BrownoutDetectedReporter.cpp",
- "ChargeStatsReporter.cpp",
- "DisplayStatsReporter.cpp",
- "DropDetect.cpp",
- "MmMetricsReporter.cpp",
- "MitigationStatsReporter.cpp",
- "MitigationDurationReporter.cpp",
- "PcaChargeStats.cpp",
- "StatsHelper.cpp",
- "SysfsCollector.cpp",
- "ThermalStatsReporter.cpp",
- "TempResidencyReporter.cpp",
- "UeventListener.cpp",
- "WirelessChargeStats.cpp",
- ],
- cflags: [
- "-Wall",
- "-Werror",
- ],
- shared_libs: [
- "android.frameworks.stats-V2-ndk",
- "libbase",
- "libbinder_ndk",
- "libcutils",
- "libhidlbase",
- "liblog",
- "libprotobuf-cpp-lite",
- "libutils",
- "libsensorndkbridge",
- "pixelatoms-cpp",
- ],
- export_shared_lib_headers: [
- "android.frameworks.stats-V2-ndk",
- "pixelatoms-cpp",
- ],
- static_libs: [
- "chre_client",
- "libpixelstatsatoms",
- ],
- header_libs: ["chre_api"],
+ tidy_disabled_srcs: [
+ "MmMetricsReporter.cpp", // b/215238264
+ ],
+ srcs: [
+ "BatteryCapacityReporter.cpp",
+ "BatteryEEPROMReporter.cpp",
+ "BatteryHealthReporter.cpp",
+ "BatteryFGReporter.cpp",
+ "BrownoutDetectedReporter.cpp",
+ "ChargeStatsReporter.cpp",
+ "DisplayStatsReporter.cpp",
+ "DropDetect.cpp",
+ "MmMetricsReporter.cpp",
+ "MitigationStatsReporter.cpp",
+ "MitigationDurationReporter.cpp",
+ "PcaChargeStats.cpp",
+ "StatsHelper.cpp",
+ "SysfsCollector.cpp",
+ "ThermalStatsReporter.cpp",
+ "TempResidencyReporter.cpp",
+ "UeventListener.cpp",
+ "WirelessChargeStats.cpp",
+ ],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+ shared_libs: [
+ "android.frameworks.stats-V2-ndk",
+ "libbase",
+ "libbinder_ndk",
+ "libcutils",
+ "libhidlbase",
+ "liblog",
+ "libprotobuf-cpp-lite",
+ "libutils",
+ "libsensorndkbridge",
+ "pixelatoms-cpp",
+ ],
+ export_shared_lib_headers: [
+ "android.frameworks.stats-V2-ndk",
+ "pixelatoms-cpp",
+ ],
+ static_libs: [
+ "chre_client",
+ "libpixelstatsatoms",
+ ],
+ header_libs: ["chre_api"],
}
filegroup {
diff --git a/pixelstats/BatteryEEPROMReporter.cpp b/pixelstats/BatteryEEPROMReporter.cpp
index b8060a75..72be985d 100644
--- a/pixelstats/BatteryEEPROMReporter.cpp
+++ b/pixelstats/BatteryEEPROMReporter.cpp
@@ -42,6 +42,12 @@ using android::hardware::google::pixel::PixelAtoms::BatteryEEPROM;
BatteryEEPROMReporter::BatteryEEPROMReporter() {}
+static bool fileExists(const std::string &path) {
+ struct stat sb;
+
+ return stat(path.c_str(), &sb) == 0;
+}
+
void BatteryEEPROMReporter::checkAndReport(const std::shared_ptr<IStats> &stats_client,
const std::string &path) {
std::string file_contents;
@@ -270,21 +276,30 @@ void BatteryEEPROMReporter::reportEvent(const std::shared_ptr<IStats> &stats_cli
}
void BatteryEEPROMReporter::checkAndReportGMSR(const std::shared_ptr<IStats> &stats_client,
- const std::string &path) {
+ const std::vector<std::string> &paths) {
struct BatteryHistory gmsr;
std::string file_contents;
+ std::string path;
int16_t num;
- if (path.empty())
+ if (paths.empty())
return;
+ for (int i = 0; i < paths.size(); i++) {
+ if (fileExists(paths[i])) {
+ path = paths[i];
+ break;
+ }
+ }
+
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) {
+ if (path.find("max77779") == std::string::npos &&
+ paths[0].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
@@ -307,6 +322,11 @@ void BatteryEEPROMReporter::checkAndReportGMSR(const std::shared_ptr<IStats> &st
}
}
+ if (gmsr.tempco == 0xFFFF || gmsr.rcomp0 == 0xFFFF || gmsr.full_cap == 0xFFFF) {
+ ALOGD("Ignore invalid gmsr");
+ return;
+ }
+
reportEvent(stats_client, gmsr);
}
diff --git a/pixelstats/BatteryFGReporter.cpp b/pixelstats/BatteryFGReporter.cpp
new file mode 100644
index 00000000..36b768a6
--- /dev/null
+++ b/pixelstats/BatteryFGReporter.cpp
@@ -0,0 +1,300 @@
+/*
+ * Copyright (C) 2024 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 "pixelstats: BatteryFGReporter"
+
+#include <log/log.h>
+#include <time.h>
+#include <utils/Timers.h>
+#include <cinttypes>
+#include <cmath>
+
+#include <android-base/file.h>
+#include <pixelstats/BatteryFGReporter.h>
+#include <hardware/google/pixel/pixelstats/pixelatoms.pb.h>
+
+namespace android {
+namespace hardware {
+namespace google {
+namespace pixel {
+
+using aidl::android::frameworks::stats::VendorAtom;
+using aidl::android::frameworks::stats::VendorAtomValue;
+using android::base::ReadFileToString;
+using android::hardware::google::pixel::PixelAtoms::BatteryEEPROM;
+
+
+BatteryFGReporter::BatteryFGReporter() {}
+
+static bool fileExists(const std::string &path) {
+ struct stat sb;
+
+ return stat(path.c_str(), &sb) == 0;
+}
+
+void BatteryFGReporter::reportEvent(const std::shared_ptr<IStats> &stats_client,
+ const struct BatteryFGLearningParam &params) {
+ // upload atom
+ const std::vector<int> eeprom_history_fields = {
+ BatteryEEPROM::kCycleCntFieldNumber, BatteryEEPROM::kFullCapFieldNumber,
+ BatteryEEPROM::kEsrFieldNumber, BatteryEEPROM::kRslowFieldNumber,
+ BatteryEEPROM::kSohFieldNumber, BatteryEEPROM::kBattTempFieldNumber,
+ BatteryEEPROM::kCutoffSocFieldNumber, BatteryEEPROM::kCcSocFieldNumber,
+ BatteryEEPROM::kSysSocFieldNumber, BatteryEEPROM::kMsocFieldNumber,
+ BatteryEEPROM::kBattSocFieldNumber, BatteryEEPROM::kReserveFieldNumber,
+ BatteryEEPROM::kMaxTempFieldNumber, BatteryEEPROM::kMinTempFieldNumber,
+ BatteryEEPROM::kMaxVbattFieldNumber, BatteryEEPROM::kMinVbattFieldNumber,
+ BatteryEEPROM::kMaxIbattFieldNumber, BatteryEEPROM::kMinIbattFieldNumber,
+ BatteryEEPROM::kChecksumFieldNumber, BatteryEEPROM::kTempcoFieldNumber,
+ BatteryEEPROM::kRcomp0FieldNumber, BatteryEEPROM::kTimerHFieldNumber,
+ BatteryEEPROM::kFullRepFieldNumber};
+
+ switch(params.type) {
+ case EvtFGLearningParams:
+ ALOGD("reportEvent: log learning fcnom: %04x, dpacc: %04x, dqacc: %04x, fcrep: %04x, "
+ "repsoc: %04x, msoc: %04x, vfsoc: %04x, fstat: %04x, rcomp0: %04x, tempco: %04x",
+ params.fcnom, params.dpacc, params.dqacc, params.fcrep, params.repsoc, params.msoc,
+ params.vfsoc, params.fstat, params.rcomp0, params.tempco);
+ break;
+ case EvtFWUpdate:
+ ALOGD("reportEvent: firmware update try: %u, success: %u, fail: %u",
+ params.fcnom, params.dpacc, params.dqacc);
+ break;
+ case EvtModelLoading:
+ ALOGD("reportEvent: model loading success: %u, fail: %u, next update %u",
+ params.fcnom, params.dpacc, params.dqacc);
+ break;
+ default:
+ ALOGD("unknown event type %04x", params.type);
+ break;
+ }
+
+ std::vector<VendorAtomValue> values(eeprom_history_fields.size());
+ VendorAtomValue val;
+
+ val.set<VendorAtomValue::intValue>(0);
+ values[BatteryEEPROM::kCycleCntFieldNumber - kVendorAtomOffset] = val;
+ val.set<VendorAtomValue::intValue>(params.fcnom);
+ values[BatteryEEPROM::kFullCapFieldNumber - kVendorAtomOffset] = val;
+ val.set<VendorAtomValue::intValue>(params.dpacc);
+ values[BatteryEEPROM::kEsrFieldNumber - kVendorAtomOffset] = val;
+ val.set<VendorAtomValue::intValue>(params.dqacc);
+ values[BatteryEEPROM::kRslowFieldNumber - kVendorAtomOffset] = val;
+ val.set<VendorAtomValue::intValue>(0);
+ values[BatteryEEPROM::kSohFieldNumber - kVendorAtomOffset] = val;
+ val.set<VendorAtomValue::intValue>(0);
+ values[BatteryEEPROM::kBattTempFieldNumber - kVendorAtomOffset] = val;
+ val.set<VendorAtomValue::intValue>(0);
+ values[BatteryEEPROM::kCutoffSocFieldNumber - kVendorAtomOffset] = val;
+ val.set<VendorAtomValue::intValue>(0);
+ values[BatteryEEPROM::kCcSocFieldNumber - kVendorAtomOffset] = val;
+ val.set<VendorAtomValue::intValue>(0);
+ values[BatteryEEPROM::kSysSocFieldNumber - kVendorAtomOffset] = val;
+ val.set<VendorAtomValue::intValue>(0);
+ values[BatteryEEPROM::kMsocFieldNumber - kVendorAtomOffset] = val;
+ val.set<VendorAtomValue::intValue>(0);
+ values[BatteryEEPROM::kBattSocFieldNumber - kVendorAtomOffset] = val;
+ val.set<VendorAtomValue::intValue>(0);
+ values[BatteryEEPROM::kReserveFieldNumber - kVendorAtomOffset] = val;
+ val.set<VendorAtomValue::intValue>(0);
+ values[BatteryEEPROM::kMaxTempFieldNumber - kVendorAtomOffset] = val;
+ val.set<VendorAtomValue::intValue>(0);
+ values[BatteryEEPROM::kMinTempFieldNumber - kVendorAtomOffset] = val;
+ val.set<VendorAtomValue::intValue>(params.fcrep);
+ values[BatteryEEPROM::kMaxVbattFieldNumber - kVendorAtomOffset] = val;
+ val.set<VendorAtomValue::intValue>(params.msoc);
+ values[BatteryEEPROM::kMinVbattFieldNumber - kVendorAtomOffset] = val;
+ val.set<VendorAtomValue::intValue>(params.vfsoc);
+ values[BatteryEEPROM::kMaxIbattFieldNumber - kVendorAtomOffset] = val;
+ val.set<VendorAtomValue::intValue>(params.fstat);
+ values[BatteryEEPROM::kMinIbattFieldNumber - kVendorAtomOffset] = val;
+ val.set<VendorAtomValue::intValue>((uint16_t)params.type);
+ values[BatteryEEPROM::kChecksumFieldNumber - kVendorAtomOffset] = val;
+ val.set<VendorAtomValue::intValue>(params.tempco);
+ values[BatteryEEPROM::kTempcoFieldNumber - kVendorAtomOffset] = val;
+ val.set<VendorAtomValue::intValue>(params.rcomp0);
+ values[BatteryEEPROM::kRcomp0FieldNumber - kVendorAtomOffset] = val;
+ val.set<VendorAtomValue::intValue>(0);
+ values[BatteryEEPROM::kTimerHFieldNumber - kVendorAtomOffset] = val;
+ val.set<VendorAtomValue::intValue>(params.repsoc);
+ values[BatteryEEPROM::kFullRepFieldNumber - kVendorAtomOffset] = val;
+
+ VendorAtom event = {.reverseDomainName = "",
+ .atomId = PixelAtoms::Atom::kBatteryEeprom,
+ .values = std::move(values)};
+ const ndk::ScopedAStatus ret = stats_client->reportVendorAtom(event);
+ if (!ret.isOk())
+ ALOGE("Unable to report BatteryEEPROM to Stats service");
+}
+
+void BatteryFGReporter::checkAndReportFGLearning(const std::shared_ptr<IStats> &stats_client,
+ const std::vector<std::string> &paths) {
+ struct BatteryFGLearningParam params;
+ std::string file_contents, line, path;
+ std::istringstream ss;
+ int16_t num;
+ const char* data;
+ int pos = 0;
+
+ if (paths.empty())
+ return;
+
+ for (int i = 0; i < paths.size(); i++) {
+ if (fileExists(paths[i])) {
+ path = paths[i];
+ break;
+ }
+ }
+
+ /* not found */
+ if (path.empty())
+ return;
+
+ if (!ReadFileToString(path, &file_contents)) {
+ ALOGE("Unable to read FG Learning History path: %s - %s", path.c_str(), strerror(errno));
+ return;
+ }
+
+ if (file_contents.length() == 0)
+ return;
+
+ /* LH: Learning History */
+ params.type = EvtFGLearningParams;
+ ss.str(file_contents);
+ while (std::getline(ss, line)) {
+ data = line.c_str();
+ num = sscanf(&data[pos], "%*2" SCNx16 ":%4" SCNx16 "%*2" SCNx16 ":%4" SCNx16
+ "%*2" SCNx16 ":%4" SCNx16 "%*2" SCNx16 ":%4" SCNx16 "%*2" SCNx16 ":%4" SCNx16
+ "%*2" SCNx16 ":%4" SCNx16 "%*2" SCNx16 ":%4" SCNx16 "%*2" SCNx16 ":%4" SCNx16
+ "%*2" SCNx16 ":%4" SCNx16 "%*2" SCNx16 ":%4" SCNx16 "\n",
+ &params.fcnom, &params.dpacc, &params.dqacc, &params.fcrep, &params.repsoc,
+ &params.msoc, &params.vfsoc, &params.fstat, &params.rcomp0, &params.tempco);
+
+ if (num != kNumFGLearningFields)
+ continue;
+
+ if (old_learn_params[0] != params.fcnom || old_learn_params[1] != params.dpacc ||
+ old_learn_params[2] != params.dqacc || old_learn_params[3] != params.fstat ) {
+ old_learn_params[0] = params.fcnom;
+ old_learn_params[1] = params.dpacc;
+ old_learn_params[2] = params.dqacc;
+ old_learn_params[3] = params.fstat;
+
+ reportEvent(stats_client, params);
+ }
+ }
+
+ /* Clear after reporting data */
+ if (!::android::base::WriteStringToFile("0", path.c_str()))
+ ALOGE("Couldn't clear %s - %s", path.c_str(), strerror(errno));
+}
+
+void BatteryFGReporter::checkAndReportFwUpdate(const std::shared_ptr<IStats> &stats_client,
+ const std::string &path) {
+ struct BatteryFGLearningParam params;
+ std::string file_contents;
+ int16_t num;
+
+ if (path.empty())
+ return;
+
+ if (!ReadFileToString(path, &file_contents)) {
+ ALOGE("Unable to read FirmwareUpdate path: %s - %s", path.c_str(), strerror(errno));
+ return;
+ }
+
+ /* FU: Firmware Update */
+ params.type = EvtFWUpdate;
+ num = sscanf(file_contents.c_str(), "%" SCNu16 " %" SCNu16 " %" SCNu16,
+ &params.fcnom, &params.dpacc, &params.dqacc);
+ if (num != kNumFwUpdateFields) {
+ ALOGE("Couldn't process FirmwareUpdate history path. num=%d\n", num);
+ return;
+ }
+
+ /* No event to report */
+ if (params.fcnom == 0 )
+ return;
+
+ if (old_fw_update[0] != params.fcnom || old_fw_update[1] != params.dpacc ||
+ old_fw_update[2] != params.dqacc) {
+ old_fw_update[0] = params.fcnom;
+ old_fw_update[1] = params.dpacc;
+ old_fw_update[2] = params.dqacc;
+
+ reportEvent(stats_client, params);
+
+ /* Clear after reporting data */
+ if (!::android::base::WriteStringToFile("0", path.c_str()))
+ ALOGE("Couldn't clear %s - %s", path.c_str(), strerror(errno));
+ }
+}
+
+void BatteryFGReporter::checkAndReportFGModelLoading(const std::shared_ptr<IStats> &stats_client,
+ const std::vector<std::string> &paths) {
+ struct BatteryFGLearningParam params = {.type = EvtModelLoading, .fcnom = 0, .dpacc = 0,
+ .dqacc = 0};
+ std::string file_contents;
+ std::string path;
+ int16_t num;
+ int pos = 0;
+ const char *data;
+
+ if (paths.empty())
+ return;
+
+ for (int i = 0; i < paths.size(); i++) {
+ if (fileExists(paths[i])) {
+ path = paths[i];
+ break;
+ }
+ }
+
+ /* not found */
+ if (path.empty())
+ return;
+
+ if (!ReadFileToString(path, &file_contents)) {
+ ALOGE("Unable to read ModelLoading History path: %s - %s", path.c_str(), strerror(errno));
+ return;
+ }
+
+ data = file_contents.c_str();
+
+ num = sscanf(&data[pos], "ModelNextUpdate: %" SCNu16 "\n"
+ "%*x:%*x\n%*x:%*x\n%*x:%*x\n%*x:%*x\n%*x:%*x\n%n",
+ &params.dqacc, &pos);
+ if (num != 1) {
+ ALOGE("Couldn't process ModelLoading History. num=%d\n", num);
+ return;
+ }
+
+ sscanf(&data[pos], "ATT: %" SCNu16 " FAIL: %" SCNu16, &params.fcnom, &params.dpacc);
+
+ if (old_model_loading[0] != params.fcnom || old_model_loading[1] != params.dpacc ||
+ old_model_loading[2] != params.dqacc) {
+ old_model_loading[0] = params.fcnom;
+ old_model_loading[1] = params.dpacc;
+ old_model_loading[2] = params.dqacc;
+
+ reportEvent(stats_client, params);
+ }
+}
+
+} // namespace pixel
+} // namespace google
+} // namespace hardware
+} // namespace android
diff --git a/pixelstats/BrownoutDetectedReporter.cpp b/pixelstats/BrownoutDetectedReporter.cpp
index a5f4e39a..d145af31 100644
--- a/pixelstats/BrownoutDetectedReporter.cpp
+++ b/pixelstats/BrownoutDetectedReporter.cpp
@@ -61,6 +61,7 @@ const std::regex kAlreadyUpdatedPattern("^(LASTMEAL_UPDATED)$");
const std::map<std::string, int> kBrownoutReason = {{"uvlo,pmic,if", BrownoutDetected::UVLO_IF},
{"ocp,pmic,if", BrownoutDetected::OCP_IF},
+ {"ocp2,pmic,if", BrownoutDetected::OCP2_IF},
{"uvlo,pmic,main", BrownoutDetected::UVLO_MAIN},
{"uvlo,pmic,sub", BrownoutDetected::UVLO_SUB},
{"ocp,buck1m", BrownoutDetected::OCP_B1M},
diff --git a/pixelstats/DisplayStatsReporter.cpp b/pixelstats/DisplayStatsReporter.cpp
index 3aff2225..c72af6e4 100644
--- a/pixelstats/DisplayStatsReporter.cpp
+++ b/pixelstats/DisplayStatsReporter.cpp
@@ -42,7 +42,7 @@ using android::hardware::google::pixel::PixelAtoms::DisplayPanelErrorStats;
DisplayStatsReporter::DisplayStatsReporter() {}
-bool DisplayStatsReporter::readDisplayPanelErrorCount(const std::string &path, int64_t *val) {
+bool DisplayStatsReporter::readDisplayErrorCount(const std::string &path, int64_t *val) {
std::string file_contents;
if (path.empty()) {
@@ -51,8 +51,7 @@ bool DisplayStatsReporter::readDisplayPanelErrorCount(const std::string &path, i
if (!ReadFileToString(path.c_str(), &file_contents)) {
if (errno != ENOENT) {
- ALOGD("readDisplayPanelErrorCount Unable to read %s - %s", path.c_str(),
- strerror(errno));
+ ALOGD("readDisplayErrorCount Unable to read %s - %s", path.c_str(), strerror(errno));
}
return false;
} else {
@@ -65,6 +64,17 @@ bool DisplayStatsReporter::readDisplayPanelErrorCount(const std::string &path, i
return true;
}
+int DisplayStatsReporter::verifyCount(int val, bool *report_stats) {
+ if (val < 0) {
+ ALOGE("Invalid display stats value(%d)", val);
+ return -EINVAL;
+ } else {
+ *report_stats |= (val != 0);
+ }
+
+ return 0;
+}
+
bool DisplayStatsReporter::captureDisplayPanelErrorStats(
const std::vector<std::string> &display_stats_paths,
struct DisplayPanelErrorStats *pcur_data) {
@@ -82,40 +92,42 @@ bool DisplayStatsReporter::captureDisplayPanelErrorStats(
path = display_stats_paths[index];
// Read primary panel error stats.
- if (!readDisplayPanelErrorCount(path, &(pcur_data->primary_error_count_te))) {
- pcur_data->primary_error_count_te = prev_data_.primary_error_count_te;
+ if (!readDisplayErrorCount(path, &(pcur_data->primary_error_count_te))) {
+ pcur_data->primary_error_count_te = prev_panel_data_.primary_error_count_te;
} else {
- report_stats |= (pcur_data->primary_error_count_te > prev_data_.primary_error_count_te);
+ report_stats |=
+ (pcur_data->primary_error_count_te > prev_panel_data_.primary_error_count_te);
}
index = PixelAtoms::DisplayPanelErrorStats::kPrimaryErrorCountUnknownFieldNumber;
index = index - kVendorAtomOffset;
path = display_stats_paths[index];
- if (!readDisplayPanelErrorCount(path, &(pcur_data->primary_error_count_unknown))) {
- pcur_data->primary_error_count_unknown = prev_data_.primary_error_count_unknown;
+ if (!readDisplayErrorCount(path, &(pcur_data->primary_error_count_unknown))) {
+ pcur_data->primary_error_count_unknown = prev_panel_data_.primary_error_count_unknown;
} else {
- report_stats |=
- (pcur_data->primary_error_count_unknown > prev_data_.primary_error_count_unknown);
+ report_stats |= (pcur_data->primary_error_count_unknown >
+ prev_panel_data_.primary_error_count_unknown);
}
// Read secondary panel error stats.
index = PixelAtoms::DisplayPanelErrorStats::kSecondaryErrorCountTeFieldNumber;
index = index - kVendorAtomOffset;
path = display_stats_paths[index];
- if (!readDisplayPanelErrorCount(path, &(pcur_data->secondary_error_count_te))) {
- pcur_data->secondary_error_count_te = prev_data_.secondary_error_count_te;
+ if (!readDisplayErrorCount(path, &(pcur_data->secondary_error_count_te))) {
+ pcur_data->secondary_error_count_te = prev_panel_data_.secondary_error_count_te;
} else {
- report_stats |= (pcur_data->secondary_error_count_te > prev_data_.secondary_error_count_te);
+ report_stats |=
+ (pcur_data->secondary_error_count_te > prev_panel_data_.secondary_error_count_te);
}
index = PixelAtoms::DisplayPanelErrorStats::kSecondaryErrorCountUnknownFieldNumber;
index = index - kVendorAtomOffset;
path = display_stats_paths[index];
- if (!readDisplayPanelErrorCount(path, &(pcur_data->secondary_error_count_unknown))) {
- pcur_data->secondary_error_count_unknown = prev_data_.secondary_error_count_unknown;
+ if (!readDisplayErrorCount(path, &(pcur_data->secondary_error_count_unknown))) {
+ pcur_data->secondary_error_count_unknown = prev_panel_data_.secondary_error_count_unknown;
} else {
report_stats |= (pcur_data->secondary_error_count_unknown >
- prev_data_.secondary_error_count_unknown);
+ prev_panel_data_.secondary_error_count_unknown);
}
return report_stats;
@@ -124,10 +136,11 @@ bool DisplayStatsReporter::captureDisplayPanelErrorStats(
void DisplayStatsReporter::logDisplayPanelErrorStats(
const std::shared_ptr<IStats> &stats_client,
const std::vector<std::string> &display_stats_paths) {
- struct DisplayPanelErrorStats cur_data = prev_data_;
+ struct DisplayPanelErrorStats cur_data = prev_panel_data_;
+ bool report_stats = false;
if (!captureDisplayPanelErrorStats(display_stats_paths, &cur_data)) {
- prev_data_ = cur_data;
+ prev_panel_data_ = cur_data;
return;
}
@@ -137,21 +150,31 @@ void DisplayStatsReporter::logDisplayPanelErrorStats(
std::vector<VendorAtomValue> values(kNumOfDisplayPanelErrorStats);
error_count = std::min<int64_t>(
- cur_data.primary_error_count_te - prev_data_.primary_error_count_te, max_error_count);
+ cur_data.primary_error_count_te - prev_panel_data_.primary_error_count_te,
+ max_error_count);
+ if (verifyCount(error_count, &report_stats) < 0)
+ return;
+
tmp.set<VendorAtomValue::intValue>(error_count);
int64_t index = PixelAtoms::DisplayPanelErrorStats::kPrimaryErrorCountTeFieldNumber;
index = index - kVendorAtomOffset;
values[index] = tmp;
error_count = std::min<int64_t>(
- cur_data.primary_error_count_unknown - prev_data_.primary_error_count_unknown,
+ cur_data.primary_error_count_unknown - prev_panel_data_.primary_error_count_unknown,
max_error_count);
+ if (verifyCount(error_count, &report_stats) < 0)
+ return;
+
tmp.set<VendorAtomValue::intValue>(error_count);
index = PixelAtoms::DisplayPanelErrorStats::kPrimaryErrorCountUnknownFieldNumber;
index = index - kVendorAtomOffset;
values[index] = tmp;
- prev_data_ = cur_data;
+ prev_panel_data_ = cur_data;
+
+ if (!report_stats)
+ return;
ALOGD("Report updated display panel metrics to stats service");
// Send vendor atom to IStats HAL
@@ -163,9 +186,156 @@ void DisplayStatsReporter::logDisplayPanelErrorStats(
ALOGE("Unable to report display Display Panel stats to Stats service");
}
+bool DisplayStatsReporter::captureDisplayPortErrorStats(
+ const std::vector<std::string> &displayport_stats_paths, int64_t *pcur_data) {
+ int64_t path_index;
+ bool report_stats = false;
+ std::string path;
+
+ if (displayport_stats_paths.size() < DISPLAY_PORT_ERROR_STATS_SIZE)
+ return false;
+
+ for (int i = 0; i < DISPLAY_PORT_ERROR_STATS_SIZE; i++) {
+ path_index = display_port_error_path_index[i];
+ path_index = path_index - kVendorAtomOffset;
+ path = displayport_stats_paths[path_index];
+
+ if (!readDisplayErrorCount(path, &(pcur_data[i]))) {
+ pcur_data[i] = prev_dp_data_[i];
+ } else {
+ report_stats |= (pcur_data[i] > prev_dp_data_[i]);
+ }
+ }
+
+ return report_stats;
+}
+
+void DisplayStatsReporter::logDisplayPortErrorStats(
+ const std::shared_ptr<IStats> &stats_client,
+ const std::vector<std::string> &displayport_stats_paths) {
+ int64_t cur_data[DISPLAY_PORT_ERROR_STATS_SIZE];
+ int64_t path_index;
+ bool report_stats = false;
+
+ memcpy(cur_data, prev_dp_data_, sizeof(prev_dp_data_));
+ if (!captureDisplayPortErrorStats(displayport_stats_paths, &cur_data[0])) {
+ memcpy(prev_dp_data_, cur_data, sizeof(cur_data));
+ return;
+ }
+
+ VendorAtomValue tmp;
+ int64_t max_error_count = static_cast<int64_t>(INT32_MAX);
+ int error_count;
+ std::vector<VendorAtomValue> values(DISPLAY_PORT_ERROR_STATS_SIZE);
+
+ for (int i = 0; i < DISPLAY_PORT_ERROR_STATS_SIZE; i++) {
+ error_count = std::min<int64_t>(cur_data[i] - prev_dp_data_[i], max_error_count);
+ if (verifyCount(error_count, &report_stats) < 0)
+ return;
+
+ tmp.set<VendorAtomValue::intValue>(error_count);
+ path_index = display_port_error_path_index[i];
+ path_index = path_index - kVendorAtomOffset;
+ values[path_index] = tmp;
+ }
+
+ memcpy(prev_dp_data_, cur_data, sizeof(cur_data));
+
+ if (!report_stats)
+ return;
+
+ ALOGD("Report updated displayport metrics to stats service");
+ // Send vendor atom to IStats HAL
+ VendorAtom event = {.reverseDomainName = "",
+ .atomId = PixelAtoms::Atom::kDisplayPortErrorStats,
+ .values = std::move(values)};
+ const ndk::ScopedAStatus ret = stats_client->reportVendorAtom(event);
+ if (!ret.isOk())
+ ALOGE("Unable to report DisplayPort stats to Stats service");
+}
+
+bool DisplayStatsReporter::captureHDCPAuthTypeStats(
+ const std::vector<std::string> &hdcp_stats_paths, int64_t *pcur_data) {
+ int64_t path_index;
+ bool report_stats = false;
+ std::string path;
+
+ if (hdcp_stats_paths.size() < HDCP_AUTH_TYPE_STATS_SIZE)
+ return false;
+
+ for (int i = 0; i < HDCP_AUTH_TYPE_STATS_SIZE; i++) {
+ path_index = hdcp_auth_type_path_index[i];
+ path_index = path_index - kVendorAtomOffset;
+ path = hdcp_stats_paths[path_index];
+
+ if (!readDisplayErrorCount(path, &(pcur_data[i]))) {
+ pcur_data[i] = prev_hdcp_data_[i];
+ } else {
+ report_stats |= (pcur_data[i] > prev_hdcp_data_[i]);
+ }
+ }
+
+ return report_stats;
+}
+
+void DisplayStatsReporter::logHDCPAuthTypeStats(const std::shared_ptr<IStats> &stats_client,
+ const std::vector<std::string> &hdcp_stats_paths) {
+ int64_t cur_data[HDCP_AUTH_TYPE_STATS_SIZE];
+ int64_t path_index;
+ bool report_stats = false;
+
+ memcpy(cur_data, prev_hdcp_data_, sizeof(prev_hdcp_data_));
+ if (!captureHDCPAuthTypeStats(hdcp_stats_paths, &cur_data[0])) {
+ memcpy(prev_hdcp_data_, cur_data, sizeof(cur_data));
+ return;
+ }
+
+ VendorAtomValue tmp;
+ int64_t max_error_count = static_cast<int64_t>(INT32_MAX);
+ int error_count;
+ std::vector<VendorAtomValue> values(HDCP_AUTH_TYPE_STATS_SIZE);
+
+ for (int i = 0; i < HDCP_AUTH_TYPE_STATS_SIZE; i++) {
+ error_count = std::min<int64_t>(cur_data[i] - prev_hdcp_data_[i], max_error_count);
+ if (verifyCount(error_count, &report_stats) < 0)
+ return;
+
+ tmp.set<VendorAtomValue::intValue>(error_count);
+ path_index = hdcp_auth_type_path_index[i];
+ path_index = path_index - kVendorAtomOffset;
+ values[path_index] = tmp;
+ }
+
+ memcpy(prev_hdcp_data_, cur_data, sizeof(cur_data));
+
+ if (!report_stats)
+ return;
+
+ ALOGD("Report updated hdcp metrics to stats service");
+ // Send vendor atom to IStats HAL
+ VendorAtom event = {.reverseDomainName = "",
+ .atomId = PixelAtoms::Atom::kHdcpAuthTypeStats,
+ .values = std::move(values)};
+ const ndk::ScopedAStatus ret = stats_client->reportVendorAtom(event);
+ if (!ret.isOk())
+ ALOGE("Unable to report hdcp stats to Stats service");
+}
void DisplayStatsReporter::logDisplayStats(const std::shared_ptr<IStats> &stats_client,
- const std::vector<std::string> &display_stats_paths) {
- logDisplayPanelErrorStats(stats_client, display_stats_paths);
+ const std::vector<std::string> &display_stats_paths,
+ const display_stats_type stats_type) {
+ switch (stats_type) {
+ case DISP_PANEL_STATE:
+ logDisplayPanelErrorStats(stats_client, display_stats_paths);
+ break;
+ case DISP_PORT_STATE:
+ logDisplayPortErrorStats(stats_client, display_stats_paths);
+ break;
+ case HDCP_STATE:
+ logHDCPAuthTypeStats(stats_client, display_stats_paths);
+ break;
+ default:
+ ALOGE("Unsupport display state type(%d)", stats_type);
+ }
}
} // namespace pixel
diff --git a/pixelstats/SysfsCollector.cpp b/pixelstats/SysfsCollector.cpp
index bce967ca..00f335f5 100644
--- a/pixelstats/SysfsCollector.cpp
+++ b/pixelstats/SysfsCollector.cpp
@@ -51,11 +51,13 @@ using android::hardware::google::pixel::PixelAtoms::BatteryCapacity;
using android::hardware::google::pixel::PixelAtoms::BlockStatsReported;
using android::hardware::google::pixel::PixelAtoms::BootStatsInfo;
using android::hardware::google::pixel::PixelAtoms::DisplayPanelErrorStats;
+using android::hardware::google::pixel::PixelAtoms::DisplayPortErrorStats;
using android::hardware::google::pixel::PixelAtoms::F2fsAtomicWriteInfo;
using android::hardware::google::pixel::PixelAtoms::F2fsCompressionInfo;
using android::hardware::google::pixel::PixelAtoms::F2fsGcSegmentInfo;
using android::hardware::google::pixel::PixelAtoms::F2fsSmartIdleMaintEnabledStateChanged;
using android::hardware::google::pixel::PixelAtoms::F2fsStatsInfo;
+using android::hardware::google::pixel::PixelAtoms::HDCPAuthTypeStats;
using android::hardware::google::pixel::PixelAtoms::PartitionsUsedSpaceReported;
using android::hardware::google::pixel::PixelAtoms::PcieLinkStatsReported;
using android::hardware::google::pixel::PixelAtoms::StorageUfsHealth;
@@ -120,6 +122,8 @@ SysfsCollector::SysfsCollector(const struct SysfsPaths &sysfs_paths)
kModemPcieLinkStatsPath(sysfs_paths.ModemPcieLinkStatsPath),
kWifiPcieLinkStatsPath(sysfs_paths.WifiPcieLinkStatsPath),
kDisplayStatsPaths(sysfs_paths.DisplayStatsPaths),
+ kDisplayPortStatsPaths(sysfs_paths.DisplayPortStatsPaths),
+ kHDCPStatsPaths(sysfs_paths.HDCPStatsPaths),
kPDMStatePath(sysfs_paths.PDMStatePath),
kWavesPath(sysfs_paths.WavesPath),
kAdaptedInfoCountPath(sysfs_paths.AdaptedInfoCountPath),
@@ -204,11 +208,7 @@ void SysfsCollector::logBatteryEEPROM(const std::shared_ptr<IStats> &stats_clien
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_.checkAndReportGMSR(stats_client, kGMSRPath);
battery_EEPROM_reporter_.checkAndReportMaxfgHistory(stats_client, kMaxfgHistoryPath);
}
@@ -417,7 +417,18 @@ void SysfsCollector::logSpeakerHealthStats(const std::shared_ptr<IStats> &stats_
}
void SysfsCollector::logDisplayStats(const std::shared_ptr<IStats> &stats_client) {
- display_stats_reporter_.logDisplayStats(stats_client, kDisplayStatsPaths);
+ display_stats_reporter_.logDisplayStats(stats_client, kDisplayStatsPaths,
+ DisplayStatsReporter::DISP_PANEL_STATE);
+}
+
+void SysfsCollector::logDisplayPortStats(const std::shared_ptr<IStats> &stats_client) {
+ display_stats_reporter_.logDisplayStats(stats_client, kDisplayPortStatsPaths,
+ DisplayStatsReporter::DISP_PORT_STATE);
+}
+
+void SysfsCollector::logHDCPStats(const std::shared_ptr<IStats> &stats_client) {
+ display_stats_reporter_.logDisplayStats(stats_client, kHDCPStatsPaths,
+ DisplayStatsReporter::HDCP_STATE);
}
void SysfsCollector::logThermalStats(const std::shared_ptr<IStats> &stats_client) {
@@ -2071,6 +2082,8 @@ void SysfsCollector::logPerDay() {
logCodec1Failed(stats_client);
logCodecFailed(stats_client);
logDisplayStats(stats_client);
+ logDisplayPortStats(stats_client);
+ logHDCPStats(stats_client);
logF2fsStats(stats_client);
logF2fsAtomicWriteInfo(stats_client);
logF2fsCompressionInfo(stats_client);
diff --git a/pixelstats/UeventListener.cpp b/pixelstats/UeventListener.cpp
index 6ab91f6f..5e033788 100644
--- a/pixelstats/UeventListener.cpp
+++ b/pixelstats/UeventListener.cpp
@@ -188,6 +188,17 @@ void UeventListener::ReportChargeMetricsEvent(const std::shared_ptr<IStats> &sta
charge_stats_reporter_.checkAndReport(stats_client, kChargeMetricsPath);
}
+void UeventListener::ReportFGMetricsEvent(const std::shared_ptr<IStats> &stats_client,
+ const char *driver) {
+ if (!driver || (strcmp(driver, "DRIVER=max77779-fg") && strcmp(driver, "DRIVER=maxfg") &&
+ strcmp(driver, "DRIVER=max1720x")))
+ return;
+
+ battery_fg_reporter_.checkAndReportFGLearning(stats_client, kFGLearningPath);
+ battery_fg_reporter_.checkAndReportFwUpdate(stats_client, kFwUpdatePath);
+ battery_fg_reporter_.checkAndReportFGModelLoading(stats_client, kFGModelLoadingPath);
+}
+
/**
* Report raw battery capacity, system battery capacity and associated
* battery capacity curves. This data is collected to verify the filter
@@ -479,6 +490,7 @@ bool UeventListener::ProcessUevent() {
ReportGpuEvent(stats_client, driver, gpu_event_type, gpu_event_info);
ReportThermalAbnormalEvent(stats_client, devpath, thermal_abnormal_event_type,
thermal_abnormal_event_info);
+ ReportFGMetricsEvent(stats_client, driver);
}
if (log_fd_ > 0) {
@@ -491,7 +503,10 @@ UeventListener::UeventListener(const std::string audio_uevent, const std::string
const std::string overheat_path,
const std::string charge_metrics_path,
const std::string typec_partner_vid_path,
- const std::string typec_partner_pid_path)
+ const std::string typec_partner_pid_path,
+ const std::vector<std::string> fg_learning_path,
+ const std::string fw_update_path,
+ const std::vector<std::string> fg_modelloading_path)
: kAudioUevent(audio_uevent),
kBatterySSOCPath(ssoc_details_path),
kUsbPortOverheatPath(overheat_path),
@@ -499,6 +514,9 @@ UeventListener::UeventListener(const std::string audio_uevent, const std::string
kTypeCPartnerUevent(typec_partner_uevent_default),
kTypeCPartnerVidPath(typec_partner_vid_path),
kTypeCPartnerPidPath(typec_partner_pid_path),
+ kFGLearningPath(fg_learning_path),
+ kFwUpdatePath(fw_update_path),
+ kFGModelLoadingPath(fg_modelloading_path),
uevent_fd_(-1),
log_fd_(-1) {}
@@ -520,6 +538,10 @@ UeventListener::UeventListener(const struct UeventPaths &uevents_paths)
kTypeCPartnerPidPath((uevents_paths.TypeCPartnerPidPath == nullptr)
? typec_partner_pid_path_default
: uevents_paths.TypeCPartnerPidPath),
+ kFGLearningPath(uevents_paths.FGLearningPath),
+ kFwUpdatePath((uevents_paths.FwUpdatePath == nullptr)
+ ? "" : uevents_paths.FwUpdatePath),
+ kFGModelLoadingPath(uevents_paths.FGModelLoadingPath),
uevent_fd_(-1),
log_fd_(-1) {}
diff --git a/pixelstats/include/pixelstats/BatteryEEPROMReporter.h b/pixelstats/include/pixelstats/BatteryEEPROMReporter.h
index 41577127..3cb4c5dc 100644
--- a/pixelstats/include/pixelstats/BatteryEEPROMReporter.h
+++ b/pixelstats/include/pixelstats/BatteryEEPROMReporter.h
@@ -45,7 +45,7 @@ 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 checkAndReportGMSR(const std::shared_ptr<IStats> &stats_client, const std::vector<std::string> &paths);
void checkAndReportMaxfgHistory(const std::shared_ptr<IStats> &stats_client,
const std::string &path);
diff --git a/pixelstats/include/pixelstats/BatteryFGReporter.h b/pixelstats/include/pixelstats/BatteryFGReporter.h
new file mode 100644
index 00000000..9954ada7
--- /dev/null
+++ b/pixelstats/include/pixelstats/BatteryFGReporter.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2024 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.
+ */
+
+ #ifndef HARDWARE_GOOGLE_PIXEL_PIXELSTATS_BATTERYFGREPORTER_H
+ #define HARDWARE_GOOGLE_PIXEL_PIXELSTATS_BATTERYFGREPORTER_H
+
+#include <cstdint>
+#include <string>
+
+#include <aidl/android/frameworks/stats/IStats.h>
+
+namespace android {
+namespace hardware {
+namespace google {
+namespace pixel {
+
+using aidl::android::frameworks::stats::IStats;
+
+class BatteryFGReporter {
+ public:
+ BatteryFGReporter();
+
+ void checkAndReportFGLearning(const std::shared_ptr<IStats> &stats_client, const std::vector<std::string> &paths);
+ void checkAndReportFwUpdate(const std::shared_ptr<IStats> &stats_client, const std::string &path);
+ void checkAndReportFGModelLoading(const std::shared_ptr<IStats> &stats_client, const std::vector<std::string> &paths);
+
+ private:
+ const int kVendorAtomOffset = 2;
+
+ enum FGEventType {
+ EvtFGLearningParams = 0x4C48,
+ EvtFWUpdate = 0x4655,
+ EvtModelLoading = 0x4D4C,
+ };
+
+ struct BatteryFGLearningParam {
+ enum FGEventType type;
+ uint16_t fcnom;
+ uint16_t dpacc;
+ uint16_t dqacc;
+ uint16_t fcrep;
+ uint16_t repsoc;
+ uint16_t msoc;
+ uint16_t vfsoc;
+ uint16_t fstat;
+ uint16_t rcomp0;
+ uint16_t tempco;
+ };
+
+ int64_t report_time_ = 0;
+ int64_t getTimeSecs();
+
+ uint16_t old_learn_params[4] = {0};
+ uint16_t old_fw_update[3] = {0};
+ uint16_t old_model_loading[3] = {0};
+
+ void reportEvent(const std::shared_ptr<IStats> &stats_client,
+ const struct BatteryFGLearningParam &params);
+
+ const int kNumFGLearningFields = 10;
+ const int kNumFwUpdateFields = 3;
+};
+
+} // namespace pixel
+} // namespace google
+} // namespace hardware
+} // namespace android
+
+ #endif // HARDWARE_GOOGLE_PIXEL_PIXELSTATS_BATTERYFGREPORTER_H \ No newline at end of file
diff --git a/pixelstats/include/pixelstats/DisplayStatsReporter.h b/pixelstats/include/pixelstats/DisplayStatsReporter.h
index 35435583..c465c416 100644
--- a/pixelstats/include/pixelstats/DisplayStatsReporter.h
+++ b/pixelstats/include/pixelstats/DisplayStatsReporter.h
@@ -36,29 +36,86 @@ using aidl::android::frameworks::stats::VendorAtomValue;
class DisplayStatsReporter {
public:
DisplayStatsReporter();
+
+ enum display_stats_type {
+ DISP_PANEL_STATE = 0,
+ DISP_PORT_STATE,
+ HDCP_STATE,
+ };
void logDisplayStats(const std::shared_ptr<IStats> &stats_client,
- const std::vector<std::string> &display_stats_paths);
+ const std::vector<std::string> &display_stats_paths,
+ const display_stats_type stats_type);
private:
+ bool readDisplayErrorCount(const std::string &path, int64_t *val);
+ // 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.
+ static constexpr int kVendorAtomOffset = 2;
+ static constexpr int kNumOfDisplayPanelErrorStats = 4;
+
+ int verifyCount(int val, bool *report_stats);
+
+ /* display state */
struct DisplayPanelErrorStats {
int64_t primary_error_count_te;
int64_t primary_error_count_unknown;
int64_t secondary_error_count_te;
int64_t secondary_error_count_unknown;
};
-
- // 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.
- const int kVendorAtomOffset = 2;
- const int kNumOfDisplayPanelErrorStats = 4;
- struct DisplayPanelErrorStats prev_data_;
-
+ struct DisplayPanelErrorStats prev_panel_data_ = {};
void logDisplayPanelErrorStats(const std::shared_ptr<IStats> &stats_client,
const std::vector<std::string> &display_stats_paths);
bool captureDisplayPanelErrorStats(const std::vector<std::string> &display_stats_paths,
struct DisplayPanelErrorStats *cur_data);
- bool readDisplayPanelErrorCount(const std::string &path, int64_t *val);
+
+ /* displayport state */
+ enum display_port_error_stats_index {
+ LINK_NEGOTIATION_FAILURES = 0,
+ EDID_READ_FAILURES,
+ DPCD_READ_FAILURES,
+ EDID_INVALID_FAILURES,
+ SINK_COUNT_INVALID_FAILURES,
+ LINK_UNSTABLE_FAILURES,
+ DISPLAY_PORT_ERROR_STATS_SIZE,
+ };
+ static constexpr int64_t display_port_error_path_index[DISPLAY_PORT_ERROR_STATS_SIZE] = {
+ PixelAtoms::DisplayPortErrorStats::kLinkNegotiationFailuresFieldNumber,
+ PixelAtoms::DisplayPortErrorStats::kEdidReadFailuresFieldNumber,
+ PixelAtoms::DisplayPortErrorStats::kDpcdReadFailuresFieldNumber,
+ PixelAtoms::DisplayPortErrorStats::kEdidInvalidFailuresFieldNumber,
+ PixelAtoms::DisplayPortErrorStats::kSinkCountInvalidFailuresFieldNumber,
+ PixelAtoms::DisplayPortErrorStats::kLinkUnstableFailuresFieldNumber};
+ int64_t prev_dp_data_[DISPLAY_PORT_ERROR_STATS_SIZE] = {0};
+
+ void logDisplayPortErrorStats(const std::shared_ptr<IStats> &stats_client,
+ const std::vector<std::string> &displayport_stats_paths);
+ bool captureDisplayPortErrorStats(const std::vector<std::string> &displayport_stats_paths,
+ int64_t *cur_data);
+
+ /* HDCP state */
+ enum hdcp_auth_type_stats_index {
+ HDCP2_SUCCESS = 0,
+ HDCP2_FALLBACK,
+ HDCP2_FAIL,
+ HDCP1_SUCCESS,
+ HDCP1_FAIL,
+ HDCP0,
+ HDCP_AUTH_TYPE_STATS_SIZE,
+ };
+ static constexpr int64_t hdcp_auth_type_path_index[HDCP_AUTH_TYPE_STATS_SIZE] = {
+ PixelAtoms::HDCPAuthTypeStats::kHdcp2SuccessCountFieldNumber,
+ PixelAtoms::HDCPAuthTypeStats::kHdcp2FallbackCountFieldNumber,
+ PixelAtoms::HDCPAuthTypeStats::kHdcp2FailCountFieldNumber,
+ PixelAtoms::HDCPAuthTypeStats::kHdcp1SuccessCountFieldNumber,
+ PixelAtoms::HDCPAuthTypeStats::kHdcp1FailCountFieldNumber,
+ PixelAtoms::HDCPAuthTypeStats::kHdcp0CountFieldNumber};
+ int64_t prev_hdcp_data_[HDCP_AUTH_TYPE_STATS_SIZE] = {0};
+
+ void logHDCPAuthTypeStats(const std::shared_ptr<IStats> &stats_client,
+ const std::vector<std::string> &hdcp_stats_paths);
+ bool captureHDCPAuthTypeStats(const std::vector<std::string> &hdcp_stats_paths,
+ int64_t *cur_data);
};
} // namespace pixel
diff --git a/pixelstats/include/pixelstats/SysfsCollector.h b/pixelstats/include/pixelstats/SysfsCollector.h
index e60842a4..ac8902ad 100644
--- a/pixelstats/include/pixelstats/SysfsCollector.h
+++ b/pixelstats/include/pixelstats/SysfsCollector.h
@@ -73,6 +73,8 @@ class SysfsCollector {
const char *const AmsRatePath;
const std::vector<std::string> ThermalStatsPaths;
const std::vector<std::string> DisplayStatsPaths;
+ const std::vector<std::string> DisplayPortStatsPaths;
+ const std::vector<std::string> HDCPStatsPaths;
const char *const CCARatePath;
const std::vector<std::pair<std::string, std::string>> TempResidencyAndResetPaths;
const char *const LongIRQMetricsPath;
@@ -91,7 +93,7 @@ class SysfsCollector {
const char *const OffloadEffectsIdPath;
const char *const OffloadEffectsDurationPath;
const char *const BluetoothAudioUsagePath;
- const char *const GMSRPath;
+ const std::vector<std::string> GMSRPath;
};
SysfsCollector(const struct SysfsPaths &paths);
@@ -129,6 +131,8 @@ class SysfsCollector {
void logThermalStats(const std::shared_ptr<IStats> &stats_client);
void logMitigationDurationCounts(const std::shared_ptr<IStats> &stats_client);
void logDisplayStats(const std::shared_ptr<IStats> &stats_client);
+ void logDisplayPortStats(const std::shared_ptr<IStats> &stats_client);
+ void logHDCPStats(const std::shared_ptr<IStats> &stats_client);
void logVendorAudioPdmStatsReported(const std::shared_ptr<IStats> &stats_client);
void reportSlowIoFromFile(const std::shared_ptr<IStats> &stats_client, const char *path,
@@ -188,6 +192,8 @@ class SysfsCollector {
const char *const kModemPcieLinkStatsPath;
const char *const kWifiPcieLinkStatsPath;
const std::vector<std::string> kDisplayStatsPaths;
+ const std::vector<std::string> kDisplayPortStatsPaths;
+ const std::vector<std::string> kHDCPStatsPaths;
const char *const kPDMStatePath;
const char *const kWavesPath;
const char *const kAdaptedInfoCountPath;
@@ -198,7 +204,7 @@ class SysfsCollector {
const char *const kOffloadEffectsIdPath;
const char *const kOffloadEffectsDurationPath;
const char *const kBluetoothAudioUsagePath;
- const char *const kGMSRPath;
+ const std::vector<std::string> kGMSRPath;
const char *const kMaxfgHistoryPath;
BatteryEEPROMReporter battery_EEPROM_reporter_;
diff --git a/pixelstats/include/pixelstats/UeventListener.h b/pixelstats/include/pixelstats/UeventListener.h
index 6e6f8174..7d2538e6 100644
--- a/pixelstats/include/pixelstats/UeventListener.h
+++ b/pixelstats/include/pixelstats/UeventListener.h
@@ -21,6 +21,7 @@
#include <android-base/chrono_utils.h>
#include <pixelstats/BatteryCapacityReporter.h>
#include <pixelstats/ChargeStatsReporter.h>
+#include <pixelstats/BatteryFGReporter.h>
namespace android {
namespace hardware {
@@ -46,7 +47,9 @@ class UeventListener {
const char *const TypeCPartnerPidPath;
const char *const WirelessChargerPtmcUevent; // Deprecated.
const char *const WirelessChargerPtmcPath; // Deprecated.
- const char *const GMSRPath;
+ const std::vector<std::string> FGLearningPath;
+ const char *const FwUpdatePath;
+ const std::vector<std::string> FGModelLoadingPath;
};
constexpr static const char *const ssoc_details_path =
"/sys/class/power_supply/battery/ssoc_details";
@@ -59,13 +62,15 @@ 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,
const std::string charge_metrics_path = charge_metrics_path_default,
const std::string typec_partner_vid_path = typec_partner_vid_path_default,
- const std::string typec_partner_pid_path = typec_partner_pid_path_default);
+ const std::string typec_partner_pid_path = typec_partner_pid_path_default,
+ const std::vector<std::string> fg_learning_path = { "" },
+ const std::string fw_update_path = "",
+ const std::vector<std::string> fg_modelloading_path = { "" });
UeventListener(const struct UeventPaths &paths);
bool ProcessUevent(); // Process a single Uevent.
@@ -94,6 +99,8 @@ class UeventListener {
void ReportThermalAbnormalEvent(const std::shared_ptr<IStats> &stats_client,
const char *devpath, const char *thermal_abnormal_event_type,
const char *thermal_abnormal_event_info);
+ void ReportFGMetricsEvent(const std::shared_ptr<IStats> &stats_client, const char *driver);
+
const std::string kAudioUevent;
const std::string kBatterySSOCPath;
const std::string kUsbPortOverheatPath;
@@ -101,7 +108,10 @@ class UeventListener {
const std::string kTypeCPartnerUevent;
const std::string kTypeCPartnerVidPath;
const std::string kTypeCPartnerPidPath;
- const std::string kBatteryGMSRPath;
+ const std::vector<std::string> kFGLearningPath;
+ const std::string kFwUpdatePath;
+ const std::vector<std::string> kFGModelLoadingPath;
+
const std::unordered_map<std::string, PixelAtoms::GpuEvent::GpuEventType>
kGpuEventTypeStrToEnum{
@@ -142,7 +152,24 @@ class UeventListener {
{"CSF_RESET_OK",
PixelAtoms::GpuEvent::GpuEventInfo::GpuEvent_GpuEventInfo_MALI_CSF_RESET_OK},
{"CSF_RESET_FAILED", PixelAtoms::GpuEvent::GpuEventInfo::
- GpuEvent_GpuEventInfo_MALI_CSF_RESET_FAILED}};
+ GpuEvent_GpuEventInfo_MALI_CSF_RESET_FAILED},
+ {"TILER_OOM",
+ PixelAtoms::GpuEvent::GpuEventInfo::GpuEvent_GpuEventInfo_MALI_TILER_OOM},
+ {"PROGRESS_TIMER",
+ PixelAtoms::GpuEvent::GpuEventInfo::GpuEvent_GpuEventInfo_MALI_PROGRESS_TIMER},
+ {"CS_ERROR",
+ PixelAtoms::GpuEvent::GpuEventInfo::GpuEvent_GpuEventInfo_MALI_CS_ERROR},
+ {"FW_ERROR",
+ PixelAtoms::GpuEvent::GpuEventInfo::GpuEvent_GpuEventInfo_MALI_FW_ERROR},
+ {"PMODE_EXIT_TIMEOUT", PixelAtoms::GpuEvent::GpuEventInfo::
+ GpuEvent_GpuEventInfo_MALI_PMODE_EXIT_TIMEOUT},
+ {"PMODE_ENTRY_FAILURE", PixelAtoms::GpuEvent::GpuEventInfo::
+ GpuEvent_GpuEventInfo_MALI_PMODE_ENTRY_FAILURE},
+ {"GPU_PAGE_FAULT",
+ PixelAtoms::GpuEvent::GpuEventInfo::GpuEvent_GpuEventInfo_MALI_GPU_PAGE_FAULT},
+ {"MMU_AS_ACTIVE_STUCK",
+ PixelAtoms::GpuEvent::GpuEventInfo::
+ GpuEvent_GpuEventInfo_MALI_MMU_AS_ACTIVE_STUCK}};
const std::unordered_map<std::string,
PixelAtoms::ThermalSensorAbnormalityDetected::AbnormalityType>
@@ -168,6 +195,7 @@ class UeventListener {
BatteryCapacityReporter battery_capacity_reporter_;
ChargeStatsReporter charge_stats_reporter_;
+ BatteryFGReporter battery_fg_reporter_;
// 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
diff --git a/pixelstats/pixelatoms.proto b/pixelstats/pixelatoms.proto
index c773c2b1..f3b9e1d4 100644
--- a/pixelstats/pixelatoms.proto
+++ b/pixelstats/pixelatoms.proto
@@ -116,6 +116,10 @@ message Atom {
ThermalSensorAbnormalityDetected thermal_sensor_abnormality_detected = 105062;
VendorAudioOffloadedEffectStatsReported vendor_audio_offloaded_effect_stats_reported = 105063;
VendorAudioBtMediaStatsReported vendor_audio_bt_media_stats_reported = 105064;
+ PixelImpulseUsageReported pixel_impulse_usage_reported = 105065;
+ DisplayPortErrorStats display_port_error_stats = 105066;
+ HDCPAuthTypeStats hdcp_auth_type_stats = 105067;
+ DisplayPortUsage display_port_usage = 105068;
}
// AOSP atom ID range ends at 109999
reserved 109997; // reserved for VtsVendorAtomJavaTest test atom
@@ -616,6 +620,10 @@ message PixelMmMetricsPerHour {
optional int32 psi_mem_some_avg300_min = 58;
optional int32 psi_mem_some_avg300_max = 59;
optional int32 psi_mem_some_avg300_avg = 60;
+ optional int32 version = 61 [deprecated = true];
+ optional int64 shmem_pages = 62;
+ optional int64 page_tables_pages = 63;
+ optional int64 dmabuf_pages = 64;
}
/* A message containing Pixel memory metrics collected daily. */
@@ -679,6 +687,11 @@ message PixelMmMetricsPerDay {
optional int64 thp_split_page = 57;
optional int64 thp_migration_split = 58;
optional int64 thp_deferred_split_page = 59;
+ optional int64 version = 60 [deprecated = true];
+ optional int64 cpu_total_time = 61;
+ optional int64 cpu_idle_time = 62;
+ optional int64 cpu_io_wait_time = 63;
+ optional int64 kswapd_pageout_run = 64;
}
/* A message containing CMA metrics collected from dogfooding only. */
@@ -1551,6 +1564,7 @@ message BrownoutDetected {
OCP_BBS = 26;
OCP_BCS = 27;
OCP_BDS = 28;
+ OCP2_IF = 29;
}
// Reverse domain name. (e.g. Pixel)
@@ -1826,6 +1840,14 @@ message GpuEvent {
MALI_PM_TIMEOUT = 13;
MALI_CSF_RESET_OK = 14;
MALI_CSF_RESET_FAILED = 15;
+ MALI_TILER_OOM = 16;
+ MALI_PROGRESS_TIMER = 17;
+ MALI_CS_ERROR = 18;
+ MALI_FW_ERROR = 19;
+ MALI_PMODE_EXIT_TIMEOUT = 20;
+ MALI_PMODE_ENTRY_FAILURE = 21;
+ MALI_GPU_PAGE_FAULT = 22;
+ MALI_MMU_AS_ACTIVE_STUCK = 23;
}
/* Vendor reverse domain name (expecting "com.google.pixel"). */
@@ -2103,3 +2125,127 @@ message VendorAudioBtMediaStatsReported {
/* Total active seconds to record. */
optional int32 active_seconds_per_day = 3;
}
+
+/*
+ * Logs the usage of APIs in Pixel Impulse.
+ */
+message PixelImpulseUsageReported {
+ /* Vendor reverse domain name */
+ optional string reverse_domain_name = 1;
+
+ /* UID of the caller package */
+ optional int32 caller_uid = 2 [(android.os.statsd.is_uid) = true];
+
+ /* UID of the callee package */
+ optional int32 callee_uid = 3 [(android.os.statsd.is_uid) = true];
+
+ enum ApiName {
+ API_UNKNOWN = 0;
+ /* Server side */
+ API_SERVICE_BIND = 1;
+ API_SERVICE_UNBIND = 2;
+ API_REGISTER = 3;
+ API_UNREGISTER = 4;
+ API_CLEAR_APP_DATA = 100;
+ /* Client side */
+ API_SERVER_START = 1001;
+ API_PIXEL_STATE_CHANGE = 1002;
+ }
+ /* Invoked API name */
+ optional ApiName api_name = 4;
+
+ enum Tag {
+ TAG_UNKNOWN = 0;
+ }
+ /* Tag for debugging purpose */
+ optional Tag tag = 5;
+
+ /* Timestamp of the usage start in epoch time */
+ optional int64 usage_start_epoch_millis = 6;
+
+ /* Timestamp of the usage start since device boot */
+ optional int64 usage_start_uptime_millis = 7;
+
+ /* Duration of the usage */
+ optional int64 usage_duration_millis = 8;
+
+ /* Whether the invocation was successful */
+ optional bool success = 9;
+
+ enum FailReason {
+ FAIL_REASON_UNKNOWN = 0;
+ FAIL_REASON_INVALID_ARGUMENT = 1;
+ FAIL_REASON_UNAUTHORIZED = 2;
+ FAIL_REASON_TIMEOUT = 3;
+ FAIL_REASON_NOT_FOUND = 4;
+ FAIL_REASON_INVALID_STATE = 5;
+ }
+ /* Fail reason (if success == false) */
+ optional FailReason fail_reason = 10;
+
+ /* Size of the arguments */
+ optional int32 request_size = 11;
+}
+
+/**
+ * Log DisplayPort link error statistics.
+ */
+message DisplayPortErrorStats {
+ /* Vendor reverse domain name */
+ optional string reverse_domain_name = 1;
+
+ /* The error count due to link training failed. */
+ optional int32 link_negotiation_failures = 2;
+ /* The error count due to read EDID failed. */
+ optional int32 edid_read_failures = 3;
+ /* The error count due to read dpcd failed. */
+ optional int32 dpcd_read_failures = 4;
+ /* The error count due to EDID is invalid. */
+ optional int32 edid_invalid_failures = 5;
+ /* The error count due to sink count is invalid. */
+ optional int32 sink_count_invalid_failures = 6;
+ /* The error count due to link unstable. */
+ optional int32 link_unstable_failures = 7;
+}
+
+/**
+ * Log HDCP authentication type statistics.
+ */
+message HDCPAuthTypeStats {
+ /* Vendor reverse domain name */
+ optional string reverse_domain_name = 1;
+
+ /* The count due to hdcp2 success */
+ optional int32 hdcp2_success_count = 2;
+ /* The count due to hdcp2 fallback */
+ optional int32 hdcp2_fallback_count = 3;
+ /* The count due to hdcp2 fail */
+ optional int32 hdcp2_fail_count = 4;
+ /* The count due to hdcp1 success */
+ optional int32 hdcp1_success_count = 5;
+ /* The count due to hdcp1 fail */
+ optional int32 hdcp1_fail_count = 6;
+ /* The count due to hdcp0 */
+ optional int32 hdcp0_count = 7;
+}
+
+/**
+ * A message containing how DisplayPort is being used in a day.
+ */
+message DisplayPortUsage {
+ /* Vendor reverse domain name */
+ optional string reverse_domain_name = 1;
+
+ /* resolution width */
+ optional int32 width = 2;
+ /* resolution height */
+ optional int32 height = 3;
+ /* refresh rate */
+ optional float refresh_rate = 4;
+ /* vendor ID. */
+ optional int32 vendor_id = 5;
+ /* product ID. */
+ optional int32 product_id = 6;
+ /* indicated start/stop event of DP usage */
+ optional bool connected = 7;
+}
diff --git a/power-libperfmgr/Android.bp b/power-libperfmgr/Android.bp
index 9ccd6fdd..d79c3faa 100644
--- a/power-libperfmgr/Android.bp
+++ b/power-libperfmgr/Android.bp
@@ -50,10 +50,13 @@ cc_test {
require_root: true,
srcs: [
"aidl/tests/BackgroundWorkerTest.cpp",
+ "aidl/tests/GpuCapacityNodeTest.cpp",
+ "aidl/tests/PhysicalQuantityTypeTest.cpp",
"aidl/tests/PowerHintSessionTest.cpp",
"aidl/tests/SessionTaskMapTest.cpp",
"aidl/tests/UClampVoterTest.cpp",
"aidl/BackgroundWorker.cpp",
+ "aidl/GpuCapacityNode.cpp",
"aidl/PowerHintSession.cpp",
"aidl/PowerSessionManager.cpp",
"aidl/SessionTaskMap.cpp",
@@ -63,11 +66,14 @@ cc_test {
cpp_std: "gnu++20",
static_libs: [
"libgmock",
+ "android.hardware.common-V2-ndk",
+ "android.hardware.common.fmq-V1-ndk",
],
shared_libs: [
"liblog",
"libbase",
"libcutils",
+ "libfmq",
"libutils",
"libperfmgr",
"libbinder_ndk",
@@ -94,9 +100,12 @@ cc_binary {
"libperfmgr",
"libprocessgroup",
"pixel-power-ext-V1-ndk",
+ "android.hardware.common.fmq-V1-ndk",
+ "libfmq",
],
srcs: [
"aidl/BackgroundWorker.cpp",
+ "aidl/GpuCapacityNode.cpp",
"aidl/service.cpp",
"aidl/Power.cpp",
"aidl/PowerExt.cpp",
diff --git a/power-libperfmgr/aidl/AdpfTypes.h b/power-libperfmgr/aidl/AdpfTypes.h
index bd9dbc9c..8bdbd78a 100644
--- a/power-libperfmgr/aidl/AdpfTypes.h
+++ b/power-libperfmgr/aidl/AdpfTypes.h
@@ -32,7 +32,11 @@ enum class AdpfHintType : int32_t {
ADPF_CPU_LOAD_UP = 2,
ADPF_CPU_LOAD_RESET = 3,
ADPF_CPU_LOAD_RESUME = 4,
- ADPF_VOTE_POWER_EFFICIENCY = 5
+ ADPF_VOTE_POWER_EFFICIENCY = 5,
+ ADPF_GPU_LOAD_UP = 6,
+ ADPF_GPU_LOAD_DOWN = 7,
+ ADPF_GPU_LOAD_RESET = 8,
+ ADPF_GPU_CAPACITY = 9,
};
constexpr int kUclampMin{0};
diff --git a/power-libperfmgr/aidl/GpuCapacityNode.cpp b/power-libperfmgr/aidl/GpuCapacityNode.cpp
new file mode 100644
index 00000000..a0ea912b
--- /dev/null
+++ b/power-libperfmgr/aidl/GpuCapacityNode.cpp
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2024 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 "GpuCapacityNode.h"
+
+#define LOG_TAG "powerhal-libperfmgr"
+#include <android-base/logging.h>
+
+#include <charconv>
+
+namespace aidl {
+namespace google {
+namespace hardware {
+namespace power {
+namespace impl {
+namespace pixel {
+
+GpuCapacityNode::~GpuCapacityNode() {
+ fd_interface_->close(frequency_fd_);
+ fd_interface_->close(capacity_headroom_fd_);
+}
+
+GpuCapacityNode::GpuCapacityNode(std::unique_ptr<FdInterface> fd_interface,
+ int validated_capacity_headroom_fd, int validated_frequency_fd,
+ std::string_view node_path)
+ : fd_interface_(std::move(fd_interface)),
+ capacity_node_path_(node_path),
+ capacity_headroom_fd_(validated_capacity_headroom_fd),
+ frequency_fd_(validated_frequency_fd) {
+ if (capacity_headroom_fd_ < 0) {
+ LOG(FATAL) << ("precondition violation for GpuCapacityNode: invalid capacity_headroom_fd_");
+ }
+ if (frequency_fd_ < 0) {
+ LOG(FATAL) << ("precondition violation for GpuCapacityNode: invalid frequency_fd_");
+ }
+}
+
+std::unique_ptr<GpuCapacityNode> GpuCapacityNode::init_gpu_capacity_node(
+ std::unique_ptr<FdInterface> fd_interface, std::string_view gpu_node_dir) {
+ static constexpr auto fd_flags_common = O_CLOEXEC | O_NONBLOCK;
+ auto const capacity_headroom_file = std::string(gpu_node_dir) + "/capacity_headroom";
+ auto const capacity_headroom_fd =
+ fd_interface->open(capacity_headroom_file.c_str(), O_RDWR | fd_flags_common);
+ if (capacity_headroom_fd < 0) {
+ LOG(ERROR) << "could not open gpu capacity path: " << capacity_headroom_file << ": "
+ << strerror(errno);
+ return nullptr;
+ }
+ auto const gpu_freq_file = std::string(gpu_node_dir) + "/cur_freq";
+ auto const frequency_fd = fd_interface->open(gpu_freq_file.c_str(), O_RDONLY | fd_flags_common);
+ if (frequency_fd < 0) {
+ fd_interface->close(capacity_headroom_fd);
+ LOG(ERROR) << "could not open gpu capacity path: " << gpu_freq_file << ": "
+ << strerror(errno);
+ return nullptr;
+ }
+ return std::make_unique<GpuCapacityNode>(std::move(fd_interface), capacity_headroom_fd,
+ frequency_fd, gpu_node_dir);
+}
+
+bool GpuCapacityNode::set_gpu_capacity(Cycles capacity) const {
+ std::lock_guard lk(capacity_mutex_);
+ auto const capacity_str = std::to_string(static_cast<int>(capacity));
+ auto const rc =
+ fd_interface_->write(capacity_headroom_fd_, capacity_str.c_str(), capacity_str.size());
+ if (!rc) {
+ LOG(ERROR) << "could not write to capacity node: " << capacity_node_path_ << ": "
+ << strerror(errno);
+ }
+ return !rc;
+}
+
+std::optional<Frequency> GpuCapacityNode::gpu_frequency() const {
+ std::lock_guard lk(freq_mutex_);
+ std::array<char, 16> buffer;
+ buffer.fill('\0');
+
+ ssize_t bytes_read_total = 0;
+ size_t const effective_size = buffer.size() - 1;
+ do {
+ auto const bytes_read = fd_interface_->read(frequency_fd_, buffer.data() + bytes_read_total,
+ effective_size - bytes_read_total);
+ if (bytes_read == 0) {
+ break;
+ }
+
+ if (bytes_read < 0) {
+ LOG(ERROR) << "could not read gpu frequency:" << bytes_read << ": " << strerror(errno);
+ return {};
+ }
+ bytes_read_total += bytes_read;
+ } while (bytes_read_total < effective_size);
+
+ auto const rc = fd_interface_->lseek(frequency_fd_, 0, SEEK_SET);
+ if (rc < 0) {
+ LOG(ERROR) << "could not seek gpu frequency file: " << strerror(errno);
+ return {};
+ }
+
+ int frequency_raw = 0;
+ auto [ptr, ec] = std::from_chars(buffer.data(), buffer.data() + buffer.size(), frequency_raw);
+ if (ec != std::errc() || frequency_raw <= 0) {
+ LOG(ERROR) << "could not parse gpu frequency" << buffer.data();
+ return {};
+ }
+
+ return Frequency(frequency_raw * 1000);
+}
+
+} // namespace pixel
+} // namespace impl
+} // namespace power
+} // namespace hardware
+} // namespace google
+} // namespace aidl
diff --git a/power-libperfmgr/aidl/GpuCapacityNode.h b/power-libperfmgr/aidl/GpuCapacityNode.h
new file mode 100644
index 00000000..6af07968
--- /dev/null
+++ b/power-libperfmgr/aidl/GpuCapacityNode.h
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2024 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 <fcntl.h>
+#include <unistd.h>
+
+#include <memory>
+#include <mutex>
+#include <optional>
+#include <string>
+#include <string_view>
+#include <utility>
+
+#include "PhysicalQuantityTypes.h"
+
+namespace aidl {
+namespace google {
+namespace hardware {
+namespace power {
+namespace impl {
+namespace pixel {
+
+struct FdInterface {
+ virtual int open(const char *, int) const = 0;
+ virtual int write(int, const char *, size_t) const = 0;
+ virtual ssize_t read(int, void *, size_t) const = 0;
+ virtual off_t lseek(int, off_t, int) const = 0;
+ virtual int close(int) const = 0;
+
+ FdInterface() = default;
+ virtual ~FdInterface() = default;
+ FdInterface(FdInterface const &) = delete;
+ FdInterface &operator=(FdInterface const &) = delete;
+};
+
+struct FdWriter : FdInterface {
+ int open(const char *path, int flags) const override { return ::open(path, flags); }
+ int write(int fd, const char *data, size_t len) const override {
+ return ::write(fd, data, len);
+ }
+ ssize_t read(int fd, void *data, size_t len) const final { return ::read(fd, data, len); }
+ off_t lseek(int fd, off_t offset, int whence) const final {
+ return ::lseek(fd, offset, whence);
+ }
+ int close(int fd) const override { return ::close(fd); }
+};
+
+struct GpuCapacityNode final {
+ // Exceptions should really be allowed, use exploded constructor pattern and provide
+ // helper construction function.
+ GpuCapacityNode(std::unique_ptr<FdInterface> fd_interface, int validated_capacity_headroom_fd,
+ int validated_frequency_fd, std::string_view gpu_node_dir);
+ static std::unique_ptr<GpuCapacityNode> init_gpu_capacity_node(
+ std::unique_ptr<FdInterface> fd_interface, std::string_view gpu_node_dir);
+
+ ~GpuCapacityNode() noexcept;
+
+ bool set_gpu_capacity(Cycles capacity) const;
+ std::optional<Frequency> gpu_frequency() const;
+
+ private:
+ std::unique_ptr<FdInterface> const fd_interface_;
+ std::string const capacity_node_path_;
+ int const capacity_headroom_fd_;
+ int const frequency_fd_;
+ std::mutex mutable freq_mutex_;
+ std::mutex mutable capacity_mutex_;
+};
+
+} // namespace pixel
+} // namespace impl
+} // namespace power
+} // namespace hardware
+} // namespace google
+} // namespace aidl
diff --git a/power-libperfmgr/aidl/PhysicalQuantityTypes.h b/power-libperfmgr/aidl/PhysicalQuantityTypes.h
new file mode 100644
index 00000000..bbb39314
--- /dev/null
+++ b/power-libperfmgr/aidl/PhysicalQuantityTypes.h
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2024 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 <compare>
+#include <numeric>
+#include <type_traits>
+
+namespace aidl {
+namespace google {
+namespace hardware {
+namespace power {
+namespace impl {
+namespace pixel {
+
+template <class C>
+struct is_chrono_duration : std::false_type {};
+
+template <class Rep, class Per>
+struct is_chrono_duration<std::chrono::duration<Rep, Per>> : std::true_type {};
+
+template <typename T>
+concept chrono_duration = is_chrono_duration<T>::value;
+
+template <typename T>
+concept arithmetic = std::is_arithmetic<T>::value;
+
+template <arithmetic T, typename W>
+struct PhysicalQuantityType final {
+ constexpr PhysicalQuantityType() noexcept = default;
+ constexpr explicit PhysicalQuantityType(T const &value) noexcept : value_(value) {}
+ constexpr PhysicalQuantityType(PhysicalQuantityType const &) noexcept = default;
+ constexpr PhysicalQuantityType &operator=(PhysicalQuantityType const &) noexcept = default;
+ explicit constexpr inline operator T() const { return value_; }
+
+ inline constexpr auto operator+(PhysicalQuantityType const &other) const {
+ return PhysicalQuantityType(value_ + other.value_);
+ }
+ inline constexpr auto operator-(PhysicalQuantityType const &other) const {
+ return PhysicalQuantityType(value_ - other.value_);
+ }
+ template <arithmetic C>
+ inline constexpr auto operator*(C const &other) const {
+ return PhysicalQuantityType(value_ * other);
+ }
+ template <arithmetic C>
+ inline constexpr auto operator/(C const &other) const {
+ return PhysicalQuantityType(value_ / other);
+ }
+ inline constexpr bool operator==(PhysicalQuantityType const &other) const {
+ return value_ == other.value_;
+ }
+ inline constexpr auto operator<=>(PhysicalQuantityType const &other) const {
+ return value_ <=> other.value_;
+ }
+
+ private:
+ T value_;
+};
+
+template <arithmetic C, arithmetic T, typename W>
+inline auto operator*(C const &other, PhysicalQuantityType<T, W> type) {
+ return type * other;
+}
+
+using Cycles = PhysicalQuantityType<int, struct CyclesTag>;
+using Frequency = PhysicalQuantityType<int, struct FrequencyTag>;
+
+inline constexpr Frequency operator/(Cycles const &cycles, chrono_duration auto time) {
+ auto const fpchrono = std::chrono::duration<float, std::chrono::seconds::period>(time);
+ return Frequency(static_cast<int>(cycles) / fpchrono.count());
+}
+
+constexpr Frequency operator""_hz(unsigned long long hz) {
+ return Frequency(hz);
+}
+
+inline constexpr Cycles operator*(Frequency const &freq, chrono_duration auto time) {
+ auto const fpchrono = std::chrono::duration<float, std::chrono::seconds::period>(time);
+ return Cycles(static_cast<int>(freq) * fpchrono.count());
+}
+
+inline constexpr Cycles operator*(chrono_duration auto time, Frequency const &freq) {
+ return freq * time;
+}
+
+} // 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 e15d985d..df405e53 100644
--- a/power-libperfmgr/aidl/Power.cpp
+++ b/power-libperfmgr/aidl/Power.cpp
@@ -23,10 +23,13 @@
#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
+#include <fmq/AidlMessageQueue.h>
+#include <fmq/EventFlag.h>
#include <perfmgr/HintManager.h>
#include <utils/Log.h>
#include <mutex>
+#include <optional>
#include "PowerHintSession.h"
#include "PowerSessionManager.h"
@@ -42,6 +45,11 @@ namespace pixel {
using ::aidl::google::hardware::power::impl::pixel::PowerHintSession;
using ::android::perfmgr::HintManager;
+using ::aidl::android::hardware::common::fmq::MQDescriptor;
+using ::aidl::android::hardware::common::fmq::SynchronizedReadWrite;
+using ::aidl::android::hardware::power::ChannelMessage;
+using ::android::AidlMessageQueue;
+
constexpr char kPowerHalStateProp[] = "vendor.powerhal.state";
constexpr char kPowerHalAudioProp[] = "vendor.powerhal.audio";
constexpr char kPowerHalRenderingProp[] = "vendor.powerhal.rendering";
@@ -83,6 +91,9 @@ Power::Power(std::shared_ptr<DisplayLowPower> dlpw)
LOG(INFO) << "Initialize with EXPENSIVE_RENDERING on";
HintManager::GetInstance()->DoHint("EXPENSIVE_RENDERING");
}
+
+ auto status = this->getInterfaceVersion(&mServiceVersion);
+ LOG(INFO) << "PowerHAL InterfaceVersion:" << mServiceVersion << " isOK: " << status.isOk();
}
ndk::ScopedAStatus Power::setMode(Mode type, bool enabled) {
@@ -178,6 +189,27 @@ ndk::ScopedAStatus Power::setMode(Mode type, bool enabled) {
}
ndk::ScopedAStatus Power::isModeSupported(Mode type, bool *_aidl_return) {
+ switch (mServiceVersion) {
+ case 5:
+ if (static_cast<int32_t>(type) <= static_cast<int32_t>(Mode::AUTOMOTIVE_PROJECTION))
+ break;
+ [[fallthrough]];
+ case 4:
+ [[fallthrough]];
+ case 3:
+ if (static_cast<int32_t>(type) <= static_cast<int32_t>(Mode::GAME_LOADING))
+ break;
+ [[fallthrough]];
+ case 2:
+ [[fallthrough]];
+ case 1:
+ if (static_cast<int32_t>(type) <= static_cast<int32_t>(Mode::CAMERA_STREAMING_HIGH))
+ break;
+ [[fallthrough]];
+ default:
+ *_aidl_return = false;
+ return ndk::ScopedAStatus::ok();
+ }
bool supported = HintManager::GetInstance()->IsHintSupported(toString(type));
// LOW_POWER handled insides PowerHAL specifically
if (type == Mode::LOW_POWER) {
@@ -233,6 +265,23 @@ ndk::ScopedAStatus Power::setBoost(Boost type, int32_t durationMs) {
}
ndk::ScopedAStatus Power::isBoostSupported(Boost type, bool *_aidl_return) {
+ switch (mServiceVersion) {
+ case 5:
+ [[fallthrough]];
+ case 4:
+ [[fallthrough]];
+ case 3:
+ [[fallthrough]];
+ case 2:
+ [[fallthrough]];
+ case 1:
+ if (static_cast<int32_t>(type) <= static_cast<int32_t>(Boost::CAMERA_SHOT))
+ break;
+ [[fallthrough]];
+ default:
+ *_aidl_return = false;
+ return ndk::ScopedAStatus::ok();
+ }
bool supported = HintManager::GetInstance()->IsHintSupported(toString(type));
if (!supported && HintManager::GetInstance()->IsAdpfProfileSupported(toString(type))) {
supported = true;
@@ -267,6 +316,25 @@ ndk::ScopedAStatus Power::createHintSession(int32_t tgid, int32_t uid,
const std::vector<int32_t> &threadIds,
int64_t durationNanos,
std::shared_ptr<IPowerHintSession> *_aidl_return) {
+ SessionConfig config;
+ return createHintSessionWithConfig(tgid, uid, threadIds, durationNanos, SessionTag::OTHER,
+ &config, _aidl_return);
+}
+
+ndk::ScopedAStatus Power::getHintSessionPreferredRate(int64_t *outNanoseconds) {
+ *outNanoseconds = HintManager::GetInstance()->GetAdpfProfile()
+ ? HintManager::GetInstance()->GetAdpfProfile()->mReportingRateLimitNs
+ : 0;
+ if (*outNanoseconds <= 0) {
+ return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+ }
+
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Power::createHintSessionWithConfig(
+ int32_t tgid, int32_t uid, const std::vector<int32_t> &threadIds, int64_t durationNanos,
+ SessionTag tag, SessionConfig *config, std::shared_ptr<IPowerHintSession> *_aidl_return) {
if (!HintManager::GetInstance()->GetAdpfProfile() ||
HintManager::GetInstance()->GetAdpfProfile()->mReportingRateLimitNs <= 0) {
*_aidl_return = nullptr;
@@ -278,19 +346,29 @@ ndk::ScopedAStatus Power::createHintSession(int32_t tgid, int32_t uid,
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
std::shared_ptr<IPowerHintSession> session =
- ndk::SharedRefBase::make<PowerHintSession>(tgid, uid, threadIds, durationNanos);
+ ndk::SharedRefBase::make<PowerHintSession>(tgid, uid, threadIds, durationNanos, tag);
*_aidl_return = session;
+ static_cast<PowerHintSession *>(_aidl_return->get())->getSessionConfig(config);
return ndk::ScopedAStatus::ok();
}
-ndk::ScopedAStatus Power::getHintSessionPreferredRate(int64_t *outNanoseconds) {
- *outNanoseconds = HintManager::GetInstance()->GetAdpfProfile()
- ? HintManager::GetInstance()->GetAdpfProfile()->mReportingRateLimitNs
- : 0;
- if (*outNanoseconds <= 0) {
- return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
- }
+ndk::ScopedAStatus Power::getSessionChannel(int32_t, int32_t, ChannelConfig *_aidl_return) {
+ static AidlMessageQueue<ChannelMessage, SynchronizedReadWrite> stubQueue{20, true};
+ static std::thread stubThread([&] {
+ ChannelMessage data;
+ // This loop will only run while there is data waiting
+ // to be processed, and blocks on a futex all other times
+ while (stubQueue.readBlocking(&data, 1, 0)) {
+ }
+ });
+ _aidl_return->channelDescriptor = stubQueue.dupeDesc();
+ _aidl_return->readFlagBitmask = 0x01;
+ _aidl_return->writeFlagBitmask = 0x02;
+ _aidl_return->eventFlagDescriptor = std::nullopt;
+ return ndk::ScopedAStatus::ok();
+}
+ndk::ScopedAStatus Power::closeSessionChannel(int32_t, int32_t) {
return ndk::ScopedAStatus::ok();
}
diff --git a/power-libperfmgr/aidl/Power.h b/power-libperfmgr/aidl/Power.h
index b7fd8f26..6404610e 100644
--- a/power-libperfmgr/aidl/Power.h
+++ b/power-libperfmgr/aidl/Power.h
@@ -22,6 +22,8 @@
#include <memory>
#include <thread>
+#include "aidl/android/hardware/power/ChannelConfig.h"
+#include "aidl/android/hardware/power/SessionConfig.h"
#include "disp-power/DisplayLowPower.h"
#include "disp-power/InteractionHandler.h"
@@ -32,9 +34,12 @@ namespace power {
namespace impl {
namespace pixel {
-using ::aidl::android::hardware::power::Boost;
-using ::aidl::android::hardware::power::IPowerHintSession;
-using ::aidl::android::hardware::power::Mode;
+using android::hardware::power::Boost;
+using android::hardware::power::ChannelConfig;
+using android::hardware::power::IPowerHintSession;
+using android::hardware::power::Mode;
+using android::hardware::power::SessionConfig;
+using android::hardware::power::SessionTag;
class Power : public ::aidl::android::hardware::power::BnPower {
public:
@@ -47,7 +52,14 @@ class Power : public ::aidl::android::hardware::power::BnPower {
const std::vector<int32_t> &threadIds,
int64_t durationNanos,
std::shared_ptr<IPowerHintSession> *_aidl_return) override;
+ ndk::ScopedAStatus createHintSessionWithConfig(
+ int32_t tgid, int32_t uid, const std::vector<int32_t> &threadIds, int64_t durationNanos,
+ SessionTag tag, SessionConfig *config,
+ std::shared_ptr<IPowerHintSession> *_aidl_return) override;
ndk::ScopedAStatus getHintSessionPreferredRate(int64_t *outNanoseconds) override;
+ ndk::ScopedAStatus getSessionChannel(int32_t tgid, int32_t uid,
+ ChannelConfig *_aidl_return) override;
+ ndk::ScopedAStatus closeSessionChannel(int32_t tgid, int32_t uid) override;
binder_status_t dump(int fd, const char **args, uint32_t numArgs) override;
private:
@@ -55,6 +67,7 @@ class Power : public ::aidl::android::hardware::power::BnPower {
std::unique_ptr<InteractionHandler> mInteractionHandler;
std::atomic<bool> mVRModeOn;
std::atomic<bool> mSustainedPerfModeOn;
+ int32_t mServiceVersion;
};
} // namespace pixel
diff --git a/power-libperfmgr/aidl/PowerHintSession.cpp b/power-libperfmgr/aidl/PowerHintSession.cpp
index 0262d40d..fd119c72 100644
--- a/power-libperfmgr/aidl/PowerHintSession.cpp
+++ b/power-libperfmgr/aidl/PowerHintSession.cpp
@@ -46,6 +46,8 @@ using ::android::perfmgr::HintManager;
using std::chrono::duration_cast;
using std::chrono::nanoseconds;
+using std::operator""ms;
+
namespace {
static std::atomic<int64_t> sSessionIDCounter{0};
@@ -117,25 +119,29 @@ int64_t PowerHintSession::convertWorkDurationToBoostByPid(
}
AppHintDesc::AppHintDesc(int64_t sessionId, int32_t tgid, int32_t uid,
+ const std::vector<int32_t> &threadIds, SessionTag tag,
std::chrono::nanoseconds pTargetNs)
: sessionId(sessionId),
tgid(tgid),
uid(uid),
targetNs(pTargetNs),
- pidSetPoint(0),
+ thread_ids(threadIds),
+ tag(tag),
+ pidControlVariable(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 durationNs)
+ int64_t durationNs, SessionTag tag)
: mPSManager(PowerSessionManager::getInstance()),
mSessionId(++sSessionIDCounter),
mIdString(StringPrintf("%" PRId32 "-%" PRId32 "-%" PRId64, tgid, uid, mSessionId)),
- mDescriptor(std::make_shared<AppHintDesc>(mSessionId, tgid, uid,
+ mDescriptor(std::make_shared<AppHintDesc>(mSessionId, tgid, uid, threadIds, tag,
std::chrono::nanoseconds(durationNs))),
- mAppDescriptorTrace(mIdString) {
+ mAppDescriptorTrace(mIdString),
+ mTag(tag) {
ATRACE_CALL();
ATRACE_INT(mAppDescriptorTrace.trace_target.c_str(), mDescriptor->targetNs.count());
ATRACE_INT(mAppDescriptorTrace.trace_active.c_str(), mDescriptor->is_active.load());
@@ -168,16 +174,17 @@ bool PowerHintSession::isAppSession() {
return mDescriptor->uid >= AID_APP_START;
}
-void PowerHintSession::updatePidSetPoint(int pidSetPoint, bool updateVote) {
- mDescriptor->pidSetPoint = pidSetPoint;
+void PowerHintSession::updatePidControlVariable(int pidControlVariable, bool updateVote) {
+ mDescriptor->pidControlVariable = pidControlVariable;
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));
+ mPSManager->voteSet(mSessionId, AdpfHintType::ADPF_VOTE_DEFAULT, pidControlVariable,
+ kUclampMax, std::chrono::steady_clock::now(),
+ std::max(duration_cast<nanoseconds>(mDescriptor->targetNs *
+ adpfConfig->mStaleTimeFactor),
+ nanoseconds(adpfConfig->mReportingRateLimitNs) * 2));
}
- ATRACE_INT(mAppDescriptorTrace.trace_min.c_str(), pidSetPoint);
+ ATRACE_INT(mAppDescriptorTrace.trace_min.c_str(), pidControlVariable);
}
void PowerHintSession::tryToSendPowerHint(std::string hint) {
@@ -191,7 +198,7 @@ void PowerHintSession::tryToSendPowerHint(std::string hint) {
void PowerHintSession::dumpToStream(std::ostream &stream) {
stream << "ID.Min.Act.Timeout(" << mIdString;
- stream << ", " << mDescriptor->pidSetPoint;
+ stream << ", " << mDescriptor->pidControlVariable;
stream << ", " << mDescriptor->is_active;
stream << ", " << isTimeout() << ")";
}
@@ -204,6 +211,7 @@ ndk::ScopedAStatus PowerHintSession::pause() {
if (!mDescriptor->is_active.load())
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
// Reset to default uclamp value.
+ mPSManager->setThreadsFromPowerSession(mSessionId, {});
mDescriptor->is_active.store(false);
mPSManager->pause(mSessionId);
ATRACE_INT(mAppDescriptorTrace.trace_active.c_str(), false);
@@ -218,11 +226,12 @@ ndk::ScopedAStatus PowerHintSession::resume() {
}
if (mDescriptor->is_active.load())
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+ mPSManager->setThreadsFromPowerSession(mSessionId, mDescriptor->thread_ids);
mDescriptor->is_active.store(true);
// resume boost
mPSManager->resume(mSessionId);
ATRACE_INT(mAppDescriptorTrace.trace_active.c_str(), true);
- ATRACE_INT(mAppDescriptorTrace.trace_min.c_str(), mDescriptor->pidSetPoint);
+ ATRACE_INT(mAppDescriptorTrace.trace_min.c_str(), mDescriptor->pidControlVariable);
return ndk::ScopedAStatus::ok();
}
@@ -299,7 +308,7 @@ ndk::ScopedAStatus PowerHintSession::reportActualWorkDuration(
mPSManager->disableBoosts(mSessionId);
if (!adpfConfig->mPidOn) {
- updatePidSetPoint(adpfConfig->mUclampMinHigh);
+ updatePidControlVariable(adpfConfig->mUclampMinHigh);
return ndk::ScopedAStatus::ok();
}
@@ -307,10 +316,10 @@ ndk::ScopedAStatus PowerHintSession::reportActualWorkDuration(
// Apply to all the threads in the group
int next_min = std::min(static_cast<int>(adpfConfig->mUclampMinHigh),
- mDescriptor->pidSetPoint + static_cast<int>(output));
+ mDescriptor->pidControlVariable + static_cast<int>(output));
next_min = std::max(static_cast<int>(adpfConfig->mUclampMinLow), next_min);
- updatePidSetPoint(next_min);
+ updatePidControlVariable(next_min);
return ndk::ScopedAStatus::ok();
}
@@ -327,18 +336,19 @@ ndk::ScopedAStatus PowerHintSession::sendHint(SessionHint hint) {
switch (hint) {
case SessionHint::CPU_LOAD_UP:
- updatePidSetPoint(mDescriptor->pidSetPoint);
+ updatePidControlVariable(mDescriptor->pidControlVariable);
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:
- updatePidSetPoint(adpfConfig->mUclampMinLow);
+ updatePidControlVariable(adpfConfig->mUclampMinLow);
break;
case SessionHint::CPU_LOAD_RESET:
- updatePidSetPoint(std::max(adpfConfig->mUclampMinInit,
- static_cast<uint32_t>(mDescriptor->pidSetPoint)),
- false);
+ updatePidControlVariable(
+ std::max(adpfConfig->mUclampMinInit,
+ static_cast<uint32_t>(mDescriptor->pidControlVariable)),
+ false);
mPSManager->voteSet(mSessionId, AdpfHintType::ADPF_CPU_LOAD_RESET,
adpfConfig->mUclampMinHigh, kUclampMax,
std::chrono::steady_clock::now(),
@@ -347,11 +357,20 @@ ndk::ScopedAStatus PowerHintSession::sendHint(SessionHint hint) {
break;
case SessionHint::CPU_LOAD_RESUME:
mPSManager->voteSet(mSessionId, AdpfHintType::ADPF_CPU_LOAD_RESUME,
- mDescriptor->pidSetPoint, kUclampMax,
+ mDescriptor->pidControlVariable, kUclampMax,
std::chrono::steady_clock::now(),
duration_cast<nanoseconds>(mDescriptor->targetNs *
adpfConfig->mStaleTimeFactor / 2.0));
break;
+ case SessionHint::GPU_LOAD_UP:
+ // TODO(kevindubois): add impl
+ break;
+ case SessionHint::GPU_LOAD_DOWN:
+ // TODO(kevindubois): add impl
+ break;
+ case SessionHint::GPU_LOAD_RESET:
+ // TODO(kevindubois): add impl
+ break;
default:
ALOGE("Error: hint is invalid");
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
@@ -392,18 +411,27 @@ ndk::ScopedAStatus PowerHintSession::setThreads(const std::vector<int32_t> &thre
ALOGE("Error: threadIds should not be empty");
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
-
+ mDescriptor->thread_ids = threadIds;
mPSManager->setThreadsFromPowerSession(mSessionId, threadIds);
// init boost
- updatePidSetPoint(HintManager::GetInstance()->GetAdpfProfile()->mUclampMinInit);
+ updatePidControlVariable(HintManager::GetInstance()->GetAdpfProfile()->mUclampMinInit);
return ndk::ScopedAStatus::ok();
}
+ndk::ScopedAStatus PowerHintSession::getSessionConfig(SessionConfig *_aidl_return) {
+ _aidl_return->id = mSessionId;
+ return ndk::ScopedAStatus::ok();
+}
+
+SessionTag PowerHintSession::getSessionTag() const {
+ return mTag;
+}
+
std::string AppHintDesc::toString() const {
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(" uclamp.min: %d \n", pidControlVariable));
out.append(StringPrintf(" uid: %d, tgid: %d\n", uid, tgid));
return out;
}
diff --git a/power-libperfmgr/aidl/PowerHintSession.h b/power-libperfmgr/aidl/PowerHintSession.h
index 4c304ff2..cf1aa9e5 100644
--- a/power-libperfmgr/aidl/PowerHintSession.h
+++ b/power-libperfmgr/aidl/PowerHintSession.h
@@ -19,6 +19,7 @@
#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/SessionTag.h>
#include <aidl/android/hardware/power/WorkDuration.h>
#include <utils/Looper.h>
#include <utils/Thread.h>
@@ -36,8 +37,10 @@ namespace impl {
namespace pixel {
using aidl::android::hardware::power::BnPowerHintSession;
+using aidl::android::hardware::power::SessionConfig;
using aidl::android::hardware::power::SessionHint;
using aidl::android::hardware::power::SessionMode;
+using aidl::android::hardware::power::SessionTag;
using aidl::android::hardware::power::WorkDuration;
using ::android::Message;
using ::android::MessageHandler;
@@ -54,13 +57,17 @@ class PowerSessionManager;
// and is separate so that it can be used as a pointer for
// easily passing to the pid function
struct AppHintDesc {
- AppHintDesc(int64_t sessionId, int32_t tgid, int32_t uid, std::chrono::nanoseconds pTargetNs);
+ AppHintDesc(int64_t sessionId, int32_t tgid, int32_t uid, const std::vector<int32_t> &threadIds,
+ SessionTag tag, std::chrono::nanoseconds pTargetNs);
+
std::string toString() const;
int64_t sessionId{0};
const int32_t tgid;
const int32_t uid;
nanoseconds targetNs;
- int pidSetPoint;
+ std::vector<int32_t> thread_ids;
+ SessionTag tag;
+ int pidControlVariable;
// status
std::atomic<bool> is_active;
// pid
@@ -76,7 +83,7 @@ struct AppHintDesc {
class PowerHintSession : public BnPowerHintSession {
public:
explicit PowerHintSession(int32_t tgid, int32_t uid, const std::vector<int32_t> &threadIds,
- int64_t durationNanos);
+ int64_t durationNanos, SessionTag tag);
~PowerHintSession();
ndk::ScopedAStatus close() override;
ndk::ScopedAStatus pause() override;
@@ -87,15 +94,18 @@ class PowerHintSession : public BnPowerHintSession {
ndk::ScopedAStatus sendHint(SessionHint hint) override;
ndk::ScopedAStatus setMode(SessionMode mode, bool enabled) override;
ndk::ScopedAStatus setThreads(const std::vector<int32_t> &threadIds) override;
+ ndk::ScopedAStatus getSessionConfig(SessionConfig *_aidl_return) override;
+
bool isActive();
bool isTimeout();
// Is hint session for a user application
bool isAppSession();
void dumpToStream(std::ostream &stream);
+ SessionTag getSessionTag() const;
private:
void tryToSendPowerHint(std::string hint);
- void updatePidSetPoint(int pidSetPoint, bool updateVote = true);
+ void updatePidControlVariable(int pidControlVariable, bool updateVote = true);
int64_t convertWorkDurationToBoostByPid(const std::vector<WorkDuration> &actualDurations);
// Data
sp<PowerSessionManager> mPSManager;
@@ -112,6 +122,8 @@ class PowerHintSession : public BnPowerHintSession {
int mLastHintSent = -1;
// Use the value of the last enum in enum_range +1 as array size
std::array<bool, enum_size<SessionMode>()> mModes{};
+ // Tag labeling what kind of session this is
+ SessionTag mTag;
};
} // namespace pixel
diff --git a/power-libperfmgr/aidl/PowerSessionManager.cpp b/power-libperfmgr/aidl/PowerSessionManager.cpp
index d8e77bd7..fc7f5896 100644
--- a/power-libperfmgr/aidl/PowerSessionManager.cpp
+++ b/power-libperfmgr/aidl/PowerSessionManager.cpp
@@ -111,8 +111,6 @@ void PowerSessionManager::addPowerSession(const std::string &idString,
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;
@@ -123,7 +121,7 @@ void PowerSessionManager::addPowerSession(const std::string &idString,
sve.votes = std::make_shared<Votes>();
sve.votes->add(
static_cast<std::underlying_type_t<AdpfHintType>>(AdpfHintType::ADPF_VOTE_DEFAULT),
- pidVoteRange);
+ CpuVote(false, timeNow, sessionDescriptor->targetNs, kUclampMin, kUclampMax));
bool addedRes = false;
{
@@ -293,7 +291,6 @@ void PowerSessionManager::voteSet(int64_t sessionId, AdpfHintType voteId, int uc
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;
{
@@ -312,7 +309,8 @@ void PowerSessionManager::voteSet(int64_t sessionId, AdpfHintType voteId, int uc
if (timeoutDeadline < sessValPtr->votes->voteTimeout(voteIdInt)) {
scheduleTimeout = true;
}
- sessValPtr->votes->add(voteIdInt, vr);
+ sessValPtr->votes->add(voteIdInt,
+ CpuVote(true, startTime, durationNs, uclampMin, uclampMax));
sessValPtr->lastUpdatedTime = startTime;
}
@@ -342,7 +340,8 @@ void PowerSessionManager::disableBoosts(int64_t sessionId) {
// 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}) {
+ AdpfHintType::ADPF_CPU_LOAD_RESUME, AdpfHintType::ADPF_VOTE_POWER_EFFICIENCY,
+ AdpfHintType::ADPF_GPU_LOAD_UP, AdpfHintType::ADPF_GPU_LOAD_RESET}) {
auto vint = static_cast<std::underlying_type_t<AdpfHintType>>(vid);
sessValPtr->votes->setUseVote(vint, false);
}
diff --git a/power-libperfmgr/aidl/SessionTaskMap.cpp b/power-libperfmgr/aidl/SessionTaskMap.cpp
index 62dae33c..fc06317a 100644
--- a/power-libperfmgr/aidl/SessionTaskMap.cpp
+++ b/power-libperfmgr/aidl/SessionTaskMap.cpp
@@ -55,7 +55,7 @@ void SessionTaskMap::addVote(int64_t sessionId, int voteId, int uclampMin, int u
}
sessItr->second.val->votes->add(voteId,
- VoteRange(true, uclampMin, uclampMax, startTime, durationNs));
+ CpuVote(true, startTime, durationNs, uclampMin, uclampMax));
}
std::shared_ptr<SessionValueEntry> SessionTaskMap::findSession(int64_t sessionId) {
diff --git a/power-libperfmgr/aidl/UClampVoter.cpp b/power-libperfmgr/aidl/UClampVoter.cpp
index 251f5410..d1a1cb0b 100644
--- a/power-libperfmgr/aidl/UClampVoter.cpp
+++ b/power-libperfmgr/aidl/UClampVoter.cpp
@@ -23,35 +23,61 @@ namespace power {
namespace impl {
namespace pixel {
-static void confine(UclampRange *uclampRange, const VoteRange &vr,
+static void confine(UclampRange *uclampRange, const CpuVote &cpu_vote,
std::chrono::steady_clock::time_point t) {
- if (!vr.isTimeInRange(t)) {
+ if (!cpu_vote.isTimeInRange(t)) {
return;
}
- uclampRange->uclampMin = std::max(uclampRange->uclampMin, vr.uclampMin());
- uclampRange->uclampMax = std::min(uclampRange->uclampMax, vr.uclampMax());
+ uclampRange->uclampMin = std::max(uclampRange->uclampMin, cpu_vote.mUclampRange.uclampMin);
+ uclampRange->uclampMax = std::min(uclampRange->uclampMax, cpu_vote.mUclampRange.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() << "]";
+std::ostream &operator<<(std::ostream &o, const UclampRange &uc) {
+ o << "[" << uc.uclampMin << "," << uc.uclampMax << "]";
return o;
}
Votes::Votes() {}
-void Votes::add(int voteId, const VoteRange &v) {
- mVotes[voteId] = v;
+constexpr static auto gpu_vote_id = static_cast<int>(AdpfHintType::ADPF_GPU_CAPACITY);
+
+static inline bool isGpuVote(int type_raw) {
+ AdpfHintType const type = static_cast<AdpfHintType>(type_raw);
+ return type == AdpfHintType::ADPF_GPU_CAPACITY || type == AdpfHintType::ADPF_GPU_LOAD_UP ||
+ type == AdpfHintType::ADPF_GPU_LOAD_DOWN || type == AdpfHintType::ADPF_GPU_LOAD_RESET;
+}
+
+void Votes::add(int id, CpuVote const &vote) {
+ if (!isGpuVote(id)) {
+ mCpuVotes[id] = vote;
+ }
+}
+
+std::optional<Cycles> Votes::getGpuCapacityRequest(std::chrono::steady_clock::time_point t) const {
+ auto it = mGpuVotes.find(static_cast<int>(AdpfHintType::ADPF_GPU_CAPACITY));
+ if (it != mGpuVotes.end() && it->second.isTimeInRange(t)) {
+ return {it->second.mCapacity};
+ }
+ return {};
+}
+
+void Votes::add(int id, GpuVote const &vote) {
+ if (isGpuVote(id)) {
+ mGpuVotes[id] = vote;
+ }
}
void Votes::updateDuration(int voteId, std::chrono::nanoseconds durationNs) {
- auto voteItr = mVotes.find(voteId);
- if (voteItr != mVotes.end()) {
+ if (isGpuVote(voteId)) {
+ auto const it = mGpuVotes.find(voteId);
+ if (it != mGpuVotes.end()) {
+ it->second.updateDuration(durationNs);
+ }
+ return;
+ }
+
+ auto const voteItr = mCpuVotes.find(voteId);
+ if (voteItr != mCpuVotes.end()) {
voteItr->second.updateDuration(durationNs);
}
}
@@ -61,13 +87,20 @@ void Votes::getUclampRange(UclampRange *uclampRange,
if (nullptr == uclampRange) {
return;
}
- for (const auto &v : mVotes) {
- confine(uclampRange, v.second, t);
+ for (auto it = mCpuVotes.begin(); it != mCpuVotes.end(); it++) {
+ auto timings_it = mCpuVotes.find(it->first);
+ confine(uclampRange, it->second, t);
}
}
bool Votes::anyTimedOut(std::chrono::steady_clock::time_point t) const {
- for (const auto &v : mVotes) {
+ for (const auto &v : mGpuVotes) {
+ if (!v.second.isTimeInRange(t)) {
+ return true;
+ }
+ }
+
+ for (const auto &v : mCpuVotes) {
if (!v.second.isTimeInRange(t)) {
return true;
}
@@ -76,7 +109,13 @@ bool Votes::anyTimedOut(std::chrono::steady_clock::time_point t) const {
}
bool Votes::allTimedOut(std::chrono::steady_clock::time_point t) const {
- for (const auto &v : mVotes) {
+ for (const auto &v : mGpuVotes) {
+ if (v.second.isTimeInRange(t)) {
+ return false;
+ }
+ }
+
+ for (const auto &v : mCpuVotes) {
if (v.second.isTimeInRange(t)) {
return false;
}
@@ -85,17 +124,36 @@ bool Votes::allTimedOut(std::chrono::steady_clock::time_point t) const {
}
bool Votes::remove(int voteId) {
- auto itr = mVotes.find(voteId);
- if (itr == mVotes.end()) {
+ if (isGpuVote(voteId)) {
+ auto const it = mGpuVotes.find(voteId);
+ if (it != mGpuVotes.end()) {
+ mGpuVotes.erase(it);
+ return true;
+ }
return false;
}
- mVotes.erase(itr);
- return true;
+
+ auto const it = mCpuVotes.find(voteId);
+ if (it != mCpuVotes.end()) {
+ mCpuVotes.erase(it);
+ return true;
+ }
+
+ return false;
}
bool Votes::setUseVote(int voteId, bool active) {
- auto itr = mVotes.find(voteId);
- if (itr == mVotes.end()) {
+ if (isGpuVote(voteId)) {
+ auto const itr = mGpuVotes.find(voteId);
+ if (itr == mGpuVotes.end()) {
+ return false;
+ }
+ itr->second.setActive(active);
+ return true;
+ }
+
+ auto const itr = mCpuVotes.find(voteId);
+ if (itr == mCpuVotes.end()) {
return false;
}
itr->second.setActive(active);
@@ -103,20 +161,36 @@ bool Votes::setUseVote(int voteId, bool active) {
}
size_t Votes::size() const {
- return mVotes.size();
+ return mCpuVotes.size() + mGpuVotes.size();
}
bool Votes::voteIsActive(int voteId) {
- auto itr = mVotes.find(voteId);
- if (itr == mVotes.end()) {
+ if (isGpuVote(voteId)) {
+ auto const itr = mGpuVotes.find(voteId);
+ if (itr == mGpuVotes.end()) {
+ return false;
+ }
+ return itr->second.active();
+ }
+
+ auto const itr = mCpuVotes.find(voteId);
+ if (itr == mCpuVotes.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()) {
+ if (isGpuVote(voteId)) {
+ auto const itr = mGpuVotes.find(voteId);
+ if (itr == mGpuVotes.end()) {
+ return std::chrono::steady_clock::time_point{};
+ }
+ return itr->second.startTime() + itr->second.durationNs();
+ }
+
+ auto const itr = mCpuVotes.find(voteId);
+ if (itr == mCpuVotes.end()) {
return std::chrono::steady_clock::time_point{};
}
return itr->second.startTime() + itr->second.durationNs();
diff --git a/power-libperfmgr/aidl/UClampVoter.h b/power-libperfmgr/aidl/UClampVoter.h
index 4b9df33a..d85e0158 100644
--- a/power-libperfmgr/aidl/UClampVoter.h
+++ b/power-libperfmgr/aidl/UClampVoter.h
@@ -16,12 +16,14 @@
#pragma once
+#include <algorithm>
#include <chrono>
#include <memory>
#include <ostream>
#include <unordered_map>
#include "AdpfTypes.h"
+#include "PhysicalQuantityTypes.h"
namespace aidl {
namespace google {
@@ -38,28 +40,24 @@ struct UclampRange {
};
// --------------------------------------------------------
-// Hold a min max range of acceptable votes, is active status, time duration
-// info, and helper methods for consistent use
+// Hold the common timing state of the of acceptable votes:
+// is active status, time duration info, and helper methods for consistent use
class VoteRange {
public:
- VoteRange() {}
+ VoteRange() = default;
+ VoteRange(VoteRange const &) = default;
+ VoteRange(VoteRange &&) = default;
+ VoteRange &operator=(VoteRange const &) = default;
+ VoteRange &operator=(VoteRange &&) = default;
+ virtual ~VoteRange() = default;
- 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) {}
+ VoteRange(bool active, std::chrono::steady_clock::time_point startTime,
+ std::chrono::nanoseconds durationNs)
+ : mActive(active), 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; }
@@ -77,17 +75,28 @@ class VoteRange {
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{};
};
+struct CpuVote final : VoteRange {
+ CpuVote() = default;
+ CpuVote(bool active, std::chrono::steady_clock::time_point startTime,
+ std::chrono::nanoseconds durationNs, int uclamp_min, int uclamp_max)
+ : VoteRange(active, startTime, durationNs), mUclampRange{uclamp_min, uclamp_max} {}
+ UclampRange mUclampRange;
+};
+
+struct GpuVote final : VoteRange {
+ GpuVote() = default;
+ GpuVote(bool active, std::chrono::steady_clock::time_point startTime,
+ std::chrono::nanoseconds durationNs, Cycles capacity)
+ : VoteRange(active, startTime, durationNs), mCapacity(capacity) {}
+ Cycles mCapacity;
+};
+
// Helper for logging
std::ostream &operator<<(std::ostream &o, const VoteRange &vr);
@@ -98,8 +107,11 @@ class Votes {
public:
Votes();
- // Add a vote and associate with vote id, overwrites existing vote
- void add(int voteId, const VoteRange &v);
+ // Add a vote and associate with vote id, overwrites existing vote.
+ // ADPF_VOTE_GPU_CAPACITY is an invalid value and will be ignored.
+ // For this vote, set_gpu_capacity_node should be used.
+ void add(int, CpuVote const &vote);
+ void add(int, GpuVote const &vote);
// Update the duration of a vote given a vote id
void updateDuration(int voteId, std::chrono::nanoseconds durationNs);
@@ -109,6 +121,7 @@ class Votes {
// the largest min and the smallest max
void getUclampRange(UclampRange *uclampRange, std::chrono::steady_clock::time_point t) const;
+ std::optional<Cycles> getGpuCapacityRequest(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;
@@ -130,7 +143,8 @@ class Votes {
std::chrono::steady_clock::time_point voteTimeout(int voteId);
private:
- std::unordered_map<int, VoteRange> mVotes;
+ std::unordered_map<int, CpuVote> mCpuVotes;
+ std::unordered_map<int, GpuVote> mGpuVotes;
};
} // namespace pixel
diff --git a/power-libperfmgr/aidl/tests/GpuCapacityNodeTest.cpp b/power-libperfmgr/aidl/tests/GpuCapacityNodeTest.cpp
new file mode 100644
index 00000000..2c24f84c
--- /dev/null
+++ b/power-libperfmgr/aidl/tests/GpuCapacityNodeTest.cpp
@@ -0,0 +1,276 @@
+/*
+ * 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 <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "aidl/GpuCapacityNode.h"
+
+using testing::Invoke;
+using testing::Return, testing::_, testing::Eq, testing::StrEq, testing::NiceMock;
+
+namespace aidl {
+namespace google {
+namespace hardware {
+namespace power {
+namespace impl {
+namespace pixel {
+
+struct MockFdInterface : FdInterface {
+ MOCK_METHOD(int, open, (const char *, int), (const, final));
+ MOCK_METHOD(int, write, (int, const char *, size_t), (const, final));
+ MOCK_METHOD(ssize_t, read, (int, void *, size_t), (const, final));
+ MOCK_METHOD(off_t, lseek, (int, off_t, int), (const, final));
+ MOCK_METHOD(int, close, (int), (const, final));
+};
+
+struct FdInterfaceWrapper : FdInterface {
+ FdInterfaceWrapper(std::shared_ptr<FdInterface> const &wrapped) : wrapped_(wrapped) {}
+
+ int open(const char *path, int flags) const final { return wrapped_->open(path, flags); }
+
+ int write(int fd, const char *data, size_t count) const final {
+ return wrapped_->write(fd, data, count);
+ }
+ ssize_t read(int fd, void *data, size_t count) const final {
+ return wrapped_->read(fd, data, count);
+ }
+ off_t lseek(int fd, off_t offset, int whence) const final {
+ return wrapped_->lseek(fd, offset, whence);
+ }
+
+ int close(int fd) const final { return wrapped_->close(fd); }
+ std::shared_ptr<FdInterface> const wrapped_;
+};
+
+struct GpuCapacityNodeTest : ::testing::Test {
+ GpuCapacityNodeTest() : mock_fd_interface(std::make_shared<NiceMock<MockFdInterface>>()) {}
+ std::shared_ptr<MockFdInterface> mock_fd_interface;
+ std::string const path = "/path/example";
+ std::string const headroom_path = "/path/example/capacity_headroom";
+ std::string const freq_path = "/path/example/cur_freq";
+ int const fake_fd = 33;
+ int const another_fake_fd = 34;
+ int const invalid_fake_fd = -33;
+ Cycles const capacity{11503};
+ std::string const capacity_str = "11503";
+};
+
+TEST_F(GpuCapacityNodeTest, OpensCorrectNode) {
+ EXPECT_CALL(*mock_fd_interface, close(fake_fd)).Times(1).WillOnce(Return(0));
+ EXPECT_CALL(*mock_fd_interface, close(another_fake_fd)).Times(1).WillOnce(Return(0));
+ GpuCapacityNode capacity_node(std::make_unique<FdInterfaceWrapper>(mock_fd_interface), fake_fd,
+ another_fake_fd, path);
+}
+
+TEST_F(GpuCapacityNodeTest, OpensCorrectNodeHelper) {
+ EXPECT_CALL(*mock_fd_interface, open(StrEq(headroom_path), O_RDWR | O_CLOEXEC | O_NONBLOCK))
+ .Times(1)
+ .WillOnce(Return(fake_fd));
+ EXPECT_CALL(*mock_fd_interface, open(StrEq(freq_path), O_RDONLY | O_CLOEXEC | O_NONBLOCK))
+ .Times(1)
+ .WillOnce(Return(another_fake_fd));
+ EXPECT_CALL(*mock_fd_interface, close(another_fake_fd)).Times(1).WillOnce(Return(0));
+ EXPECT_CALL(*mock_fd_interface, close(fake_fd)).Times(1).WillOnce(Return(0));
+ auto const node = GpuCapacityNode::init_gpu_capacity_node(
+ std::make_unique<FdInterfaceWrapper>(mock_fd_interface), path);
+}
+
+TEST_F(GpuCapacityNodeTest, node_open_helper_failure_one) {
+ EXPECT_CALL(*mock_fd_interface, open(_, _)).Times(1).WillOnce(Return(invalid_fake_fd));
+ EXPECT_CALL(*mock_fd_interface, close(0)).Times(0);
+ auto const node = GpuCapacityNode::init_gpu_capacity_node(
+ std::make_unique<FdInterfaceWrapper>(mock_fd_interface), path);
+ EXPECT_THAT(node, testing::Eq(nullptr));
+}
+
+TEST_F(GpuCapacityNodeTest, node_open_helper_failure_two) {
+ testing::Sequence seq;
+ EXPECT_CALL(*mock_fd_interface, open(_, _)).InSequence(seq).WillOnce(Return(fake_fd));
+ EXPECT_CALL(*mock_fd_interface, open(_, _)).InSequence(seq).WillOnce(Return(invalid_fake_fd));
+ EXPECT_CALL(*mock_fd_interface, close(fake_fd)).Times(1);
+ auto const node = GpuCapacityNode::init_gpu_capacity_node(
+ std::make_unique<FdInterfaceWrapper>(mock_fd_interface), path);
+ EXPECT_THAT(node, testing::Eq(nullptr));
+}
+
+TEST_F(GpuCapacityNodeTest, writes_correct_value_to_node) {
+ EXPECT_CALL(*mock_fd_interface, write(fake_fd, StrEq(capacity_str), capacity_str.size()))
+ .Times(1);
+ GpuCapacityNode capacity_node(std::make_unique<FdInterfaceWrapper>(mock_fd_interface), fake_fd,
+ another_fake_fd, path);
+ EXPECT_THAT(capacity_node.set_gpu_capacity(capacity), Eq(true));
+}
+
+TEST_F(GpuCapacityNodeTest, writes_failure) {
+ EXPECT_CALL(*mock_fd_interface, write(_, _, _)).Times(1).WillOnce(Return(-12));
+ GpuCapacityNode capacity_node(std::make_unique<FdInterfaceWrapper>(mock_fd_interface), fake_fd,
+ another_fake_fd, path);
+ EXPECT_THAT(capacity_node.set_gpu_capacity(capacity), Eq(false));
+}
+
+TEST_F(GpuCapacityNodeTest, reads_freq_correctly) {
+ static constexpr auto value = "100";
+ testing::Sequence seq;
+ EXPECT_CALL(*mock_fd_interface, read(another_fake_fd, _, _))
+ .InSequence(seq)
+ .WillOnce(Invoke([&](auto, void *buf, size_t len) {
+ strncpy(static_cast<char *>(buf), value, len);
+ return 3;
+ }));
+ EXPECT_CALL(*mock_fd_interface, read(another_fake_fd, _, _))
+ .InSequence(seq)
+ .WillOnce(Return(0));
+ EXPECT_CALL(*mock_fd_interface, lseek(another_fake_fd, 0, SEEK_SET))
+ .InSequence(seq)
+ .WillOnce(Return(0));
+
+ GpuCapacityNode capacity_node(std::make_unique<FdInterfaceWrapper>(mock_fd_interface), fake_fd,
+ another_fake_fd, path);
+ auto const frequency = capacity_node.gpu_frequency();
+ ASSERT_TRUE(frequency);
+ EXPECT_THAT(*frequency, Eq(Frequency(100000)));
+}
+
+TEST_F(GpuCapacityNodeTest, reads_freq_correctly_partial) {
+ static constexpr auto value = "100";
+ int i = 0;
+ testing::Sequence seq;
+ EXPECT_CALL(*mock_fd_interface, read(another_fake_fd, _, _))
+ .Times(4)
+ .WillRepeatedly(Invoke([&](auto, void *buf, size_t) {
+ if (i >= 3) {
+ return 0;
+ }
+ auto c = reinterpret_cast<char *>(buf);
+ *c = value[i++];
+ return 1;
+ }));
+ EXPECT_CALL(*mock_fd_interface, lseek(another_fake_fd, 0, SEEK_SET)).WillOnce(Return(0));
+
+ GpuCapacityNode capacity_node(std::make_unique<FdInterfaceWrapper>(mock_fd_interface), fake_fd,
+ another_fake_fd, path);
+ auto const frequency = capacity_node.gpu_frequency();
+ ASSERT_TRUE(frequency);
+ EXPECT_THAT(*frequency, Eq(Frequency(100000)));
+}
+
+TEST_F(GpuCapacityNodeTest, reads_freq_correctly_full) {
+ testing::Sequence seq;
+ EXPECT_CALL(*mock_fd_interface, read(another_fake_fd, _, _))
+ .Times(1)
+ .WillRepeatedly(Invoke([&](auto, void *buf, size_t size) {
+ auto c = reinterpret_cast<char *>(buf);
+ for (auto i = 0u; i < size; i++) {
+ c[i] = i < 3 ? '1' : '\0';
+ }
+ return size;
+ }));
+ EXPECT_CALL(*mock_fd_interface, lseek(another_fake_fd, 0, SEEK_SET)).WillOnce(Return(0));
+
+ GpuCapacityNode capacity_node(std::make_unique<FdInterfaceWrapper>(mock_fd_interface), fake_fd,
+ another_fake_fd, path);
+ auto const frequency = capacity_node.gpu_frequency();
+ ASSERT_TRUE(frequency);
+ EXPECT_THAT(*frequency, Eq(Frequency(111000)));
+}
+
+TEST_F(GpuCapacityNodeTest, read_failure) {
+ EXPECT_CALL(*mock_fd_interface, read(another_fake_fd, _, _)).Times(1).WillOnce(Return(-1));
+
+ GpuCapacityNode capacity_node(std::make_unique<FdInterfaceWrapper>(mock_fd_interface), fake_fd,
+ another_fake_fd, path);
+ auto const frequency = capacity_node.gpu_frequency();
+ EXPECT_FALSE(frequency);
+}
+
+TEST_F(GpuCapacityNodeTest, lseek_failure) {
+ testing::Sequence seq;
+ EXPECT_CALL(*mock_fd_interface, read(_, _, _)).InSequence(seq).WillOnce(Return(7));
+ EXPECT_CALL(*mock_fd_interface, read(_, _, _)).InSequence(seq).WillOnce(Return(0));
+ EXPECT_CALL(*mock_fd_interface, lseek(_, _, _)).InSequence(seq).WillOnce(Return(-1));
+
+ GpuCapacityNode capacity_node(std::make_unique<FdInterfaceWrapper>(mock_fd_interface), fake_fd,
+ another_fake_fd, path);
+ auto const frequency = capacity_node.gpu_frequency();
+ EXPECT_FALSE(frequency);
+}
+
+TEST_F(GpuCapacityNodeTest, truncates_positive_floats) {
+ static constexpr auto value = "1068.2";
+ EXPECT_CALL(*mock_fd_interface, read(another_fake_fd, _, _))
+ .Times(2)
+ .WillOnce(Invoke([&](auto, void *buf, size_t len) {
+ strncpy(static_cast<char *>(buf), value, len);
+ return 6;
+ }))
+ .WillOnce(Return(0));
+
+ GpuCapacityNode capacity_node(std::make_unique<FdInterfaceWrapper>(mock_fd_interface), fake_fd,
+ another_fake_fd, path);
+ auto const frequency = capacity_node.gpu_frequency();
+ ASSERT_TRUE(frequency);
+ EXPECT_THAT(*frequency, Eq(Frequency(1068000)));
+}
+
+TEST_F(GpuCapacityNodeTest, nonsense_returned_from_frequency) {
+ static constexpr auto value = "zappyzapzoo";
+ EXPECT_CALL(*mock_fd_interface, read(another_fake_fd, _, _))
+ .Times(1)
+ .WillOnce(Invoke([&](auto, void *buf, size_t len) {
+ strncpy(static_cast<char *>(buf), value, len);
+ return 0;
+ }));
+
+ GpuCapacityNode capacity_node(std::make_unique<FdInterfaceWrapper>(mock_fd_interface), fake_fd,
+ another_fake_fd, path);
+ EXPECT_FALSE(capacity_node.gpu_frequency());
+}
+
+TEST_F(GpuCapacityNodeTest, nonsense_returned_from_frequency2) {
+ static constexpr auto value = "-1068";
+ EXPECT_CALL(*mock_fd_interface, read(another_fake_fd, _, _))
+ .Times(1)
+ .WillOnce(Invoke([&](auto, void *buf, size_t len) {
+ strncpy(static_cast<char *>(buf), value, len);
+ return 0;
+ }));
+
+ GpuCapacityNode capacity_node(std::make_unique<FdInterfaceWrapper>(mock_fd_interface), fake_fd,
+ another_fake_fd, path);
+ EXPECT_FALSE(capacity_node.gpu_frequency());
+}
+
+TEST_F(GpuCapacityNodeTest, nonsense_returned_from_frequency4) {
+ static constexpr auto value = "0";
+ EXPECT_CALL(*mock_fd_interface, read(another_fake_fd, _, _))
+ .Times(1)
+ .WillOnce(Invoke([&](auto, void *buf, size_t len) {
+ strncpy(static_cast<char *>(buf), value, len);
+ return 0;
+ }));
+
+ GpuCapacityNode capacity_node(std::make_unique<FdInterfaceWrapper>(mock_fd_interface), fake_fd,
+ another_fake_fd, path);
+ EXPECT_FALSE(capacity_node.gpu_frequency());
+}
+
+} // namespace pixel
+} // namespace impl
+} // namespace power
+} // namespace hardware
+} // namespace google
+} // namespace aidl
diff --git a/power-libperfmgr/aidl/tests/PhysicalQuantityTypeTest.cpp b/power-libperfmgr/aidl/tests/PhysicalQuantityTypeTest.cpp
new file mode 100644
index 00000000..15734304
--- /dev/null
+++ b/power-libperfmgr/aidl/tests/PhysicalQuantityTypeTest.cpp
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "aidl/PhysicalQuantityTypes.h"
+
+using std::chrono_literals::operator""ms;
+using std::chrono_literals::operator""s;
+using std::chrono_literals::operator""min;
+using testing::Eq;
+using testing::Gt;
+using testing::Lt;
+
+namespace aidl {
+namespace google {
+namespace hardware {
+namespace power {
+namespace impl {
+namespace pixel {
+
+// compile time tests
+static_assert(Cycles(10) * 2 == Cycles(20));
+static_assert(100_hz + 200_hz == Frequency(300));
+static_assert(Cycles(100) / 1s == Frequency(100));
+
+TEST(PhysicalQuantityTypeTest, type_check_basic_cycles) {
+ Cycles a(0);
+ Cycles b(-1);
+ Cycles c(8);
+ Cycles d(11);
+ Cycles e(8);
+ EXPECT_THAT(a, Eq(a));
+ EXPECT_THAT(e, Eq(c));
+ EXPECT_THAT(c, Eq(e));
+ EXPECT_THAT(b, Lt(a));
+ EXPECT_THAT(a, Gt(b));
+ EXPECT_THAT(a + b, Eq(b));
+ EXPECT_THAT(b + c, Eq(Cycles(7)));
+ EXPECT_THAT(c - b, Eq(Cycles(9)));
+ EXPECT_THAT(c * 8, Eq(Cycles(64)));
+ EXPECT_THAT(3 * c, Eq(Cycles(24)));
+ EXPECT_THAT(c / 2, Eq(Cycles(4)));
+}
+
+TEST(PhysicalQuantityTypeTest, type_check_basic_frequency) {
+ Frequency a(1000);
+ Frequency b(1111);
+ EXPECT_THAT(a, Eq(a));
+ EXPECT_THAT(a + Frequency(111), Eq(b));
+ EXPECT_THAT(b, Gt(a));
+ EXPECT_THAT(a, Lt(b));
+}
+
+TEST(PhysicalQuantityTypeTest, freq_cycles_time_conversions) {
+ EXPECT_THAT(Cycles(1000) / 2s, Eq(500_hz));
+ EXPECT_THAT(Cycles(1000) / 500ms, Eq(2000_hz));
+
+ EXPECT_THAT(1000_hz * 12ms, Eq(Cycles(12)));
+ EXPECT_THAT(6min * 500_hz, Eq(Cycles(180000)));
+ EXPECT_THAT(1000_hz * 2min, Eq(Cycles(120000)));
+}
+
+} // 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
index 110889f6..a9f43cba 100644
--- a/power-libperfmgr/aidl/tests/PowerHintSessionTest.cpp
+++ b/power-libperfmgr/aidl/tests/PowerHintSessionTest.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include <aidl/android/hardware/power/SessionTag.h>
#include <gtest/gtest.h>
#include <sys/syscall.h>
@@ -72,8 +73,10 @@ class PowerHintSessionTest : public ::testing::Test {
}
}
- sess1 = ndk::SharedRefBase::make<PowerHintSession>(1, 1, session1Threads, 1000000);
- sess2 = ndk::SharedRefBase::make<PowerHintSession>(2, 2, session2Threads, 1000000);
+ sess1 = ndk::SharedRefBase::make<PowerHintSession>(1, 1, session1Threads, 1000000,
+ SessionTag::OTHER);
+ sess2 = ndk::SharedRefBase::make<PowerHintSession>(2, 2, session2Threads, 1000000,
+ SessionTag::OTHER);
}
void TearDown() {
diff --git a/power-libperfmgr/aidl/tests/SessionTaskMapTest.cpp b/power-libperfmgr/aidl/tests/SessionTaskMapTest.cpp
index 7f7dab3f..6de4e076 100644
--- a/power-libperfmgr/aidl/tests/SessionTaskMapTest.cpp
+++ b/power-libperfmgr/aidl/tests/SessionTaskMapTest.cpp
@@ -198,7 +198,7 @@ TEST(SessionTaskMapTest, remove) {
SessionValueEntry sve;
sve.isAppSession = true;
sve.votes = std::make_shared<Votes>();
- sve.votes->add(sessionId, VoteRange::makeMinRange(123, tNow, 400ms));
+ sve.votes->add(sessionId, CpuVote(true, tNow, 400ms, 123, 1024));
m.add(sessionId, sve, {10, 20, 30});
EXPECT_TRUE(m.isAnyAppSessionActive(tNow));
EXPECT_TRUE(m.remove(sessionId));
@@ -217,7 +217,7 @@ TEST(SessionTaskMapTest, isAnyAppActive) {
sv.isAppSession = true;
sv.lastUpdatedTime = tNow;
sv.votes = std::make_shared<Votes>();
- sv.votes->add(1, VoteRange::makeMinRange(123, tNow, 400ms));
+ sv.votes->add(1, CpuVote(true, tNow, 400ms, 123, 1024));
EXPECT_TRUE(m.add(sessionId, sv, {10, 20, 30}));
EXPECT_TRUE(m.isAnyAppSessionActive(tNow));
@@ -300,7 +300,7 @@ TEST(SessionTaskMapTest, TwoSessionsOneInactive) {
sv.isAppSession = true;
sv.lastUpdatedTime = tNow;
sv.votes = std::make_shared<Votes>();
- sv.votes->add(11, VoteRange::makeMinRange(111, tNow, 400ms));
+ sv.votes->add(11, CpuVote(true, tNow, 400ms, 111, 1024));
EXPECT_TRUE(m.add(1001, sv, {10, 20, 30}));
}
@@ -310,7 +310,7 @@ TEST(SessionTaskMapTest, TwoSessionsOneInactive) {
sv.isAppSession = true;
sv.lastUpdatedTime = tNow;
sv.votes = std::make_shared<Votes>();
- sv.votes->add(22, VoteRange::makeMinRange(222, tNow, 400ms));
+ sv.votes->add(22, CpuVote(true, tNow, 400ms, 222, 1024));
EXPECT_TRUE(m.add(2001, sv, {10, 20, 30}));
}
diff --git a/power-libperfmgr/aidl/tests/UClampVoterTest.cpp b/power-libperfmgr/aidl/tests/UClampVoterTest.cpp
index 53418a4e..fa4d3c92 100644
--- a/power-libperfmgr/aidl/tests/UClampVoterTest.cpp
+++ b/power-libperfmgr/aidl/tests/UClampVoterTest.cpp
@@ -31,46 +31,42 @@ using std::literals::chrono_literals::operator""ns;
TEST(VoteRange, active) {
auto tNow = std::chrono::steady_clock::now();
- VoteRange vr(true, 101, 202, tNow, 200ms);
+ VoteRange vr(true, tNow, 200ms);
EXPECT_TRUE(vr.active());
}
TEST(VoteRange, inActive) {
auto tNow = std::chrono::steady_clock::now();
- VoteRange vr(false, 101, 202, tNow, 200ms);
+ VoteRange vr(false, 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, defaultConstructorValues) {
+ UclampRange uclamp_range;
+ EXPECT_EQ(0, uclamp_range.uclampMin);
+ EXPECT_EQ(1024, uclamp_range.uclampMax);
}
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));
+ VoteRange vote_range(true, tNow, 250ms);
+ EXPECT_FALSE(vote_range.isTimeInRange(tNow - 1ns));
+ EXPECT_TRUE(vote_range.isTimeInRange(tNow));
+ EXPECT_TRUE(vote_range.isTimeInRange(tNow + 1ns));
+ EXPECT_TRUE(vote_range.isTimeInRange(tNow + 250ms));
+ EXPECT_FALSE(vote_range.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));
+ VoteRange vote_range(true, tNow, 250ms);
+ EXPECT_TRUE(vote_range.active());
+ vote_range.setActive(false);
+ EXPECT_FALSE(vote_range.active());
+ EXPECT_FALSE(vote_range.isTimeInRange(tNow));
+ EXPECT_FALSE(vote_range.isTimeInRange(tNow + 1ns));
+ EXPECT_FALSE(vote_range.isTimeInRange(tNow + 250ms));
+ EXPECT_FALSE(vote_range.isTimeInRange(tNow + 250ms + 1ns));
}
TEST(VoteRange, getUclampRange) {
@@ -79,13 +75,14 @@ TEST(VoteRange, getUclampRange) {
const auto tEnd1 = tNow + 4000000001ns;
const auto tPrev = tNow - 1s;
- const auto voteFirst = VoteRange::makeMinRange(11, tNow, 4000000000ns);
+ VoteRange voteFirst(true, tNow, 4000000000ns);
EXPECT_FALSE(voteFirst.isTimeInRange(tPrev));
EXPECT_TRUE(voteFirst.isTimeInRange(tNext));
EXPECT_FALSE(voteFirst.isTimeInRange(tEnd1));
Votes votes;
- votes.add(1, voteFirst);
+ votes.add(1,
+ CpuVote(voteFirst.active(), voteFirst.startTime(), voteFirst.durationNs(), 11, 1024));
UclampRange ur;
votes.getUclampRange(&ur, tNext);
@@ -99,10 +96,10 @@ TEST(UclampVoter, simple) {
auto votes = std::make_shared<Votes>();
EXPECT_EQ(0, votes->size());
- votes->add(1, VoteRange::makeMinRange(11, tNow, 4s));
+ votes->add(1, CpuVote(true, tNow, 4s, 11, 1024));
EXPECT_EQ(1, votes->size());
- votes->add(2, VoteRange::makeMinRange(22, tNow, 1s));
+ votes->add(2, CpuVote(true, tNow, 1s, 22, 1024));
EXPECT_EQ(2, votes->size());
UclampRange ur1;
@@ -136,17 +133,17 @@ TEST(UclampVoter, overwrite) {
auto votes = std::make_shared<Votes>();
EXPECT_EQ(0, votes->size());
- votes->add(1, VoteRange::makeMinRange(11, tNow, 4s));
+ votes->add(1, CpuVote(true, tNow, 4s, 11, 1024));
EXPECT_EQ(1, votes->size());
- votes->add(2, VoteRange::makeMinRange(22, tNow, 2s));
+ votes->add(2, CpuVote(true, tNow, 2s, 22, 1024));
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));
+ votes->add(1, CpuVote(true, tNow, 5s, 32, 1024));
UclampRange ucr2;
votes->getUclampRange(&ucr2, tNow + 1s);
EXPECT_EQ(32, ucr2.uclampMin);
@@ -158,8 +155,8 @@ TEST(UclampVoter, updateDuration) {
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));
+ votes->add(1, CpuVote(true, tNow, 4s, 11, 1024));
+ votes->add(2, CpuVote(true, tNow, 2s, 22, 1024));
EXPECT_EQ(2, votes->size());
EXPECT_TRUE(votes->allTimedOut(tNow + 7s));
@@ -180,14 +177,14 @@ TEST(UclampVoter, loadVoteTest) {
auto votes = std::make_shared<Votes>();
// Default: min = 50 (original)
- votes->add(defaultVoteId, VoteRange::makeMinRange(uclampMinDefault, tNow, 400ms));
+ votes->add(defaultVoteId, CpuVote(true, tNow, 400ms, uclampMinDefault, 1024));
votes->getUclampRange(&ucr, tNow + 100ms);
EXPECT_EQ(uclampMinDefault, ucr.uclampMin);
// Default: min = UclampMinInit
- votes->add(defaultVoteId, VoteRange::makeMinRange(uclampMinInit, tNow, 400ns));
+ votes->add(defaultVoteId, CpuVote(true, tNow, 400ns, uclampMinInit, 1024));
// Load: min = uclampMinHigh
- votes->add(loadVoteId, VoteRange::makeMinRange(uclampMinHigh, tNow, 250ns));
+ votes->add(loadVoteId, CpuVote(true, tNow, 250ns, uclampMinHigh, 1024));
// Check that load is enabled
ucr.uclampMin = 0;
@@ -201,6 +198,121 @@ TEST(UclampVoter, loadVoteTest) {
EXPECT_EQ(uclampMinInit, ucr.uclampMin);
}
+TEST(GpuCapacityVoter, testIncorrectTyping) {
+ const auto now = std::chrono::steady_clock::now();
+ Votes votes;
+ static constexpr int gpu_vote_id = static_cast<int>(AdpfHintType::ADPF_GPU_CAPACITY);
+ static constexpr int cpu_vote_id = static_cast<int>(AdpfHintType::ADPF_CPU_LOAD_UP);
+
+ votes.add(cpu_vote_id, GpuVote(true, now, 250ns, Cycles(1024)));
+ EXPECT_FALSE(votes.voteIsActive(cpu_vote_id));
+ EXPECT_FALSE(votes.setUseVote(cpu_vote_id, true));
+ EXPECT_FALSE(votes.remove(cpu_vote_id));
+
+ votes.add(gpu_vote_id, CpuVote(true, now, 250ns, 66, 77));
+ EXPECT_FALSE(votes.voteIsActive(gpu_vote_id));
+ EXPECT_FALSE(votes.setUseVote(cpu_vote_id, true));
+ EXPECT_FALSE(votes.remove(cpu_vote_id));
+
+ UclampRange range;
+ votes.getUclampRange(&range, now);
+ EXPECT_EQ(range.uclampMin, 0);
+ EXPECT_EQ(range.uclampMax, 1024);
+
+ EXPECT_FALSE(votes.getGpuCapacityRequest(now));
+}
+
+TEST(GpuCapacityVoter, testGpuUseVote) {
+ const auto now = std::chrono::steady_clock::now();
+ Votes votes;
+ static constexpr int gpu_vote_id1 = static_cast<int>(AdpfHintType::ADPF_GPU_CAPACITY);
+ static constexpr int gpu_vote_id2 = static_cast<int>(AdpfHintType::ADPF_GPU_LOAD_UP);
+
+ votes.add(gpu_vote_id1, GpuVote(true, now, 250ns, Cycles(1024)));
+ EXPECT_TRUE(votes.setUseVote(gpu_vote_id1, true));
+ EXPECT_FALSE(votes.setUseVote(gpu_vote_id2, true));
+}
+
+TEST(GpuCapacityVoter, testBasicVoteActivation) {
+ auto const now = std::chrono::steady_clock::now();
+ auto const timeout = 1s;
+ auto const gpu_vote_id = static_cast<int>(AdpfHintType::ADPF_GPU_CAPACITY);
+ Votes votes;
+
+ votes.add(gpu_vote_id, GpuVote(true, now, 250ns, Cycles(100)));
+
+ EXPECT_EQ(votes.size(), 1);
+ EXPECT_TRUE(votes.voteIsActive(gpu_vote_id));
+
+ votes.setUseVote(gpu_vote_id, false);
+ EXPECT_FALSE(votes.voteIsActive(gpu_vote_id));
+
+ votes.setUseVote(gpu_vote_id, true);
+ EXPECT_TRUE(votes.voteIsActive(gpu_vote_id));
+
+ EXPECT_TRUE(votes.remove(gpu_vote_id));
+}
+
+TEST(GpuCapacityVoter, testBasicVoteTimeouts) {
+ auto const now = std::chrono::steady_clock::now();
+ auto const timeout = 1s;
+ auto const gpu_vote_id = static_cast<int>(AdpfHintType::ADPF_GPU_CAPACITY);
+ Cycles const cycles(100);
+
+ Votes votes;
+ votes.add(gpu_vote_id, GpuVote(true, now, timeout, cycles));
+
+ auto capacity = votes.getGpuCapacityRequest(now + 1ns);
+ ASSERT_TRUE(capacity);
+ EXPECT_EQ(*capacity, cycles);
+
+ auto capacity2 = votes.getGpuCapacityRequest(now + 2 * timeout);
+ EXPECT_FALSE(capacity2);
+}
+
+TEST(GpuCapacityVoter, testVoteTimeouts) {
+ auto const now = std::chrono::steady_clock::now();
+ auto const timeout = 1s;
+ auto const timeout2 = 10s;
+ auto const gpu_vote_id = static_cast<int>(AdpfHintType::ADPF_GPU_CAPACITY);
+ auto const cpu_vote_id = static_cast<int>(AdpfHintType::ADPF_CPU_LOAD_UP);
+ Cycles const cycles(100);
+
+ Votes votes;
+ votes.add(gpu_vote_id, GpuVote(true, now, timeout, cycles));
+ votes.add(cpu_vote_id, CpuVote(true, now, timeout2, 66, 88));
+
+ EXPECT_EQ(votes.size(), 2);
+ EXPECT_EQ(votes.voteTimeout(gpu_vote_id), now + timeout);
+
+ EXPECT_FALSE(votes.allTimedOut(now + std::chrono::microseconds(56)));
+ EXPECT_FALSE(votes.anyTimedOut(now + std::chrono::microseconds(56)));
+ EXPECT_FALSE(votes.allTimedOut(now + 2 * timeout));
+ EXPECT_TRUE(votes.anyTimedOut(now + 2 * timeout));
+ EXPECT_TRUE(votes.allTimedOut(now + 20 * timeout));
+ EXPECT_TRUE(votes.anyTimedOut(now + 20 * timeout));
+}
+
+TEST(GpuCapacityVoter, testGpuVoteActive) {
+ auto const now = std::chrono::steady_clock::now();
+ auto const timeout = 1s;
+ auto const gpu_vote_id = static_cast<int>(AdpfHintType::ADPF_GPU_CAPACITY);
+ Cycles const cycles(100);
+
+ Votes votes;
+ votes.add(gpu_vote_id, GpuVote(true, now, timeout, cycles));
+
+ EXPECT_TRUE(votes.voteIsActive(gpu_vote_id));
+ auto const gpu_capacity_request = votes.getGpuCapacityRequest(now);
+ ASSERT_TRUE(gpu_capacity_request);
+ EXPECT_EQ(*gpu_capacity_request, cycles);
+ EXPECT_TRUE(votes.setUseVote(gpu_vote_id, false));
+ ASSERT_FALSE(votes.getGpuCapacityRequest(now));
+
+ EXPECT_FALSE(votes.voteIsActive(gpu_vote_id));
+
+ EXPECT_EQ(votes.size(), 1);
+}
} // namespace pixel
} // namespace impl
} // namespace power
diff --git a/powerstats/aidl/android/vendor/powerstats/IPixelStateResidencyProvider.aidl b/powerstats/aidl/android/vendor/powerstats/IPixelStateResidencyProvider.aidl
index f2c0faf2..4d73faf4 100644
--- a/powerstats/aidl/android/vendor/powerstats/IPixelStateResidencyProvider.aidl
+++ b/powerstats/aidl/android/vendor/powerstats/IPixelStateResidencyProvider.aidl
@@ -16,10 +16,15 @@
package android.vendor.powerstats;
+import android.hardware.power.stats.State;
import android.vendor.powerstats.IPixelStateResidencyCallback;
interface IPixelStateResidencyProvider
{
void registerCallback(in @utf8InCpp String entityName, in IPixelStateResidencyCallback cb);
+ void registerCallbackByStates(
+ in @utf8InCpp String entityName,
+ in IPixelStateResidencyCallback cb,
+ in State[] states);
void unregisterCallback(in IPixelStateResidencyCallback cb);
}
diff --git a/powerstats/dataproviders/PixelStateResidencyDataProvider.cpp b/powerstats/dataproviders/PixelStateResidencyDataProvider.cpp
index ce1951e6..7ca69418 100644
--- a/powerstats/dataproviders/PixelStateResidencyDataProvider.cpp
+++ b/powerstats/dataproviders/PixelStateResidencyDataProvider.cpp
@@ -107,9 +107,10 @@ std::unordered_map<std::string, std::vector<State>> PixelStateResidencyDataProvi
return ret;
}
-::ndk::ScopedAStatus PixelStateResidencyDataProvider::registerCallback(
+::ndk::ScopedAStatus PixelStateResidencyDataProvider::registerCallbackByStates(
const std::string &in_entityName,
- const std::shared_ptr<IPixelStateResidencyCallback> &in_cb) {
+ const std::shared_ptr<IPixelStateResidencyCallback> &in_cb,
+ const std::vector<State> &in_states) {
std::lock_guard<std::mutex> lock(mLock);
if (!in_cb) {
@@ -127,10 +128,20 @@ std::unordered_map<std::string, std::vector<State>> PixelStateResidencyDataProvi
toRegister->mCallback = in_cb;
+ if (!in_states.empty()) {
+ toRegister->mStates = std::move(in_states);
+ }
+
LOG(INFO) << __func__ << ": Registered " << in_entityName;
return ::ndk::ScopedAStatus::ok();
}
+::ndk::ScopedAStatus PixelStateResidencyDataProvider::registerCallback(
+ const std::string &in_entityName,
+ const std::shared_ptr<IPixelStateResidencyCallback> &in_cb) {
+ return registerCallbackByStates(in_entityName, in_cb, {});
+}
+
::ndk::ScopedAStatus PixelStateResidencyDataProvider::unregisterCallback(
const std::shared_ptr<IPixelStateResidencyCallback> &in_cb) {
std::lock_guard<std::mutex> lock(mLock);
diff --git a/powerstats/include/dataproviders/PixelStateResidencyDataProvider.h b/powerstats/include/dataproviders/PixelStateResidencyDataProvider.h
index 6352c1fe..daa03d28 100644
--- a/powerstats/include/dataproviders/PixelStateResidencyDataProvider.h
+++ b/powerstats/include/dataproviders/PixelStateResidencyDataProvider.h
@@ -54,6 +54,13 @@ class PixelStateResidencyDataProvider : public PowerStats::IStateResidencyDataPr
return mEnclosed->registerCallback(in_entityName, in_cb);
}
+ ::ndk::ScopedAStatus registerCallbackByStates(
+ const std::string &in_entityName,
+ const std::shared_ptr<IPixelStateResidencyCallback> &in_cb,
+ const std::vector<State> &in_states) override {
+ return mEnclosed->registerCallbackByStates(in_entityName, in_cb, in_states);
+ }
+
::ndk::ScopedAStatus unregisterCallback(
const std::shared_ptr<IPixelStateResidencyCallback> &in_cb) override {
return mEnclosed->unregisterCallback(in_cb);
@@ -75,6 +82,11 @@ class PixelStateResidencyDataProvider : public PowerStats::IStateResidencyDataPr
const std::string &in_entityName,
const std::shared_ptr<IPixelStateResidencyCallback> &in_cb);
+ ::ndk::ScopedAStatus registerCallbackByStates(
+ const std::string &in_entityName,
+ const std::shared_ptr<IPixelStateResidencyCallback> &in_cb,
+ const std::vector<State> &in_states);
+
::ndk::ScopedAStatus unregisterCallback(
const std::shared_ptr<IPixelStateResidencyCallback> &in_cb);
diff --git a/presubmit_tests/schemas/Android.bp b/presubmit_tests/schemas/Android.bp
new file mode 100644
index 00000000..dc484e5e
--- /dev/null
+++ b/presubmit_tests/schemas/Android.bp
@@ -0,0 +1,17 @@
+cc_library_static {
+ name: "powerhint_schema",
+ host_supported: true,
+ srcs: [
+ "powerhint-schema.proto",
+ ],
+ proto: {
+ type: "full",
+ export_proto_headers: true,
+ include_dirs: ["external/protobuf/src"],
+ },
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wno-unused-parameter",
+ ],
+} \ No newline at end of file
diff --git a/presubmit_tests/schemas/powerhint-schema.proto b/presubmit_tests/schemas/powerhint-schema.proto
new file mode 100644
index 00000000..90c6fde3
--- /dev/null
+++ b/presubmit_tests/schemas/powerhint-schema.proto
@@ -0,0 +1,64 @@
+/*
+ * 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.
+ */
+ syntax = "proto3";
+ package devices;
+
+ message PowerhintConfig {
+ repeated Node nodes = 1 [ json_name = "Nodes" ];
+ repeated Action actions = 2 [ json_name = "Actions" ];
+ repeated AdpfConfig adpf_configs = 3 [ json_name = "AdpfConfig" ];
+ }
+ message Node {
+ string name = 1 [ json_name = "Name" ];
+ string path = 2 [ json_name = "Path" ];
+ repeated string values = 3 [ json_name = "Values" ];
+ bool reset_on_init = 4 [ json_name = "ResetOnInit" ];
+ int32 default_index = 5 [ json_name = "DefaultIndex" ];
+ string type = 6 [ json_name = "Type"];
+ bool hold_fd = 7 [ json_name = "HoldFd"];
+ bool write_only = 8 [ json_name = "WriteOnly" ];
+ }
+ message Action {
+ string powerhint = 1 [ json_name = "PowerHint" ];
+ string node = 2 [ json_name = "Node" ];
+ int32 duration = 3 [ json_name = "Duration" ];
+ string value = 4 [ json_name = "Value" ];
+ string type = 5 [ json_name = "Type"];
+ }
+ message AdpfConfig {
+ string name = 1 [ json_name = "Name" ];
+ bool pid_on = 2 [ json_name = "PID_On" ];
+ float pid_po = 3 [ json_name = "PID_Po" ];
+ float pid_pu = 4 [ json_name = "PID_Pu" ];
+ float pid_i = 5 [ json_name = "PID_I" ];
+ float pid_i_init = 6 [ json_name = "PID_I_Init" ];
+ float pid_i_high = 7 [ json_name = "PID_I_High" ];
+ float pid_i_low = 8 [ json_name = "PID_I_Low" ];
+ float pid_do = 9 [ json_name = "PID_Do" ];
+ float pid_du = 10 [ json_name = "PID_Du" ];
+ bool uclampmin_on = 11 [ json_name = "UclampMin_On" ];
+ float uclampmin_init = 12 [ json_name = "UclampMin_Init" ];
+ float uclampmin_high = 13 [ json_name = "UclampMin_High" ];
+ float uclampmin_low = 14 [ json_name = "UclampMin_Low" ];
+ float samplingwindow_p = 15 [ json_name = "SamplingWindow_P" ];
+ float samplingwindow_i = 16 [ json_name = "SamplingWindow_I" ];
+ float samplingwindow_d = 17 [ json_name = "SamplingWindow_D" ];
+ float reportingratelimitns = 18 [ json_name = "ReportingRateLimitNs" ];
+ bool earlyboost_on = 19 [ json_name = "EarlyBoost_On" ];
+ float earlyboost_timefactor = 20 [ json_name = "EarlyBoost_TimeFactor" ];
+ float targettimefactor = 21 [ json_name = "TargetTimeFactor" ];
+ float staletimefactor = 22 [ json_name = "StaleTimeFactor" ];
+} \ No newline at end of file
diff --git a/thermal/Thermal.cpp b/thermal/Thermal.cpp
index 13cdcaa9..d07d1268 100644
--- a/thermal/Thermal.cpp
+++ b/thermal/Thermal.cpp
@@ -241,6 +241,72 @@ void Thermal::sendThermalChangedCallback(const Temperature &t) {
callbacks_.end());
}
+ndk::ScopedAStatus Thermal::registerCoolingDeviceChangedCallbackWithType(
+ const std::shared_ptr<ICoolingDeviceChangedCallback> &callback, CoolingType type) {
+ ATRACE_CALL();
+
+ if (callback == nullptr) {
+ return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT,
+ "Invalid nullptr callback");
+ }
+
+ if (!thermal_helper_->isInitializedOk()) {
+ return initErrorStatus();
+ }
+
+ std::lock_guard<std::mutex> _lock(cdev_callback_mutex_);
+ if (std::any_of(cdev_callbacks_.begin(), cdev_callbacks_.end(),
+ [&](const CoolingDeviceCallbackSetting &c) {
+ return interfacesEqual(c.callback, callback);
+ })) {
+ return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT,
+ "Callback already registered");
+ }
+ cdev_callbacks_.emplace_back(callback, true, type);
+
+ // b/315858553 to develope the callback
+ LOG(INFO) << __func__ << ":" << toString(type) << " is under development";
+
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Thermal::unregisterCoolingDeviceChangedCallback(
+ const std::shared_ptr<ICoolingDeviceChangedCallback> &callback) {
+ ATRACE_CALL();
+
+ if (callback == nullptr) {
+ return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT,
+ "Invalid nullptr callback");
+ }
+
+ bool removed = false;
+ std::lock_guard<std::mutex> _lock(cdev_callback_mutex_);
+ cdev_callbacks_.erase(
+ std::remove_if(
+ cdev_callbacks_.begin(), cdev_callbacks_.end(),
+ [&](const CoolingDeviceCallbackSetting &c) {
+ if (interfacesEqual(c.callback, callback)) {
+ LOG(INFO)
+ << "a callback has been unregistered to ThermalHAL, isFilter: "
+ << c.is_filter_type << " Type: " << toString(c.type);
+ removed = true;
+ return true;
+ }
+ return false;
+ }),
+ cdev_callbacks_.end());
+
+ if (!removed) {
+ return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT,
+ "Callback wasn't registered");
+ }
+
+ // b/315858553 to develope the callback
+ LOG(INFO) << __func__ << " is under development";
+
+ return ndk::ScopedAStatus::ok();
+}
+
void Thermal::dumpVirtualSensorInfo(std::ostringstream *dump_buf) {
*dump_buf << "getVirtualSensorInfo:" << std::endl;
const auto &map = thermal_helper_->GetSensorInfoMap();
diff --git a/thermal/Thermal.h b/thermal/Thermal.h
index f8f9c63a..8c940718 100644
--- a/thermal/Thermal.h
+++ b/thermal/Thermal.h
@@ -38,6 +38,15 @@ struct CallbackSetting {
TemperatureType type;
};
+struct CoolingDeviceCallbackSetting {
+ CoolingDeviceCallbackSetting(std::shared_ptr<ICoolingDeviceChangedCallback> callback,
+ bool is_filter_type, CoolingType type)
+ : callback(std::move(callback)), is_filter_type(is_filter_type), type(type) {}
+ std::shared_ptr<ICoolingDeviceChangedCallback> callback;
+ bool is_filter_type;
+ CoolingType type;
+};
+
class Thermal : public BnThermal {
public:
Thermal();
@@ -47,10 +56,6 @@ class Thermal : public BnThermal {
ndk::ScopedAStatus getTemperaturesWithType(TemperatureType type,
std::vector<Temperature> *_aidl_return) override;
- ndk::ScopedAStatus getCoolingDevices(std::vector<CoolingDevice> *_aidl_return) override;
- ndk::ScopedAStatus getCoolingDevicesWithType(CoolingType type,
- std::vector<CoolingDevice> *_aidl_return) override;
-
ndk::ScopedAStatus getTemperatureThresholds(
std::vector<TemperatureThreshold> *_aidl_return) override;
ndk::ScopedAStatus getTemperatureThresholdsWithType(
@@ -63,6 +68,17 @@ class Thermal : public BnThermal {
TemperatureType type) override;
ndk::ScopedAStatus unregisterThermalChangedCallback(
const std::shared_ptr<IThermalChangedCallback> &callback) override;
+
+ ndk::ScopedAStatus getCoolingDevices(std::vector<CoolingDevice> *_aidl_return) override;
+ ndk::ScopedAStatus getCoolingDevicesWithType(CoolingType type,
+ std::vector<CoolingDevice> *_aidl_return) override;
+
+ ndk::ScopedAStatus registerCoolingDeviceChangedCallbackWithType(
+ const std::shared_ptr<ICoolingDeviceChangedCallback> &callback,
+ CoolingType type) override;
+ ndk::ScopedAStatus unregisterCoolingDeviceChangedCallback(
+ const std::shared_ptr<ICoolingDeviceChangedCallback> &callback) override;
+
binder_status_t dump(int fd, const char **args, uint32_t numArgs) override;
// Helper function for calling callbacks
@@ -95,6 +111,9 @@ class Thermal : public BnThermal {
std::shared_ptr<ThermalHelper> thermal_helper_;
std::mutex thermal_callback_mutex_;
std::vector<CallbackSetting> callbacks_;
+ std::mutex cdev_callback_mutex_;
+ std::vector<CoolingDeviceCallbackSetting> cdev_callbacks_;
+
Looper looper_;
ndk::ScopedAStatus getFilteredTemperatures(bool filterType, TemperatureType type,
diff --git a/thermal/thermal-helper.cpp b/thermal/thermal-helper.cpp
index 61de8bcc..51f23bec 100644
--- a/thermal/thermal-helper.cpp
+++ b/thermal/thermal-helper.cpp
@@ -418,7 +418,7 @@ bool ThermalHelperImpl::readTemperature(
std::pair<ThrottlingSeverity, ThrottlingSeverity> *throttling_status,
const bool force_no_cache) {
// Return fail if the thermal sensor cannot be read.
- float temp;
+ float temp = NAN;
std::map<std::string, float> sensor_log_map;
auto &sensor_status = sensor_status_map_.at(sensor_name.data());
@@ -770,7 +770,7 @@ void ThermalHelperImpl::initializeTrip(const std::unordered_map<std::string, std
if (!std::isnan(sensor_info.second.hot_thresholds[i]) &&
!std::isnan(sensor_info.second.hot_hysteresis[i])) {
// Update trip_point_0_temp threshold
- std::string threshold = std::to_string(static_cast<int>(
+ std::string threshold = std::to_string(std::lround(
sensor_info.second.hot_thresholds[i] / sensor_info.second.multiplier));
path = ::android::base::StringPrintf("%s/%s", (tz_path.data()),
kSensorTripPointTempZeroFile.data());
@@ -781,8 +781,8 @@ void ThermalHelperImpl::initializeTrip(const std::unordered_map<std::string, std
break;
}
// Update trip_point_0_hyst threshold
- threshold = std::to_string(static_cast<int>(
- sensor_info.second.hot_hysteresis[i] / sensor_info.second.multiplier));
+ threshold = std::to_string(std::lround(sensor_info.second.hot_hysteresis[i] /
+ sensor_info.second.multiplier));
path = ::android::base::StringPrintf("%s/%s", (tz_path.data()),
kSensorTripPointHystZeroFile.data());
if (!::android::base::WriteStringToFile(threshold, path)) {
@@ -910,7 +910,6 @@ 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());
@@ -933,7 +932,7 @@ float ThermalHelperImpl::runVirtualTempEstimator(std::string_view sensor_name,
if ((*sensor_log_map).count(linked_sensor.data())) {
float value = (*sensor_log_map)[linked_sensor.data()];
- model_inputs.push_back(value / kCelsius2mC);
+ model_inputs.push_back(value);
} else {
LOG(ERROR) << "failed to read sensor: " << linked_sensor;
return NAN;
@@ -947,7 +946,7 @@ float ThermalHelperImpl::runVirtualTempEstimator(std::string_view sensor_name,
return NAN;
}
- return (estimated_vt * kCelsius2mC);
+ return estimated_vt;
}
constexpr int kTranTimeoutParam = 2;
@@ -971,6 +970,7 @@ bool ThermalHelperImpl::readThermalSensor(std::string_view sensor_name, float *t
std::shared_lock<std::shared_mutex> _lock(sensor_status_map_mutex_);
if (sensor_status.override_status.emul_temp != nullptr) {
*temp = sensor_status.override_status.emul_temp->temp;
+ (*sensor_log_map)[sensor_name.data()] = *temp;
return true;
}
}
@@ -999,7 +999,7 @@ bool ThermalHelperImpl::readThermalSensor(std::string_view sensor_name, float *t
*temp = std::stof(::android::base::Trim(file_reading));
} else {
const auto &linked_sensors_size = sensor_info.virtual_sensor_info->linked_sensors.size();
- std::vector<float> sensor_readings(linked_sensors_size, 0.0);
+ std::vector<float> sensor_readings(linked_sensors_size, NAN);
// Calculate temperature of each of the linked sensor
for (size_t i = 0; i < linked_sensors_size; i++) {
@@ -1017,7 +1017,8 @@ bool ThermalHelperImpl::readThermalSensor(std::string_view sensor_name, float *t
}
}
- if (sensor_info.virtual_sensor_info->formula == FormulaOption::USE_ML_MODEL) {
+ if ((sensor_info.virtual_sensor_info->formula == FormulaOption::USE_ML_MODEL) ||
+ (sensor_info.virtual_sensor_info->formula == FormulaOption::USE_LINEAR_MODEL)) {
*temp = runVirtualTempEstimator(sensor_name, sensor_log_map);
if (std::isnan(*temp)) {
@@ -1027,7 +1028,7 @@ bool ThermalHelperImpl::readThermalSensor(std::string_view sensor_name, float *t
} else {
float temp_val = 0.0;
for (size_t i = 0; i < linked_sensors_size; i++) {
- float coefficient = 0.0;
+ float coefficient = NAN;
if (!readDataByType(sensor_info.virtual_sensor_info->coefficients[i], &coefficient,
sensor_info.virtual_sensor_info->coefficients_type[i],
force_no_cache, sensor_log_map)) {
diff --git a/thermal/utils/power_files.cpp b/thermal/utils/power_files.cpp
index 2e4a77ff..5560f0a7 100644
--- a/thermal/utils/power_files.cpp
+++ b/thermal/utils/power_files.cpp
@@ -45,10 +45,10 @@ bool calculateAvgPower(std::string_view power_rail, const PowerSample &last_samp
*avg_power = NAN;
const auto duration = curr_sample.duration - last_sample.duration;
const auto deltaEnergy = curr_sample.energy_counter - last_sample.energy_counter;
- if (!last_sample.duration) {
+ if (duration == 0) {
LOG(VERBOSE) << "Power rail " << power_rail.data()
- << ": all power samples have not been collected yet";
- } else if (duration <= 0 || deltaEnergy < 0) {
+ << ": has not collected min 2 samples yet";
+ } else if (duration < 0 || deltaEnergy < 0) {
LOG(ERROR) << "Power rail " << power_rail.data() << " is invalid: duration = " << duration
<< ", deltaEnergy = " << deltaEnergy;
return false;
@@ -89,33 +89,30 @@ bool PowerFiles::registerPowerRailsToWatch(const Json::Value &config) {
continue;
}
- PowerSample power_sample = {
- .energy_counter = 0,
- .duration = 0,
- };
-
if (power_rail_info_pair.second.virtual_power_rail_info != nullptr &&
power_rail_info_pair.second.virtual_power_rail_info->linked_power_rails.size()) {
for (size_t i = 0;
i < power_rail_info_pair.second.virtual_power_rail_info->linked_power_rails.size();
++i) {
- if (!energy_info_map_.count(power_rail_info_pair.second.virtual_power_rail_info
- ->linked_power_rails[i])) {
- LOG(ERROR) << " Could not find energy source "
- << power_rail_info_pair.second.virtual_power_rail_info
- ->linked_power_rails[i];
+ std::string power_rail =
+ power_rail_info_pair.second.virtual_power_rail_info->linked_power_rails[i];
+ if (!energy_info_map_.count(power_rail)) {
+ LOG(ERROR) << " Could not find energy source " << power_rail;
return false;
}
+
+ const auto curr_sample = energy_info_map_.at(power_rail);
power_history.emplace_back(std::queue<PowerSample>());
for (int j = 0; j < power_rail_info_pair.second.power_sample_count; j++) {
- power_history[i].emplace(power_sample);
+ power_history[i].emplace(curr_sample);
}
}
} else {
if (energy_info_map_.count(power_rail_info_pair.first)) {
+ const auto curr_sample = energy_info_map_.at(power_rail_info_pair.first);
power_history.emplace_back(std::queue<PowerSample>());
for (int j = 0; j < power_rail_info_pair.second.power_sample_count; j++) {
- power_history[0].emplace(power_sample);
+ power_history[0].emplace(curr_sample);
}
} else {
LOG(ERROR) << "Could not find energy source " << power_rail_info_pair.first;
@@ -357,7 +354,8 @@ void PowerFiles::logPowerStatus(const boot_clock::time_point &now) {
const auto &last_sample = power_status_log_.prev_energy_info_map.at(rail);
const auto &curr_sample = energy_info_pair.second;
float avg_power = NAN;
- if (calculateAvgPower(rail, last_sample, curr_sample, &avg_power) && avg_power != NAN) {
+ if (calculateAvgPower(rail, last_sample, curr_sample, &avg_power) &&
+ !std::isnan(avg_power)) {
// start of new line
if (power_rail_log_cnt % kMaxPowerLogPerLine == 0) {
if (power_rail_log_cnt != 0) {
diff --git a/thermal/utils/thermal_info.cpp b/thermal/utils/thermal_info.cpp
index d03b6d87..122d4b52 100644
--- a/thermal/utils/thermal_info.cpp
+++ b/thermal/utils/thermal_info.cpp
@@ -257,6 +257,8 @@ bool ParseVirtualSensorInfo(const std::string_view name, const Json::Value &sens
formula = FormulaOption::MINIMUM;
} else if (sensor["Formula"].asString().compare("USE_ML_MODEL") == 0) {
formula = FormulaOption::USE_ML_MODEL;
+ } else if (sensor["Formula"].asString().compare("USE_LINEAR_MODEL") == 0) {
+ formula = FormulaOption::USE_LINEAR_MODEL;
} else {
LOG(ERROR) << "Sensor[" << name << "]'s Formula is invalid";
return false;
@@ -296,12 +298,12 @@ bool ParseVirtualSensorInfo(const std::string_view name, const Json::Value &sens
coefficients.emplace_back(values[j].asString());
LOG(INFO) << "Sensor[" << name << "]'s coefficient[" << j << "]: " << coefficients[j];
}
- } else {
+ } else if ((formula != FormulaOption::USE_ML_MODEL)) {
LOG(ERROR) << "Sensor[" << name << "] has no Coefficient setting";
return false;
}
if ((linked_sensors.size() != coefficients.size()) &&
- (formula != FormulaOption::USE_ML_MODEL)) {
+ (formula != FormulaOption::USE_ML_MODEL) && (formula != FormulaOption::USE_LINEAR_MODEL)) {
LOG(ERROR) << "Sensor[" << name << "] has invalid Coefficient size";
return false;
}
@@ -366,6 +368,7 @@ bool ParseVirtualSensorInfo(const std::string_view name, const Json::Value &sens
}
if (formula == FormulaOption::USE_ML_MODEL) {
+ ::thermal::vtestimator::VtEstimationInitData init_data(::thermal::vtestimator::kUseMLModel);
if (sensor["ModelPath"].empty()) {
LOG(ERROR) << "Sensor[" << name << "] has no ModelPath";
return false;
@@ -377,7 +380,7 @@ bool ParseVirtualSensorInfo(const std::string_view name, const Json::Value &sens
}
vt_estimator = std::make_unique<::thermal::vtestimator::VirtualTempEstimator>(
- linked_sensors.size());
+ ::thermal::vtestimator::kUseMLModel, linked_sensors.size());
if (!vt_estimator) {
LOG(ERROR) << "Failed to create vt estimator for Sensor[" << name
<< "] with linked sensor size : " << linked_sensors.size();
@@ -385,9 +388,29 @@ bool ParseVirtualSensorInfo(const std::string_view name, const Json::Value &sens
}
vt_estimator_model_file = "vendor/etc/" + sensor["ModelPath"].asString();
+ init_data.ml_model_init_data.model_path = vt_estimator_model_file;
+ init_data.ml_model_init_data.offset = offset;
+
+ if (!sensor["PreviousSampleCount"].empty()) {
+ init_data.ml_model_init_data.use_prev_samples = true;
+ init_data.ml_model_init_data.prev_samples_order = sensor["PreviousSampleCount"].asInt();
+ LOG(INFO) << "Sensor[" << name << "] takes "
+ << init_data.ml_model_init_data.prev_samples_order << " historic samples";
+ }
+
+ if (!sensor["OutputLabelCount"].empty()) {
+ init_data.ml_model_init_data.output_label_count = sensor["OutputLabelCount"].asInt();
+ LOG(INFO) << "Sensor[" << name << "] outputs "
+ << init_data.ml_model_init_data.output_label_count << " labels";
+ }
- ::thermal::vtestimator::VtEstimatorStatus ret =
- vt_estimator->Initialize(vt_estimator_model_file.c_str());
+ if (!sensor["PredictHotSpotCount"].empty()) {
+ init_data.ml_model_init_data.num_hot_spots = sensor["PredictHotSpotCount"].asInt();
+ LOG(INFO) << "Sensor[" << name << "] predicts temperature at "
+ << init_data.ml_model_init_data.num_hot_spots << " hot spots";
+ }
+
+ ::thermal::vtestimator::VtEstimatorStatus ret = vt_estimator->Initialize(init_data);
if (ret != ::thermal::vtestimator::kVtEstimatorOk) {
LOG(ERROR) << "Failed to initialize vt estimator for Sensor[" << name
<< "] with ModelPath: " << vt_estimator_model_file
@@ -397,6 +420,52 @@ bool ParseVirtualSensorInfo(const std::string_view name, const Json::Value &sens
LOG(INFO) << "Successfully created vt_estimator for Sensor[" << name
<< "] with input samples: " << linked_sensors.size();
+
+ } else if (formula == FormulaOption::USE_LINEAR_MODEL) {
+ ::thermal::vtestimator::VtEstimationInitData init_data(
+ ::thermal::vtestimator::kUseLinearModel);
+
+ if ((!linked_sensors.size()) || (linked_sensors.size() > coefficients.size())) {
+ LOG(ERROR) << "Sensor[" << name
+ << "] uses USE_LINEAR_MODEL and has invalid linked_sensors size["
+ << linked_sensors.size() << "] or coefficients size[" << coefficients.size()
+ << "]";
+ return false;
+ }
+
+ vt_estimator = std::make_unique<::thermal::vtestimator::VirtualTempEstimator>(
+ ::thermal::vtestimator::kUseLinearModel, 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;
+ }
+
+ init_data.linear_model_init_data.prev_samples_order =
+ coefficients.size() / linked_sensors.size();
+ init_data.linear_model_init_data.offset = offset;
+
+ for (size_t i = 0; i < coefficients.size(); ++i) {
+ float coefficient = getFloatFromValue(coefficients[i]);
+ if (std::isnan(coefficient)) {
+ LOG(ERROR) << "Nan coefficient unexpected for sensor " << name;
+ return false;
+ }
+ init_data.linear_model_init_data.coefficients.emplace_back(coefficient);
+ }
+ if (coefficients.size() > linked_sensors.size()) {
+ init_data.linear_model_init_data.use_prev_samples = true;
+ }
+
+ ::thermal::vtestimator::VtEstimatorStatus ret = vt_estimator->Initialize(init_data);
+ if (ret != ::thermal::vtestimator::kVtEstimatorOk) {
+ LOG(ERROR) << "Failed to initialize vt estimator for Sensor[" << name
+ << "] 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{
@@ -958,7 +1027,11 @@ bool ParseSensorInfo(const Json::Value &config,
vr_threshold = getFloatFromValue(sensors[i]["VrThreshold"]);
LOG(INFO) << "Sensor[" << name << "]'s VrThreshold: " << vr_threshold;
}
- float multiplier = sensors[i]["Multiplier"].asFloat();
+
+ float multiplier = 1.0;
+ if (!sensors[i]["Multiplier"].empty()) {
+ multiplier = sensors[i]["Multiplier"].asFloat();
+ }
LOG(INFO) << "Sensor[" << name << "]'s Multiplier: " << multiplier;
std::chrono::milliseconds polling_delay = kUeventPollTimeoutMs;
diff --git a/thermal/utils/thermal_info.h b/thermal/utils/thermal_info.h
index 843424e6..50477d74 100644
--- a/thermal/utils/thermal_info.h
+++ b/thermal/utils/thermal_info.h
@@ -55,7 +55,8 @@ enum class FormulaOption : uint32_t {
WEIGHTED_AVG,
MAXIMUM,
MINIMUM,
- USE_ML_MODEL
+ USE_ML_MODEL,
+ USE_LINEAR_MODEL
};
template <typename T>
diff --git a/thermal/virtualtemp_estimator/virtualtemp_estimator.cpp b/thermal/virtualtemp_estimator/virtualtemp_estimator.cpp
index 2dc2185c..e5374714 100644
--- a/thermal/virtualtemp_estimator/virtualtemp_estimator.cpp
+++ b/thermal/virtualtemp_estimator/virtualtemp_estimator.cpp
@@ -24,12 +24,12 @@ namespace thermal {
namespace vtestimator {
void VirtualTempEstimator::LoadTFLiteWrapper() {
- if (!data_) {
- LOG(ERROR) << "data_ is nullptr during LoadTFLiteWrapper";
+ if (!tflite_instance_) {
+ LOG(ERROR) << "tflite_instance_ is nullptr during LoadTFLiteWrapper";
return;
}
- std::unique_lock<std::mutex> lock(data_->tflite_methods.mutex_);
+ std::unique_lock<std::mutex> lock(tflite_instance_->tflite_methods.mutex);
void *mLibHandle = dlopen("/vendor/lib64/libthermal_tflite_wrapper.so", 0);
if (mLibHandle == nullptr) {
@@ -37,122 +37,335 @@ void VirtualTempEstimator::LoadTFLiteWrapper() {
return;
}
- data_->tflite_methods.create =
+ tflite_instance_->tflite_methods.create =
reinterpret_cast<tflitewrapper_create>(dlsym(mLibHandle, "Create"));
- if (!data_->tflite_methods.create) {
+ if (!tflite_instance_->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) {
+ tflite_instance_->tflite_methods.init =
+ reinterpret_cast<tflitewrapper_init>(dlsym(mLibHandle, "Init"));
+ if (!tflite_instance_->tflite_methods.init) {
LOG(ERROR) << "Could not link and cast tflitewrapper_init with error: " << dlerror();
}
- data_->tflite_methods.invoke =
+ tflite_instance_->tflite_methods.invoke =
reinterpret_cast<tflitewrapper_invoke>(dlsym(mLibHandle, "Invoke"));
- if (!data_->tflite_methods.invoke) {
+ if (!tflite_instance_->tflite_methods.invoke) {
LOG(ERROR) << "Could not link and cast tflitewrapper_invoke with error: " << dlerror();
}
- data_->tflite_methods.destroy =
+ tflite_instance_->tflite_methods.destroy =
reinterpret_cast<tflitewrapper_destroy>(dlsym(mLibHandle, "Destroy"));
- if (!data_->tflite_methods.destroy) {
+ if (!tflite_instance_->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(VtEstimationType estimationType,
+ size_t num_linked_sensors) {
+ type = estimationType;
+
+ common_instance_ = std::make_unique<VtEstimatorCommonData>(num_linked_sensors);
+ if (estimationType == kUseMLModel) {
+ tflite_instance_ = std::make_unique<VtEstimatorTFLiteData>();
+ LoadTFLiteWrapper();
+ } else if (estimationType == kUseLinearModel) {
+ linear_model_instance_ = std::make_unique<VtEstimatorLinearModelData>();
+ } else {
+ LOG(ERROR) << "Unsupported estimationType [" << estimationType << "]";
+ }
}
VirtualTempEstimator::~VirtualTempEstimator() {
LOG(INFO) << "VirtualTempEstimator destructor";
}
-VtEstimatorStatus VirtualTempEstimator::Initialize(const char *model_path) {
- LOG(INFO) << "Initialize VirtualTempEstimator\n";
+VtEstimatorStatus VirtualTempEstimator::LinearModelInitialize(LinearModelInitData data) {
+ if (linear_model_instance_ == nullptr || common_instance_ == nullptr) {
+ LOG(ERROR) << "linear_model_instance_ or common_instance_ is nullptr during Initialize";
+ return kVtEstimatorInitFailed;
+ }
+
+ size_t num_linked_sensors = common_instance_->num_linked_sensors;
+ std::unique_lock<std::mutex> lock(linear_model_instance_->mutex);
- if (!data_) {
- LOG(ERROR) << "data_ is nullptr during Initialize\n";
+ if ((num_linked_sensors == 0) || (data.coefficients.size() == 0) ||
+ (data.prev_samples_order == 0)) {
+ LOG(ERROR) << "Invalid num_linked_sensors [" << num_linked_sensors
+ << "] or coefficients.size() [" << data.coefficients.size()
+ << "] or prev_samples_order [" << data.prev_samples_order << "]";
return kVtEstimatorInitFailed;
}
- std::unique_lock<std::mutex> lock(data_->tflite_methods.mutex_);
+ if (data.coefficients.size() != (num_linked_sensors * data.prev_samples_order)) {
+ LOG(ERROR) << "In valid args coefficients.size()[" << data.coefficients.size()
+ << "] num_linked_sensors [" << num_linked_sensors << "] prev_samples_order["
+ << data.prev_samples_order << "]";
+ return kVtEstimatorInvalidArgs;
+ }
+
+ common_instance_->use_prev_samples = data.use_prev_samples;
+ common_instance_->prev_samples_order = data.prev_samples_order;
+
+ linear_model_instance_->input_samples.reserve(common_instance_->prev_samples_order);
+ linear_model_instance_->coefficients.reserve(common_instance_->prev_samples_order);
- if (!model_path) {
+ // Store coefficients
+ for (size_t i = 0; i < data.prev_samples_order; ++i) {
+ std::vector<float> single_order_coefficients;
+ for (size_t j = 0; j < num_linked_sensors; ++j) {
+ single_order_coefficients.emplace_back(data.coefficients[i * num_linked_sensors + j]);
+ }
+ linear_model_instance_->coefficients.emplace_back(single_order_coefficients);
+ }
+
+ common_instance_->cur_sample_index = 0;
+ common_instance_->offset = data.offset;
+ common_instance_->is_initialized = true;
+
+ return kVtEstimatorOk;
+}
+
+VtEstimatorStatus VirtualTempEstimator::TFliteInitialize(MLModelInitData data) {
+ if (!tflite_instance_ || !common_instance_) {
+ LOG(ERROR) << "tflite_instance_ or common_instance_ is nullptr during Initialize\n";
+ return kVtEstimatorInitFailed;
+ }
+
+ std::string model_path = data.model_path;
+ size_t num_linked_sensors = common_instance_->num_linked_sensors;
+ bool use_prev_samples = data.use_prev_samples;
+ size_t prev_samples_order = data.prev_samples_order;
+ size_t num_hot_spots = data.num_hot_spots;
+ size_t output_label_count = data.output_label_count;
+
+ std::unique_lock<std::mutex> lock(tflite_instance_->tflite_methods.mutex);
+
+ if (model_path.empty()) {
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;
+ if (num_linked_sensors == 0 || prev_samples_order < 1 ||
+ (!use_prev_samples && prev_samples_order > 1)) {
+ LOG(ERROR) << "Invalid tflite_instance_ config: "
+ << "number of linked sensor: " << num_linked_sensors
+ << " use previous: " << use_prev_samples
+ << " previous sample order: " << prev_samples_order;
+ return kVtEstimatorInitFailed;
+ }
+
+ common_instance_->use_prev_samples = data.use_prev_samples;
+ common_instance_->prev_samples_order = prev_samples_order;
+ tflite_instance_->input_buffer_size = num_linked_sensors * prev_samples_order;
+ tflite_instance_->input_buffer = new float[tflite_instance_->input_buffer_size];
+ if (common_instance_->use_prev_samples) {
+ tflite_instance_->scratch_buffer = new float[tflite_instance_->input_buffer_size];
+ }
+
+ if (output_label_count < 1 || num_hot_spots < 1) {
+ LOG(ERROR) << "Invalid tflite_instance_ config:"
+ << "number of hot spots: " << num_hot_spots
+ << " predicted sample order: " << output_label_count;
return kVtEstimatorInitFailed;
}
- if (!data_->tflite_methods.create || !data_->tflite_methods.init ||
- !data_->tflite_methods.invoke || !data_->tflite_methods.destroy) {
+ tflite_instance_->output_label_count = output_label_count;
+ tflite_instance_->num_hot_spots = num_hot_spots;
+ tflite_instance_->output_buffer_size = output_label_count * num_hot_spots;
+ tflite_instance_->output_buffer = new float[tflite_instance_->output_buffer_size];
+
+ if (!tflite_instance_->tflite_methods.create || !tflite_instance_->tflite_methods.init ||
+ !tflite_instance_->tflite_methods.invoke || !tflite_instance_->tflite_methods.destroy) {
LOG(ERROR) << "Invalid tflite methods";
return kVtEstimatorInitFailed;
}
- data_->tflite_wrapper = data_->tflite_methods.create(kNumInputTensors, kNumOutputTensors);
- if (!data_->tflite_wrapper) {
+ tflite_instance_->tflite_wrapper =
+ tflite_instance_->tflite_methods.create(kNumInputTensors, kNumOutputTensors);
+ if (!tflite_instance_->tflite_wrapper) {
LOG(ERROR) << "Failed to create tflite wrapper";
return kVtEstimatorInitFailed;
}
- int ret = data_->tflite_methods.init(data_->tflite_wrapper, model_path);
+ int ret = tflite_instance_->tflite_methods.init(tflite_instance_->tflite_wrapper,
+ model_path.c_str());
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;
+ common_instance_->cur_sample_index = 0;
+ common_instance_->offset = data.offset;
+ common_instance_->is_initialized = true;
+ tflite_instance_->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";
+VtEstimatorStatus VirtualTempEstimator::LinearModelEstimate(const std::vector<float> &thermistors,
+ float *output) {
+ if (linear_model_instance_ == nullptr || common_instance_ == nullptr) {
+ LOG(ERROR) << "linear_model_instance_ or common_instance_ is nullptr during Initialize";
return kVtEstimatorInitFailed;
}
- std::unique_lock<std::mutex> lock(data_->tflite_methods.mutex_);
+ size_t prev_samples_order = common_instance_->prev_samples_order;
+ size_t num_linked_sensors = common_instance_->num_linked_sensors;
+
+ std::unique_lock<std::mutex> lock(linear_model_instance_->mutex);
- if (!data_->is_initialized) {
- LOG(ERROR) << "data_ not initialized for " << data_->model_path;
+ if ((thermistors.size() != num_linked_sensors) || (output == nullptr)) {
+ LOG(ERROR) << "Invalid args Thermistors size[" << thermistors.size()
+ << "] num_linked_sensors[" << num_linked_sensors << "] output[" << output << "]";
+ return kVtEstimatorInvalidArgs;
+ }
+
+ if (common_instance_->is_initialized == false) {
+ LOG(ERROR) << "VirtualTempEstimator not initialized to estimate";
return kVtEstimatorInitFailed;
}
- if ((thermistors.size() != data_->input_buffer_size) || (!output)) {
- LOG(ERROR) << "Invalid args for " << data_->model_path
+ // For the first iteration copy current inputs to all previous inputs
+ // This would allow the estimator to have previous samples from the first iteration itself
+ // and provide a valid predicted value
+ if (common_instance_->first_iteration) {
+ for (size_t i = 0; i < prev_samples_order; ++i) {
+ linear_model_instance_->input_samples[i] = thermistors;
+ }
+ common_instance_->first_iteration = false;
+ }
+
+ size_t cur_sample_index = common_instance_->cur_sample_index;
+ linear_model_instance_->input_samples[cur_sample_index] = thermistors;
+
+ // Calculate Weighted Average Value
+ int input_level = cur_sample_index;
+ float estimated_value = 0;
+ for (size_t i = 0; i < prev_samples_order; ++i) {
+ for (size_t j = 0; j < num_linked_sensors; ++j) {
+ estimated_value += linear_model_instance_->coefficients[i][j] *
+ linear_model_instance_->input_samples[input_level][j];
+ }
+ input_level--; // go to previous samples
+ input_level = (input_level >= 0) ? input_level : (prev_samples_order - 1);
+ }
+
+ estimated_value += common_instance_->offset;
+
+ // Update sample index
+ cur_sample_index++;
+ cur_sample_index = (cur_sample_index % prev_samples_order);
+ common_instance_->cur_sample_index = cur_sample_index;
+
+ *output = estimated_value;
+ return kVtEstimatorOk;
+}
+
+VtEstimatorStatus VirtualTempEstimator::TFliteEstimate(const std::vector<float> &thermistors,
+ float *output) {
+ if (tflite_instance_ == nullptr || common_instance_ == nullptr) {
+ LOG(ERROR) << "tflite_instance_ or common_instance_ is nullptr during Estimate\n";
+ return kVtEstimatorInitFailed;
+ }
+
+ std::unique_lock<std::mutex> lock(tflite_instance_->tflite_methods.mutex);
+
+ if (!common_instance_->is_initialized) {
+ LOG(ERROR) << "tflite_instance_ not initialized for " << tflite_instance_->model_path;
+ return kVtEstimatorInitFailed;
+ }
+
+ size_t num_linked_sensors = common_instance_->num_linked_sensors;
+ if ((thermistors.size() != num_linked_sensors) || (!output)) {
+ LOG(ERROR) << "Invalid args for " << tflite_instance_->model_path
<< " thermistors.size(): " << thermistors.size()
- << " input_buffer_size: " << data_->input_buffer_size << " output: " << output;
+ << " num_linked_sensors: " << num_linked_sensors << " 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];
+ size_t cur_sample_index = common_instance_->cur_sample_index;
+ size_t prev_samples_order = common_instance_->prev_samples_order;
+ size_t sample_start_index;
+ if (common_instance_->first_iteration) {
+ // For the first iteration copy current inputs to all previous inputs
+ // This would allow the estimator to have previous samples from the first iteration itself
+ // and provide a valid predicted value
+ for (size_t i = 0; i < prev_samples_order; ++i) {
+ sample_start_index = num_linked_sensors * i;
+ for (size_t j = 0; j < num_linked_sensors; ++j) {
+ tflite_instance_->input_buffer[sample_start_index + j] = thermistors[j];
+ }
+ }
+ common_instance_->first_iteration = false;
+ } else {
+ sample_start_index = cur_sample_index * num_linked_sensors;
+ for (size_t i = 0; i < num_linked_sensors; ++i) {
+ tflite_instance_->input_buffer[sample_start_index + i] = thermistors[i];
+ }
}
- int ret = data_->tflite_methods.invoke(data_->tflite_wrapper, data_->input_buffer,
- data_->input_buffer_size, output, 1);
+ // prepare model input
+ float *model_input;
+ size_t input_buffer_size = tflite_instance_->input_buffer_size;
+ size_t output_buffer_size = tflite_instance_->output_buffer_size;
+ if (!common_instance_->use_prev_samples) {
+ model_input = tflite_instance_->input_buffer;
+ } else {
+ sample_start_index = ((cur_sample_index + 1) * num_linked_sensors) % input_buffer_size;
+ for (size_t i = 0; i < input_buffer_size; ++i) {
+ size_t input_index = (sample_start_index + i) % input_buffer_size;
+ tflite_instance_->scratch_buffer[i] = tflite_instance_->input_buffer[input_index];
+ }
+ model_input = tflite_instance_->scratch_buffer;
+ }
+
+ int ret = tflite_instance_->tflite_methods.invoke(
+ tflite_instance_->tflite_wrapper, model_input, input_buffer_size,
+ tflite_instance_->output_buffer, output_buffer_size);
if (ret) {
- LOG(ERROR) << "Failed to Invoke for " << data_->model_path << " (ret: " << ret << ")";
+ LOG(ERROR) << "Failed to Invoke for " << tflite_instance_->model_path << " (ret: " << ret
+ << ")";
return kVtEstimatorInvokeFailed;
}
+ // Update sample index
+ common_instance_->cur_sample_index = (cur_sample_index + 1) % prev_samples_order;
+
+ // virtual sensor currently only support scalar output
+ *output = tflite_instance_->output_buffer[0] + common_instance_->offset;
+
return kVtEstimatorOk;
}
+VtEstimatorStatus VirtualTempEstimator::Estimate(const std::vector<float> &thermistors,
+ float *output) {
+ if (type == kUseMLModel) {
+ return TFliteEstimate(thermistors, output);
+ } else if (type == kUseLinearModel) {
+ return LinearModelEstimate(thermistors, output);
+ }
+
+ LOG(ERROR) << "Unsupported estimationType [" << type << "]";
+ return kVtEstimatorUnSupported;
+}
+
+VtEstimatorStatus VirtualTempEstimator::Initialize(const VtEstimationInitData &data) {
+ LOG(INFO) << "Initialize VirtualTempEstimator for " << type;
+
+ if (type == kUseMLModel) {
+ return TFliteInitialize(data.ml_model_init_data);
+ } else if (type == kUseLinearModel) {
+ return LinearModelInitialize(data.linear_model_init_data);
+ }
+
+ LOG(ERROR) << "Unsupported estimationType [" << type << "]";
+ return kVtEstimatorUnSupported;
+}
+
} // namespace vtestimator
} // namespace thermal
diff --git a/thermal/virtualtemp_estimator/virtualtemp_estimator.h b/thermal/virtualtemp_estimator/virtualtemp_estimator.h
index 0ae37f9e..8b2a5367 100644
--- a/thermal/virtualtemp_estimator/virtualtemp_estimator.h
+++ b/thermal/virtualtemp_estimator/virtualtemp_estimator.h
@@ -30,7 +30,46 @@ enum VtEstimatorStatus {
kVtEstimatorUnSupported = 4,
};
-// Class to estimate virtual temperature based on a model
+enum VtEstimationType { kUseMLModel = 0, kUseLinearModel = 1, kInvalidEstimationType = 2 };
+
+struct MLModelInitData {
+ std::string model_path;
+ bool use_prev_samples;
+ size_t prev_samples_order;
+ float offset;
+ size_t output_label_count;
+ size_t num_hot_spots;
+};
+
+struct LinearModelInitData {
+ bool use_prev_samples;
+ size_t prev_samples_order;
+ std::vector<float> coefficients;
+ float offset;
+};
+
+union VtEstimationInitData {
+ VtEstimationInitData(VtEstimationType type) {
+ if (type == kUseMLModel) {
+ ml_model_init_data.model_path = "";
+ ml_model_init_data.use_prev_samples = false;
+ ml_model_init_data.prev_samples_order = 1;
+ ml_model_init_data.offset = 0;
+ ml_model_init_data.output_label_count = 1;
+ ml_model_init_data.num_hot_spots = 1;
+ } else if (type == kUseLinearModel) {
+ linear_model_init_data.use_prev_samples = false;
+ linear_model_init_data.prev_samples_order = 1;
+ linear_model_init_data.offset = 0;
+ }
+ }
+ ~VtEstimationInitData() {}
+
+ MLModelInitData ml_model_init_data;
+ LinearModelInitData linear_model_init_data;
+};
+
+// Class to estimate virtual temperature
class VirtualTempEstimator {
public:
// Implicit copy-move headers.
@@ -39,19 +78,27 @@ class VirtualTempEstimator {
VirtualTempEstimator &operator=(const VirtualTempEstimator &) = delete;
VirtualTempEstimator &operator=(VirtualTempEstimator &&) = default;
- VirtualTempEstimator(size_t num_input_samples);
+ VirtualTempEstimator(VtEstimationType type, size_t num_linked_sensors);
~VirtualTempEstimator();
- // Initializes the model provided by model_path.
- VtEstimatorStatus Initialize(const char *model_path);
+ // Initializes the estimator based on init_data
+ VtEstimatorStatus Initialize(const VtEstimationInitData &init_data);
- // Performs the inference on the loaded VT model.
- // Output of the inference is returned in output argument
+ // Performs the prediction and returns estimated value in output
VtEstimatorStatus Estimate(const std::vector<float> &thermistors, float *output);
private:
void LoadTFLiteWrapper();
- std::unique_ptr<VirtualTempEstimatorTFLiteData> data_;
+ VtEstimationType type;
+ std::unique_ptr<VtEstimatorCommonData> common_instance_;
+ std::unique_ptr<VtEstimatorTFLiteData> tflite_instance_;
+ std::unique_ptr<VtEstimatorLinearModelData> linear_model_instance_;
+
+ VtEstimatorStatus LinearModelInitialize(LinearModelInitData data);
+ VtEstimatorStatus TFliteInitialize(MLModelInitData data);
+
+ VtEstimatorStatus LinearModelEstimate(const std::vector<float> &thermistors, float *output);
+ VtEstimatorStatus TFliteEstimate(const std::vector<float> &thermistors, float *output);
};
} // namespace vtestimator
diff --git a/thermal/virtualtemp_estimator/virtualtemp_estimator_data.h b/thermal/virtualtemp_estimator/virtualtemp_estimator_data.h
index 935c753b..2b122b23 100644
--- a/thermal/virtualtemp_estimator/virtualtemp_estimator_data.h
+++ b/thermal/virtualtemp_estimator/virtualtemp_estimator_data.h
@@ -38,14 +38,38 @@ struct TFLiteWrapperMethods {
tflitewrapper_init init;
tflitewrapper_invoke invoke;
tflitewrapper_destroy destroy;
- mutable std::mutex mutex_;
+ 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;
+struct VtEstimatorCommonData {
+ VtEstimatorCommonData(size_t num_input_sensors) {
+ num_linked_sensors = num_input_sensors;
+ prev_samples_order = 1;
+ cur_sample_index = 0;
+ first_iteration = true;
+ offset = 0;
is_initialized = false;
+ use_prev_samples = false;
+ }
+
+ size_t num_linked_sensors;
+ size_t prev_samples_order;
+ size_t cur_sample_index;
+ float offset;
+ bool use_prev_samples;
+ bool is_initialized;
+ bool first_iteration;
+};
+
+struct VtEstimatorTFLiteData {
+ VtEstimatorTFLiteData() {
+ scratch_buffer = nullptr;
+ input_buffer = nullptr;
+ input_buffer_size = 0;
+ output_label_count = 1;
+ num_hot_spots = 1;
+ output_buffer = nullptr;
+ output_buffer_size = 1;
tflite_wrapper = nullptr;
tflite_methods.create = nullptr;
@@ -55,22 +79,44 @@ struct VirtualTempEstimatorTFLiteData {
}
void *tflite_wrapper;
+ float *scratch_buffer;
float *input_buffer;
size_t input_buffer_size;
+ size_t num_hot_spots;
+ size_t output_label_count;
+ float *output_buffer;
+ size_t output_buffer_size;
std::string model_path;
TFLiteWrapperMethods tflite_methods;
- bool is_initialized;
- ~VirtualTempEstimatorTFLiteData() {
+ ~VtEstimatorTFLiteData() {
if (tflite_wrapper && tflite_methods.destroy) {
tflite_methods.destroy(tflite_wrapper);
}
+ if (scratch_buffer) {
+ delete scratch_buffer;
+ }
+
if (input_buffer) {
delete input_buffer;
}
+
+ if (output_buffer) {
+ delete output_buffer;
+ }
}
};
+struct VtEstimatorLinearModelData {
+ VtEstimatorLinearModelData() {}
+
+ ~VtEstimatorLinearModelData() {}
+
+ std::vector<std::vector<float>> input_samples;
+ std::vector<std::vector<float>> coefficients;
+ mutable std::mutex mutex;
+};
+
} // namespace vtestimator
} // namespace thermal
diff --git a/thermal/virtualtemp_estimator/virtualtemp_estimator_test.cpp b/thermal/virtualtemp_estimator/virtualtemp_estimator_test.cpp
index fde99977..9da1a832 100644
--- a/thermal/virtualtemp_estimator/virtualtemp_estimator_test.cpp
+++ b/thermal/virtualtemp_estimator/virtualtemp_estimator_test.cpp
@@ -98,16 +98,21 @@ static std::vector<std::string> get_input_combination(std::string_view thermal_c
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) {
+ int inference_delay_sec, int prev_samples_order) {
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);
+ thermal::vtestimator::VirtualTempEstimator vt_estimator_(thermal::vtestimator::kUseMLModel,
+ input_size);
+ ::thermal::vtestimator::VtEstimationInitData init_data(thermal::vtestimator::kUseMLModel);
+ init_data.ml_model_init_data.model_path = model_path;
+ init_data.ml_model_init_data.prev_samples_order = prev_samples_order;
+ init_data.ml_model_init_data.use_prev_samples = (prev_samples_order > 1) ? true : false;
std::cout << "Initialize estimator\n";
- ret = vt_estimator_.Initialize(model_path.data());
+ ret = vt_estimator_.Initialize(init_data);
if (ret != thermal::vtestimator::kVtEstimatorOk) {
std::cout << "Failed to Initialize estimator (ret: " << ret << ")\n";
return -1;
@@ -120,15 +125,16 @@ static int run_random_input_inference(std::string_view model_path,
float avg_inference_time = 0;
std::vector<unsigned long> inference_times;
+ std::srand(time(NULL));
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;
+ // preparing random inputs with starting temperature between 0C to 50C
+ int r = std::rand() % 50000;
for (int i = 0; i < input_size; ++i) {
- thermistors.push_back(r + i);
+ thermistors.push_back(r + i * 1000);
}
gettimeofday(&begin, nullptr);
@@ -139,6 +145,14 @@ static int run_random_input_inference(std::string_view model_path,
return -1;
}
+ std::cout << "inference_count: " << inference_count << " random_value (r): " << r
+ << " output: " << output << "\n";
+
+ if (output > 55000) {
+ std::cout << "Temperature above 55C observed\n";
+ return -1;
+ }
+
unsigned long inference_time_usec = get_elapsed_time_usec(begin, end);
inference_count++;
@@ -176,7 +190,7 @@ static int run_random_input_inference(std::string_view model_path,
return 0;
}
-static int run_single_inference(std::string_view model_path, char *input) {
+static int run_single_inference(std::string_view model_path, char *input, int prev_samples_order) {
if (!input) {
std::cout << "input is nullptr" << std::endl;
return -1;
@@ -205,10 +219,15 @@ static int run_single_inference(std::string_view model_path, char *input) {
float output;
thermal::vtestimator::VtEstimatorStatus ret;
- thermal::vtestimator::VirtualTempEstimator vt_estimator_(thermistors.size());
+ thermal::vtestimator::VirtualTempEstimator vt_estimator_(thermal::vtestimator::kUseMLModel,
+ thermistors.size());
+ ::thermal::vtestimator::VtEstimationInitData init_data(thermal::vtestimator::kUseMLModel);
+ init_data.ml_model_init_data.model_path = model_path;
+ init_data.ml_model_init_data.prev_samples_order = prev_samples_order;
+ init_data.ml_model_init_data.use_prev_samples = (prev_samples_order > 1) ? true : false;
std::cout << "Initialize estimator\n";
- ret = vt_estimator_.Initialize(model_path.data());
+ ret = vt_estimator_.Initialize(init_data);
if (ret != thermal::vtestimator::kVtEstimatorOk) {
std::cout << "Failed to Initialize estimator (ret: " << ret << ")\n";
return -1;
@@ -226,7 +245,8 @@ static int run_single_inference(std::string_view model_path, char *input) {
}
static int run_batch_process(std::string_view model_path, std::string_view thermal_config_path,
- const char *input_file, const char *output_file) {
+ const char *input_file, const char *output_file,
+ int prev_samples_order) {
if (!input_file || !output_file) {
std::cout << "input and output files required for batch process\n";
return -1;
@@ -240,10 +260,15 @@ static int run_batch_process(std::string_view model_path, std::string_view therm
}
thermal::vtestimator::VtEstimatorStatus ret;
- thermal::vtestimator::VirtualTempEstimator vt_estimator_(input_combination.size());
+ thermal::vtestimator::VirtualTempEstimator vt_estimator_(thermal::vtestimator::kUseMLModel,
+ input_combination.size());
+ ::thermal::vtestimator::VtEstimationInitData init_data(thermal::vtestimator::kUseMLModel);
+ init_data.ml_model_init_data.model_path = model_path;
+ init_data.ml_model_init_data.prev_samples_order = prev_samples_order;
+ init_data.ml_model_init_data.use_prev_samples = (prev_samples_order > 1) ? true : false;
std::cout << "Initialize estimator\n";
- ret = vt_estimator_.Initialize(model_path.data());
+ ret = vt_estimator_.Initialize(init_data);
if (ret != thermal::vtestimator::kVtEstimatorOk) {
std::cout << "Failed to Initialize estimator (ret: " << ret << ")\n";
return -1;
@@ -280,6 +305,7 @@ static int run_batch_process(std::string_view model_path, std::string_view therm
std::vector<float> model_inputs;
float model_output;
int num_inputs = input_combination.size();
+ constexpr int kCelsius2mC = 1000;
for (int j = 0; j < num_inputs; ++j) {
std::string input_name = input_combination[j];
@@ -293,7 +319,7 @@ static int run_batch_process(std::string_view model_path, std::string_view therm
std::cout << "Failed to parse value_str : " << value_str << " to float\n";
}
- model_inputs.push_back(value);
+ model_inputs.push_back(value * kCelsius2mC);
}
ret = vt_estimator_.Estimate(model_inputs, &model_output);
@@ -302,6 +328,8 @@ static int run_batch_process(std::string_view model_path, std::string_view therm
return -1;
}
+ model_output /= kCelsius2mC;
+
model_vt_outputs[std::to_string(i)] = std::to_string(model_output);
}
@@ -331,6 +359,7 @@ void print_usage() {
message += "-o : output file (mode 1) \n";
message += "-d : delay between inferences in seconds (mode 2) \n";
message += "-c : inference count (mode 2)";
+ message += "-s : prev_samples_order";
std::cout << message << std::endl;
}
@@ -341,8 +370,9 @@ int main(int argc, char *argv[]) {
std::string model_path, thermal_config_path;
int min_inference_count = -1;
int inference_delay_sec = 0;
+ int prev_samples_order = 1;
- while ((c = getopt(argc, argv, "hm:p:i:c:o:d:t:")) != -1) switch (c) {
+ while ((c = getopt(argc, argv, "hm:p:i:c:o:d:t:s:")) != -1) switch (c) {
case 'm':
mode = atoi(optarg);
std::cout << "mode: " << mode << std::endl;
@@ -351,6 +381,10 @@ int main(int argc, char *argv[]) {
model_path = optarg;
std::cout << "model_path: " << model_path << std::endl;
break;
+ case 's':
+ prev_samples_order = atoi(optarg);
+ std::cout << "prev_samples_order: " << prev_samples_order << std::endl;
+ break;
case 't':
thermal_config_path = optarg;
std::cout << "thermal_config_path: " << thermal_config_path << std::endl;
@@ -394,14 +428,15 @@ int main(int argc, char *argv[]) {
int ret = -1;
switch (mode) {
case 0:
- ret = run_single_inference(model_path, input);
+ ret = run_single_inference(model_path, input, prev_samples_order);
break;
case 1:
- ret = run_batch_process(model_path, thermal_config_path, input, output);
+ ret = run_batch_process(model_path, thermal_config_path, input, output,
+ prev_samples_order);
break;
case 2:
ret = run_random_input_inference(model_path, thermal_config_path, min_inference_count,
- inference_delay_sec);
+ inference_delay_sec, prev_samples_order);
break;
default:
std::cout << "unsupported mode" << std::endl;
diff --git a/usb/Android.bp b/usb/Android.bp
index 1e05ef2d..4b00b97c 100644
--- a/usb/Android.bp
+++ b/usb/Android.bp
@@ -31,6 +31,7 @@ cc_library_static {
"UsbOverheatEvent.cpp",
"CommonUtils.cpp",
"MonitorFfs.cpp",
+ "I2cHelper.cpp",
],
cflags: [
@@ -49,7 +50,7 @@ cc_library_static {
"android.hardware.usb.gadget@1.0",
"android.hardware.thermal@1.0",
"android.hardware.thermal@2.0",
- "android.hardware.thermal-V1-ndk"
+ "android.hardware.thermal-V1-ndk",
],
export_shared_lib_headers: [
@@ -58,7 +59,7 @@ cc_library_static {
static_libs: [
"libthermalutils",
- ]
+ ],
}
cc_library_static {
@@ -74,6 +75,7 @@ cc_library_static {
"UsbOverheatEvent.cpp",
"CommonUtils.cpp",
"MonitorFfs.cpp",
+ "I2cHelper.cpp",
],
cflags: [
@@ -90,7 +92,7 @@ cc_library_static {
"android.hardware.usb.gadget-V1-ndk",
"android.hardware.thermal@1.0",
"android.hardware.thermal@2.0",
- "android.hardware.thermal-V1-ndk"
+ "android.hardware.thermal-V1-ndk",
],
export_shared_lib_headers: [
@@ -99,15 +101,15 @@ cc_library_static {
static_libs: [
"libthermalutils",
- ]
+ ],
}
cc_fuzz {
name: "libpixelusb_gadgetutils_fuzzer",
vendor: true,
- srcs:[
- "UsbGadgetUtils_fuzz.cpp"
+ srcs: [
+ "UsbGadgetUtils_fuzz.cpp",
],
shared_libs: [
diff --git a/usb/I2cHelper.cpp b/usb/I2cHelper.cpp
new file mode 100644
index 00000000..7a6a142a
--- /dev/null
+++ b/usb/I2cHelper.cpp
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2024 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 "libpixelusb-i2chelper"
+
+#include "include/pixelusb/I2cHelper.h"
+
+#include <dirent.h>
+#include <utils/Log.h>
+
+namespace android {
+namespace hardware {
+namespace google {
+namespace pixel {
+namespace usb {
+
+// getI2cBusNumberString: Return the I2C bus number as the string type
+//
+// The bus number can be extracted from the sub-directory under the hsi2c sysfs
+// device directory (e.g. /sys/devices/platform/10d60000.hsi2c/) and the pattern
+// of the sub-directory is /^i2c-\d+$/ where \d+ is the bus number.
+static string getI2cBusNumberString(const string hsi2cPath) {
+ DIR *dp = opendir(hsi2cPath.c_str());
+
+ if (dp != NULL) {
+ struct dirent *ep;
+
+ while ((ep = readdir(dp))) {
+ if (ep->d_type == DT_DIR) {
+ // Supposed that there is only one sub dir in the pattern "i2c-"
+ if (string::npos != string(ep->d_name).find("i2c-")) {
+ std::strtok(ep->d_name, "-");
+ string busNumber = std::strtok(NULL, "-");
+ closedir(dp);
+ return busNumber;
+ }
+ }
+ }
+ closedir(dp);
+ ALOGE("Failed to find the i2c sub dir under %s", hsi2cPath.c_str());
+ return string("");
+ }
+
+ ALOGE("Failed to open %s", hsi2cPath.c_str());
+ return string("");
+}
+
+// getI2cClientPath: Return the full path of the I2C client directory
+//
+// There are two forms of the directory path: in client ID and in I2C device name
+// For example:
+// client ID: /sys/devices/platform/10d60000.hsi2c/i2c-7/7-0025/
+// device name: /sys/devices/platform/10d60000.hsi2c/i2c-7/i2c-max77759tcpc/
+//
+// The bus number and the client directory name differs across kernel versions and
+// build targets. Search the bus number first to locate the first level of the sub
+// directory, and then search the I2C device name under it.
+//
+// Append the I2c device name to the full path if found, otherwise, append "bus
+// number" + "-" + client ID. Note that the client ID must be a 4-digit number
+// with 0 stuffed in the type of string.
+string getI2cClientPath(const string hsi2cPath, const string devName, const string clientId) {
+ DIR *dp;
+ string strBusNumber, i2cPathPartial, i2cClientPath;
+
+ strBusNumber = getI2cBusNumberString(hsi2cPath);
+ if (strBusNumber.empty()) {
+ return string("");
+ }
+
+ i2cPathPartial = hsi2cPath + "/i2c-" + strBusNumber;
+ dp = opendir(i2cPathPartial.c_str());
+ if (dp != NULL) {
+ struct dirent *ep;
+ string i2cClientDevice = strBusNumber + "-" + clientId;
+
+ while ((ep = readdir(dp))) {
+ if (ep->d_type == DT_DIR) {
+ if (string::npos != string(ep->d_name).find(devName)) {
+ closedir(dp);
+ return string(i2cPathPartial + "/" + devName + "/");
+ }
+ if (string::npos != string(ep->d_name).find(i2cClientDevice)) {
+ closedir(dp);
+ return string(i2cPathPartial + "/" + i2cClientDevice + "/");
+ }
+ }
+ }
+ closedir(dp);
+ }
+
+ ALOGE("Failed to open %s", i2cPathPartial.c_str());
+ return string("");
+}
+
+} // namespace usb
+} // namespace pixel
+} // namespace google
+} // namespace hardware
+} // namespace android
diff --git a/battery_mitigation/include/battery_mitigation/BatteryMitigation.h b/usb/include/pixelusb/I2cHelper.h
index de22b9af..e3955098 100644
--- a/battery_mitigation/include/battery_mitigation/BatteryMitigation.h
+++ b/usb/include/pixelusb/I2cHelper.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2024 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.
@@ -14,32 +14,26 @@
* limitations under the License.
*/
-#pragma once
+#ifndef HARDWARE_GOOGLE_PIXEL_USB_I2CHELPER_H_
+#define HARDWARE_GOOGLE_PIXEL_USB_I2CHELPER_H_
+#include <string>
-#include <utils/RefBase.h>
-
-#include "MitigationThermalManager.h"
+using ::std::string;
namespace android {
namespace hardware {
namespace google {
namespace pixel {
+namespace usb {
-using ::android::sp;
-
-class BatteryMitigation : public RefBase {
- public:
- BatteryMitigation(const struct MitigationConfig::Config &cfg);
- bool isMitigationLogTimeValid(std::chrono::system_clock::time_point startTime,
- const char *const logFilePath, const char *const timestampFormat,
- const std::regex pattern);
-
- private:
- MitigationThermalManager *mThermalMgr;
-};
+// Search the path of the i2c client
+string getI2cClientPath(const string hsi2cPath, const string devName, const string clientId);
+} // namespace usb
} // namespace pixel
} // namespace google
} // namespace hardware
} // namespace android
+
+#endif // HARDWARE_GOOGLE_PIXEL_USB_I2CHELPER_H_
diff --git a/vibrator/common/Android.bp b/vibrator/common/Android.bp
index 8e99827f..cb21005d 100644
--- a/vibrator/common/Android.bp
+++ b/vibrator/common/Android.bp
@@ -57,6 +57,11 @@ haptics_feature_cc_defaults {
"-DLUXSHARE_ICT_LT_XLRA1906D",
],
},
+ conditions_default: {
+ cflags: [
+ "-DUNSPECIFIED_ACTUATOR",
+ ],
+ },
},
adaptive_haptics_feature: {
adaptive_haptics_v1: {
@@ -69,7 +74,7 @@ haptics_feature_cc_defaults {
"-DDISABLE_ADAPTIVE_HAPTICS_FEATURE",
],
},
- }
+ },
},
}
diff --git a/vibrator/cs40l26/Android.bp b/vibrator/cs40l26/Android.bp
index 6a2076a8..be1840c6 100644
--- a/vibrator/cs40l26/Android.bp
+++ b/vibrator/cs40l26/Android.bp
@@ -37,6 +37,8 @@ cc_defaults {
shared_libs: [
"libcutils",
"libtinyalsa",
+ "libbase",
+ "libutils",
],
}
@@ -62,6 +64,10 @@ cc_library {
],
srcs: [
"Vibrator.cpp",
+ "DspMemChunk.cpp",
+ ],
+ shared_libs: [
+ "PixelVibratorFlagsL26",
],
export_include_dirs: [
".",
@@ -103,9 +109,33 @@ cc_binary {
proprietary: true,
}
+aconfig_declarations {
+ name: "VibratorFlagsL26",
+ package: "vendor.vibrator.hal.flags",
+ container: "vendor",
+ exportable: true,
+ srcs: ["VibratorFlags.aconfig"],
+}
+
+cc_aconfig_library {
+ name: "PixelVibratorFlagsL26",
+ aconfig_declarations: "VibratorFlagsL26",
+ vendor_available: true,
+}
+
+java_aconfig_library {
+ name: "PixelVibratorFlagsL26_java",
+ aconfig_declarations: "VibratorFlagsL26",
+ mode: "exported",
+ visibility: ["//vendor:__subpackages__"],
+}
+
filegroup {
name: "haptics_srcs",
- srcs: ["service.cpp", "Vibrator.cpp"],
+ srcs: [
+ "service.cpp",
+ "Vibrator.cpp",
+ ],
}
filegroup {
diff --git a/vibrator/cs40l26/DspMemChunk.cpp b/vibrator/cs40l26/DspMemChunk.cpp
new file mode 100644
index 00000000..8d460653
--- /dev/null
+++ b/vibrator/cs40l26/DspMemChunk.cpp
@@ -0,0 +1,316 @@
+/*
+ * Copyright (C) 2024 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 "DspMemChunk.h"
+
+#include <linux/version.h>
+#include <log/log.h>
+#include <utils/Trace.h>
+
+#include <cmath>
+
+#include "Trace.h"
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace vibrator {
+
+#ifdef VIBRATOR_TRACE
+/* Function Trace */
+#define VFTRACE(...) \
+ ATRACE_NAME(StringPrintf("Vibrator::%s", __func__).c_str()); \
+ auto f_trace_ = std::make_unique<FunctionTrace>("Vibrator", __func__); \
+ __VA_OPT__(f_trace_->addParameter(PREPEND_EACH_ARG_WITH_NAME(__VA_ARGS__))); \
+ f_trace_->save()
+/* Effect Trace */
+#define VETRACE(i, s, d, ch) \
+ auto e_trace_ = std::make_unique<EffectTrace>(i, s, d, ch); \
+ e_trace_->save()
+#else
+#define VFTRACE(...) ATRACE_NAME(StringPrintf("Vibrator::%s", __func__).c_str())
+#define VETRACE(...)
+#endif
+
+enum WaveformIndex : uint16_t {
+ /* Physical waveform */
+ WAVEFORM_LONG_VIBRATION_EFFECT_INDEX = 0,
+ WAVEFORM_RESERVED_INDEX_1 = 1,
+ WAVEFORM_CLICK_INDEX = 2,
+ WAVEFORM_SHORT_VIBRATION_EFFECT_INDEX = 3,
+ WAVEFORM_THUD_INDEX = 4,
+ WAVEFORM_SPIN_INDEX = 5,
+ WAVEFORM_QUICK_RISE_INDEX = 6,
+ WAVEFORM_SLOW_RISE_INDEX = 7,
+ WAVEFORM_QUICK_FALL_INDEX = 8,
+ WAVEFORM_LIGHT_TICK_INDEX = 9,
+ WAVEFORM_LOW_TICK_INDEX = 10,
+ WAVEFORM_RESERVED_MFG_1,
+ WAVEFORM_RESERVED_MFG_2,
+ WAVEFORM_RESERVED_MFG_3,
+ WAVEFORM_MAX_PHYSICAL_INDEX,
+ /* OWT waveform */
+ WAVEFORM_COMPOSE = WAVEFORM_MAX_PHYSICAL_INDEX,
+ WAVEFORM_PWLE,
+ /*
+ * Refer to <linux/input.h>, the WAVEFORM_MAX_INDEX must not exceed 96.
+ * #define FF_GAIN 0x60 // 96 in decimal
+ * #define FF_MAX_EFFECTS FF_GAIN
+ */
+ WAVEFORM_MAX_INDEX,
+};
+
+DspMemChunk::DspMemChunk(uint8_t type, size_t size) : head(new uint8_t[size]{0x00}) {
+ VFTRACE(type, size);
+ waveformType = type;
+ _current = head.get();
+ _max = _current + size;
+
+ if (waveformType == WAVEFORM_COMPOSE) {
+ write(8, 0); /* Padding */
+ write(8, 0); /* nsections placeholder */
+ write(8, 0); /* repeat */
+ } else if (waveformType == WAVEFORM_PWLE) {
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 0, 0)
+ write(16, (PWLE_FTR_BUZZ_BIT | PWLE_FTR_DVL_BIT)
+ << PWLE_HEADER_FTR_SHIFT); /* Feature flag */
+ write(8, PWLE_WT_TYPE); /* type12 */
+ write(24, PWLE_HEADER_WORD_COUNT); /* Header word count */
+ write(24, 0); /* Body word count placeholder */
+#endif
+ write(24, 0); /* Waveform length placeholder */
+ write(8, 0); /* Repeat */
+ write(12, 0); /* Wait time between repeats */
+ write(8, 0); /* nsections placeholder */
+ } else {
+ ALOGE("%s: Invalid type: %u", __func__, waveformType);
+ }
+}
+
+int DspMemChunk::write(int nbits, uint32_t val) {
+ VFTRACE(nbits, val);
+ int nwrite;
+
+ nwrite = min(24 - _cachebits, nbits);
+ _cache <<= nwrite;
+ _cache |= val >> (nbits - nwrite);
+ _cachebits += nwrite;
+ nbits -= nwrite;
+
+ if (_cachebits == 24) {
+ if (isEnd())
+ return -ENOSPC;
+
+ _cache &= 0xFFFFFF;
+ for (size_t i = 0; i < sizeof(_cache); i++, _cache <<= 8)
+ *_current++ = (_cache & 0xFF000000) >> 24;
+
+ bytes += sizeof(_cache);
+ _cachebits = 0;
+ }
+
+ if (nbits)
+ return write(nbits, val);
+
+ return 0;
+}
+
+int DspMemChunk::fToU16(float input, uint16_t *output, float scale, float min, float max) {
+ VFTRACE(input, output, scale, min, max);
+ if (input < min || input > max)
+ return -ERANGE;
+
+ *output = roundf(input * scale);
+ return 0;
+}
+
+void DspMemChunk::constructPwleSegment(uint16_t delay, uint16_t amplitude, uint16_t frequency,
+ uint8_t flags, uint32_t vbemfTarget) {
+ VFTRACE(delay, amplitude, frequency, flags, vbemfTarget);
+ write(16, delay);
+ write(12, amplitude);
+ write(12, frequency);
+ /* feature flags to control the chirp, CLAB braking, back EMF amplitude regulation */
+ write(8, (flags | 1) << 4);
+ if (flags & PWLE_AMP_REG_BIT) {
+ write(24, vbemfTarget); /* target back EMF voltage */
+ }
+}
+
+int DspMemChunk::flush() {
+ VFTRACE();
+ if (!_cachebits)
+ return 0;
+
+ return write(24 - _cachebits, 0);
+}
+
+int DspMemChunk::constructComposeSegment(uint32_t effectVolLevel, uint32_t effectIndex,
+ uint8_t repeat, uint8_t flags, uint16_t nextEffectDelay) {
+ VFTRACE(effectVolLevel, effectIndex, repeat, flags, nextEffectDelay);
+ if (waveformType != WAVEFORM_COMPOSE) {
+ ALOGE("%s: Invalid type: %d", __func__, waveformType);
+ return -EDOM;
+ }
+ if (effectVolLevel > 100 || effectIndex > WAVEFORM_MAX_PHYSICAL_INDEX) {
+ ALOGE("%s: Invalid argument: %u, %u", __func__, effectVolLevel, effectIndex);
+ return -EINVAL;
+ }
+ write(8, effectVolLevel); /* amplitude */
+ write(8, effectIndex); /* index */
+ write(8, repeat); /* repeat */
+ write(8, flags); /* flags */
+ write(16, nextEffectDelay); /* delay */
+ return 0;
+}
+
+int DspMemChunk::constructActiveSegment(int duration, float amplitude, float frequency,
+ bool chirp) {
+ VFTRACE(duration, amplitude, frequency, chirp);
+ uint16_t delay = 0;
+ uint16_t amp = 0;
+ uint16_t freq = 0;
+ uint8_t flags = 0x0;
+ if (waveformType != WAVEFORM_PWLE) {
+ ALOGE("%s: Invalid type: %d", __func__, waveformType);
+ return -EDOM;
+ }
+ if ((fToU16(duration, &delay, 4, 0.0f, COMPOSE_PWLE_PRIMITIVE_DURATION_MAX_MS) < 0) ||
+ (fToU16(amplitude, &amp, 2048, CS40L26_PWLE_LEVEL_MIN, CS40L26_PWLE_LEVEL_MAX) < 0) ||
+ (fToU16(frequency, &freq, 4, PWLE_FREQUENCY_MIN_HZ, PWLE_FREQUENCY_MAX_HZ) < 0)) {
+ ALOGE("%s: Invalid argument: %d, %f, %f", __func__, duration, amplitude, frequency);
+ return -ERANGE;
+ }
+ if (chirp) {
+ flags |= PWLE_CHIRP_BIT;
+ }
+ constructPwleSegment(delay, amp, freq, flags, 0 /*ignored*/);
+ return 0;
+}
+
+int DspMemChunk::constructBrakingSegment(int duration, Braking brakingType) {
+ VFTRACE(duration, brakingType);
+ uint16_t delay = 0;
+ uint16_t freq = 0;
+ uint8_t flags = 0x00;
+ if (waveformType != WAVEFORM_PWLE) {
+ ALOGE("%s: Invalid type: %d", __func__, waveformType);
+ return -EDOM;
+ }
+ if (fToU16(duration, &delay, 4, 0.0f, COMPOSE_PWLE_PRIMITIVE_DURATION_MAX_MS) < 0) {
+ ALOGE("%s: Invalid argument: %d", __func__, duration);
+ return -ERANGE;
+ }
+ fToU16(PWLE_FREQUENCY_MIN_HZ, &freq, 4, PWLE_FREQUENCY_MIN_HZ, PWLE_FREQUENCY_MAX_HZ);
+ if (static_cast<std::underlying_type<Braking>::type>(brakingType)) {
+ flags |= PWLE_BRAKE_BIT;
+ }
+
+ constructPwleSegment(delay, 0 /*ignored*/, freq, flags, 0 /*ignored*/);
+ return 0;
+}
+
+int DspMemChunk::updateWLength(uint32_t totalDuration) {
+ VFTRACE(totalDuration);
+ uint8_t *f = front();
+ if (f == nullptr) {
+ ALOGE("%s: head does not exist!", __func__);
+ return -ENOMEM;
+ }
+ if (waveformType != WAVEFORM_PWLE) {
+ ALOGE("%s: Invalid type: %d", __func__, waveformType);
+ return -EDOM;
+ }
+ if (totalDuration > 0x7FFFF) {
+ ALOGE("%s: Invalid argument: %u", __func__, totalDuration);
+ return -EINVAL;
+ }
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 0, 0)
+ f += PWLE_HEADER_WORD_COUNT * PWLE_WORD_SIZE;
+#endif
+ totalDuration *= 8; /* Unit: 0.125 ms (since wlength played @ 8kHz). */
+ totalDuration |= WT_LEN_CALCD; /* Bit 23 is for WT_LEN_CALCD; Bit 22 is for WT_INDEFINITE. */
+ *(f + 0) = (totalDuration >> 24) & 0xFF;
+ *(f + 1) = (totalDuration >> 16) & 0xFF;
+ *(f + 2) = (totalDuration >> 8) & 0xFF;
+ *(f + 3) = totalDuration & 0xFF;
+ return 0;
+}
+
+int DspMemChunk::updateNSection(int segmentIdx) {
+ VFTRACE(segmentIdx);
+ uint8_t *f = front();
+ if (f == nullptr) {
+ ALOGE("%s: head does not exist!", __func__);
+ return -ENOMEM;
+ }
+
+ if (waveformType == WAVEFORM_COMPOSE) {
+ if (segmentIdx > COMPOSE_SIZE_MAX + 1 /*1st effect may have a delay*/) {
+ ALOGE("%s: Invalid argument: %d", __func__, segmentIdx);
+ return -EINVAL;
+ }
+ *(f + 2) = (0xFF & segmentIdx);
+ } else if (waveformType == WAVEFORM_PWLE) {
+ if (segmentIdx > COMPOSE_PWLE_SIZE_MAX_DEFAULT) {
+ ALOGE("%s: Invalid argument: %d", __func__, segmentIdx);
+ return -EINVAL;
+ }
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 0, 0)
+ f += PWLE_HEADER_WORD_COUNT * PWLE_WORD_SIZE;
+#endif
+ *(f + 7) |= (0xF0 & segmentIdx) >> 4; /* Bit 4 to 7 */
+ *(f + 9) |= (0x0F & segmentIdx) << 4; /* Bit 3 to 0 */
+ } else {
+ ALOGE("%s: Invalid type: %d", __func__, waveformType);
+ return -EDOM;
+ }
+
+ return 0;
+}
+
+int DspMemChunk::updateWCount(int segmentCount) {
+ uint8_t *f = front();
+
+ if (segmentCount > COMPOSE_SIZE_MAX + 1 /*1st effect may have a delay*/) {
+ ALOGE("%s: Invalid argument: %d", __func__, segmentCount);
+ return -EINVAL;
+ }
+ if (f == nullptr) {
+ ALOGE("%s: head does not exist!", __func__);
+ return -ENOMEM;
+ }
+ if (waveformType != WAVEFORM_PWLE) {
+ ALOGE("%s: Invalid type: %d", __func__, waveformType);
+ return -EDOM;
+ }
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 0, 0)
+ f += PWLE_HEADER_WORD_COUNT * PWLE_WORD_SIZE;
+#endif
+ uint32_t dataSize = segmentCount * PWLE_SEGMENT_WORD_COUNT + PWLE_HEADER_WORD_COUNT;
+ *(f + 0) = (dataSize >> 24) & 0xFF;
+ *(f + 1) = (dataSize >> 16) & 0xFF;
+ *(f + 2) = (dataSize >> 8) & 0xFF;
+ *(f + 3) = dataSize & 0xFF;
+
+ return 0;
+}
+
+} // namespace vibrator
+} // namespace hardware
+} // namespace android
+} // namespace aidl
diff --git a/vibrator/cs40l26/DspMemChunk.h b/vibrator/cs40l26/DspMemChunk.h
new file mode 100644
index 00000000..1bc15f49
--- /dev/null
+++ b/vibrator/cs40l26/DspMemChunk.h
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2024 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/vibrator/BnVibrator.h>
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace vibrator {
+
+constexpr int32_t COMPOSE_PWLE_PRIMITIVE_DURATION_MAX_MS = 16383;
+
+constexpr uint32_t WT_LEN_CALCD = 0x00800000;
+constexpr uint8_t PWLE_CHIRP_BIT = 0x8; // Dynamic/frequency and voltage
+constexpr uint8_t PWLE_BRAKE_BIT = 0x4;
+constexpr uint8_t PWLE_AMP_REG_BIT = 0x2;
+
+static constexpr uint8_t PWLE_WT_TYPE = 12;
+static constexpr uint8_t PWLE_HEADER_WORD_COUNT = 3;
+static constexpr uint8_t PWLE_HEADER_FTR_SHIFT = 8;
+static constexpr uint8_t PWLE_SVC_METADATA_WORD_COUNT = 3;
+static constexpr uint32_t PWLE_SVC_METADATA_TERMINATOR = 0xFFFFFF;
+static constexpr uint8_t PWLE_SEGMENT_WORD_COUNT = 2;
+static constexpr uint8_t PWLE_HEADER_WCOUNT_WORD_OFFSET = 2;
+static constexpr uint8_t PWLE_WORD_SIZE = sizeof(uint32_t);
+
+static constexpr uint8_t PWLE_SVC_NO_BRAKING = -1;
+static constexpr uint8_t PWLE_SVC_CAT_BRAKING = 0;
+static constexpr uint8_t PWLE_SVC_OPEN_BRAKING = 1;
+static constexpr uint8_t PWLE_SVC_CLOSED_BRAKING = 2;
+static constexpr uint8_t PWLE_SVC_MIXED_BRAKING = 3;
+
+static constexpr uint32_t PWLE_SVC_MAX_BRAKING_TIME_MS = 1000;
+
+static constexpr uint8_t PWLE_FTR_BUZZ_BIT = 0x80;
+static constexpr uint8_t PWLE_FTR_CLICK_BIT = 0x00;
+static constexpr uint8_t PWLE_FTR_DYNAMIC_F0_BIT = 0x10;
+static constexpr uint8_t PWLE_FTR_SVC_METADATA_BIT = 0x04;
+static constexpr uint8_t PWLE_FTR_DVL_BIT = 0x02;
+static constexpr uint8_t PWLE_FTR_LF0T_BIT = 0x01;
+
+constexpr float CS40L26_PWLE_LEVEL_MIN = -1.0;
+constexpr float CS40L26_PWLE_LEVEL_MAX = 0.9995118;
+
+constexpr float PWLE_FREQUENCY_MIN_HZ = 30.0f;
+constexpr float PWLE_FREQUENCY_MAX_HZ = 300.0f;
+
+/* nsections is 8 bits. Need to preserve 1 section for the first delay before the first effect. */
+static constexpr int32_t COMPOSE_SIZE_MAX = 254;
+static constexpr int32_t COMPOSE_PWLE_SIZE_MAX_DEFAULT = 127;
+
+class DspMemChunk {
+ public:
+ DspMemChunk(uint8_t type, size_t size);
+
+ uint8_t *front() const { return head.get(); }
+ uint8_t type() const { return waveformType; }
+ size_t size() const { return bytes; }
+
+ int flush();
+
+ int constructComposeSegment(uint32_t effectVolLevel, uint32_t effectIndex, uint8_t repeat,
+ uint8_t flags, uint16_t nextEffectDelay);
+ int constructActiveSegment(int duration, float amplitude, float frequency, bool chirp);
+ int constructBrakingSegment(int duration, Braking brakingType);
+
+ int updateWLength(uint32_t totalDuration);
+ int updateNSection(int segmentIdx);
+ int updateWCount(int segmentCount);
+
+ private:
+ std::unique_ptr<uint8_t[]> head;
+ size_t bytes = 0;
+ uint8_t waveformType;
+ uint8_t *_current;
+ const uint8_t *_max;
+ uint32_t _cache = 0;
+ int _cachebits = 0;
+
+ bool isEnd() const { return _current == _max; }
+ int min(int x, int y) { return x < y ? x : y; }
+
+ int write(int nbits, uint32_t val);
+
+ int fToU16(float input, uint16_t *output, float scale, float min, float max);
+
+ void constructPwleSegment(uint16_t delay, uint16_t amplitude, uint16_t frequency, uint8_t flags,
+ uint32_t vbemfTarget = 0);
+};
+
+} // namespace vibrator
+} // namespace hardware
+} // namespace android
+} // namespace aidl
diff --git a/vibrator/cs40l26/Hardware.h b/vibrator/cs40l26/Hardware.h
index 12ddb59b..f7a9c035 100644
--- a/vibrator/cs40l26/Hardware.h
+++ b/vibrator/cs40l26/Hardware.h
@@ -210,12 +210,11 @@ class HwApi : public Vibrator::HwApi, private HwApiBase {
ALOGE("Invalid ff_effect");
return false;
}
- if (((*effect).replay.length != timeoutMs) || (ioctl(mInputFd, EVIOCSFF, effect) < 0)) {
+ if (ioctl(mInputFd, EVIOCSFF, effect) < 0) {
ALOGE("setFFEffect fail");
return false;
}
- HWAPI_RECORD(StringPrintf("#%d: %dms", (*effect).id, (*effect).replay.length),
- &mInputIoStream);
+ HWAPI_RECORD(StringPrintf("#%d: %dms", (*effect).id, timeoutMs), &mInputIoStream);
return true;
}
bool setFFPlay(int8_t index, bool value) override {
diff --git a/vibrator/cs40l26/Trace.cpp b/vibrator/cs40l26/Trace.cpp
new file mode 100644
index 00000000..10b54534
--- /dev/null
+++ b/vibrator/cs40l26/Trace.cpp
@@ -0,0 +1,256 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Trace.h"
+
+#include <aidl/android/hardware/vibrator/BnVibrator.h>
+#include <log/log.h>
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace vibrator {
+
+enum WaveformIndex : uint16_t {
+ /* Physical waveform */
+ WAVEFORM_LONG_VIBRATION_EFFECT_INDEX = 0,
+ WAVEFORM_RESERVED_INDEX_1 = 1,
+ WAVEFORM_CLICK_INDEX = 2,
+ WAVEFORM_SHORT_VIBRATION_EFFECT_INDEX = 3,
+ WAVEFORM_THUD_INDEX = 4,
+ WAVEFORM_SPIN_INDEX = 5,
+ WAVEFORM_QUICK_RISE_INDEX = 6,
+ WAVEFORM_SLOW_RISE_INDEX = 7,
+ WAVEFORM_QUICK_FALL_INDEX = 8,
+ WAVEFORM_LIGHT_TICK_INDEX = 9,
+ WAVEFORM_LOW_TICK_INDEX = 10,
+ WAVEFORM_RESERVED_MFG_1,
+ WAVEFORM_RESERVED_MFG_2,
+ WAVEFORM_RESERVED_MFG_3,
+ WAVEFORM_MAX_PHYSICAL_INDEX,
+ /* OWT waveform */
+ WAVEFORM_COMPOSE = WAVEFORM_MAX_PHYSICAL_INDEX,
+ WAVEFORM_PWLE,
+ /*
+ * Refer to <linux/input.h>, the WAVEFORM_MAX_INDEX must not exceed 96.
+ * #define FF_GAIN 0x60 // 96 in decimal
+ * #define FF_MAX_EFFECTS FF_GAIN
+ */
+ WAVEFORM_MAX_INDEX,
+};
+
+/* Support printing */
+
+std::ostream &operator<<(std::ostream &out, std::shared_ptr<IVibratorCallback> arg) {
+ return out << arg->descriptor << "()";
+}
+
+std::ostream &operator<<(std::ostream &out, const ff_effect *arg) {
+ if (arg == nullptr) {
+ return out;
+ }
+
+ return out << StringPrintf("%p", arg).c_str();
+}
+
+std::ostream &operator<<(std::ostream &out, const ff_effect &arg) {
+ out << "(";
+ out << "FF_PERIODIC, " << arg.id << ", " << arg.replay.length << "ms, "
+ << arg.u.periodic.custom_len << " bytes";
+ out << ")";
+ return out;
+}
+
+std::ostream &operator<<(std::ostream &out, const CompositePrimitive &arg) {
+ return out << toString(arg).c_str();
+}
+
+std::ostream &operator<<(std::ostream &out, const Braking &arg) {
+ return out << toString(arg).c_str();
+}
+
+std::ostream &operator<<(std::ostream &out, const PrimitivePwle &arg) {
+ out << "(";
+ switch (arg.getTag()) {
+ case PrimitivePwle::active: {
+ auto active = arg.get<PrimitivePwle::active>();
+ out << std::fixed << std::setprecision(2) << active.startAmplitude << ", "
+ << active.startFrequency << "Hz, " << active.endAmplitude << ", "
+ << active.endFrequency << "Hz, " << active.duration << "ms";
+ break;
+ }
+ case PrimitivePwle::braking: {
+ out << "Deprecated!";
+ break;
+ }
+ }
+ out << ")";
+ return out;
+}
+
+std::ostream &operator<<(std::ostream &out, const CompositeEffect &arg) {
+ out << "(" << arg.delayMs << "ms, " << toString(arg.primitive) << ", " << arg.scale << ")";
+ return out;
+}
+
+std::ostream &operator<<(std::ostream &out, const DspMemChunk *arg) {
+ if (arg == nullptr) {
+ return out << "NULL";
+ }
+
+ out << "(";
+ if (arg->type() == 14) {
+ out << "WAVEFORM_COMPOSE, ";
+ } else if (arg->type() == 15) {
+ out << "WAVEFORM_PWLE, ";
+ }
+ out << arg->size() << " bytes";
+ out << ")";
+ return out;
+}
+
+std::ostream &operator<<(std::ostream &out, Effect arg) {
+ return out << toString(arg).c_str();
+}
+
+std::ostream &operator<<(std::ostream &out, EffectStrength arg) {
+ return out << toString(arg).c_str();
+}
+
+/* Trace Interface */
+
+int Trace::mDepth = -1;
+std::vector<std::string> Trace::mTrace = {};
+std::vector<std::vector<std::string>> Trace::mPreviousTraces = {};
+
+void Trace::debug(int fd) {
+ std::vector<std::string> tTrace;
+ std::swap(mTrace, tTrace);
+
+ std::vector<std::vector<std::string>> tPreviousTraces;
+ std::swap(mPreviousTraces, tPreviousTraces);
+
+ dprintf(fd, "\nCurrent Trace:\n");
+ for (auto line : tTrace) {
+ dprintf(fd, "%s\n", line.c_str());
+ }
+
+ if (tPreviousTraces.size() > 0) {
+ for (auto i = tPreviousTraces.size(); i--;) {
+ dprintf(fd, "\nPrevious Trace #%zu:\n", i);
+ for (auto line : tPreviousTraces[i]) {
+ dprintf(fd, "%s\n", line.c_str());
+ }
+ }
+ }
+}
+
+/* FunctionTrace Interface */
+
+FunctionTrace::FunctionTrace(const char *funcName) : mClassName(""), mFuncName(funcName) {
+ Trace::enter();
+}
+
+FunctionTrace::FunctionTrace(const char *className, const char *funcName)
+ : mClassName(className), mFuncName(funcName) {
+ Trace::enter();
+}
+
+FunctionTrace::~FunctionTrace() {
+ Trace::exit();
+}
+
+void FunctionTrace::save() {
+ std::stringstream fmt;
+ int d = Trace::depth();
+ for (int i = 0; i < d; i++) {
+ fmt << " ";
+ }
+
+ if (mClassName != "") {
+ fmt << mClassName << "::";
+ }
+ fmt << mFuncName << "(";
+
+ for (auto param : mParameters) {
+ fmt << param;
+ if (param != mParameters.back()) {
+ fmt << ", ";
+ }
+ }
+
+ fmt << ")";
+
+ std::string fmtOut = fmt.str();
+ ALOGI("%s", fmtOut.c_str());
+ Trace::push(fmtOut);
+}
+
+/* Effect Trace Implementation */
+
+EffectTrace::EffectTrace(uint16_t index, float scale, int32_t duration, const DspMemChunk *ch) {
+ std::stringstream fmt;
+ fmt << "Effect(";
+ switch (index) {
+ case WAVEFORM_LONG_VIBRATION_EFFECT_INDEX:
+ fmt << "LONG_VIBRATION, " << scale << ", " << duration << ")";
+ break;
+ case WAVEFORM_CLICK_INDEX:
+ fmt << "CLICK, " << scale << ")";
+ break;
+ case WAVEFORM_SHORT_VIBRATION_EFFECT_INDEX:
+ fmt << "SHORT_VIBRATION, " << scale << ", " << duration << ")";
+ break;
+ case WAVEFORM_THUD_INDEX:
+ case WAVEFORM_SPIN_INDEX:
+ case WAVEFORM_QUICK_RISE_INDEX:
+ case WAVEFORM_SLOW_RISE_INDEX:
+ case WAVEFORM_QUICK_FALL_INDEX:
+ break;
+ case WAVEFORM_LIGHT_TICK_INDEX:
+ fmt << "LIGHT_TICK, " << scale << ")";
+ break;
+ case WAVEFORM_LOW_TICK_INDEX:
+ break;
+ case WAVEFORM_COMPOSE:
+ fmt << "COMPOSITE, " << ch->size() << " bytes)";
+ break;
+ case WAVEFORM_PWLE:
+ fmt << "PWLE, " << ch->size() << " bytes)";
+ break;
+ default:
+ break;
+ }
+ mDescription = fmt.str();
+}
+
+void EffectTrace::save() {
+ std::stringstream fmt;
+ for (int i = 0; i < depth(); i++) {
+ fmt << " ";
+ }
+ fmt << mDescription;
+
+ std::string fmtOut = fmt.str();
+ ALOGI("%s", fmtOut.c_str());
+ Trace::push(fmtOut);
+ Trace::save();
+}
+
+} // namespace vibrator
+} // namespace hardware
+} // namespace android
+} // namespace aidl
diff --git a/vibrator/cs40l26/Trace.h b/vibrator/cs40l26/Trace.h
new file mode 100644
index 00000000..4b49126d
--- /dev/null
+++ b/vibrator/cs40l26/Trace.h
@@ -0,0 +1,231 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <aidl/android/hardware/vibrator/BnVibrator.h>
+#include <aidl/android/hardware/vibrator/BnVibratorCallback.h>
+#include <android-base/stringprintf.h>
+#include <hardware/hardware.h>
+#include <hardware/vibrator.h>
+#include <linux/input.h>
+
+#include <iomanip>
+#include <iostream>
+#include <sstream>
+#include <string>
+#include <typeinfo>
+#include <vector>
+
+#include "DspMemChunk.h"
+
+/* Macros to expand argument (x) into pair("x", x) for nicer tracing logs
+ * Easily extendible past 7 elements
+ */
+#define WITH_NAME(a) std::make_pair(#a, a)
+
+#define VA_NUM_ARGS(...) VA_NUM_ARGS_IMPL(__VA_ARGS__, 7, 6, 5, 4, 3, 2, 1)
+#define VA_NUM_ARGS_IMPL(_1, _2, _3, _4, _5, _6, _7, N, ...) N
+
+#define CONCAT_IMPL(x, y) x##y
+#define MACRO_CONCAT(x, y) CONCAT_IMPL(x, y)
+
+#define PREPEND_EACH_ARG_WITH_NAME_1(a) WITH_NAME(a)
+#define PREPEND_EACH_ARG_WITH_NAME_2(a, ...) WITH_NAME(a), PREPEND_EACH_ARG_WITH_NAME_1(__VA_ARGS__)
+#define PREPEND_EACH_ARG_WITH_NAME_3(a, ...) WITH_NAME(a), PREPEND_EACH_ARG_WITH_NAME_2(__VA_ARGS__)
+#define PREPEND_EACH_ARG_WITH_NAME_4(a, ...) WITH_NAME(a), PREPEND_EACH_ARG_WITH_NAME_3(__VA_ARGS__)
+#define PREPEND_EACH_ARG_WITH_NAME_5(a, ...) WITH_NAME(a), PREPEND_EACH_ARG_WITH_NAME_4(__VA_ARGS__)
+#define PREPEND_EACH_ARG_WITH_NAME_6(a, ...) WITH_NAME(a), PREPEND_EACH_ARG_WITH_NAME_5(__VA_ARGS__)
+#define PREPEND_EACH_ARG_WITH_NAME_7(a, ...) WITH_NAME(a), PREPEND_EACH_ARG_WITH_NAME_6(__VA_ARGS__)
+#define PREPEND_EACH_ARG_WITH_NAME(...) \
+ MACRO_CONCAT(PREPEND_EACH_ARG_WITH_NAME_, VA_NUM_ARGS(__VA_ARGS__))(__VA_ARGS__)
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace vibrator {
+
+using ::android::base::StringPrintf;
+
+/* Supported typenames */
+
+// Fallback to typeid
+template <typename T>
+struct TypeName {
+ static const char *Get() { return "<unknown>"; }
+};
+
+// Helper Macro
+#define SUPPORT_TYPENAME(T) \
+ template <> \
+ struct TypeName<T> { \
+ static const char *Get() { \
+ return #T; \
+ } \
+ }
+
+SUPPORT_TYPENAME(bool);
+
+SUPPORT_TYPENAME(int8_t);
+SUPPORT_TYPENAME(int16_t);
+SUPPORT_TYPENAME(int32_t);
+SUPPORT_TYPENAME(uint8_t);
+SUPPORT_TYPENAME(uint16_t);
+SUPPORT_TYPENAME(uint32_t);
+
+SUPPORT_TYPENAME(int8_t *);
+SUPPORT_TYPENAME(int16_t *);
+SUPPORT_TYPENAME(int32_t *);
+SUPPORT_TYPENAME(uint8_t *);
+SUPPORT_TYPENAME(uint16_t *);
+SUPPORT_TYPENAME(uint32_t *);
+
+SUPPORT_TYPENAME(const int16_t *);
+SUPPORT_TYPENAME(const int32_t *);
+SUPPORT_TYPENAME(const uint16_t *);
+SUPPORT_TYPENAME(const uint32_t *);
+
+SUPPORT_TYPENAME(float);
+SUPPORT_TYPENAME(float *);
+SUPPORT_TYPENAME(const float *);
+
+SUPPORT_TYPENAME(std::string);
+SUPPORT_TYPENAME(const std::string &);
+SUPPORT_TYPENAME(const char **);
+
+SUPPORT_TYPENAME(std::vector<ff_effect> *);
+SUPPORT_TYPENAME(const ff_effect *);
+SUPPORT_TYPENAME(ff_effect);
+SUPPORT_TYPENAME(ff_effect *);
+
+SUPPORT_TYPENAME(Effect);
+SUPPORT_TYPENAME(EffectStrength);
+SUPPORT_TYPENAME(std::vector<Effect> *);
+
+SUPPORT_TYPENAME(const std::vector<PrimitivePwle> &);
+SUPPORT_TYPENAME(const std::vector<PrimitivePwle>);
+SUPPORT_TYPENAME(std::vector<PrimitivePwle> &);
+SUPPORT_TYPENAME(std::vector<PrimitivePwle>);
+
+SUPPORT_TYPENAME(const std::shared_ptr<IVibratorCallback> &&);
+SUPPORT_TYPENAME(const std::shared_ptr<IVibratorCallback> &);
+SUPPORT_TYPENAME(const std::shared_ptr<IVibratorCallback>);
+SUPPORT_TYPENAME(std::shared_ptr<IVibratorCallback> &&);
+SUPPORT_TYPENAME(std::shared_ptr<IVibratorCallback> &);
+SUPPORT_TYPENAME(std::shared_ptr<IVibratorCallback>);
+
+SUPPORT_TYPENAME(std::vector<CompositePrimitive> *);
+SUPPORT_TYPENAME(CompositePrimitive);
+
+SUPPORT_TYPENAME(const std::vector<CompositeEffect> &);
+SUPPORT_TYPENAME(const std::vector<CompositeEffect>);
+SUPPORT_TYPENAME(std::vector<CompositeEffect> &);
+SUPPORT_TYPENAME(std::vector<CompositeEffect>);
+
+SUPPORT_TYPENAME(std::vector<Braking> *);
+SUPPORT_TYPENAME(struct pcm **);
+SUPPORT_TYPENAME(const DspMemChunk *);
+SUPPORT_TYPENAME(DspMemChunk *);
+
+/* Support printing */
+
+template <typename T>
+std::ostream &operator<<(std::ostream &out, const std::vector<T> &arg) {
+ out << "{";
+ for (size_t i = 0; i < arg.size(); i++) {
+ out << arg[i];
+ if (i != arg.size() - 1) {
+ out << ", ";
+ }
+ }
+ out << "}";
+ return out;
+}
+
+std::ostream &operator<<(std::ostream &out, const std::shared_ptr<IVibratorCallback> arg);
+std::ostream &operator<<(std::ostream &out, const ff_effect *arg);
+std::ostream &operator<<(std::ostream &out, const ff_effect &arg);
+std::ostream &operator<<(std::ostream &out, const CompositePrimitive &arg);
+std::ostream &operator<<(std::ostream &out, const Braking &arg);
+std::ostream &operator<<(std::ostream &out, const PrimitivePwle &arg);
+std::ostream &operator<<(std::ostream &out, const CompositeEffect &arg);
+std::ostream &operator<<(std::ostream &out, const DspMemChunk *arg);
+std::ostream &operator<<(std::ostream &out, Effect arg);
+std::ostream &operator<<(std::ostream &out, EffectStrength arg);
+
+/* Tracing classes */
+
+class Trace {
+ public:
+ static void debug(int fd);
+ static int depth() { return mDepth; }
+ static void enter() { mDepth++; }
+ static void exit() { mDepth--; }
+ static void push(const std::string &t) { mTrace.push_back(t); }
+ static void pop() { mTrace.pop_back(); }
+ static void save() {
+ std::vector<std::string> temp;
+ std::swap(mTrace, temp);
+ mPreviousTraces.push_back(std::move(temp));
+ }
+
+ private:
+ static int mDepth;
+ static std::vector<std::string> mTrace;
+ static std::vector<std::vector<std::string>> mPreviousTraces;
+};
+
+class FunctionTrace : public Trace {
+ public:
+ FunctionTrace(const char *funcName);
+ FunctionTrace(const char *className, const char *funcName);
+ ~FunctionTrace();
+
+ template <typename T>
+ void addParameter(std::pair<const char *, T> t) {
+ std::stringstream fmt;
+ fmt << TypeName<T>::Get() << " " << t.first << ":" << t.second;
+ mParameters.push_back(fmt.str());
+ }
+
+ template <typename T, typename... Ts>
+ void addParameter(std::pair<const char *, T> t, Ts... ts) {
+ addParameter(t);
+ addParameter(ts...);
+ }
+
+ void addParameter() { return; }
+
+ void save();
+
+ private:
+ std::string mClassName;
+ std::string mFuncName;
+ std::vector<std::string> mParameters;
+};
+
+class EffectTrace : public Trace {
+ public:
+ EffectTrace(uint16_t index, float scale, int32_t duration, const DspMemChunk *ch);
+ void save();
+
+ private:
+ std::string mDescription;
+};
+
+} // namespace vibrator
+} // namespace hardware
+} // namespace android
+} // namespace aidl
diff --git a/vibrator/cs40l26/Vibrator.cpp b/vibrator/cs40l26/Vibrator.cpp
index c2fd73a5..324d8835 100644
--- a/vibrator/cs40l26/Vibrator.cpp
+++ b/vibrator/cs40l26/Vibrator.cpp
@@ -19,8 +19,10 @@
#include <android-base/properties.h>
#include <hardware/hardware.h>
#include <hardware/vibrator.h>
+#include <linux/version.h>
#include <log/log.h>
#include <utils/Trace.h>
+#include <vendor_vibrator_hal_flags.h>
#include <chrono>
#include <cinttypes>
@@ -32,21 +34,35 @@
#include <optional>
#include <sstream>
+#include "DspMemChunk.h"
#include "Stats.h"
+#include "Trace.h"
#ifndef ARRAY_SIZE
#define ARRAY_SIZE(x) (sizeof((x)) / sizeof((x)[0]))
#endif
+namespace vibrator_aconfig_flags = vendor::vibrator::hal::flags;
+
namespace aidl {
namespace android {
namespace hardware {
namespace vibrator {
-#ifdef HAPTIC_TRACE
-#define HAPTICS_TRACE(...) ALOGD(__VA_ARGS__)
+#ifdef VIBRATOR_TRACE
+/* Function Trace */
+#define VFTRACE(...) \
+ ATRACE_NAME(StringPrintf("Vibrator::%s", __func__).c_str()); \
+ auto f_trace_ = std::make_unique<FunctionTrace>("Vibrator", __func__); \
+ __VA_OPT__(f_trace_->addParameter(PREPEND_EACH_ARG_WITH_NAME(__VA_ARGS__))); \
+ f_trace_->save()
+/* Effect Trace */
+#define VETRACE(i, s, d, ch) \
+ auto e_trace_ = std::make_unique<EffectTrace>(i, s, d, ch); \
+ e_trace_->save()
#else
-#define HAPTICS_TRACE(...)
+#define VFTRACE(...) ATRACE_NAME(StringPrintf("Vibrator::%s", __func__).c_str())
+#define VETRACE(...)
#endif
static constexpr uint16_t FF_CUSTOM_DATA_LEN_MAX_COMP = 2044; // (COMPOSE_SIZE_MAX + 1) * 8 + 4
@@ -67,10 +83,6 @@ static constexpr auto ASYNC_COMPLETION_TIMEOUT = std::chrono::milliseconds(100);
static constexpr auto POLLING_TIMEOUT = 50; // POLLING_TIMEOUT < ASYNC_COMPLETION_TIMEOUT
static constexpr int32_t COMPOSE_DELAY_MAX_MS = 10000;
-/* nsections is 8 bits. Need to preserve 1 section for the first delay before the first effect. */
-static constexpr int32_t COMPOSE_SIZE_MAX = 254;
-static constexpr int32_t COMPOSE_PWLE_SIZE_MAX_DEFAULT = 127;
-
// Measured resonant frequency, f0_measured, is represented by Q10.14 fixed
// point format on cs40l26 devices. The expression to calculate f0 is:
// f0 = f0_measured / 2^Q14_BIT_SHIFT
@@ -91,21 +103,10 @@ static constexpr int32_t Q15_BIT_SHIFT = 15;
// See the LRA Calibration Support documentation for more details.
static constexpr int32_t Q16_BIT_SHIFT = 16;
-static constexpr int32_t COMPOSE_PWLE_PRIMITIVE_DURATION_MAX_MS = 16383;
-
-static constexpr uint32_t WT_LEN_CALCD = 0x00800000;
-static constexpr uint8_t PWLE_CHIRP_BIT = 0x8; // Dynamic/static frequency and voltage
-static constexpr uint8_t PWLE_BRAKE_BIT = 0x4;
-static constexpr uint8_t PWLE_AMP_REG_BIT = 0x2;
-
static constexpr float PWLE_LEVEL_MIN = 0.0;
static constexpr float PWLE_LEVEL_MAX = 1.0;
-static constexpr float CS40L26_PWLE_LEVEL_MIN = -1.0;
-static constexpr float CS40L26_PWLE_LEVEL_MAX = 0.9995118;
static constexpr float PWLE_FREQUENCY_RESOLUTION_HZ = 1.00;
-static constexpr float PWLE_FREQUENCY_MIN_HZ = 30.0f;
static constexpr float RESONANT_FREQUENCY_DEFAULT = 145.0f;
-static constexpr float PWLE_FREQUENCY_MAX_HZ = 300.0f;
static constexpr float PWLE_BW_MAP_SIZE =
1 + ((PWLE_FREQUENCY_MAX_HZ - PWLE_FREQUENCY_MIN_HZ) / PWLE_FREQUENCY_RESOLUTION_HZ);
@@ -154,230 +155,6 @@ enum vibe_state {
std::mutex mActiveId_mutex; // protects mActiveId
-class DspMemChunk {
- private:
- std::unique_ptr<uint8_t[]> head;
- size_t bytes = 0;
- uint8_t waveformType;
- uint8_t *_current;
- const uint8_t *_max;
- uint32_t _cache = 0;
- int _cachebits = 0;
-
- bool isEnd() const { return _current == _max; }
- int min(int x, int y) { return x < y ? x : y; }
-
- int write(int nbits, uint32_t val) {
- HAPTICS_TRACE(" DspMemChunk::write(nbits:%d, val:%u)", nbits, val);
- int nwrite, i;
-
- nwrite = min(24 - _cachebits, nbits);
- _cache <<= nwrite;
- _cache |= val >> (nbits - nwrite);
- _cachebits += nwrite;
- nbits -= nwrite;
-
- if (_cachebits == 24) {
- if (isEnd())
- return -ENOSPC;
-
- _cache &= 0xFFFFFF;
- for (i = 0; i < sizeof(_cache); i++, _cache <<= 8)
- *_current++ = (_cache & 0xFF000000) >> 24;
-
- bytes += sizeof(_cache);
- _cachebits = 0;
- }
-
- if (nbits)
- return write(nbits, val);
-
- return 0;
- }
-
- int fToU16(float input, uint16_t *output, float scale, float min, float max) {
- HAPTICS_TRACE(" DspMemChunk::fToU16(input:%f, output, scale:%f, min:%f, max:%f", input,
- scale, min, max);
- if (input < min || input > max)
- return -ERANGE;
-
- *output = roundf(input * scale);
- return 0;
- }
-
- void constructPwleSegment(uint16_t delay, uint16_t amplitude, uint16_t frequency, uint8_t flags,
- uint32_t vbemfTarget = 0) {
- HAPTICS_TRACE(
- " constructPwleSegment(ch, delay:%u, amplitude:%u, frequency:%u, flags:%u"
- ", vbemfTarget:%u)",
- delay, amplitude, frequency, flags, vbemfTarget);
- write(16, delay);
- write(12, amplitude);
- write(12, frequency);
- /* feature flags to control the chirp, CLAB braking, back EMF amplitude regulation */
- write(8, (flags | 1) << 4);
- if (flags & PWLE_AMP_REG_BIT) {
- write(24, vbemfTarget); /* target back EMF voltage */
- }
- }
-
- public:
- uint8_t *front() const { return head.get(); }
- uint8_t type() const { return waveformType; }
- size_t size() const { return bytes; }
-
- DspMemChunk(uint8_t type, size_t size) : head(new uint8_t[size]{0x00}) {
- HAPTICS_TRACE(" DspMemChunk(size:%zu)", size);
- waveformType = type;
- _current = head.get();
- _max = _current + size;
-
- if (waveformType == WAVEFORM_COMPOSE) {
- write(8, 0); /* Padding */
- write(8, 0); /* nsections placeholder */
- write(8, 0); /* repeat */
- } else if (waveformType == WAVEFORM_PWLE) {
- write(24, 0); /* Waveform length placeholder */
- write(8, 0); /* Repeat */
- write(12, 0); /* Wait time between repeats */
- write(8, 0); /* nsections placeholder */
- } else {
- ALOGE("%s: Invalid type: %u", __func__, waveformType);
- }
- }
-
- int flush() {
- HAPTICS_TRACE(" DspMemChunk::flush()");
- if (!_cachebits)
- return 0;
-
- return write(24 - _cachebits, 0);
- }
-
- int constructComposeSegment(uint32_t effectVolLevel, uint32_t effectIndex, uint8_t repeat,
- uint8_t flags, uint16_t nextEffectDelay) {
- HAPTICS_TRACE(
- " constructComposeSegment(effectVolLevel:%u, effectIndex:%u, repeat:%d, "
- "flags:%d, nextEffectDelay:%u",
- effectVolLevel, effectIndex, repeat, flags, nextEffectDelay);
- if (waveformType != WAVEFORM_COMPOSE) {
- ALOGE("%s: Invalid type: %d", __func__, waveformType);
- return -EDOM;
- }
- if (effectVolLevel > 100 || effectIndex > WAVEFORM_MAX_PHYSICAL_INDEX) {
- ALOGE("%s: Invalid argument: %u, %u", __func__, effectVolLevel, effectIndex);
- return -EINVAL;
- }
- write(8, effectVolLevel); /* amplitude */
- write(8, effectIndex); /* index */
- write(8, repeat); /* repeat */
- write(8, flags); /* flags */
- write(16, nextEffectDelay); /* delay */
- return 0;
- }
-
- int constructActiveSegment(int duration, float amplitude, float frequency, bool chirp) {
- HAPTICS_TRACE(" constructActiveSegment(duration:%d, amplitude:%f, frequency:%f)",
- duration, amplitude, frequency);
- uint16_t delay = 0;
- uint16_t amp = 0;
- uint16_t freq = 0;
- uint8_t flags = 0x0;
- if (waveformType != WAVEFORM_PWLE) {
- ALOGE("%s: Invalid type: %d", __func__, waveformType);
- return -EDOM;
- }
- if ((fToU16(duration, &delay, 4, 0.0f, COMPOSE_PWLE_PRIMITIVE_DURATION_MAX_MS) < 0) ||
- (fToU16(amplitude, &amp, 2048, CS40L26_PWLE_LEVEL_MIN, CS40L26_PWLE_LEVEL_MAX) < 0) ||
- (fToU16(frequency, &freq, 4, PWLE_FREQUENCY_MIN_HZ, PWLE_FREQUENCY_MAX_HZ) < 0)) {
- ALOGE("%s: Invalid argument: %d, %f, %f", __func__, duration, amplitude, frequency);
- return -ERANGE;
- }
- if (chirp) {
- flags |= PWLE_CHIRP_BIT;
- }
- constructPwleSegment(delay, amp, freq, flags, 0 /*ignored*/);
- return 0;
- }
-
- int constructBrakingSegment(int duration, Braking brakingType) {
- HAPTICS_TRACE(" constructBrakingSegment(duration:%d, brakingType:%s)", duration,
- toString(brakingType).c_str());
- uint16_t delay = 0;
- uint16_t freq = 0;
- uint8_t flags = 0x00;
- if (waveformType != WAVEFORM_PWLE) {
- ALOGE("%s: Invalid type: %d", __func__, waveformType);
- return -EDOM;
- }
- if (fToU16(duration, &delay, 4, 0.0f, COMPOSE_PWLE_PRIMITIVE_DURATION_MAX_MS) < 0) {
- ALOGE("%s: Invalid argument: %d", __func__, duration);
- return -ERANGE;
- }
- fToU16(PWLE_FREQUENCY_MIN_HZ, &freq, 4, PWLE_FREQUENCY_MIN_HZ, PWLE_FREQUENCY_MAX_HZ);
- if (static_cast<std::underlying_type<Braking>::type>(brakingType)) {
- flags |= PWLE_BRAKE_BIT;
- }
-
- constructPwleSegment(delay, 0 /*ignored*/, freq, flags, 0 /*ignored*/);
- return 0;
- }
-
- int updateWLength(uint32_t totalDuration) {
- HAPTICS_TRACE(" updateWLength(totalDuration:%u)", totalDuration);
- uint8_t *f = front();
- if (f == nullptr) {
- ALOGE("%s: head does not exist!", __func__);
- return -ENOMEM;
- }
- if (waveformType != WAVEFORM_PWLE) {
- ALOGE("%s: Invalid type: %d", __func__, waveformType);
- return -EDOM;
- }
- if (totalDuration > 0x7FFFF) {
- ALOGE("%s: Invalid argument: %u", __func__, totalDuration);
- return -EINVAL;
- }
- totalDuration *= 8; /* Unit: 0.125 ms (since wlength played @ 8kHz). */
- totalDuration |=
- WT_LEN_CALCD; /* Bit 23 is for WT_LEN_CALCD; Bit 22 is for WT_INDEFINITE. */
- *(f + 0) = (totalDuration >> 24) & 0xFF;
- *(f + 1) = (totalDuration >> 16) & 0xFF;
- *(f + 2) = (totalDuration >> 8) & 0xFF;
- *(f + 3) = totalDuration & 0xFF;
- return 0;
- }
-
- int updateNSection(int segmentIdx) {
- HAPTICS_TRACE(" updateNSection(segmentIdx:%u)", segmentIdx);
- uint8_t *f = front();
- if (f == nullptr) {
- ALOGE("%s: head does not exist!", __func__);
- return -ENOMEM;
- }
-
- if (waveformType == WAVEFORM_COMPOSE) {
- if (segmentIdx > COMPOSE_SIZE_MAX + 1 /*1st effect may have a delay*/) {
- ALOGE("%s: Invalid argument: %d", __func__, segmentIdx);
- return -EINVAL;
- }
- *(f + 2) = (0xFF & segmentIdx);
- } else if (waveformType == WAVEFORM_PWLE) {
- if (segmentIdx > COMPOSE_PWLE_SIZE_MAX_DEFAULT) {
- ALOGE("%s: Invalid argument: %d", __func__, segmentIdx);
- return -EINVAL;
- }
- *(f + 7) |= (0xF0 & segmentIdx) >> 4; /* Bit 4 to 7 */
- *(f + 9) |= (0x0F & segmentIdx) << 4; /* Bit 3 to 0 */
- } else {
- ALOGE("%s: Invalid type: %d", __func__, waveformType);
- return -EDOM;
- }
-
- return 0;
- }
-};
-
// Discrete points of frequency:max_level pairs around resonant(145Hz default) frequency
// Initialize the actuator LUXSHARE_ICT_081545 limits to 0.447 and others 1.0
#if defined(LUXSHARE_ICT_081545)
@@ -408,7 +185,12 @@ Vibrator::Vibrator(std::unique_ptr<HwApi> hwapi, std::unique_ptr<HwCal> hwcal,
mFfEffects.resize(WAVEFORM_MAX_INDEX);
mEffectDurations.resize(WAVEFORM_MAX_INDEX);
mEffectDurations = {
- 1000, 100, 12, 1000, 300, 130, 150, 500, 100, 5, 12, 1000, 1000, 1000,
+#if defined(UNSPECIFIED_ACTUATOR)
+ /* For Z-LRA actuators */
+ 1000, 100, 25, 1000, 300, 133, 150, 500, 100, 6, 12, 1000, 13, 5,
+#else
+ 1000, 100, 12, 1000, 300, 133, 150, 500, 100, 5, 12, 1000, 13, 5,
+#endif
}; /* 11+3 waveforms. The duration must < UINT16_MAX */
mEffectCustomData.reserve(WAVEFORM_MAX_INDEX);
@@ -421,7 +203,8 @@ Vibrator::Vibrator(std::unique_ptr<HwApi> hwapi, std::unique_ptr<HwCal> hwcal,
mFfEffects[effectIndex] = {
.type = FF_PERIODIC,
.id = -1,
- .replay.length = static_cast<uint16_t>(mEffectDurations[effectIndex]),
+ // Length == 0 to allow firmware control of the duration
+ .replay.length = 0,
.u.periodic.waveform = FF_CUSTOM,
.u.periodic.custom_data = mEffectCustomData[effectIndex].data(),
.u.periodic.custom_len =
@@ -429,9 +212,9 @@ Vibrator::Vibrator(std::unique_ptr<HwApi> hwapi, std::unique_ptr<HwCal> hwcal,
};
// Bypass the waveform update due to different input name
if (INPUT_EVENT_NAME.find("cs40l26") != std::string::npos) {
- if (!mHwApi->setFFEffect(
- &mFfEffects[effectIndex],
- static_cast<uint16_t>(mFfEffects[effectIndex].replay.length))) {
+ // Let the firmware control the playback duration to avoid
+ // cutting any effect that is played short
+ if (!mHwApi->setFFEffect(&mFfEffects[effectIndex], mEffectDurations[effectIndex])) {
mStatsApi->logError(kHwApiError);
ALOGE("Failed upload effect %d (%d): %s", effectIndex, errno, strerror(errno));
}
@@ -491,7 +274,6 @@ Vibrator::Vibrator(std::unique_ptr<HwApi> hwapi, std::unique_ptr<HwCal> hwcal,
} else {
ALOGD("Unsupported calibration version: %u!", calVer);
}
- HAPTICS_TRACE("Vibrator(hwapi, hwcal:%u)", calVer);
mHwApi->setF0CompEnable(mHwCal->isF0CompEnabled());
mHwApi->setRedcCompEnable(mHwCal->isRedcCompEnabled());
@@ -530,8 +312,7 @@ Vibrator::Vibrator(std::unique_ptr<HwApi> hwapi, std::unique_ptr<HwCal> hwcal,
}
ndk::ScopedAStatus Vibrator::getCapabilities(int32_t *_aidl_return) {
- HAPTICS_TRACE("getCapabilities(_aidl_return)");
- ATRACE_NAME("Vibrator::getCapabilities");
+ VFTRACE(_aidl_return);
int32_t ret = IVibrator::CAP_ON_CALLBACK | IVibrator::CAP_PERFORM_CALLBACK |
IVibrator::CAP_AMPLITUDE_CONTROL | IVibrator::CAP_GET_RESONANT_FREQUENCY |
@@ -553,8 +334,7 @@ ndk::ScopedAStatus Vibrator::getCapabilities(int32_t *_aidl_return) {
}
ndk::ScopedAStatus Vibrator::off() {
- HAPTICS_TRACE("off()");
- ATRACE_NAME("Vibrator::off");
+ VFTRACE();
bool ret{true};
const std::scoped_lock<std::mutex> lock(mActiveId_mutex);
@@ -607,8 +387,7 @@ ndk::ScopedAStatus Vibrator::off() {
ndk::ScopedAStatus Vibrator::on(int32_t timeoutMs,
const std::shared_ptr<IVibratorCallback> &callback) {
- ATRACE_NAME(StringPrintf("Vibrator::on %dms", timeoutMs).c_str());
- HAPTICS_TRACE("on(timeoutMs:%d, callback)", timeoutMs);
+ VFTRACE(timeoutMs, callback);
mStatsApi->logLatencyStart(kWaveformEffectLatency);
if (timeoutMs > MAX_TIME_MS) {
@@ -633,11 +412,7 @@ ndk::ScopedAStatus Vibrator::on(int32_t timeoutMs,
ndk::ScopedAStatus Vibrator::perform(Effect effect, EffectStrength strength,
const std::shared_ptr<IVibratorCallback> &callback,
int32_t *_aidl_return) {
- ATRACE_NAME(StringPrintf("Vibrator::perform %s,%s", toString(effect).c_str(),
- toString(strength).c_str())
- .c_str());
- HAPTICS_TRACE("perform(effect:%s, strength:%s, callback, _aidl_return)",
- toString(effect).c_str(), toString(strength).c_str());
+ VFTRACE(effect, strength, callback, _aidl_return);
mStatsApi->logLatencyStart(kPrebakedEffectLatency);
@@ -645,15 +420,14 @@ ndk::ScopedAStatus Vibrator::perform(Effect effect, EffectStrength strength,
}
ndk::ScopedAStatus Vibrator::getSupportedEffects(std::vector<Effect> *_aidl_return) {
- HAPTICS_TRACE("getSupportedEffects(_aidl_return)");
+ VFTRACE(_aidl_return);
*_aidl_return = {Effect::TEXTURE_TICK, Effect::TICK, Effect::CLICK, Effect::HEAVY_CLICK,
Effect::DOUBLE_CLICK};
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus Vibrator::setAmplitude(float amplitude) {
- HAPTICS_TRACE("setAmplitude(amplitude:%f)", amplitude);
- ATRACE_NAME("Vibrator::setAmplitude");
+ VFTRACE(amplitude);
if (amplitude <= 0.0f || amplitude > 1.0f) {
mStatsApi->logError(kBadAmplitudeError);
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
@@ -669,8 +443,7 @@ ndk::ScopedAStatus Vibrator::setAmplitude(float amplitude) {
}
ndk::ScopedAStatus Vibrator::setExternalControl(bool enabled) {
- HAPTICS_TRACE("setExternalControl(enabled:%u)", enabled);
- ATRACE_NAME("Vibrator::setExternalControl");
+ VFTRACE(enabled);
setGlobalAmplitude(enabled);
if (!mHasPassthroughHapticDevice) {
@@ -695,28 +468,26 @@ ndk::ScopedAStatus Vibrator::setExternalControl(bool enabled) {
}
ndk::ScopedAStatus Vibrator::getCompositionDelayMax(int32_t *maxDelayMs) {
- HAPTICS_TRACE("getCompositionDelayMax(maxDelayMs)");
- ATRACE_NAME("Vibrator::getCompositionDelayMax");
+ VFTRACE(maxDelayMs);
*maxDelayMs = COMPOSE_DELAY_MAX_MS;
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus Vibrator::getCompositionSizeMax(int32_t *maxSize) {
- HAPTICS_TRACE("getCompositionSizeMax(maxSize)");
- ATRACE_NAME("Vibrator::getCompositionSizeMax");
+ VFTRACE(maxSize);
*maxSize = COMPOSE_SIZE_MAX;
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus Vibrator::getSupportedPrimitives(std::vector<CompositePrimitive> *supported) {
- HAPTICS_TRACE("getSupportedPrimitives(supported)");
+ VFTRACE(supported);
*supported = mSupportedPrimitives;
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus Vibrator::getPrimitiveDuration(CompositePrimitive primitive,
int32_t *durationMs) {
- HAPTICS_TRACE("getPrimitiveDuration(primitive:%s, durationMs)", toString(primitive).c_str());
+ VFTRACE(primitive, durationMs);
ndk::ScopedAStatus status;
uint32_t effectIndex;
if (primitive != CompositePrimitive::NOOP) {
@@ -734,8 +505,7 @@ ndk::ScopedAStatus Vibrator::getPrimitiveDuration(CompositePrimitive primitive,
ndk::ScopedAStatus Vibrator::compose(const std::vector<CompositeEffect> &composite,
const std::shared_ptr<IVibratorCallback> &callback) {
- ATRACE_NAME(StringPrintf("Vibrator::compose size=%zu", composite.size()).c_str());
- HAPTICS_TRACE("compose(composite, callback)");
+ VFTRACE(composite, callback);
uint16_t size;
uint16_t nextEffectDelay;
uint16_t totalDuration = 0;
@@ -826,7 +596,8 @@ ndk::ScopedAStatus Vibrator::compose(const std::vector<CompositeEffect> &composi
mStatsApi->logError(kComposeFailError);
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
} else {
- mFfEffects[WAVEFORM_COMPOSE].replay.length = totalDuration;
+ // Composition duration should be 0 to allow firmware to play the whole effect
+ mFfEffects[WAVEFORM_COMPOSE].replay.length = 0;
return performEffect(WAVEFORM_MAX_INDEX /*ignored*/, VOLTAGE_SCALE_MAX /*ignored*/, &ch,
callback);
}
@@ -834,7 +605,7 @@ ndk::ScopedAStatus Vibrator::compose(const std::vector<CompositeEffect> &composi
ndk::ScopedAStatus Vibrator::on(uint32_t timeoutMs, uint32_t effectIndex, const DspMemChunk *ch,
const std::shared_ptr<IVibratorCallback> &callback) {
- HAPTICS_TRACE("on(timeoutMs:%u, effectIndex:%u, ch, callback)", timeoutMs, effectIndex);
+ VFTRACE(timeoutMs, effectIndex, ch, callback);
ndk::ScopedAStatus status = ndk::ScopedAStatus::ok();
if (effectIndex >= FF_MAX_EFFECTS) {
@@ -881,6 +652,7 @@ ndk::ScopedAStatus Vibrator::on(uint32_t timeoutMs, uint32_t effectIndex, const
} else if (effectIndex == WAVEFORM_SHORT_VIBRATION_EFFECT_INDEX ||
effectIndex == WAVEFORM_LONG_VIBRATION_EFFECT_INDEX) {
/* Update duration for long/short vibration. */
+ // We can pass in the timeout for long/short vibration effects
mFfEffects[effectIndex].replay.length = static_cast<uint16_t>(timeoutMs);
if (!mHwApi->setFFEffect(&mFfEffects[effectIndex], static_cast<uint16_t>(timeoutMs))) {
mStatsApi->logError(kHwApiError);
@@ -892,6 +664,7 @@ ndk::ScopedAStatus Vibrator::on(uint32_t timeoutMs, uint32_t effectIndex, const
const std::scoped_lock<std::mutex> lock(mActiveId_mutex);
mActiveId = effectIndex;
/* Play the event now. */
+ VETRACE(effectIndex, mLongEffectScale, timeoutMs, ch);
mStatsApi->logLatencyEnd();
if (!mHwApi->setFFPlay(effectIndex, true)) {
mStatsApi->logError(kHwApiError);
@@ -905,8 +678,7 @@ ndk::ScopedAStatus Vibrator::on(uint32_t timeoutMs, uint32_t effectIndex, const
}
uint16_t Vibrator::amplitudeToScale(float amplitude, float maximum, bool scalable) {
- HAPTICS_TRACE("amplitudeToScale(amplitude:%f, maximum:%f, scalable:%d)", amplitude, maximum,
- scalable ? 1 : 0);
+ VFTRACE(amplitude, maximum, scalable);
float ratio = 100; /* Unit: % */
if (maximum != 0)
@@ -968,6 +740,13 @@ uint16_t Vibrator::amplitudeToScale(float amplitude, float maximum, bool scalabl
}
void Vibrator::updateContext() {
+ /* Don't enable capo from HAL if flag is set to remove it */
+ if (vibrator_aconfig_flags::remove_capo()) {
+ mContextEnable = false;
+ return;
+ }
+
+ VFTRACE();
mContextEnable = mHwApi->getContextEnable();
if (mContextEnable && !mContextEnabledPreviously) {
mContextListener = CapoDetector::start();
@@ -990,8 +769,7 @@ void Vibrator::updateContext() {
}
ndk::ScopedAStatus Vibrator::setEffectAmplitude(float amplitude, float maximum, bool scalable) {
- HAPTICS_TRACE("setEffectAmplitude(amplitude:%f, maximum:%f, scalable:%d)", amplitude, maximum,
- scalable ? 1 : 0);
+ VFTRACE(amplitude, maximum, scalable);
uint16_t scale;
#ifdef ADAPTIVE_HAPTICS_V1
@@ -1009,7 +787,7 @@ ndk::ScopedAStatus Vibrator::setEffectAmplitude(float amplitude, float maximum,
}
ndk::ScopedAStatus Vibrator::setGlobalAmplitude(bool set) {
- HAPTICS_TRACE("setGlobalAmplitude(set:%u)", set);
+ VFTRACE(set);
uint8_t amplitude = set ? roundf(mLongEffectScale * mLongEffectVol[1]) : VOLTAGE_SCALE_MAX;
if (!set) {
mLongEffectScale = 1.0; // Reset the scale for the later new effect.
@@ -1018,12 +796,14 @@ ndk::ScopedAStatus Vibrator::setGlobalAmplitude(bool set) {
}
ndk::ScopedAStatus Vibrator::getSupportedAlwaysOnEffects(std::vector<Effect> * /*_aidl_return*/) {
+ VFTRACE();
mStatsApi->logError(kUnsupportedOpError);
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
}
ndk::ScopedAStatus Vibrator::alwaysOnEnable(int32_t /*id*/, Effect /*effect*/,
EffectStrength /*strength*/) {
+ VFTRACE();
mStatsApi->logError(kUnsupportedOpError);
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
}
@@ -1033,14 +813,14 @@ ndk::ScopedAStatus Vibrator::alwaysOnDisable(int32_t /*id*/) {
}
ndk::ScopedAStatus Vibrator::getResonantFrequency(float *resonantFreqHz) {
- HAPTICS_TRACE("getResonantFrequency(resonantFreqHz)");
+ VFTRACE(resonantFreqHz);
*resonantFreqHz = mResonantFrequency;
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus Vibrator::getQFactor(float *qFactor) {
- HAPTICS_TRACE("getQFactor(qFactor)");
+ VFTRACE(qFactor);
std::string caldata{8, '0'};
if (!mHwCal->getQ(&caldata)) {
mStatsApi->logError(kHwCalError);
@@ -1053,7 +833,7 @@ ndk::ScopedAStatus Vibrator::getQFactor(float *qFactor) {
}
ndk::ScopedAStatus Vibrator::getFrequencyResolution(float *freqResolutionHz) {
- HAPTICS_TRACE("getFrequencyResolution(freqResolutionHz)");
+ VFTRACE(freqResolutionHz);
int32_t capabilities;
Vibrator::getCapabilities(&capabilities);
if (capabilities & IVibrator::CAP_FREQUENCY_CONTROL) {
@@ -1066,7 +846,7 @@ ndk::ScopedAStatus Vibrator::getFrequencyResolution(float *freqResolutionHz) {
}
ndk::ScopedAStatus Vibrator::getFrequencyMinimum(float *freqMinimumHz) {
- HAPTICS_TRACE("getFrequencyMinimum(freqMinimumHz)");
+ VFTRACE(freqMinimumHz);
int32_t capabilities;
Vibrator::getCapabilities(&capabilities);
if (capabilities & IVibrator::CAP_FREQUENCY_CONTROL) {
@@ -1079,6 +859,7 @@ ndk::ScopedAStatus Vibrator::getFrequencyMinimum(float *freqMinimumHz) {
}
void Vibrator::createPwleMaxLevelLimitMap() {
+ VFTRACE();
int32_t capabilities;
Vibrator::getCapabilities(&capabilities);
if (!(capabilities & IVibrator::CAP_FREQUENCY_CONTROL)) {
@@ -1130,6 +911,7 @@ void Vibrator::createPwleMaxLevelLimitMap() {
}
void Vibrator::createBandwidthAmplitudeMap() {
+ VFTRACE();
// Use constant Q Factor of 10 from HW's suggestion
const float qFactor = 10.0f;
const float blSys = 1.1f;
@@ -1204,7 +986,7 @@ void Vibrator::createBandwidthAmplitudeMap() {
}
ndk::ScopedAStatus Vibrator::getBandwidthAmplitudeMap(std::vector<float> *_aidl_return) {
- HAPTICS_TRACE("getBandwidthAmplitudeMap(_aidl_return)");
+ VFTRACE(_aidl_return);
int32_t capabilities;
Vibrator::getCapabilities(&capabilities);
if (capabilities & IVibrator::CAP_FREQUENCY_CONTROL) {
@@ -1223,7 +1005,7 @@ ndk::ScopedAStatus Vibrator::getBandwidthAmplitudeMap(std::vector<float> *_aidl_
}
ndk::ScopedAStatus Vibrator::getPwlePrimitiveDurationMax(int32_t *durationMs) {
- HAPTICS_TRACE("getPwlePrimitiveDurationMax(durationMs)");
+ VFTRACE(durationMs);
int32_t capabilities;
Vibrator::getCapabilities(&capabilities);
if (capabilities & IVibrator::CAP_COMPOSE_PWLE_EFFECTS) {
@@ -1236,7 +1018,7 @@ ndk::ScopedAStatus Vibrator::getPwlePrimitiveDurationMax(int32_t *durationMs) {
}
ndk::ScopedAStatus Vibrator::getPwleCompositionSizeMax(int32_t *maxSize) {
- HAPTICS_TRACE("getPwleCompositionSizeMax(maxSize)");
+ VFTRACE(maxSize);
int32_t capabilities;
Vibrator::getCapabilities(&capabilities);
if (capabilities & IVibrator::CAP_COMPOSE_PWLE_EFFECTS) {
@@ -1249,7 +1031,7 @@ ndk::ScopedAStatus Vibrator::getPwleCompositionSizeMax(int32_t *maxSize) {
}
ndk::ScopedAStatus Vibrator::getSupportedBraking(std::vector<Braking> *supported) {
- HAPTICS_TRACE("getSupportedBraking(supported)");
+ VFTRACE(supported);
int32_t capabilities;
Vibrator::getCapabilities(&capabilities);
if (capabilities & IVibrator::CAP_COMPOSE_PWLE_EFFECTS) {
@@ -1265,20 +1047,20 @@ ndk::ScopedAStatus Vibrator::getSupportedBraking(std::vector<Braking> *supported
static void resetPreviousEndAmplitudeEndFrequency(float *prevEndAmplitude,
float *prevEndFrequency) {
- HAPTICS_TRACE(" resetPreviousEndAmplitudeEndFrequency(prevEndAmplitude, prevEndFrequency)");
+ VFTRACE(prevEndAmplitude, prevEndFrequency);
const float reset = -1.0;
*prevEndAmplitude = reset;
*prevEndFrequency = reset;
}
static void incrementIndex(int *index) {
+ VFTRACE(index);
*index += 1;
}
ndk::ScopedAStatus Vibrator::composePwle(const std::vector<PrimitivePwle> &composite,
const std::shared_ptr<IVibratorCallback> &callback) {
- ATRACE_NAME(StringPrintf("Vibrator::composePwle size=%zu", composite.size()).c_str());
- HAPTICS_TRACE("composePwle(composite, callback)");
+ VFTRACE(composite, callback);
int32_t capabilities;
mStatsApi->logLatencyStart(kPwleEffectLatency);
@@ -1386,8 +1168,8 @@ ndk::ScopedAStatus Vibrator::composePwle(const std::vector<PrimitivePwle> &compo
auto braking = e.get<PrimitivePwle::braking>();
if (braking.braking > Braking::CLAB) {
mStatsApi->logError(kBadPrimitiveError);
- ALOGE("%s: #%u: braking: Invalid braking type %d", __func__, c,
- braking.braking);
+ ALOGE("%s: #%u: braking: Invalid braking type %s", __func__, c,
+ toString(braking.braking).c_str());
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
} else if (!isClabSupported && (braking.braking == Braking::CLAB)) {
mStatsApi->logError(kBadPrimitiveError);
@@ -1403,16 +1185,16 @@ ndk::ScopedAStatus Vibrator::composePwle(const std::vector<PrimitivePwle> &compo
if (ch.constructBrakingSegment(0, braking.braking) < 0) {
mStatsApi->logError(kPwleConstructionFailError);
- ALOGE("%s: #%u: braking: Failed to construct for type %d", __func__, c,
- braking.braking);
+ ALOGE("%s: #%u: braking: Failed to construct for type %s", __func__, c,
+ toString(braking.braking).c_str());
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
incrementIndex(&segmentIdx);
if (ch.constructBrakingSegment(braking.duration, braking.braking) < 0) {
mStatsApi->logError(kPwleConstructionFailError);
- ALOGE("%s: #%u: braking: Failed to construct for type %d with duration %d",
- __func__, c, braking.braking, braking.duration);
+ ALOGE("%s: #%u: braking: Failed to construct for type %s with duration %d",
+ __func__, c, toString(braking.braking).c_str(), braking.duration);
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
incrementIndex(&segmentIdx);
@@ -1440,9 +1222,18 @@ ndk::ScopedAStatus Vibrator::composePwle(const std::vector<PrimitivePwle> &compo
ALOGE("Total duration is too long (%d)!", totalDuration);
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
} else {
+ // For now, let's pass the duration for PWLEs
mFfEffects[WAVEFORM_PWLE].replay.length = totalDuration;
}
+ /* Update word count */
+ if (ch.updateWCount(segmentIdx) < 0) {
+ mStatsApi->logError(kPwleConstructionFailError);
+ ALOGE("%s: Failed to update the waveform word count", __func__);
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+ }
+
+ /* Update waveform length */
if (ch.updateWLength(totalDuration) < 0) {
mStatsApi->logError(kPwleConstructionFailError);
ALOGE("%s: Failed to update the waveform length length", __func__);
@@ -1461,12 +1252,11 @@ ndk::ScopedAStatus Vibrator::composePwle(const std::vector<PrimitivePwle> &compo
}
bool Vibrator::isUnderExternalControl() {
- HAPTICS_TRACE("isUnderExternalControl()");
+ VFTRACE();
return mIsUnderExternalControl;
}
binder_status_t Vibrator::dump(int fd, const char **args, uint32_t numArgs) {
- HAPTICS_TRACE("dump(fd:%d, args, numArgs:%u)", fd, numArgs);
if (fd < 0) {
ALOGE("Called debug() with invalid fd.");
return STATUS_OK;
@@ -1611,12 +1401,16 @@ binder_status_t Vibrator::dump(int fd, const char **args, uint32_t numArgs) {
dprintf(fd, "\nDBC Enabled\n");
}
+#ifdef VIBRATOR_TRACE
+ Trace::debug(fd);
+#endif
+
fsync(fd);
return STATUS_OK;
}
bool Vibrator::hasHapticAlsaDevice() {
- HAPTICS_TRACE("hasHapticAlsaDevice()");
+ VFTRACE();
// We need to call findHapticAlsaDevice once only. Calling in the
// constructor is too early in the boot process and the pcm file contents
// are empty. Hence we make the call here once only right before we need to.
@@ -1637,10 +1431,7 @@ bool Vibrator::hasHapticAlsaDevice() {
ndk::ScopedAStatus Vibrator::getSimpleDetails(Effect effect, EffectStrength strength,
uint32_t *outEffectIndex, uint32_t *outTimeMs,
uint32_t *outVolLevel) {
- HAPTICS_TRACE(
- "getSimpleDetails(effect:%s, strength:%s, outEffectIndex, outTimeMs"
- ", outVolLevel)",
- toString(effect).c_str(), toString(strength).c_str());
+ VFTRACE(effect, strength, outEffectIndex, outTimeMs, outVolLevel);
uint32_t effectIndex;
uint32_t timeMs;
float intensity;
@@ -1693,8 +1484,7 @@ ndk::ScopedAStatus Vibrator::getSimpleDetails(Effect effect, EffectStrength stre
ndk::ScopedAStatus Vibrator::getCompoundDetails(Effect effect, EffectStrength strength,
uint32_t *outTimeMs, DspMemChunk *outCh) {
- HAPTICS_TRACE("getCompoundDetails(effect:%s, strength:%s, outTimeMs, outCh)",
- toString(effect).c_str(), toString(strength).c_str());
+ VFTRACE(effect, strength, outTimeMs, outCh);
ndk::ScopedAStatus status;
uint32_t timeMs = 0;
uint32_t thisEffectIndex;
@@ -1738,14 +1528,15 @@ ndk::ScopedAStatus Vibrator::getCompoundDetails(Effect effect, EffectStrength st
}
*outTimeMs = timeMs;
- mFfEffects[WAVEFORM_COMPOSE].replay.length = static_cast<uint16_t>(timeMs);
+ // Compositions should have 0 duration
+ mFfEffects[WAVEFORM_COMPOSE].replay.length = 0;
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus Vibrator::getPrimitiveDetails(CompositePrimitive primitive,
uint32_t *outEffectIndex) {
- HAPTICS_TRACE("getPrimitiveDetails(primitive:%s, outEffectIndex)", toString(primitive).c_str());
+ VFTRACE(primitive, outEffectIndex);
uint32_t effectIndex;
uint32_t primitiveBit = 1 << int32_t(primitive);
if ((primitiveBit & mSupportedPrimitivesBits) == 0x0) {
@@ -1793,8 +1584,7 @@ ndk::ScopedAStatus Vibrator::getPrimitiveDetails(CompositePrimitive primitive,
ndk::ScopedAStatus Vibrator::performEffect(Effect effect, EffectStrength strength,
const std::shared_ptr<IVibratorCallback> &callback,
int32_t *outTimeMs) {
- HAPTICS_TRACE("performEffect(effect:%s, strength:%s, callback, outTimeMs)",
- toString(effect).c_str(), toString(strength).c_str());
+ VFTRACE(effect, strength, callback, outTimeMs);
ndk::ScopedAStatus status;
uint32_t effectIndex;
uint32_t timeMs = 0;
@@ -1832,15 +1622,14 @@ ndk::ScopedAStatus Vibrator::performEffect(Effect effect, EffectStrength strengt
ndk::ScopedAStatus Vibrator::performEffect(uint32_t effectIndex, uint32_t volLevel,
const DspMemChunk *ch,
const std::shared_ptr<IVibratorCallback> &callback) {
- HAPTICS_TRACE("performEffect(effectIndex:%u, volLevel:%u, ch, callback)", effectIndex,
- volLevel);
+ VFTRACE(effectIndex, volLevel, ch, callback);
setEffectAmplitude(volLevel, VOLTAGE_SCALE_MAX, false);
return on(MAX_TIME_MS, effectIndex, ch, callback);
}
void Vibrator::waitForComplete(std::shared_ptr<IVibratorCallback> &&callback) {
- HAPTICS_TRACE("waitForComplete(callback)");
+ VFTRACE(callback);
if (!mHwApi->pollVibeState(VIBE_STATE_HAPTIC, POLLING_TIMEOUT)) {
ALOGW("Failed to get state \"Haptic\"");
@@ -1880,7 +1669,7 @@ void Vibrator::waitForComplete(std::shared_ptr<IVibratorCallback> &&callback) {
}
uint32_t Vibrator::intensityToVolLevel(float intensity, uint32_t effectIndex) {
- HAPTICS_TRACE("intensityToVolLevel(intensity:%f, effectIndex:%u)", intensity, effectIndex);
+ VFTRACE(intensity, effectIndex);
uint32_t volLevel;
auto calc = [](float intst, std::array<uint32_t, 2> v) -> uint32_t {
diff --git a/vibrator/cs40l26/VibratorFlags.aconfig b/vibrator/cs40l26/VibratorFlags.aconfig
new file mode 100644
index 00000000..ec6e2d4e
--- /dev/null
+++ b/vibrator/cs40l26/VibratorFlags.aconfig
@@ -0,0 +1,10 @@
+package: "vendor.vibrator.hal.flags"
+container: "vendor"
+
+flag {
+ name: "remove_capo"
+ namespace: "vibrator"
+ is_exported: true
+ description: "This flag controls the removal of utilizing Capo at the HAL level"
+ bug: "290223630"
+}
diff --git a/vibrator/cs40l26/tests/Android.bp b/vibrator/cs40l26/tests/Android.bp
index 287d4914..86215373 100644
--- a/vibrator/cs40l26/tests/Android.bp
+++ b/vibrator/cs40l26/tests/Android.bp
@@ -30,5 +30,6 @@ cc_test {
],
shared_libs: [
"libbase",
+ "PixelVibratorFlagsL26",
],
}
diff --git a/vibrator/cs40l26/tests/test-vibrator.cpp b/vibrator/cs40l26/tests/test-vibrator.cpp
index 0b150955..698b68e1 100644
--- a/vibrator/cs40l26/tests/test-vibrator.cpp
+++ b/vibrator/cs40l26/tests/test-vibrator.cpp
@@ -75,7 +75,7 @@ static constexpr std::array<EffectLevel, 2> V_TICK_DEFAULT = {1, 100};
static constexpr std::array<EffectLevel, 2> V_CLICK_DEFAULT{1, 100};
static constexpr std::array<EffectLevel, 2> V_LONG_DEFAULT{1, 100};
static constexpr std::array<EffectDuration, 14> EFFECT_DURATIONS{
- 0, 100, 12, 1000, 300, 130, 150, 500, 100, 5, 12, 1000, 1000, 1000};
+ 0, 100, 12, 1000, 300, 133, 150, 500, 100, 5, 12, 1000, 1000, 1000};
// Constants With Prescribed Values