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