diff --git a/powerstats/DisplayMrrStateResidencyDataProvider.cpp b/powerstats/DisplayMrrStateResidencyDataProvider.cpp new file mode 100644 index 0000000..8ab9af4 --- /dev/null +++ b/powerstats/DisplayMrrStateResidencyDataProvider.cpp @@ -0,0 +1,169 @@ +/* + * 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 "DisplayMrrStateResidencyDataProvider.h" + +#include +#include +#include + +using android::base::ParseInt; +using android::base::ParseUint; +using android::base::Split; +using android::base::Trim; + +static const std::string TIME_IN_STATE = "time_in_state"; +static const std::string AVAILABLE_STATE = "available_disp_stats"; +static const std::vector DISP_STATE = { "On", "HBM", "LP", "Off" }; + +namespace aidl { +namespace android { +namespace hardware { +namespace power { +namespace stats { + +DisplayMrrStateResidencyDataProvider::DisplayMrrStateResidencyDataProvider( + const std::string& name, const std::string& path) : mName(name), mPath(path) { + mConfigs = std::vector(); + std::string statePath = mPath + AVAILABLE_STATE; + std::unique_ptr fp(fopen(statePath.c_str(), "r"), fclose); + if (fp) { + char *line = nullptr; + size_t len = 0; + Config config = { .state = 0, .resX = 0, .resY = 0, .rr = 0 }; + while (getline(&line, &len, fp.get()) != -1) { + if (parseAvailableState(line, &config)) { + mConfigs.push_back(config); + } else { + PLOG(ERROR) << "Failed to parse display config for [" << std::string(line) + << "] from " << statePath; + mConfigs.clear(); + break; + } + } + free(line); + } else { + PLOG(ERROR) << "Failed to open file " << statePath; + } +} + +bool DisplayMrrStateResidencyDataProvider::parseConfig( + char const *line, Config *config, uint64_t *duration) { + std::vector parts = Split(line, " "); + + if (duration == nullptr) { + if (parts.size() != 4) return false; + } else { + if (parts.size() != 5) return false; + + if (!ParseUint(Trim(parts[4]), duration)) return false; + } + + if (!ParseInt(Trim(parts[0]), &config->state)) return false; + if (!ParseInt(Trim(parts[1]), &config->resX)) return false; + if (!ParseInt(Trim(parts[2]), &config->resY)) return false; + if (!ParseInt(Trim(parts[3]), &config->rr)) return false; + + return true; +} + +bool DisplayMrrStateResidencyDataProvider::parseAvailableState( + char const *line, Config *config) { + return parseConfig(line, config, nullptr); +} + +bool DisplayMrrStateResidencyDataProvider::parseTimeInState( + char const *line, Config *config, uint64_t *duration) { + return parseConfig(line, config, duration); +} + +bool DisplayMrrStateResidencyDataProvider::getStateResidencies( + std::unordered_map> *residencies) { + if (mConfigs.empty()) { + LOG(ERROR) << "Display MRR state list is empty!"; + return false; + } + + std::string path = mPath + TIME_IN_STATE; + std::unique_ptr fp(fopen(path.c_str(), "r"), fclose); + if (!fp) { + PLOG(ERROR) << "Failed to open file " << path; + return false; + } + + std::vector stateResidencies; + for (int i = 0; i < mConfigs.size(); i++) { + StateResidency s = {.id = i, .totalTimeInStateMs = 0}; + stateResidencies.push_back(s); + } + + char *line = nullptr; + size_t len = 0; + Config config = { .state = 0, .resX = 0, .resY = 0, .rr = 0 }; + uint64_t duration; + std::vector::const_iterator found; + while (getline(&line, &len, fp.get()) != -1) { + if (parseTimeInState(line, &config, &duration)) { + found = std::find(mConfigs.begin(), mConfigs.end(), config); + if (found != mConfigs.end()) { + stateResidencies[found - mConfigs.begin()].totalTimeInStateMs = duration; + } else { + LOG(ERROR) << "Failed to find config for [" << std::string(line) + << "] in display MRR state list"; + } + } else { + LOG(ERROR) << "Failed to parse state and duration from [" << std::string(line) << "]"; + free(line); + return false; + } + } + + residencies->emplace(mName, stateResidencies); + + free(line); + + return true; +} + +std::unordered_map> DisplayMrrStateResidencyDataProvider::getInfo() +{ + int32_t dispId; + std::string name; + std::vector states; + for (int32_t id = 0; id < mConfigs.size(); id++) { + dispId = mConfigs[id].state; + if (dispId >= DISP_STATE.size()) { + LOG(ERROR) << "Display state id " << dispId << " is out of bound"; + return {}; + } + + name = DISP_STATE[dispId]; + if (dispId != DISP_STATE.size() - 1) { + name += ": " + std::to_string(mConfigs[id].resX) + + "x" + std::to_string(mConfigs[id].resY) + + "@" + std::to_string(mConfigs[id].rr); + } + State s = { .id = id, .name = name }; + states.push_back(s); + } + + return {{ mName, states }}; +} + +} // namespace stats +} // namespace power +} // namespace hardware +} // namespace android +} // namespace aidl diff --git a/powerstats/include/DisplayMrrStateResidencyDataProvider.h b/powerstats/include/DisplayMrrStateResidencyDataProvider.h new file mode 100644 index 0000000..81ea17e --- /dev/null +++ b/powerstats/include/DisplayMrrStateResidencyDataProvider.h @@ -0,0 +1,68 @@ +/* + * 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 + +namespace aidl { +namespace android { +namespace hardware { +namespace power { +namespace stats { + +class DisplayMrrStateResidencyDataProvider : public PowerStats::IStateResidencyDataProvider { + public: + DisplayMrrStateResidencyDataProvider(const std::string& name, const std::string& path); + ~DisplayMrrStateResidencyDataProvider() = default; + + /* + * See IStateResidencyDataProvider::getStateResidencies + */ + bool getStateResidencies( + std::unordered_map> *residencies) override; + + /* + * See IStateResidencyDataProvider::getInfo + */ + std::unordered_map> getInfo() override; + + private: + struct Config { + int32_t state; // Display state (On, HBM, LP, Off) + int32_t resX; // Resolution X + int32_t resY; // Resolution Y + int32_t rr; // Refresh rate + + bool operator==(const Config& r) const { + return state == r.state && resX == r.resX && resY == r.resY && rr == r.rr; + } + }; + + bool parseConfig(char const *line, Config *config, uint64_t *duration); + bool parseAvailableState(char const *line, Config *config); + bool parseTimeInState(char const *line, Config *config, uint64_t *duration); + bool loadAvailableState(); + + const std::string mName; + const std::string mPath; + std::vector mConfigs; +}; + +} // namespace stats +} // namespace power +} // namespace hardware +} // namespace android +} // namespace aidl