1 /* 2 * Copyright (C) 2020 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.power.stats; 18 19 import static com.android.internal.os.PowerProfile.POWER_GROUP_DISPLAY_SCREEN_FULL; 20 import static com.android.internal.os.PowerProfile.POWER_GROUP_DISPLAY_SCREEN_ON; 21 22 import android.os.BatteryConsumer; 23 import android.os.BatteryStats; 24 import android.os.BatteryUsageStats; 25 import android.os.BatteryUsageStatsQuery; 26 import android.os.UidBatteryConsumer; 27 import android.text.format.DateUtils; 28 import android.util.Slog; 29 import android.util.SparseArray; 30 import android.util.SparseLongArray; 31 32 import com.android.internal.annotations.VisibleForTesting; 33 import com.android.internal.os.PowerProfile; 34 35 /** 36 * Estimates power consumed by the screen(s) 37 */ 38 public class ScreenPowerCalculator extends PowerCalculator { 39 private static final String TAG = "ScreenPowerCalculator"; 40 private static final boolean DEBUG = PowerCalculator.DEBUG; 41 42 // Minimum amount of time the screen should be on to start smearing drain to apps 43 public static final long MIN_ACTIVE_TIME_FOR_SMEARING = 10 * DateUtils.MINUTE_IN_MILLIS; 44 45 private final UsageBasedPowerEstimator[] mScreenOnPowerEstimators; 46 private final UsageBasedPowerEstimator[] mScreenFullPowerEstimators; 47 48 private static class PowerAndDuration { 49 public long durationMs; 50 public double powerMah; 51 } 52 ScreenPowerCalculator(PowerProfile powerProfile)53 public ScreenPowerCalculator(PowerProfile powerProfile) { 54 final int numDisplays = powerProfile.getNumDisplays(); 55 mScreenOnPowerEstimators = new UsageBasedPowerEstimator[numDisplays]; 56 mScreenFullPowerEstimators = new UsageBasedPowerEstimator[numDisplays]; 57 for (int display = 0; display < numDisplays; display++) { 58 mScreenOnPowerEstimators[display] = new UsageBasedPowerEstimator( 59 powerProfile.getAveragePowerForOrdinal(POWER_GROUP_DISPLAY_SCREEN_ON, display)); 60 mScreenFullPowerEstimators[display] = new UsageBasedPowerEstimator( 61 powerProfile.getAveragePowerForOrdinal(POWER_GROUP_DISPLAY_SCREEN_FULL, 62 display)); 63 } 64 } 65 66 @Override isPowerComponentSupported(@atteryConsumer.PowerComponent int powerComponent)67 public boolean isPowerComponentSupported(@BatteryConsumer.PowerComponent int powerComponent) { 68 return powerComponent == BatteryConsumer.POWER_COMPONENT_SCREEN; 69 } 70 71 @Override calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats, long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query)72 public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats, 73 long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) { 74 final PowerAndDuration totalPowerAndDuration = new PowerAndDuration(); 75 76 final long consumptionUC = batteryStats.getScreenOnEnergyConsumptionUC(); 77 final int powerModel = getPowerModel(consumptionUC, query); 78 calculateTotalDurationAndPower(totalPowerAndDuration, powerModel, batteryStats, 79 rawRealtimeUs, BatteryStats.STATS_SINCE_CHARGED, consumptionUC); 80 81 double totalAppPower = 0; 82 long totalAppDuration = 0; 83 84 // Now deal with each app's UidBatteryConsumer. The results are stored in the 85 // BatteryConsumer.POWER_COMPONENT_SCREEN power component, which is considered smeared, 86 // but the method depends on the data source. 87 final SparseArray<UidBatteryConsumer.Builder> uidBatteryConsumerBuilders = 88 builder.getUidBatteryConsumerBuilders(); 89 switch (powerModel) { 90 case BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION: 91 final PowerAndDuration appPowerAndDuration = new PowerAndDuration(); 92 for (int i = uidBatteryConsumerBuilders.size() - 1; i >= 0; i--) { 93 final UidBatteryConsumer.Builder app = uidBatteryConsumerBuilders.valueAt(i); 94 calculateAppUsingEnergyConsumption(appPowerAndDuration, 95 app.getBatteryStatsUid(), rawRealtimeUs); 96 app.setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_SCREEN, 97 appPowerAndDuration.durationMs) 98 .setConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN, 99 appPowerAndDuration.powerMah, powerModel); 100 if (!app.isVirtualUid()) { 101 totalAppPower += appPowerAndDuration.powerMah; 102 totalAppDuration += appPowerAndDuration.durationMs; 103 } 104 } 105 break; 106 case BatteryConsumer.POWER_MODEL_POWER_PROFILE: 107 default: 108 smearScreenBatteryDrain(uidBatteryConsumerBuilders, totalPowerAndDuration, 109 rawRealtimeUs); 110 totalAppPower = totalPowerAndDuration.powerMah; 111 totalAppDuration = totalPowerAndDuration.durationMs; 112 } 113 114 builder.getAggregateBatteryConsumerBuilder( 115 BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE) 116 .setConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN, 117 Math.max(totalPowerAndDuration.powerMah, totalAppPower), powerModel) 118 .setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_SCREEN, 119 totalPowerAndDuration.durationMs); 120 121 builder.getAggregateBatteryConsumerBuilder( 122 BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS) 123 .setConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN, totalAppPower, powerModel) 124 .setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_SCREEN, totalAppDuration); 125 } 126 127 /** 128 * Stores duration and power information in totalPowerAndDuration. 129 */ calculateTotalDurationAndPower(PowerAndDuration totalPowerAndDuration, @BatteryConsumer.PowerModel int powerModel, BatteryStats batteryStats, long rawRealtimeUs, int statsType, long consumptionUC)130 private void calculateTotalDurationAndPower(PowerAndDuration totalPowerAndDuration, 131 @BatteryConsumer.PowerModel int powerModel, BatteryStats batteryStats, 132 long rawRealtimeUs, int statsType, long consumptionUC) { 133 totalPowerAndDuration.durationMs = calculateDuration(batteryStats, rawRealtimeUs, 134 statsType); 135 136 switch (powerModel) { 137 case BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION: 138 totalPowerAndDuration.powerMah = uCtoMah(consumptionUC); 139 break; 140 case BatteryConsumer.POWER_MODEL_POWER_PROFILE: 141 default: 142 totalPowerAndDuration.powerMah = calculateTotalPowerFromBrightness(batteryStats, 143 rawRealtimeUs); 144 } 145 } 146 calculateAppUsingEnergyConsumption(PowerAndDuration appPowerAndDuration, BatteryStats.Uid u, long rawRealtimeUs)147 private void calculateAppUsingEnergyConsumption(PowerAndDuration appPowerAndDuration, 148 BatteryStats.Uid u, long rawRealtimeUs) { 149 appPowerAndDuration.durationMs = getProcessForegroundTimeMs(u, rawRealtimeUs); 150 151 final long chargeUC = u.getScreenOnEnergyConsumptionUC(); 152 if (chargeUC < 0) { 153 Slog.wtf(TAG, "Screen energy not supported, so calculateApp shouldn't de called"); 154 appPowerAndDuration.powerMah = 0; 155 return; 156 } 157 158 appPowerAndDuration.powerMah = uCtoMah(chargeUC); 159 } 160 calculateDuration(BatteryStats batteryStats, long rawRealtimeUs, int statsType)161 private long calculateDuration(BatteryStats batteryStats, long rawRealtimeUs, int statsType) { 162 return batteryStats.getScreenOnTime(rawRealtimeUs, statsType) / 1000; 163 } 164 calculateTotalPowerFromBrightness(BatteryStats batteryStats, long rawRealtimeUs)165 private double calculateTotalPowerFromBrightness(BatteryStats batteryStats, 166 long rawRealtimeUs) { 167 final int numDisplays = mScreenOnPowerEstimators.length; 168 double power = 0; 169 for (int display = 0; display < numDisplays; display++) { 170 final long displayTime = batteryStats.getDisplayScreenOnTime(display, rawRealtimeUs) 171 / 1000; 172 power += mScreenOnPowerEstimators[display].calculatePower(displayTime); 173 for (int bin = 0; bin < BatteryStats.NUM_SCREEN_BRIGHTNESS_BINS; bin++) { 174 final long brightnessTime = batteryStats.getDisplayScreenBrightnessTime(display, 175 bin, rawRealtimeUs) / 1000; 176 final double binPowerMah = mScreenFullPowerEstimators[display].calculatePower( 177 brightnessTime) * (bin + 0.5f) / BatteryStats.NUM_SCREEN_BRIGHTNESS_BINS; 178 if (DEBUG && binPowerMah != 0) { 179 Slog.d(TAG, "Screen bin #" + bin + ": time=" + brightnessTime 180 + " power=" + BatteryStats.formatCharge(binPowerMah)); 181 } 182 power += binPowerMah; 183 } 184 } 185 return power; 186 } 187 188 /** 189 * Smear the screen on power usage among {@code UidBatteryConsumers}, based on ratio of 190 * foreground activity time. 191 */ smearScreenBatteryDrain( SparseArray<UidBatteryConsumer.Builder> uidBatteryConsumerBuilders, PowerAndDuration totalPowerAndDuration, long rawRealtimeUs)192 private void smearScreenBatteryDrain( 193 SparseArray<UidBatteryConsumer.Builder> uidBatteryConsumerBuilders, 194 PowerAndDuration totalPowerAndDuration, long rawRealtimeUs) { 195 long totalActivityTimeMs = 0; 196 final SparseLongArray activityTimeArray = new SparseLongArray(); 197 for (int i = uidBatteryConsumerBuilders.size() - 1; i >= 0; i--) { 198 final UidBatteryConsumer.Builder app = uidBatteryConsumerBuilders.valueAt(i); 199 final BatteryStats.Uid uid = app.getBatteryStatsUid(); 200 final long timeMs = getProcessForegroundTimeMs(uid, rawRealtimeUs); 201 activityTimeArray.put(uid.getUid(), timeMs); 202 if (!app.isVirtualUid()) { 203 totalActivityTimeMs += timeMs; 204 } 205 } 206 207 if (totalActivityTimeMs >= MIN_ACTIVE_TIME_FOR_SMEARING) { 208 final double totalScreenPowerMah = totalPowerAndDuration.powerMah; 209 for (int i = uidBatteryConsumerBuilders.size() - 1; i >= 0; i--) { 210 final UidBatteryConsumer.Builder app = uidBatteryConsumerBuilders.valueAt(i); 211 final long durationMs = activityTimeArray.get(app.getUid(), 0); 212 final double powerMah = totalScreenPowerMah * durationMs / totalActivityTimeMs; 213 app.setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_SCREEN, durationMs) 214 .setConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN, powerMah, 215 BatteryConsumer.POWER_MODEL_POWER_PROFILE); 216 } 217 } 218 } 219 220 /** Get the minimum of the uid's ForegroundActivity time and its TOP time. */ 221 @VisibleForTesting getProcessForegroundTimeMs(BatteryStats.Uid uid, long rawRealTimeUs)222 public long getProcessForegroundTimeMs(BatteryStats.Uid uid, long rawRealTimeUs) { 223 final int[] foregroundTypes = {BatteryStats.Uid.PROCESS_STATE_TOP}; 224 225 long timeUs = 0; 226 for (int type : foregroundTypes) { 227 final long localTime = uid.getProcessStateTime(type, rawRealTimeUs, 228 BatteryStats.STATS_SINCE_CHARGED); 229 timeUs += localTime; 230 } 231 232 // Return the min value of STATE_TOP time and foreground activity time, since both of these 233 // time have some errors. 234 return Math.min(timeUs, getForegroundActivityTotalTimeUs(uid, rawRealTimeUs)) / 1000; 235 } 236 237 /** Get the ForegroundActivity time of the given uid. */ 238 @VisibleForTesting getForegroundActivityTotalTimeUs(BatteryStats.Uid uid, long rawRealtimeUs)239 public long getForegroundActivityTotalTimeUs(BatteryStats.Uid uid, long rawRealtimeUs) { 240 final BatteryStats.Timer timer = uid.getForegroundActivityTimer(); 241 if (timer == null) { 242 return 0; 243 } 244 return timer.getTotalTimeLocked(rawRealtimeUs, BatteryStats.STATS_SINCE_CHARGED); 245 } 246 } 247