| /* |
| * 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. |
| */ |
| |
| package com.android.internal.os; |
| |
| import android.os.BatteryConsumer; |
| import android.os.BatteryStats; |
| import android.os.BatteryUsageStats; |
| import android.os.BatteryUsageStatsQuery; |
| import android.os.Process; |
| import android.os.UidBatteryConsumer; |
| import android.os.UserHandle; |
| import android.util.Log; |
| import android.util.SparseArray; |
| |
| import java.util.List; |
| |
| /** |
| * Estimates the amount of power consumed by the System Server handling requests from |
| * a given app. |
| */ |
| public class SystemServicePowerCalculator extends PowerCalculator { |
| private static final boolean DEBUG = false; |
| private static final String TAG = "SystemServicePowerCalc"; |
| |
| // Power estimators per CPU cluster, per CPU frequency. The array is flattened according |
| // to this layout: |
| // {cluster1-speed1, cluster1-speed2, ..., cluster2-speed1, cluster2-speed2, ...} |
| private final UsageBasedPowerEstimator[] mPowerEstimators; |
| private final CpuPowerCalculator mCpuPowerCalculator; |
| |
| public SystemServicePowerCalculator(PowerProfile powerProfile) { |
| mCpuPowerCalculator = new CpuPowerCalculator(powerProfile); |
| int numFreqs = 0; |
| final int numCpuClusters = powerProfile.getNumCpuClusters(); |
| for (int cluster = 0; cluster < numCpuClusters; cluster++) { |
| numFreqs += powerProfile.getNumSpeedStepsInCpuCluster(cluster); |
| } |
| |
| mPowerEstimators = new UsageBasedPowerEstimator[numFreqs]; |
| int index = 0; |
| for (int cluster = 0; cluster < numCpuClusters; cluster++) { |
| final int numSpeeds = powerProfile.getNumSpeedStepsInCpuCluster(cluster); |
| for (int speed = 0; speed < numSpeeds; speed++) { |
| mPowerEstimators[index++] = new UsageBasedPowerEstimator( |
| powerProfile.getAveragePowerForCpuCore(cluster, speed)); |
| } |
| } |
| } |
| |
| @Override |
| public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats, |
| long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) { |
| final BatteryStats.Uid systemUid = batteryStats.getUidStats().get(Process.SYSTEM_UID); |
| if (systemUid == null) { |
| return; |
| } |
| |
| final long consumptionUC = systemUid.getCpuMeasuredBatteryConsumptionUC(); |
| final int powerModel = getPowerModel(consumptionUC, query); |
| |
| double systemServicePowerMah; |
| if (powerModel == BatteryConsumer.POWER_MODEL_MEASURED_ENERGY) { |
| systemServicePowerMah = calculatePowerUsingMeasuredConsumption(batteryStats, |
| systemUid, consumptionUC); |
| } else { |
| systemServicePowerMah = calculatePowerUsingPowerProfile(batteryStats); |
| } |
| |
| final SparseArray<UidBatteryConsumer.Builder> uidBatteryConsumerBuilders = |
| builder.getUidBatteryConsumerBuilders(); |
| final UidBatteryConsumer.Builder systemServerConsumer = uidBatteryConsumerBuilders.get( |
| Process.SYSTEM_UID); |
| |
| if (systemServerConsumer != null) { |
| systemServicePowerMah = Math.min(systemServicePowerMah, |
| systemServerConsumer.getTotalPower()); |
| |
| // The system server power needs to be adjusted because part of it got |
| // distributed to applications |
| systemServerConsumer.setConsumedPower( |
| BatteryConsumer.POWER_COMPONENT_REATTRIBUTED_TO_OTHER_CONSUMERS, |
| -systemServicePowerMah, powerModel); |
| } |
| |
| for (int i = uidBatteryConsumerBuilders.size() - 1; i >= 0; i--) { |
| final UidBatteryConsumer.Builder app = uidBatteryConsumerBuilders.valueAt(i); |
| if (app != systemServerConsumer) { |
| final BatteryStats.Uid uid = app.getBatteryStatsUid(); |
| app.setConsumedPower(BatteryConsumer.POWER_COMPONENT_SYSTEM_SERVICES, |
| systemServicePowerMah * uid.getProportionalSystemServiceUsage(), |
| powerModel); |
| } |
| } |
| |
| builder.getAggregateBatteryConsumerBuilder( |
| BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE) |
| .setConsumedPower(BatteryConsumer.POWER_COMPONENT_SYSTEM_SERVICES, |
| systemServicePowerMah); |
| builder.getAggregateBatteryConsumerBuilder( |
| BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS) |
| .setConsumedPower(BatteryConsumer.POWER_COMPONENT_SYSTEM_SERVICES, |
| systemServicePowerMah); |
| } |
| |
| @Override |
| public void calculate(List<BatterySipper> sippers, BatteryStats batteryStats, |
| long rawRealtimeUs, long rawUptimeUs, int statsType, |
| SparseArray<UserHandle> asUsers) { |
| final BatteryStats.Uid systemUid = batteryStats.getUidStats().get(Process.SYSTEM_UID); |
| if (systemUid == null) { |
| return; |
| } |
| |
| final long consumptionUC = systemUid.getCpuMeasuredBatteryConsumptionUC(); |
| double systemServicePowerMah; |
| if (getPowerModel(consumptionUC) == BatteryConsumer.POWER_MODEL_MEASURED_ENERGY) { |
| systemServicePowerMah = calculatePowerUsingMeasuredConsumption(batteryStats, |
| systemUid, consumptionUC); |
| } else { |
| systemServicePowerMah = calculatePowerUsingPowerProfile(batteryStats); |
| } |
| |
| BatterySipper systemServerSipper = null; |
| for (int i = sippers.size() - 1; i >= 0; i--) { |
| final BatterySipper app = sippers.get(i); |
| if (app.drainType == BatterySipper.DrainType.APP) { |
| if (app.getUid() == Process.SYSTEM_UID) { |
| systemServerSipper = app; |
| break; |
| } |
| } |
| } |
| |
| if (systemServerSipper != null) { |
| systemServicePowerMah = Math.min(systemServicePowerMah, systemServerSipper.sumPower()); |
| |
| // The system server power needs to be adjusted because part of it got |
| // distributed to applications |
| systemServerSipper.powerReattributedToOtherSippersMah = -systemServicePowerMah; |
| } |
| |
| for (int i = sippers.size() - 1; i >= 0; i--) { |
| final BatterySipper app = sippers.get(i); |
| if (app.drainType == BatterySipper.DrainType.APP) { |
| if (app != systemServerSipper) { |
| final BatteryStats.Uid uid = app.uidObj; |
| app.systemServiceCpuPowerMah = |
| systemServicePowerMah * uid.getProportionalSystemServiceUsage(); |
| } |
| } |
| } |
| } |
| |
| private double calculatePowerUsingMeasuredConsumption(BatteryStats batteryStats, |
| BatteryStats.Uid systemUid, long consumptionUC) { |
| // Use the PowerProfile based model to estimate the ratio between the power consumed |
| // while handling incoming binder calls and the entire System UID power consumption. |
| // Apply that ratio to the _measured_ system UID power consumption to get a more |
| // accurate estimate of the power consumed by incoming binder calls. |
| final double systemServiceModeledPowerMah = calculatePowerUsingPowerProfile(batteryStats); |
| final double systemUidModeledPowerMah = mCpuPowerCalculator.calculateUidModeledPowerMah( |
| systemUid, BatteryStats.STATS_SINCE_CHARGED); |
| |
| return uCtoMah(consumptionUC) * systemServiceModeledPowerMah |
| / systemUidModeledPowerMah; |
| } |
| |
| private double calculatePowerUsingPowerProfile(BatteryStats batteryStats) { |
| final long[] systemServiceTimeAtCpuSpeeds = batteryStats.getSystemServiceTimeAtCpuSpeeds(); |
| if (systemServiceTimeAtCpuSpeeds == null) { |
| return 0; |
| } |
| |
| // TODO(179210707): additionally account for CPU active and per cluster battery use |
| |
| double powerMah = 0; |
| final int size = Math.min(mPowerEstimators.length, systemServiceTimeAtCpuSpeeds.length); |
| for (int i = 0; i < size; i++) { |
| powerMah += mPowerEstimators[i].calculatePower(systemServiceTimeAtCpuSpeeds[i] / 1000); |
| } |
| |
| if (DEBUG) { |
| Log.d(TAG, "System service power:" + powerMah); |
| } |
| return powerMah; |
| } |
| } |