• 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.server.power.stats;
18 
19 import android.content.Context;
20 import android.hardware.SensorManager;
21 import android.os.BatteryConsumer;
22 import android.os.BatteryStats;
23 import android.os.BatteryUsageStats;
24 import android.os.BatteryUsageStatsQuery;
25 import android.os.Process;
26 import android.os.UidBatteryConsumer;
27 import android.util.Log;
28 import android.util.Slog;
29 import android.util.SparseArray;
30 import android.util.SparseBooleanArray;
31 
32 import com.android.internal.os.Clock;
33 import com.android.internal.os.CpuScalingPolicies;
34 import com.android.internal.os.PowerProfile;
35 
36 import java.util.ArrayList;
37 import java.util.Arrays;
38 import java.util.List;
39 
40 /**
41  * Uses accumulated battery stats data and PowerCalculators to produce power
42  * usage data attributed to subsystems and UIDs.
43  */
44 public class BatteryUsageStatsProvider {
45     private static final String TAG = "BatteryUsageStatsProv";
46     private final Context mContext;
47     private final SparseBooleanArray mPowerStatsExporterEnabled = new SparseBooleanArray();
48     private final PowerStatsExporter mPowerStatsExporter;
49     private final PowerStatsStore mPowerStatsStore;
50     private final PowerProfile mPowerProfile;
51     private final CpuScalingPolicies mCpuScalingPolicies;
52     private final Clock mClock;
53     private final Object mLock = new Object();
54     private List<PowerCalculator> mPowerCalculators;
55 
BatteryUsageStatsProvider(Context context, PowerStatsExporter powerStatsExporter, PowerProfile powerProfile, CpuScalingPolicies cpuScalingPolicies, PowerStatsStore powerStatsStore, Clock clock)56     public BatteryUsageStatsProvider(Context context,
57             PowerStatsExporter powerStatsExporter,
58             PowerProfile powerProfile, CpuScalingPolicies cpuScalingPolicies,
59             PowerStatsStore powerStatsStore, Clock clock) {
60         mContext = context;
61         mPowerStatsExporter = powerStatsExporter;
62         mPowerStatsStore = powerStatsStore;
63         mPowerProfile = powerProfile;
64         mCpuScalingPolicies = cpuScalingPolicies;
65         mClock = clock;
66     }
67 
getPowerCalculators()68     private List<PowerCalculator> getPowerCalculators() {
69         synchronized (mLock) {
70             if (mPowerCalculators == null) {
71                 mPowerCalculators = new ArrayList<>();
72 
73                 // Power calculators are applied in the order of registration
74                 mPowerCalculators.add(new BatteryChargeCalculator());
75                 if (!mPowerStatsExporterEnabled.get(BatteryConsumer.POWER_COMPONENT_CPU)) {
76                     mPowerCalculators.add(
77                             new CpuPowerCalculator(mCpuScalingPolicies, mPowerProfile));
78                 }
79                 mPowerCalculators.add(new MemoryPowerCalculator(mPowerProfile));
80                 mPowerCalculators.add(new WakelockPowerCalculator(mPowerProfile));
81                 if (!BatteryStats.checkWifiOnly(mContext)) {
82                     if (!mPowerStatsExporterEnabled.get(
83                             BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)) {
84                         mPowerCalculators.add(new MobileRadioPowerCalculator(mPowerProfile));
85                     }
86                     if (!mPowerStatsExporterEnabled.get(BatteryConsumer.POWER_COMPONENT_PHONE)) {
87                         mPowerCalculators.add(new PhonePowerCalculator(mPowerProfile));
88                     }
89                 }
90                 if (!mPowerStatsExporterEnabled.get(BatteryConsumer.POWER_COMPONENT_WIFI)) {
91                     mPowerCalculators.add(new WifiPowerCalculator(mPowerProfile));
92                 }
93                 if (!mPowerStatsExporterEnabled.get(BatteryConsumer.POWER_COMPONENT_BLUETOOTH)) {
94                     mPowerCalculators.add(new BluetoothPowerCalculator(mPowerProfile));
95                 }
96                 mPowerCalculators.add(new SensorPowerCalculator(
97                         mContext.getSystemService(SensorManager.class)));
98                 if (!mPowerStatsExporterEnabled.get(BatteryConsumer.POWER_COMPONENT_GNSS)) {
99                     mPowerCalculators.add(new GnssPowerCalculator(mPowerProfile));
100                 }
101                 if (!mPowerStatsExporterEnabled.get(BatteryConsumer.POWER_COMPONENT_CAMERA)) {
102                     mPowerCalculators.add(new CameraPowerCalculator(mPowerProfile));
103                 }
104                 if (!mPowerStatsExporterEnabled.get(BatteryConsumer.POWER_COMPONENT_FLASHLIGHT)) {
105                     mPowerCalculators.add(new FlashlightPowerCalculator(mPowerProfile));
106                 }
107                 if (!mPowerStatsExporterEnabled.get(BatteryConsumer.POWER_COMPONENT_AUDIO)) {
108                     mPowerCalculators.add(new AudioPowerCalculator(mPowerProfile));
109                 }
110                 if (!mPowerStatsExporterEnabled.get(BatteryConsumer.POWER_COMPONENT_VIDEO)) {
111                     mPowerCalculators.add(new VideoPowerCalculator(mPowerProfile));
112                 }
113                 mPowerCalculators.add(new ScreenPowerCalculator(mPowerProfile));
114                 mPowerCalculators.add(new AmbientDisplayPowerCalculator(mPowerProfile));
115                 mPowerCalculators.add(new IdlePowerCalculator(mPowerProfile));
116                 mPowerCalculators.add(new CustomEnergyConsumerPowerCalculator(mPowerProfile));
117                 mPowerCalculators.add(new UserPowerCalculator());
118 
119                 if (!com.android.server.power.optimization.Flags.disableSystemServicePowerAttr()) {
120                     // It is important that SystemServicePowerCalculator be applied last,
121                     // because it re-attributes some of the power estimated by the other
122                     // calculators.
123                     mPowerCalculators.add(
124                             new SystemServicePowerCalculator(mCpuScalingPolicies, mPowerProfile));
125                 }
126             }
127         }
128         return mPowerCalculators;
129     }
130 
131     /**
132      * Returns true if the last update was too long ago for the tolerances specified
133      * by the supplied queries.
134      */
shouldUpdateStats(List<BatteryUsageStatsQuery> queries, long elapsedRealtime, long lastUpdateTimeStampMs)135     public static boolean shouldUpdateStats(List<BatteryUsageStatsQuery> queries,
136             long elapsedRealtime, long lastUpdateTimeStampMs) {
137         long allowableStatsAge = Long.MAX_VALUE;
138         for (int i = queries.size() - 1; i >= 0; i--) {
139             BatteryUsageStatsQuery query = queries.get(i);
140             allowableStatsAge = Math.min(allowableStatsAge, query.getMaxStatsAge());
141         }
142 
143         return elapsedRealtime - lastUpdateTimeStampMs > allowableStatsAge;
144     }
145 
146     /**
147      * Returns snapshots of battery attribution data, one per supplied query.
148      */
getBatteryUsageStats(BatteryStatsImpl stats, List<BatteryUsageStatsQuery> queries)149     public List<BatteryUsageStats> getBatteryUsageStats(BatteryStatsImpl stats,
150             List<BatteryUsageStatsQuery> queries) {
151         ArrayList<BatteryUsageStats> results = new ArrayList<>(queries.size());
152         synchronized (stats) {
153             stats.prepareForDumpLocked();
154         }
155         final long currentTimeMillis = mClock.currentTimeMillis();
156         for (int i = 0; i < queries.size(); i++) {
157             results.add(getBatteryUsageStats(stats, queries.get(i), currentTimeMillis));
158         }
159 
160         return results;
161     }
162 
163     /**
164      * Returns a snapshot of battery attribution data.
165      */
getBatteryUsageStats(BatteryStatsImpl stats, BatteryUsageStatsQuery query)166     public BatteryUsageStats getBatteryUsageStats(BatteryStatsImpl stats,
167             BatteryUsageStatsQuery query) {
168         return getBatteryUsageStats(stats, query, mClock.currentTimeMillis());
169     }
170 
getBatteryUsageStats(BatteryStatsImpl stats, BatteryUsageStatsQuery query, long currentTimeMs)171     private BatteryUsageStats getBatteryUsageStats(BatteryStatsImpl stats,
172             BatteryUsageStatsQuery query, long currentTimeMs) {
173         if (query.getToTimestamp() == 0) {
174             return getCurrentBatteryUsageStats(stats, query, currentTimeMs);
175         } else {
176             return getAggregatedBatteryUsageStats(stats, query);
177         }
178     }
179 
getCurrentBatteryUsageStats(BatteryStatsImpl stats, BatteryUsageStatsQuery query, long currentTimeMs)180     private BatteryUsageStats getCurrentBatteryUsageStats(BatteryStatsImpl stats,
181             BatteryUsageStatsQuery query, long currentTimeMs) {
182         final long realtimeUs = mClock.elapsedRealtime() * 1000;
183         final long uptimeUs = mClock.uptimeMillis() * 1000;
184 
185         final boolean includePowerModels = (query.getFlags()
186                 & BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_INCLUDE_POWER_MODELS) != 0;
187         final boolean includeProcessStateData = ((query.getFlags()
188                 & BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_INCLUDE_PROCESS_STATE_DATA) != 0)
189                 && stats.isProcessStateDataAvailable();
190         final boolean includeVirtualUids =  ((query.getFlags()
191                 & BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_INCLUDE_VIRTUAL_UIDS) != 0);
192         final double minConsumedPowerThreshold = query.getMinConsumedPowerThreshold();
193 
194         final BatteryUsageStats.Builder batteryUsageStatsBuilder;
195         long monotonicStartTime, monotonicEndTime;
196         synchronized (stats) {
197             monotonicStartTime = stats.getMonotonicStartTime();
198             monotonicEndTime = stats.getMonotonicEndTime();
199 
200             batteryUsageStatsBuilder = new BatteryUsageStats.Builder(
201                     stats.getCustomEnergyConsumerNames(), includePowerModels,
202                     includeProcessStateData, minConsumedPowerThreshold);
203 
204             // TODO(b/188068523): use a monotonic clock to ensure resilience of order and duration
205             // of batteryUsageStats sessions to wall-clock adjustments
206             batteryUsageStatsBuilder.setStatsStartTimestamp(stats.getStartClockTime());
207             batteryUsageStatsBuilder.setStatsEndTimestamp(currentTimeMs);
208             SparseArray<? extends BatteryStats.Uid> uidStats = stats.getUidStats();
209             for (int i = uidStats.size() - 1; i >= 0; i--) {
210                 final BatteryStats.Uid uid = uidStats.valueAt(i);
211                 if (!includeVirtualUids && uid.getUid() == Process.SDK_SANDBOX_VIRTUAL_UID) {
212                     continue;
213                 }
214 
215                 batteryUsageStatsBuilder.getOrCreateUidBatteryConsumerBuilder(uid)
216                         .setTimeInProcessStateMs(UidBatteryConsumer.PROCESS_STATE_BACKGROUND,
217                                 getProcessBackgroundTimeMs(uid, realtimeUs))
218                         .setTimeInProcessStateMs(UidBatteryConsumer.PROCESS_STATE_FOREGROUND,
219                                 getProcessForegroundTimeMs(uid, realtimeUs))
220                         .setTimeInProcessStateMs(
221                                 UidBatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE,
222                                 getProcessForegroundServiceTimeMs(uid, realtimeUs));
223             }
224 
225             final int[] powerComponents = query.getPowerComponents();
226             final List<PowerCalculator> powerCalculators = getPowerCalculators();
227             for (int i = 0, count = powerCalculators.size(); i < count; i++) {
228                 PowerCalculator powerCalculator = powerCalculators.get(i);
229                 if (powerComponents != null) {
230                     boolean include = false;
231                     for (int powerComponent : powerComponents) {
232                         if (powerCalculator.isPowerComponentSupported(powerComponent)) {
233                             include = true;
234                             break;
235                         }
236                     }
237                     if (!include) {
238                         continue;
239                     }
240                 }
241                 powerCalculator.calculate(batteryUsageStatsBuilder, stats, realtimeUs, uptimeUs,
242                         query);
243             }
244 
245             if ((query.getFlags()
246                     & BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_INCLUDE_HISTORY) != 0) {
247                 batteryUsageStatsBuilder.setBatteryHistory(stats.copyHistory());
248             }
249         }
250 
251         if (mPowerStatsExporterEnabled.indexOfValue(true) >= 0) {
252             mPowerStatsExporter.exportAggregatedPowerStats(batteryUsageStatsBuilder,
253                     monotonicStartTime, monotonicEndTime);
254         }
255 
256         BatteryUsageStats batteryUsageStats = batteryUsageStatsBuilder.build();
257         if (includeProcessStateData) {
258             verify(batteryUsageStats);
259         }
260         return batteryUsageStats;
261     }
262 
263     // STOPSHIP(b/229906525): remove verification before shipping
264     private static boolean sErrorReported;
verify(BatteryUsageStats stats)265     private void verify(BatteryUsageStats stats) {
266         if (sErrorReported) {
267             return;
268         }
269 
270         final double precision = 2.0;   // Allow rounding errors up to 2 mAh
271         final int[] components =
272                 {BatteryConsumer.POWER_COMPONENT_CPU,
273                         BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO,
274                         BatteryConsumer.POWER_COMPONENT_WIFI,
275                         BatteryConsumer.POWER_COMPONENT_BLUETOOTH};
276         final int[] states =
277                 {BatteryConsumer.PROCESS_STATE_FOREGROUND,
278                         BatteryConsumer.PROCESS_STATE_BACKGROUND,
279                         BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE,
280                         BatteryConsumer.PROCESS_STATE_CACHED};
281         for (UidBatteryConsumer ubc : stats.getUidBatteryConsumers()) {
282             for (int component : components) {
283                 double consumedPower = ubc.getConsumedPower(ubc.getKey(component));
284                 double sumStates = 0;
285                 for (int state : states) {
286                     sumStates += ubc.getConsumedPower(ubc.getKey(component, state));
287                 }
288                 if (sumStates > consumedPower + precision) {
289                     String error = "Sum of states exceeds total. UID = " + ubc.getUid() + " "
290                             + BatteryConsumer.powerComponentIdToString(component)
291                             + " total = " + consumedPower + " states = " + sumStates;
292                     if (!sErrorReported) {
293                         Slog.wtf(TAG, error);
294                         sErrorReported = true;
295                     } else {
296                         Slog.e(TAG, error);
297                     }
298                     return;
299                 }
300             }
301         }
302     }
303 
getProcessForegroundTimeMs(BatteryStats.Uid uid, long realtimeUs)304     private long getProcessForegroundTimeMs(BatteryStats.Uid uid, long realtimeUs) {
305         final long topStateDurationUs = uid.getProcessStateTime(BatteryStats.Uid.PROCESS_STATE_TOP,
306                 realtimeUs, BatteryStats.STATS_SINCE_CHARGED);
307         long foregroundActivityDurationUs = 0;
308         final BatteryStats.Timer foregroundActivityTimer = uid.getForegroundActivityTimer();
309         if (foregroundActivityTimer != null) {
310             foregroundActivityDurationUs = foregroundActivityTimer.getTotalTimeLocked(realtimeUs,
311                     BatteryStats.STATS_SINCE_CHARGED);
312         }
313 
314         // Use the min value of STATE_TOP time and foreground activity time, since both of these
315         // times are imprecise
316         long totalForegroundDurationUs = Math.min(topStateDurationUs, foregroundActivityDurationUs);
317 
318         totalForegroundDurationUs += uid.getProcessStateTime(
319                 BatteryStats.Uid.PROCESS_STATE_FOREGROUND, realtimeUs,
320                 BatteryStats.STATS_SINCE_CHARGED);
321 
322         return totalForegroundDurationUs / 1000;
323     }
324 
getProcessBackgroundTimeMs(BatteryStats.Uid uid, long realtimeUs)325     private long getProcessBackgroundTimeMs(BatteryStats.Uid uid, long realtimeUs) {
326         return uid.getProcessStateTime(BatteryStats.Uid.PROCESS_STATE_BACKGROUND,
327                 realtimeUs, BatteryStats.STATS_SINCE_CHARGED)
328                 / 1000;
329     }
330 
getProcessForegroundServiceTimeMs(BatteryStats.Uid uid, long realtimeUs)331     private long getProcessForegroundServiceTimeMs(BatteryStats.Uid uid, long realtimeUs) {
332         return uid.getProcessStateTime(BatteryStats.Uid.PROCESS_STATE_FOREGROUND_SERVICE,
333                 realtimeUs, BatteryStats.STATS_SINCE_CHARGED)
334                 / 1000;
335     }
336 
getAggregatedBatteryUsageStats(BatteryStatsImpl stats, BatteryUsageStatsQuery query)337     private BatteryUsageStats getAggregatedBatteryUsageStats(BatteryStatsImpl stats,
338             BatteryUsageStatsQuery query) {
339         final boolean includePowerModels = (query.getFlags()
340                 & BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_INCLUDE_POWER_MODELS) != 0;
341         final boolean includeProcessStateData = ((query.getFlags()
342                 & BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_INCLUDE_PROCESS_STATE_DATA) != 0)
343                 && stats.isProcessStateDataAvailable();
344         final double minConsumedPowerThreshold = query.getMinConsumedPowerThreshold();
345 
346         final String[] customEnergyConsumerNames = stats.getCustomEnergyConsumerNames();
347         final BatteryUsageStats.Builder builder = new BatteryUsageStats.Builder(
348                 customEnergyConsumerNames, includePowerModels, includeProcessStateData,
349                 minConsumedPowerThreshold);
350         if (mPowerStatsStore == null) {
351             Log.e(TAG, "PowerStatsStore is unavailable");
352             return builder.build();
353         }
354 
355         List<PowerStatsSpan.Metadata> toc = mPowerStatsStore.getTableOfContents();
356         for (PowerStatsSpan.Metadata spanMetadata : toc) {
357             if (!spanMetadata.getSections().contains(BatteryUsageStatsSection.TYPE)) {
358                 continue;
359             }
360 
361             // BatteryUsageStatsQuery is expressed in terms of wall-clock time range for the
362             // session end time.
363             //
364             // The following algorithm is correct when there is only one time frame in the span.
365             // When the wall-clock time is adjusted in the middle of an stats span,
366             // constraining it by wall-clock time becomes ambiguous. In this case, the algorithm
367             // only covers some situations, but not others.  When using the resulting data for
368             // analysis, we should always pay attention to the full set of included timeframes.
369             // TODO(b/298459065): switch to monotonic clock
370             long minTime = Long.MAX_VALUE;
371             long maxTime = 0;
372             for (PowerStatsSpan.TimeFrame timeFrame : spanMetadata.getTimeFrames()) {
373                 long spanEndTime = timeFrame.startTime + timeFrame.duration;
374                 minTime = Math.min(minTime, spanEndTime);
375                 maxTime = Math.max(maxTime, spanEndTime);
376             }
377 
378             // Per BatteryUsageStatsQuery API, the "from" timestamp is *exclusive*,
379             // while the "to" timestamp is *inclusive*.
380             boolean isInRange =
381                     (query.getFromTimestamp() == 0 || minTime > query.getFromTimestamp())
382                     && (query.getToTimestamp() == 0 || maxTime <= query.getToTimestamp());
383             if (!isInRange) {
384                 continue;
385             }
386 
387             PowerStatsSpan powerStatsSpan = mPowerStatsStore.loadPowerStatsSpan(
388                     spanMetadata.getId(), BatteryUsageStatsSection.TYPE);
389             if (powerStatsSpan == null) {
390                 continue;
391             }
392 
393             for (PowerStatsSpan.Section section : powerStatsSpan.getSections()) {
394                 BatteryUsageStats snapshot =
395                         ((BatteryUsageStatsSection) section).getBatteryUsageStats();
396                 if (!Arrays.equals(snapshot.getCustomPowerComponentNames(),
397                         customEnergyConsumerNames)) {
398                     Log.w(TAG, "Ignoring older BatteryUsageStats snapshot, which has different "
399                             + "custom power components: "
400                             + Arrays.toString(snapshot.getCustomPowerComponentNames()));
401                     continue;
402                 }
403 
404                 if (includeProcessStateData && !snapshot.isProcessStateDataIncluded()) {
405                     Log.w(TAG, "Ignoring older BatteryUsageStats snapshot, which "
406                             + " does not include process state data");
407                     continue;
408                 }
409 
410                 builder.add(snapshot);
411             }
412         }
413         return builder.build();
414     }
415 
416     /**
417      * Specify whether PowerStats based attribution is supported for the specified component.
418      */
setPowerStatsExporterEnabled(int powerComponentId, boolean enabled)419     public void setPowerStatsExporterEnabled(int powerComponentId, boolean enabled) {
420         mPowerStatsExporterEnabled.put(powerComponentId, enabled);
421     }
422 }
423