diff options
author | Xin Li <delphij@google.com> | 2024-06-13 10:50:18 -0700 |
---|---|---|
committer | Xin Li <delphij@google.com> | 2024-06-13 10:50:18 -0700 |
commit | a47a38edeaff82199dbc6023c0cf318dc5f0f02c (patch) | |
tree | ed8c025e9e71f45fde5a8fc28a5ab4c9e2d7e130 | |
parent | d5f7bc349d001fe5eca44f9ae30faa6a88643def (diff) | |
parent | 8b12fb16bdbd43b8916101b283fa6c4ecb772a38 (diff) | |
download | pixel-master.tar.gz |
Bug: 346855327
Merged-In: I5efa37f3ff793f07556c5a9bf9610db51e09bda8
Change-Id: I9a0cab92196064fb471ec8799273f5dbc9713393
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 ¶ms) { + // 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", + ¶ms.fcnom, ¶ms.dpacc, ¶ms.dqacc, ¶ms.fcrep, ¶ms.repsoc, + ¶ms.msoc, ¶ms.vfsoc, ¶ms.fstat, ¶ms.rcomp0, ¶ms.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, + ¶ms.fcnom, ¶ms.dpacc, ¶ms.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", + ¶ms.dqacc, &pos); + if (num != 1) { + ALOGE("Couldn't process ModelLoading History. num=%d\n", num); + return; + } + + sscanf(&data[pos], "ATT: %" SCNu16 " FAIL: %" SCNu16, ¶ms.fcnom, ¶ms.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 ¶ms); + + 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, &, 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, &, 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 |