1 /*
2  * Copyright (C) 2015 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 package com.android.internal.os;
17 
18 import android.os.BatteryConsumer;
19 import android.os.BatteryStats;
20 import android.os.BatteryUsageStats;
21 import android.os.BatteryUsageStatsQuery;
22 import android.os.Process;
23 import android.os.UidBatteryConsumer;
24 import android.os.UserHandle;
25 import android.util.ArrayMap;
26 import android.util.Log;
27 import android.util.SparseArray;
28 
29 import java.util.List;
30 
31 public class WakelockPowerCalculator extends PowerCalculator {
32     private static final String TAG = "WakelockPowerCalculator";
33     private static final boolean DEBUG = BatteryStatsHelper.DEBUG;
34     private final UsageBasedPowerEstimator mPowerEstimator;
35 
36     private static class PowerAndDuration {
37         public long durationMs;
38         public double powerMah;
39     }
40 
WakelockPowerCalculator(PowerProfile profile)41     public WakelockPowerCalculator(PowerProfile profile) {
42         mPowerEstimator = new UsageBasedPowerEstimator(
43                 profile.getAveragePower(PowerProfile.POWER_CPU_IDLE));
44     }
45 
46     @Override
calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats, long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query)47     public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats,
48             long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) {
49         final PowerAndDuration result = new PowerAndDuration();
50         UidBatteryConsumer.Builder osBatteryConsumer = null;
51         double osPowerMah = 0;
52         long osDurationMs = 0;
53         long totalAppDurationMs = 0;
54         double appPowerMah = 0;
55         final SparseArray<UidBatteryConsumer.Builder> uidBatteryConsumerBuilders =
56                 builder.getUidBatteryConsumerBuilders();
57         for (int i = uidBatteryConsumerBuilders.size() - 1; i >= 0; i--) {
58             final UidBatteryConsumer.Builder app = uidBatteryConsumerBuilders.valueAt(i);
59             calculateApp(result, app.getBatteryStatsUid(), rawRealtimeUs,
60                     BatteryStats.STATS_SINCE_CHARGED);
61             app.setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_WAKELOCK, result.durationMs)
62                     .setConsumedPower(BatteryConsumer.POWER_COMPONENT_WAKELOCK, result.powerMah);
63             totalAppDurationMs += result.durationMs;
64             appPowerMah += result.powerMah;
65 
66             if (app.getUid() == Process.ROOT_UID) {
67                 osBatteryConsumer = app;
68                 osDurationMs = result.durationMs;
69                 osPowerMah = result.powerMah;
70             }
71         }
72 
73         // The device has probably been awake for longer than the screen on
74         // time and application wake lock time would account for.  Assign
75         // this remainder to the OS, if possible.
76         calculateRemaining(result, batteryStats, rawRealtimeUs, rawUptimeUs,
77                 BatteryStats.STATS_SINCE_CHARGED, osPowerMah, osDurationMs, totalAppDurationMs);
78         final double remainingPowerMah = result.powerMah;
79         if (osBatteryConsumer != null) {
80             osBatteryConsumer.setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_WAKELOCK,
81                     result.durationMs)
82                     .setConsumedPower(BatteryConsumer.POWER_COMPONENT_WAKELOCK, remainingPowerMah);
83         }
84 
85         long wakeTimeMs = calculateWakeTimeMillis(batteryStats, rawRealtimeUs, rawUptimeUs);
86         if (wakeTimeMs < 0) {
87             wakeTimeMs = 0;
88         }
89         builder.getAggregateBatteryConsumerBuilder(
90                 BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE)
91                 .setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_WAKELOCK,
92                         wakeTimeMs)
93                 .setConsumedPower(BatteryConsumer.POWER_COMPONENT_WAKELOCK,
94                         appPowerMah + remainingPowerMah);
95         builder.getAggregateBatteryConsumerBuilder(
96                 BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS)
97                 .setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_WAKELOCK,
98                         totalAppDurationMs)
99                 .setConsumedPower(BatteryConsumer.POWER_COMPONENT_WAKELOCK,
100                         appPowerMah);
101     }
102 
103     @Override
calculate(List<BatterySipper> sippers, BatteryStats batteryStats, long rawRealtimeUs, long rawUptimeUs, int statsType, SparseArray<UserHandle> asUsers)104     public void calculate(List<BatterySipper> sippers, BatteryStats batteryStats,
105             long rawRealtimeUs, long rawUptimeUs, int statsType, SparseArray<UserHandle> asUsers) {
106         final PowerAndDuration result = new PowerAndDuration();
107         BatterySipper osSipper = null;
108         double osPowerMah = 0;
109         long osDurationMs = 0;
110         long totalAppDurationMs = 0;
111         for (int i = sippers.size() - 1; i >= 0; i--) {
112             final BatterySipper app = sippers.get(i);
113             if (app.drainType == BatterySipper.DrainType.APP) {
114                 calculateApp(result, app.uidObj, rawRealtimeUs, statsType);
115                 app.wakeLockTimeMs = result.durationMs;
116                 app.wakeLockPowerMah = result.powerMah;
117                 totalAppDurationMs += result.durationMs;
118 
119                 if (app.getUid() == Process.ROOT_UID) {
120                     osSipper = app;
121                     osPowerMah = result.powerMah;
122                     osDurationMs = result.durationMs;
123                 }
124             }
125         }
126 
127         // The device has probably been awake for longer than the screen on
128         // time and application wake lock time would account for.  Assign
129         // this remainder to the OS, if possible.
130         if (osSipper != null) {
131             calculateRemaining(result, batteryStats, rawRealtimeUs, rawUptimeUs, statsType,
132                     osPowerMah, osDurationMs, totalAppDurationMs);
133             osSipper.wakeLockTimeMs = result.durationMs;
134             osSipper.wakeLockPowerMah = result.powerMah;
135             osSipper.sumPower();
136         }
137     }
138 
calculateApp(PowerAndDuration result, BatteryStats.Uid u, long rawRealtimeUs, int statsType)139     private void calculateApp(PowerAndDuration result, BatteryStats.Uid u, long rawRealtimeUs,
140             int statsType) {
141         long wakeLockTimeUs = 0;
142         final ArrayMap<String, ? extends BatteryStats.Uid.Wakelock> wakelockStats =
143                 u.getWakelockStats();
144         final int wakelockStatsCount = wakelockStats.size();
145         for (int i = 0; i < wakelockStatsCount; i++) {
146             final BatteryStats.Uid.Wakelock wakelock = wakelockStats.valueAt(i);
147 
148             // Only care about partial wake locks since full wake locks
149             // are canceled when the user turns the screen off.
150             BatteryStats.Timer timer = wakelock.getWakeTime(BatteryStats.WAKE_TYPE_PARTIAL);
151             if (timer != null) {
152                 wakeLockTimeUs += timer.getTotalTimeLocked(rawRealtimeUs, statsType);
153             }
154         }
155         result.durationMs = wakeLockTimeUs / 1000; // convert to millis
156 
157         // Add cost of holding a wake lock.
158         result.powerMah = mPowerEstimator.calculatePower(result.durationMs);
159         if (DEBUG && result.powerMah != 0) {
160             Log.d(TAG, "UID " + u.getUid() + ": wake " + result.durationMs
161                     + " power=" + formatCharge(result.powerMah));
162         }
163     }
164 
calculateRemaining(PowerAndDuration result, BatteryStats stats, long rawRealtimeUs, long rawUptimeUs, int statsType, double osPowerMah, long osDurationMs, long totalAppDurationMs)165     private void calculateRemaining(PowerAndDuration result, BatteryStats stats, long rawRealtimeUs,
166             long rawUptimeUs, int statsType, double osPowerMah, long osDurationMs,
167             long totalAppDurationMs) {
168         final long wakeTimeMillis = calculateWakeTimeMillis(stats, rawRealtimeUs, rawUptimeUs)
169                 - totalAppDurationMs;
170         if (wakeTimeMillis > 0) {
171             final double power = mPowerEstimator.calculatePower(wakeTimeMillis);
172             if (DEBUG) {
173                 Log.d(TAG, "OS wakeLockTime " + wakeTimeMillis + " power " + formatCharge(power));
174             }
175             result.durationMs = osDurationMs + wakeTimeMillis;
176             result.powerMah = osPowerMah + power;
177         } else {
178             result.durationMs = 0;
179             result.powerMah = 0;
180         }
181     }
182 
183     /**
184      * Return on-battery/screen-off time.  May be negative if the screen-on time exceeds
185      * the on-battery time.
186      */
calculateWakeTimeMillis(BatteryStats batteryStats, long rawRealtimeUs, long rawUptimeUs)187     private long calculateWakeTimeMillis(BatteryStats batteryStats, long rawRealtimeUs,
188             long rawUptimeUs) {
189         final long batteryUptimeUs = batteryStats.getBatteryUptime(rawUptimeUs);
190         final long screenOnTimeUs =
191                 batteryStats.getScreenOnTime(rawRealtimeUs, BatteryStats.STATS_SINCE_CHARGED);
192         return (batteryUptimeUs - screenOnTimeUs) / 1000;
193     }
194 }
195