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.internal.os; 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.os.UserHandle; 28 import android.text.format.DateUtils; 29 import android.util.Slog; 30 import android.util.SparseArray; 31 import android.util.SparseLongArray; 32 33 import com.android.internal.annotations.VisibleForTesting; 34 35 import java.util.List; 36 37 /** 38 * Estimates power consumed by the screen(s) 39 */ 40 public class ScreenPowerCalculator extends PowerCalculator { 41 private static final String TAG = "ScreenPowerCalculator"; 42 private static final boolean DEBUG = BatteryStatsHelper.DEBUG; 43 44 // Minimum amount of time the screen should be on to start smearing drain to apps 45 public static final long MIN_ACTIVE_TIME_FOR_SMEARING = 10 * DateUtils.MINUTE_IN_MILLIS; 46 47 private final UsageBasedPowerEstimator[] mScreenOnPowerEstimators; 48 private final UsageBasedPowerEstimator[] mScreenFullPowerEstimators; 49 50 private static class PowerAndDuration { 51 public long durationMs; 52 public double powerMah; 53 } 54 ScreenPowerCalculator(PowerProfile powerProfile)55 public ScreenPowerCalculator(PowerProfile powerProfile) { 56 final int numDisplays = powerProfile.getNumDisplays(); 57 mScreenOnPowerEstimators = new UsageBasedPowerEstimator[numDisplays]; 58 mScreenFullPowerEstimators = new UsageBasedPowerEstimator[numDisplays]; 59 for (int display = 0; display < numDisplays; display++) { 60 mScreenOnPowerEstimators[display] = new UsageBasedPowerEstimator( 61 powerProfile.getAveragePowerForOrdinal(POWER_GROUP_DISPLAY_SCREEN_ON, display)); 62 mScreenFullPowerEstimators[display] = new UsageBasedPowerEstimator( 63 powerProfile.getAveragePowerForOrdinal(POWER_GROUP_DISPLAY_SCREEN_FULL, 64 display)); 65 } 66 } 67 68 @Override calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats, long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query)69 public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats, 70 long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) { 71 final PowerAndDuration totalPowerAndDuration = new PowerAndDuration(); 72 73 final long consumptionUC = batteryStats.getScreenOnMeasuredBatteryConsumptionUC(); 74 final int powerModel = getPowerModel(consumptionUC, query); 75 calculateTotalDurationAndPower(totalPowerAndDuration, powerModel, batteryStats, 76 rawRealtimeUs, BatteryStats.STATS_SINCE_CHARGED, consumptionUC); 77 78 double totalAppPower = 0; 79 long totalAppDuration = 0; 80 81 // Now deal with each app's UidBatteryConsumer. The results are stored in the 82 // BatteryConsumer.POWER_COMPONENT_SCREEN power component, which is considered smeared, 83 // but the method depends on the data source. 84 final SparseArray<UidBatteryConsumer.Builder> uidBatteryConsumerBuilders = 85 builder.getUidBatteryConsumerBuilders(); 86 switch (powerModel) { 87 case BatteryConsumer.POWER_MODEL_MEASURED_ENERGY: 88 final PowerAndDuration appPowerAndDuration = new PowerAndDuration(); 89 for (int i = uidBatteryConsumerBuilders.size() - 1; i >= 0; i--) { 90 final UidBatteryConsumer.Builder app = uidBatteryConsumerBuilders.valueAt(i); 91 calculateAppUsingMeasuredEnergy(appPowerAndDuration, app.getBatteryStatsUid(), 92 rawRealtimeUs); 93 app.setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_SCREEN, 94 appPowerAndDuration.durationMs) 95 .setConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN, 96 appPowerAndDuration.powerMah, powerModel); 97 totalAppPower += appPowerAndDuration.powerMah; 98 totalAppDuration += appPowerAndDuration.durationMs; 99 } 100 break; 101 case BatteryConsumer.POWER_MODEL_POWER_PROFILE: 102 default: 103 smearScreenBatteryDrain(uidBatteryConsumerBuilders, totalPowerAndDuration, 104 rawRealtimeUs); 105 totalAppPower = totalPowerAndDuration.powerMah; 106 totalAppDuration = totalPowerAndDuration.durationMs; 107 } 108 109 builder.getAggregateBatteryConsumerBuilder( 110 BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE) 111 .setConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN, 112 Math.max(totalPowerAndDuration.powerMah, totalAppPower), powerModel) 113 .setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_SCREEN, 114 totalPowerAndDuration.durationMs); 115 116 builder.getAggregateBatteryConsumerBuilder( 117 BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS) 118 .setConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN, totalAppPower, powerModel) 119 .setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_SCREEN, totalAppDuration); 120 } 121 122 /** 123 * Screen power is the additional power the screen takes while the device is running. 124 */ 125 @Override calculate(List<BatterySipper> sippers, BatteryStats batteryStats, long rawRealtimeUs, long rawUptimeUs, int statsType, SparseArray<UserHandle> asUsers)126 public void calculate(List<BatterySipper> sippers, BatteryStats batteryStats, 127 long rawRealtimeUs, long rawUptimeUs, int statsType, SparseArray<UserHandle> asUsers) { 128 final PowerAndDuration totalPowerAndDuration = new PowerAndDuration(); 129 final long consumptionUC = batteryStats.getScreenOnMeasuredBatteryConsumptionUC(); 130 final int powerModel = getPowerModel(consumptionUC); 131 calculateTotalDurationAndPower(totalPowerAndDuration, powerModel, batteryStats, 132 rawRealtimeUs, statsType, consumptionUC); 133 if (totalPowerAndDuration.powerMah == 0) { 134 return; 135 } 136 137 // First deal with the SCREEN BatterySipper (since we need this for smearing over apps). 138 final BatterySipper bs = new BatterySipper(BatterySipper.DrainType.SCREEN, null, 0); 139 bs.usagePowerMah = totalPowerAndDuration.powerMah; 140 bs.usageTimeMs = totalPowerAndDuration.durationMs; 141 bs.sumPower(); 142 sippers.add(bs); 143 144 // Now deal with each app's BatterySipper. The results are stored in the screenPowerMah 145 // field, which is considered smeared, but the method depends on the data source. 146 switch (powerModel) { 147 case BatteryConsumer.POWER_MODEL_MEASURED_ENERGY: 148 final PowerAndDuration appPowerAndDuration = new PowerAndDuration(); 149 for (int i = sippers.size() - 1; i >= 0; i--) { 150 final BatterySipper app = sippers.get(i); 151 if (app.drainType == BatterySipper.DrainType.APP) { 152 calculateAppUsingMeasuredEnergy(appPowerAndDuration, app.uidObj, 153 rawRealtimeUs); 154 app.screenPowerMah = appPowerAndDuration.powerMah; 155 } 156 } 157 break; 158 case BatteryConsumer.POWER_MODEL_POWER_PROFILE: 159 default: 160 smearScreenBatterySipper(sippers, bs, rawRealtimeUs); 161 } 162 } 163 164 /** 165 * Stores duration and power information in totalPowerAndDuration. 166 */ calculateTotalDurationAndPower(PowerAndDuration totalPowerAndDuration, @BatteryConsumer.PowerModel int powerModel, BatteryStats batteryStats, long rawRealtimeUs, int statsType, long consumptionUC)167 private void calculateTotalDurationAndPower(PowerAndDuration totalPowerAndDuration, 168 @BatteryConsumer.PowerModel int powerModel, BatteryStats batteryStats, 169 long rawRealtimeUs, int statsType, long consumptionUC) { 170 totalPowerAndDuration.durationMs = calculateDuration(batteryStats, rawRealtimeUs, 171 statsType); 172 173 switch (powerModel) { 174 case BatteryConsumer.POWER_MODEL_MEASURED_ENERGY: 175 totalPowerAndDuration.powerMah = uCtoMah(consumptionUC); 176 break; 177 case BatteryConsumer.POWER_MODEL_POWER_PROFILE: 178 default: 179 totalPowerAndDuration.powerMah = calculateTotalPowerFromBrightness(batteryStats, 180 rawRealtimeUs); 181 } 182 } 183 calculateAppUsingMeasuredEnergy(PowerAndDuration appPowerAndDuration, BatteryStats.Uid u, long rawRealtimeUs)184 private void calculateAppUsingMeasuredEnergy(PowerAndDuration appPowerAndDuration, 185 BatteryStats.Uid u, long rawRealtimeUs) { 186 appPowerAndDuration.durationMs = getProcessForegroundTimeMs(u, rawRealtimeUs); 187 188 final long chargeUC = u.getScreenOnMeasuredBatteryConsumptionUC(); 189 if (chargeUC < 0) { 190 Slog.wtf(TAG, "Screen energy not supported, so calculateApp shouldn't de called"); 191 appPowerAndDuration.powerMah = 0; 192 return; 193 } 194 195 appPowerAndDuration.powerMah = uCtoMah(chargeUC); 196 } 197 calculateDuration(BatteryStats batteryStats, long rawRealtimeUs, int statsType)198 private long calculateDuration(BatteryStats batteryStats, long rawRealtimeUs, int statsType) { 199 return batteryStats.getScreenOnTime(rawRealtimeUs, statsType) / 1000; 200 } 201 calculateTotalPowerFromBrightness(BatteryStats batteryStats, long rawRealtimeUs)202 private double calculateTotalPowerFromBrightness(BatteryStats batteryStats, 203 long rawRealtimeUs) { 204 final int numDisplays = mScreenOnPowerEstimators.length; 205 double power = 0; 206 for (int display = 0; display < numDisplays; display++) { 207 final long displayTime = batteryStats.getDisplayScreenOnTime(display, rawRealtimeUs) 208 / 1000; 209 power += mScreenOnPowerEstimators[display].calculatePower(displayTime); 210 for (int bin = 0; bin < BatteryStats.NUM_SCREEN_BRIGHTNESS_BINS; bin++) { 211 final long brightnessTime = batteryStats.getDisplayScreenBrightnessTime(display, 212 bin, rawRealtimeUs) / 1000; 213 final double binPowerMah = mScreenFullPowerEstimators[display].calculatePower( 214 brightnessTime) * (bin + 0.5f) / BatteryStats.NUM_SCREEN_BRIGHTNESS_BINS; 215 if (DEBUG && binPowerMah != 0) { 216 Slog.d(TAG, "Screen bin #" + bin + ": time=" + brightnessTime 217 + " power=" + formatCharge(binPowerMah)); 218 } 219 power += binPowerMah; 220 } 221 } 222 return power; 223 } 224 225 /** 226 * Smear the screen on power usage among {@code sippers}, based on ratio of foreground activity 227 * time, and store this in the {@link BatterySipper#screenPowerMah} field. 228 */ 229 @VisibleForTesting smearScreenBatterySipper(List<BatterySipper> sippers, BatterySipper screenSipper, long rawRealtimeUs)230 public void smearScreenBatterySipper(List<BatterySipper> sippers, BatterySipper screenSipper, 231 long rawRealtimeUs) { 232 long totalActivityTimeMs = 0; 233 final SparseLongArray activityTimeArray = new SparseLongArray(); 234 for (int i = sippers.size() - 1; i >= 0; i--) { 235 final BatteryStats.Uid uid = sippers.get(i).uidObj; 236 if (uid != null) { 237 final long timeMs = getProcessForegroundTimeMs(uid, rawRealtimeUs); 238 activityTimeArray.put(uid.getUid(), timeMs); 239 totalActivityTimeMs += timeMs; 240 } 241 } 242 243 if (screenSipper != null && totalActivityTimeMs >= MIN_ACTIVE_TIME_FOR_SMEARING) { 244 final double totalScreenPowerMah = screenSipper.totalPowerMah; 245 for (int i = sippers.size() - 1; i >= 0; i--) { 246 final BatterySipper sipper = sippers.get(i); 247 sipper.screenPowerMah = totalScreenPowerMah 248 * activityTimeArray.get(sipper.getUid(), 0) 249 / totalActivityTimeMs; 250 } 251 } 252 } 253 254 /** 255 * Smear the screen on power usage among {@code sippers}, based on ratio of foreground activity 256 * time, and store this in the {@link BatterySipper#screenPowerMah} field. 257 */ smearScreenBatteryDrain( SparseArray<UidBatteryConsumer.Builder> uidBatteryConsumerBuilders, PowerAndDuration totalPowerAndDuration, long rawRealtimeUs)258 private void smearScreenBatteryDrain( 259 SparseArray<UidBatteryConsumer.Builder> uidBatteryConsumerBuilders, 260 PowerAndDuration totalPowerAndDuration, long rawRealtimeUs) { 261 long totalActivityTimeMs = 0; 262 final SparseLongArray activityTimeArray = new SparseLongArray(); 263 for (int i = uidBatteryConsumerBuilders.size() - 1; i >= 0; i--) { 264 final BatteryStats.Uid uid = uidBatteryConsumerBuilders.valueAt(i).getBatteryStatsUid(); 265 final long timeMs = getProcessForegroundTimeMs(uid, rawRealtimeUs); 266 activityTimeArray.put(uid.getUid(), timeMs); 267 totalActivityTimeMs += timeMs; 268 } 269 270 if (totalActivityTimeMs >= MIN_ACTIVE_TIME_FOR_SMEARING) { 271 final double totalScreenPowerMah = totalPowerAndDuration.powerMah; 272 for (int i = uidBatteryConsumerBuilders.size() - 1; i >= 0; i--) { 273 final UidBatteryConsumer.Builder app = uidBatteryConsumerBuilders.valueAt(i); 274 final long durationMs = activityTimeArray.get(app.getUid(), 0); 275 final double powerMah = totalScreenPowerMah * durationMs / totalActivityTimeMs; 276 app.setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_SCREEN, durationMs) 277 .setConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN, powerMah, 278 BatteryConsumer.POWER_MODEL_POWER_PROFILE); 279 } 280 } 281 } 282 283 /** Get the minimum of the uid's ForegroundActivity time and its TOP time. */ 284 @VisibleForTesting getProcessForegroundTimeMs(BatteryStats.Uid uid, long rawRealTimeUs)285 public long getProcessForegroundTimeMs(BatteryStats.Uid uid, long rawRealTimeUs) { 286 final int[] foregroundTypes = {BatteryStats.Uid.PROCESS_STATE_TOP}; 287 288 long timeUs = 0; 289 for (int type : foregroundTypes) { 290 final long localTime = uid.getProcessStateTime(type, rawRealTimeUs, 291 BatteryStats.STATS_SINCE_CHARGED); 292 timeUs += localTime; 293 } 294 295 // Return the min value of STATE_TOP time and foreground activity time, since both of these 296 // time have some errors. 297 return Math.min(timeUs, getForegroundActivityTotalTimeUs(uid, rawRealTimeUs)) / 1000; 298 } 299 300 /** Get the ForegroundActivity time of the given uid. */ 301 @VisibleForTesting getForegroundActivityTotalTimeUs(BatteryStats.Uid uid, long rawRealtimeUs)302 public long getForegroundActivityTotalTimeUs(BatteryStats.Uid uid, long rawRealtimeUs) { 303 final BatteryStats.Timer timer = uid.getForegroundActivityTimer(); 304 if (timer == null) { 305 return 0; 306 } 307 return timer.getTotalTimeLocked(rawRealtimeUs, BatteryStats.STATS_SINCE_CHARGED); 308 } 309 } 310