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