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.frameworks.core.batterystatsviewer;
18 
19 import android.content.Context;
20 import android.os.BatteryConsumer;
21 import android.os.BatteryUsageStats;
22 import android.os.UidBatteryConsumer;
23 import android.os.UserHandle;
24 import android.util.DebugUtils;
25 
26 import java.util.ArrayList;
27 import java.util.List;
28 
29 public class BatteryConsumerData {
30 
31     public static final String UID_BATTERY_CONSUMER_ID_PREFIX = "APP|";
32     public static final String AGGREGATE_BATTERY_CONSUMER_ID = "SYS|";
33 
34     enum EntryType {
35         UID_TOTAL_POWER,
36         UID_POWER_PROFILE,
37         UID_POWER_PROFILE_PROCESS_STATE,
38         UID_POWER_ENERGY_CONSUMPTION,
39         UID_POWER_ENERGY_PROCESS_STATE,
40         UID_POWER_CUSTOM,
41         UID_DURATION,
42         DEVICE_TOTAL_POWER,
43         DEVICE_POWER_MODELED,
44         DEVICE_POWER_ENERGY_CONSUMPTION,
45         DEVICE_POWER_CUSTOM,
46         DEVICE_DURATION,
47     }
48 
49     enum ConsumerType {
50         UID_BATTERY_CONSUMER,
51         DEVICE_POWER_COMPONENT,
52     }
53 
54     public static class Entry {
55         public EntryType entryType;
56         public String title;
57         public double value1;
58         public double value2;
59     }
60 
61     private BatteryConsumerInfoHelper.BatteryConsumerInfo mBatteryConsumerInfo;
62     private final List<Entry> mEntries = new ArrayList<>();
63 
BatteryConsumerData(Context context, List<BatteryUsageStats> batteryUsageStatsList, String batteryConsumerId)64     public BatteryConsumerData(Context context,
65             List<BatteryUsageStats> batteryUsageStatsList, String batteryConsumerId) {
66         switch (getConsumerType(batteryConsumerId)) {
67             case UID_BATTERY_CONSUMER:
68                 populateForUidBatteryConsumer(context, batteryUsageStatsList, batteryConsumerId);
69                 break;
70             case DEVICE_POWER_COMPONENT:
71                 populateForAggregateBatteryConsumer(context, batteryUsageStatsList);
72                 break;
73         }
74     }
75 
populateForUidBatteryConsumer( Context context, List<BatteryUsageStats> batteryUsageStatsList, String batteryConsumerId)76     private void populateForUidBatteryConsumer(
77             Context context, List<BatteryUsageStats> batteryUsageStatsList,
78             String batteryConsumerId) {
79         BatteryUsageStats batteryUsageStats = batteryUsageStatsList.get(0);
80         BatteryUsageStats modeledBatteryUsageStats = batteryUsageStatsList.get(1);
81         BatteryConsumer requestedBatteryConsumer = getRequestedBatteryConsumer(batteryUsageStats,
82                 batteryConsumerId);
83         BatteryConsumer requestedModeledBatteryConsumer = getRequestedBatteryConsumer(
84                 modeledBatteryUsageStats, batteryConsumerId);
85 
86         if (requestedBatteryConsumer == null || requestedModeledBatteryConsumer == null) {
87             mBatteryConsumerInfo = null;
88             return;
89         }
90 
91         mBatteryConsumerInfo = BatteryConsumerInfoHelper.makeBatteryConsumerInfo(
92                 batteryUsageStats, batteryConsumerId, context.getPackageManager());
93 
94         double[] totalPowerByComponentMah = new double[BatteryConsumer.POWER_COMPONENT_COUNT];
95         double[] totalModeledPowerByComponentMah =
96                 new double[BatteryConsumer.POWER_COMPONENT_COUNT];
97         long[] totalDurationByComponentMs = new long[BatteryConsumer.POWER_COMPONENT_COUNT];
98         final int customComponentCount =
99                 requestedBatteryConsumer.getCustomPowerComponentCount();
100         double[] totalCustomPowerByComponentMah = new double[customComponentCount];
101 
102         computeTotalPower(batteryUsageStats, totalPowerByComponentMah);
103         computeTotalPower(modeledBatteryUsageStats, totalModeledPowerByComponentMah);
104         computeTotalPowerForCustomComponent(batteryUsageStats, totalCustomPowerByComponentMah);
105         computeTotalDuration(batteryUsageStats, totalDurationByComponentMs);
106 
107         if (isPowerProfileModelsOnly(requestedBatteryConsumer)) {
108             addEntry("Consumed", EntryType.UID_TOTAL_POWER,
109                     requestedBatteryConsumer.getConsumedPower(),
110                     batteryUsageStats.getAggregateBatteryConsumer(
111                             BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS)
112                             .getConsumedPower());
113         } else {
114             addEntry("Consumed (PowerStats)", EntryType.UID_TOTAL_POWER,
115                     requestedBatteryConsumer.getConsumedPower(),
116                     batteryUsageStats.getAggregateBatteryConsumer(
117                             BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS)
118                             .getConsumedPower());
119             addEntry("Consumed (PowerProfile)", EntryType.UID_TOTAL_POWER,
120                     requestedModeledBatteryConsumer.getConsumedPower(),
121                     modeledBatteryUsageStats.getAggregateBatteryConsumer(
122                             BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS)
123                             .getConsumedPower());
124         }
125 
126         for (int component = 0; component < BatteryConsumer.POWER_COMPONENT_COUNT; component++) {
127             final String metricTitle = getPowerMetricTitle(component);
128             final int powerModel = requestedBatteryConsumer.getPowerModel(component);
129             if (powerModel == BatteryConsumer.POWER_MODEL_POWER_PROFILE
130                     || powerModel == BatteryConsumer.POWER_MODEL_UNDEFINED) {
131                 addEntry(metricTitle, EntryType.UID_POWER_PROFILE,
132                         requestedBatteryConsumer.getConsumedPower(component),
133                         totalPowerByComponentMah[component]);
134                 addProcessStateEntries(metricTitle, EntryType.UID_POWER_PROFILE_PROCESS_STATE,
135                         requestedBatteryConsumer, component);
136             } else {
137                 addEntry(metricTitle + " (PowerStats)", EntryType.UID_POWER_ENERGY_CONSUMPTION,
138                         requestedBatteryConsumer.getConsumedPower(component),
139                         totalPowerByComponentMah[component]);
140                 addProcessStateEntries(metricTitle, EntryType.UID_POWER_ENERGY_PROCESS_STATE,
141                         requestedBatteryConsumer, component);
142                 addEntry(metricTitle + " (PowerProfile)", EntryType.UID_POWER_PROFILE,
143                         requestedModeledBatteryConsumer.getConsumedPower(component),
144                         totalModeledPowerByComponentMah[component]);
145                 addProcessStateEntries(metricTitle, EntryType.UID_POWER_PROFILE_PROCESS_STATE,
146                         requestedModeledBatteryConsumer, component);
147             }
148         }
149 
150         for (int component = 0; component < customComponentCount; component++) {
151             final String name = requestedBatteryConsumer.getCustomPowerComponentName(
152                     BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID + component);
153             addEntry(name + " (PowerStats)", EntryType.UID_POWER_CUSTOM,
154                     requestedBatteryConsumer.getConsumedPowerForCustomComponent(
155                             BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID + component),
156                     totalCustomPowerByComponentMah[component]
157             );
158         }
159 
160         for (int component = 0; component < BatteryConsumer.POWER_COMPONENT_COUNT; component++) {
161             final String metricTitle = getTimeMetricTitle(component);
162             addEntry(metricTitle, EntryType.UID_DURATION,
163                     requestedBatteryConsumer.getUsageDurationMillis(component),
164                     totalDurationByComponentMs[component]
165             );
166         }
167 
168         mBatteryConsumerInfo = BatteryConsumerInfoHelper.makeBatteryConsumerInfo(batteryUsageStats,
169                 batteryConsumerId, context.getPackageManager());
170     }
171 
addProcessStateEntries(String metricTitle, EntryType entryType, BatteryConsumer batteryConsumer, int component)172     private void addProcessStateEntries(String metricTitle, EntryType entryType,
173             BatteryConsumer batteryConsumer, int component) {
174         final BatteryConsumer.Key[] keys = batteryConsumer.getKeys(component);
175         if (keys == null || keys.length <= 1) {
176             return;
177         }
178 
179         for (BatteryConsumer.Key key : keys) {
180             String label;
181             switch (key.processState) {
182                 case BatteryConsumer.PROCESS_STATE_FOREGROUND:
183                     label = "foreground";
184                     break;
185                 case BatteryConsumer.PROCESS_STATE_BACKGROUND:
186                     label = "background";
187                     break;
188                 case BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE:
189                     label = "FGS";
190                     break;
191                 case BatteryConsumer.PROCESS_STATE_CACHED:
192                     label = "cached";
193                     break;
194                 default:
195                     continue;
196             }
197             addEntry(metricTitle + " \u2022 " + label, entryType,
198                     batteryConsumer.getConsumedPower(key), 0);
199         }
200     }
201 
populateForAggregateBatteryConsumer(Context context, List<BatteryUsageStats> batteryUsageStatsList)202     private void populateForAggregateBatteryConsumer(Context context,
203             List<BatteryUsageStats> batteryUsageStatsList) {
204         BatteryUsageStats batteryUsageStats = batteryUsageStatsList.get(0);
205         BatteryUsageStats modeledBatteryUsageStats = batteryUsageStatsList.get(1);
206 
207         final BatteryConsumer deviceBatteryConsumer =
208                 batteryUsageStats.getAggregateBatteryConsumer(
209                         BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE);
210         BatteryConsumer appsBatteryConsumer =
211                 batteryUsageStats.getAggregateBatteryConsumer(
212                         BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS);
213 
214         BatteryConsumer modeledDeviceBatteryConsumer =
215                 modeledBatteryUsageStats.getAggregateBatteryConsumer(
216                         BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE);
217         BatteryConsumer modeledAppsBatteryConsumer =
218                 modeledBatteryUsageStats.getAggregateBatteryConsumer(
219                         BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS);
220 
221         if (isPowerProfileModelsOnly(deviceBatteryConsumer)) {
222             addEntry("Consumed", EntryType.DEVICE_TOTAL_POWER,
223                     deviceBatteryConsumer.getConsumedPower(),
224                     appsBatteryConsumer.getConsumedPower());
225         } else {
226             addEntry("Consumed (PowerStats)", EntryType.DEVICE_TOTAL_POWER,
227                     deviceBatteryConsumer.getConsumedPower(),
228                     appsBatteryConsumer.getConsumedPower());
229             addEntry("Consumed (PowerProfile)", EntryType.DEVICE_TOTAL_POWER,
230                     modeledDeviceBatteryConsumer.getConsumedPower(),
231                     modeledAppsBatteryConsumer.getConsumedPower());
232         }
233 
234         mBatteryConsumerInfo = BatteryConsumerInfoHelper.makeBatteryConsumerInfo(batteryUsageStats,
235                 AGGREGATE_BATTERY_CONSUMER_ID, context.getPackageManager());
236 
237 
238         for (int component = 0; component < BatteryConsumer.POWER_COMPONENT_COUNT; component++) {
239             final String metricTitle = getPowerMetricTitle(component);
240             final int powerModel = deviceBatteryConsumer.getPowerModel(component);
241             if (powerModel == BatteryConsumer.POWER_MODEL_POWER_PROFILE
242                     || powerModel == BatteryConsumer.POWER_MODEL_UNDEFINED) {
243                 addEntry(metricTitle, EntryType.DEVICE_POWER_MODELED,
244                         deviceBatteryConsumer.getConsumedPower(component),
245                         appsBatteryConsumer.getConsumedPower(component));
246             } else {
247                 addEntry(metricTitle + " (PowerStats)", EntryType.DEVICE_POWER_ENERGY_CONSUMPTION,
248                         deviceBatteryConsumer.getConsumedPower(component),
249                         appsBatteryConsumer.getConsumedPower(component));
250                 addEntry(metricTitle + " (PowerProfile)", EntryType.DEVICE_POWER_MODELED,
251                         modeledDeviceBatteryConsumer.getConsumedPower(component),
252                         modeledAppsBatteryConsumer.getConsumedPower(component));
253             }
254         }
255 
256         final int customComponentCount =
257                 deviceBatteryConsumer.getCustomPowerComponentCount();
258         for (int component = 0; component < customComponentCount; component++) {
259             final String name = deviceBatteryConsumer.getCustomPowerComponentName(
260                     BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID + component);
261             addEntry(name + " (PowerStats)", EntryType.DEVICE_POWER_CUSTOM,
262                     deviceBatteryConsumer.getConsumedPowerForCustomComponent(
263                             BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID + component),
264                     appsBatteryConsumer.getConsumedPowerForCustomComponent(
265                             BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID + component));
266         }
267 
268         for (int component = 0; component < BatteryConsumer.POWER_COMPONENT_COUNT; component++) {
269             final String metricTitle = getTimeMetricTitle(component);
270             addEntry(metricTitle, EntryType.DEVICE_DURATION,
271                     deviceBatteryConsumer.getUsageDurationMillis(component), 0);
272         }
273     }
274 
isPowerProfileModelsOnly(BatteryConsumer batteryConsumer)275     private boolean isPowerProfileModelsOnly(BatteryConsumer batteryConsumer) {
276         for (int component = 0; component < BatteryConsumer.POWER_COMPONENT_COUNT; component++) {
277             final int powerModel = batteryConsumer.getPowerModel(component);
278             if (powerModel != BatteryConsumer.POWER_MODEL_POWER_PROFILE
279                     && powerModel != BatteryConsumer.POWER_MODEL_UNDEFINED) {
280                 return false;
281             }
282         }
283         return true;
284     }
285 
getRequestedBatteryConsumer(BatteryUsageStats batteryUsageStats, String batteryConsumerId)286     private BatteryConsumer getRequestedBatteryConsumer(BatteryUsageStats batteryUsageStats,
287             String batteryConsumerId) {
288         for (UidBatteryConsumer consumer : batteryUsageStats.getUidBatteryConsumers()) {
289             if (batteryConsumerId(consumer).equals(batteryConsumerId)) {
290                 return consumer;
291             }
292         }
293 
294         return null;
295     }
296 
getPowerMetricTitle(int componentId)297     static String getPowerMetricTitle(int componentId) {
298         return getPowerComponentName(componentId);
299     }
300 
getTimeMetricTitle(int componentId)301     static String getTimeMetricTitle(int componentId) {
302         return getPowerComponentName(componentId) + " time";
303     }
304 
getPowerComponentName(int componentId)305     private static String getPowerComponentName(int componentId) {
306         switch (componentId) {
307             case BatteryConsumer.POWER_COMPONENT_CPU:
308                 return "CPU";
309             case BatteryConsumer.POWER_COMPONENT_GNSS:
310                 return "GNSS";
311             case BatteryConsumer.POWER_COMPONENT_WIFI:
312                 return "Wi-Fi";
313             default:
314                 String componentName = DebugUtils.constantToString(BatteryConsumer.class,
315                         "POWER_COMPONENT_", componentId);
316                 return componentName.charAt(0) + componentName.substring(1).toLowerCase()
317                         .replace('_', ' ');
318         }
319     }
320 
computeTotalPower(BatteryUsageStats batteryUsageStats, double[] powerByComponentMah)321     private void computeTotalPower(BatteryUsageStats batteryUsageStats,
322             double[] powerByComponentMah) {
323         final BatteryConsumer consumer =
324                 batteryUsageStats.getAggregateBatteryConsumer(
325                         BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE);
326         for (int component = 0; component < BatteryConsumer.POWER_COMPONENT_COUNT; component++) {
327             powerByComponentMah[component] += consumer.getConsumedPower(component);
328         }
329     }
330 
computeTotalPowerForCustomComponent( BatteryUsageStats batteryUsageStats, double[] powerByComponentMah)331     private void computeTotalPowerForCustomComponent(
332             BatteryUsageStats batteryUsageStats, double[] powerByComponentMah) {
333         final BatteryConsumer consumer =
334                 batteryUsageStats.getAggregateBatteryConsumer(
335                         BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE);
336         final int customComponentCount = consumer.getCustomPowerComponentCount();
337         for (int component = 0;
338                 component < Math.min(customComponentCount, powerByComponentMah.length);
339                 component++) {
340             powerByComponentMah[component] += consumer.getConsumedPowerForCustomComponent(
341                     BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID + component);
342         }
343     }
344 
computeTotalDuration(BatteryUsageStats batteryUsageStats, long[] durationByComponentMs)345     private void computeTotalDuration(BatteryUsageStats batteryUsageStats,
346             long[] durationByComponentMs) {
347         for (BatteryConsumer consumer : batteryUsageStats.getUidBatteryConsumers()) {
348             for (int component = 0; component < BatteryConsumer.POWER_COMPONENT_COUNT;
349                     component++) {
350                 durationByComponentMs[component] += consumer.getUsageDurationMillis(component);
351             }
352         }
353     }
354 
addEntry(String title, EntryType entryType, double value1, double value2)355     private void addEntry(String title, EntryType entryType, double value1, double value2) {
356         Entry entry = new Entry();
357         entry.title = title;
358         entry.entryType = entryType;
359         entry.value1 = value1;
360         entry.value2 = value2;
361         mEntries.add(entry);
362     }
363 
getBatteryConsumerInfo()364     public BatteryConsumerInfoHelper.BatteryConsumerInfo getBatteryConsumerInfo() {
365         return mBatteryConsumerInfo;
366     }
367 
getEntries()368     public List<Entry> getEntries() {
369         return mEntries;
370     }
371 
getConsumerType(String batteryConsumerId)372     public static ConsumerType getConsumerType(String batteryConsumerId) {
373         if (batteryConsumerId.startsWith(UID_BATTERY_CONSUMER_ID_PREFIX)) {
374             return ConsumerType.UID_BATTERY_CONSUMER;
375         }
376         return ConsumerType.DEVICE_POWER_COMPONENT;
377     }
378 
batteryConsumerId(UidBatteryConsumer consumer)379     public static String batteryConsumerId(UidBatteryConsumer consumer) {
380         return UID_BATTERY_CONSUMER_ID_PREFIX + UserHandle.getUserId(consumer.getUid()) + "|"
381                 + consumer.getUid();
382     }
383 }