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