| /* |
| * Copyright (C) 2020 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 <dataproviders/PowerStatsEnergyConsumer.h> |
| |
| #include <android-base/logging.h> |
| |
| namespace aidl { |
| namespace android { |
| namespace hardware { |
| namespace power { |
| namespace stats { |
| |
| PowerStatsEnergyConsumer::PowerStatsEnergyConsumer(std::shared_ptr<PowerStats> p, |
| EnergyConsumerType type, std::string name, |
| bool attr) |
| : kType(type), kName(name), mPowerStats(p), mWithAttribution(attr) {} |
| |
| std::unique_ptr<PowerStatsEnergyConsumer> PowerStatsEnergyConsumer::createMeterConsumer( |
| std::shared_ptr<PowerStats> p, EnergyConsumerType type, std::string name, |
| std::set<std::string> channelNames) { |
| return createMeterAndEntityConsumer(p, type, name, channelNames, "", {}); |
| } |
| |
| std::unique_ptr<PowerStatsEnergyConsumer> PowerStatsEnergyConsumer::createEntityConsumer( |
| std::shared_ptr<PowerStats> p, EnergyConsumerType type, std::string name, |
| std::string powerEntityName, std::map<std::string, int32_t> stateCoeffs) { |
| return createMeterAndEntityConsumer(p, type, name, {}, powerEntityName, stateCoeffs); |
| } |
| |
| std::unique_ptr<PowerStatsEnergyConsumer> PowerStatsEnergyConsumer::createMeterAndEntityConsumer( |
| std::shared_ptr<PowerStats> p, EnergyConsumerType type, std::string name, |
| std::set<std::string> channelNames, std::string powerEntityName, |
| std::map<std::string, int32_t> stateCoeffs) { |
| auto ret = |
| std::unique_ptr<PowerStatsEnergyConsumer>(new PowerStatsEnergyConsumer(p, type, name)); |
| |
| if (ret->addEnergyMeter(channelNames) && ret->addPowerEntity(powerEntityName, stateCoeffs)) { |
| return ret; |
| } |
| |
| LOG(ERROR) << "Failed to create PowerStatsEnergyConsumer for " << name; |
| return nullptr; |
| } |
| |
| std::unique_ptr<PowerStatsEnergyConsumer> PowerStatsEnergyConsumer::createMeterAndAttrConsumer( |
| std::shared_ptr<PowerStats> p, EnergyConsumerType type, std::string name, |
| std::set<std::string> channelNames, std::unordered_map<int32_t, std::string> paths, |
| std::map<std::string, int32_t> stateCoeffs) { |
| auto ret = std::unique_ptr<PowerStatsEnergyConsumer>( |
| new PowerStatsEnergyConsumer(p, type, name, true)); |
| |
| if (ret->addEnergyMeter(channelNames) && ret->addAttribution(paths, stateCoeffs)) { |
| return ret; |
| } |
| |
| LOG(ERROR) << "Failed to create PowerStatsEnergyConsumer for " << name; |
| return nullptr; |
| } |
| |
| bool PowerStatsEnergyConsumer::addEnergyMeter(std::set<std::string> channelNames) { |
| if (channelNames.empty()) { |
| return true; |
| } |
| |
| std::vector<Channel> channels; |
| mPowerStats->getEnergyMeterInfo(&channels); |
| |
| for (const auto &c : channels) { |
| if (channelNames.count(c.name)) { |
| mChannelIds.push_back(c.id); |
| } |
| } |
| |
| return (mChannelIds.size() == channelNames.size()); |
| } |
| |
| bool PowerStatsEnergyConsumer::addPowerEntity(std::string powerEntityName, |
| std::map<std::string, int32_t> stateCoeffs) { |
| if (powerEntityName.empty() || stateCoeffs.empty()) { |
| return true; |
| } |
| |
| std::vector<PowerEntity> powerEntities; |
| mPowerStats->getPowerEntityInfo(&powerEntities); |
| |
| for (const auto &p : powerEntities) { |
| if (powerEntityName == p.name) { |
| mPowerEntityId = p.id; |
| for (const auto &s : p.states) { |
| if (stateCoeffs.count(s.name)) { |
| mCoefficients.emplace(s.id, stateCoeffs.at(s.name)); |
| } |
| } |
| break; |
| } |
| } |
| |
| return (mCoefficients.size() == stateCoeffs.size()); |
| } |
| |
| bool PowerStatsEnergyConsumer::addAttribution(std::unordered_map<int32_t, std::string> paths, |
| std::map<std::string, int32_t> stateCoeffs) { |
| mAttrInfoPath = paths; |
| |
| if (paths.count(UID_TIME_IN_STATE)) { |
| mEnergyAttribution = PowerStatsEnergyAttribution(); |
| AttributionStats attrStats = mEnergyAttribution.getAttributionStats(paths); |
| if (attrStats.uidTimeInStateNames.empty()) { |
| LOG(ERROR) << "Failed to read uid_time_in_state"; |
| return false; |
| } |
| |
| // stateCoeffs should not blocking energy consumer to return power meter |
| // so just handle this in getEnergyConsumed() |
| if (stateCoeffs.empty()) { |
| return true; |
| } |
| |
| int32_t stateId = 0; |
| for (const auto &stateName : attrStats.uidTimeInStateNames) { |
| if (stateCoeffs.count(stateName)) { |
| // When uid_time_in_state is not the only type of attribution, |
| // should condider to separate the coefficients just for attribution. |
| mCoefficients.emplace(stateId, stateCoeffs.at(stateName)); |
| } |
| stateId++; |
| } |
| } |
| |
| return (mCoefficients.size() == stateCoeffs.size()); |
| } |
| |
| std::optional<EnergyConsumerResult> PowerStatsEnergyConsumer::getEnergyConsumed() { |
| int64_t totalEnergyUWs = 0; |
| int64_t timestampMs = 0; |
| |
| if (!mChannelIds.empty()) { |
| std::vector<EnergyMeasurement> measurements; |
| if (mPowerStats->readEnergyMeter(mChannelIds, &measurements).isOk()) { |
| for (const auto &m : measurements) { |
| totalEnergyUWs += m.energyUWs; |
| timestampMs = m.timestampMs; |
| } |
| } else { |
| LOG(ERROR) << "Failed to read energy meter"; |
| return {}; |
| } |
| } |
| |
| std::vector<EnergyConsumerAttribution> attribution; |
| if (!mCoefficients.empty()) { |
| if (mWithAttribution) { |
| AttributionStats attrStats = mEnergyAttribution.getAttributionStats(mAttrInfoPath); |
| if (attrStats.uidTimeInStats.empty() || attrStats.uidTimeInStateNames.empty()) { |
| LOG(ERROR) << "Failed to read uid_time_in_state for attribution, return default EnergyConsumer"; |
| } else { |
| int64_t totalRelativeEnergyUWs = 0; |
| for (const auto &uidTimeInStat : attrStats.uidTimeInStats) { |
| int64_t uidEnergyUWs = 0; |
| for (int id = 0; id < uidTimeInStat.second.size(); id++) { |
| if (mCoefficients.count(id)) { |
| int64_t d_time_in_state = uidTimeInStat.second.at(id); |
| if (mUidTimeInStateSS.count(uidTimeInStat.first)) { |
| d_time_in_state -= mUidTimeInStateSS.at(uidTimeInStat.first).at(id); |
| } |
| uidEnergyUWs += mCoefficients.at(id) * d_time_in_state; |
| } |
| } |
| totalRelativeEnergyUWs += uidEnergyUWs; |
| |
| EnergyConsumerAttribution attr = { |
| .uid = uidTimeInStat.first, |
| .energyUWs = uidEnergyUWs, |
| }; |
| attribution.emplace_back(attr); |
| } |
| |
| int64_t d_totalEnergyUWs = totalEnergyUWs - mTotalEnergySS; |
| float powerScale = 0; |
| if (totalRelativeEnergyUWs != 0) { |
| powerScale = static_cast<float>(d_totalEnergyUWs) / totalRelativeEnergyUWs; |
| } |
| for (auto &attr : attribution) { |
| attr.energyUWs = (int64_t)(attr.energyUWs * powerScale) + |
| (mUidEnergySS.count(attr.uid) ? mUidEnergySS.at(attr.uid) : 0); |
| mUidEnergySS[attr.uid] = attr.energyUWs; |
| } |
| |
| mUidTimeInStateSS = attrStats.uidTimeInStats; |
| mTotalEnergySS = totalEnergyUWs; |
| } |
| } else { |
| std::vector<StateResidencyResult> results; |
| if (mPowerStats->getStateResidency({mPowerEntityId}, &results).isOk()) { |
| for (const auto &s : results[0].stateResidencyData) { |
| if (mCoefficients.count(s.id)) { |
| totalEnergyUWs += mCoefficients.at(s.id) * s.totalTimeInStateMs; |
| } |
| } |
| } else { |
| LOG(ERROR) << "Failed to get state residency"; |
| return {}; |
| } |
| } |
| } |
| |
| return EnergyConsumerResult{.timestampMs = timestampMs, |
| .energyUWs = totalEnergyUWs, |
| .attribution = attribution}; |
| } |
| |
| std::string PowerStatsEnergyConsumer::getConsumerName() { |
| return kName; |
| } |
| |
| } // namespace stats |
| } // namespace power |
| } // namespace hardware |
| } // namespace android |
| } // namespace aidl |