• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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