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