• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2022 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.settings.fuelgauge.batteryusage;
18 
19 import static com.android.settings.fuelgauge.batteryusage.ConvertUtils.utcToLocalTimeForLogging;
20 
21 import android.content.Context;
22 import android.os.BatteryConsumer;
23 
24 import androidx.annotation.NonNull;
25 
26 import com.android.internal.annotations.VisibleForTesting;
27 import com.android.settings.fuelgauge.PowerUsageFeatureProvider;
28 import com.android.settings.overlay.FeatureFactory;
29 
30 import java.util.Collections;
31 import java.util.Iterator;
32 import java.util.List;
33 import java.util.Set;
34 
35 /** Wraps the battery usage diff data for each entry used for battery usage app list. */
36 public class BatteryDiffData {
37     static final double SMALL_PERCENTAGE_THRESHOLD = 1f;
38 
39     private final long mStartTimestamp;
40     private final long mEndTimestamp;
41     private final int mStartBatteryLevel;
42     private final int mEndBatteryLevel;
43     private final long mScreenOnTime;
44     private final List<BatteryDiffEntry> mAppEntries;
45     private final List<BatteryDiffEntry> mSystemEntries;
46 
47     /** Constructor for the diff entries. */
BatteryDiffData( final Context context, final long startTimestamp, final long endTimestamp, final int startBatteryLevel, final int endBatteryLevel, final long screenOnTime, final @NonNull List<BatteryDiffEntry> appDiffEntries, final @NonNull List<BatteryDiffEntry> systemDiffEntries, final @NonNull Set<String> systemAppsPackageNames, final @NonNull Set<Integer> systemAppsUids, final boolean isAccumulated)48     public BatteryDiffData(
49             final Context context,
50             final long startTimestamp,
51             final long endTimestamp,
52             final int startBatteryLevel,
53             final int endBatteryLevel,
54             final long screenOnTime,
55             final @NonNull List<BatteryDiffEntry> appDiffEntries,
56             final @NonNull List<BatteryDiffEntry> systemDiffEntries,
57             final @NonNull Set<String> systemAppsPackageNames,
58             final @NonNull Set<Integer> systemAppsUids,
59             final boolean isAccumulated) {
60         mStartTimestamp = startTimestamp;
61         mEndTimestamp = endTimestamp;
62         mStartBatteryLevel = startBatteryLevel;
63         mEndBatteryLevel = endBatteryLevel;
64         mScreenOnTime = screenOnTime;
65         mAppEntries = appDiffEntries;
66         mSystemEntries = systemDiffEntries;
67 
68         if (!isAccumulated) {
69             final PowerUsageFeatureProvider featureProvider =
70                     FeatureFactory.getFeatureFactory().getPowerUsageFeatureProvider();
71             purgeBatteryDiffData(featureProvider);
72             combineBatteryDiffEntry(
73                     context, featureProvider, systemAppsPackageNames, systemAppsUids);
74         }
75 
76         processAndSortEntries(mAppEntries);
77         processAndSortEntries(mSystemEntries);
78     }
79 
80     /** Gets the start timestamp. */
getStartTimestamp()81     public long getStartTimestamp() {
82         return mStartTimestamp;
83     }
84 
85     /** Gets the end timestamp. */
getEndTimestamp()86     public long getEndTimestamp() {
87         return mEndTimestamp;
88     }
89 
getStartBatteryLevel()90     int getStartBatteryLevel() {
91         return mStartBatteryLevel;
92     }
93 
getEndBatteryLevel()94     int getEndBatteryLevel() {
95         return mEndBatteryLevel;
96     }
97 
getScreenOnTime()98     long getScreenOnTime() {
99         return mScreenOnTime;
100     }
101 
102     /** Gets the {@link BatteryDiffEntry} list for apps. */
getAppDiffEntryList()103     public List<BatteryDiffEntry> getAppDiffEntryList() {
104         return mAppEntries;
105     }
106 
getSystemDiffEntryList()107     List<BatteryDiffEntry> getSystemDiffEntryList() {
108         return mSystemEntries;
109     }
110 
111     @Override
toString()112     public String toString() {
113         return new StringBuilder("BatteryDiffData{")
114                 .append("startTimestamp:" + utcToLocalTimeForLogging(mStartTimestamp))
115                 .append("|endTimestamp:" + utcToLocalTimeForLogging(mEndTimestamp))
116                 .append("|startLevel:" + mStartBatteryLevel)
117                 .append("|endLevel:" + mEndBatteryLevel)
118                 .append("|screenOnTime:" + mScreenOnTime)
119                 .append("|appEntries.size:" + mAppEntries.size())
120                 .append("|systemEntries.size:" + mSystemEntries.size())
121                 .append("}")
122                 .toString();
123     }
124 
125     /** Removes fake usage data and hidden packages. */
purgeBatteryDiffData(final PowerUsageFeatureProvider featureProvider)126     private void purgeBatteryDiffData(final PowerUsageFeatureProvider featureProvider) {
127         purgeBatteryDiffData(featureProvider, mAppEntries);
128         purgeBatteryDiffData(featureProvider, mSystemEntries);
129     }
130 
131     /** Combines into SystemAppsBatteryDiffEntry and OthersBatteryDiffEntry. */
combineBatteryDiffEntry( final Context context, final PowerUsageFeatureProvider featureProvider, final @NonNull Set<String> systemAppsPackageNames, final @NonNull Set<Integer> systemAppsUids)132     private void combineBatteryDiffEntry(
133             final Context context,
134             final PowerUsageFeatureProvider featureProvider,
135             final @NonNull Set<String> systemAppsPackageNames,
136             final @NonNull Set<Integer> systemAppsUids) {
137         combineIntoUninstalledApps(context, mAppEntries);
138         combineIntoSystemApps(
139                 context, featureProvider, systemAppsPackageNames, systemAppsUids, mAppEntries);
140         combineSystemItemsIntoOthers(context, featureProvider, mSystemEntries);
141     }
142 
purgeBatteryDiffData( final PowerUsageFeatureProvider featureProvider, final List<BatteryDiffEntry> entries)143     private static void purgeBatteryDiffData(
144             final PowerUsageFeatureProvider featureProvider, final List<BatteryDiffEntry> entries) {
145         final double screenOnTimeThresholdInMs =
146                 featureProvider.getBatteryUsageListScreenOnTimeThresholdInMs();
147         final double consumePowerThreshold =
148                 featureProvider.getBatteryUsageListConsumePowerThreshold();
149         final Set<Integer> hideSystemComponentSet = featureProvider.getHideSystemComponentSet();
150         final Set<String> hideBackgroundUsageTimeSet =
151                 featureProvider.getHideBackgroundUsageTimeSet();
152         final Set<String> hideApplicationSet = featureProvider.getHideApplicationSet();
153         final Iterator<BatteryDiffEntry> iterator = entries.iterator();
154         while (iterator.hasNext()) {
155             final BatteryDiffEntry entry = iterator.next();
156             final long screenOnTimeInMs =
157                     entry.isSystemEntry()
158                             ? entry.mForegroundUsageTimeInMs
159                             : entry.mScreenOnTimeInMs;
160             final double comsumePower = entry.mConsumePower;
161             final String packageName = entry.getPackageName();
162             final Integer componentId = entry.mComponentId;
163             if ((screenOnTimeInMs < screenOnTimeThresholdInMs
164                             && comsumePower < consumePowerThreshold)
165                     || ConvertUtils.FAKE_PACKAGE_NAME.equals(packageName)
166                     || hideSystemComponentSet.contains(componentId)
167                     || (packageName != null && hideApplicationSet.contains(packageName))) {
168                 iterator.remove();
169             }
170             if (packageName != null && hideBackgroundUsageTimeSet.contains(packageName)) {
171                 entry.mBackgroundUsageTimeInMs = 0;
172                 entry.mForegroundServiceUsageTimeInMs = 0;
173             }
174         }
175     }
176 
combineIntoSystemApps( final Context context, final PowerUsageFeatureProvider featureProvider, final @NonNull Set<String> systemAppsPackageNames, final @NonNull Set<Integer> systemAppsUids, final @NonNull List<BatteryDiffEntry> appEntries)177     private static void combineIntoSystemApps(
178             final Context context,
179             final PowerUsageFeatureProvider featureProvider,
180             final @NonNull Set<String> systemAppsPackageNames,
181             final @NonNull Set<Integer> systemAppsUids,
182             final @NonNull List<BatteryDiffEntry> appEntries) {
183         final List<String> systemAppsAllowlist = featureProvider.getSystemAppsAllowlist();
184         BatteryDiffEntry systemAppsDiffEntry = null;
185         final Iterator<BatteryDiffEntry> appListIterator = appEntries.iterator();
186         while (appListIterator.hasNext()) {
187             final BatteryDiffEntry batteryDiffEntry = appListIterator.next();
188             if (needsCombineInSystemApp(
189                     batteryDiffEntry,
190                     systemAppsAllowlist,
191                     systemAppsPackageNames,
192                     systemAppsUids)) {
193                 if (systemAppsDiffEntry == null) {
194                     systemAppsDiffEntry =
195                             new BatteryDiffEntry(
196                                     context,
197                                     BatteryDiffEntry.SYSTEM_APPS_KEY,
198                                     BatteryDiffEntry.SYSTEM_APPS_KEY,
199                                     ConvertUtils.CONSUMER_TYPE_UID_BATTERY);
200                 }
201                 systemAppsDiffEntry.mConsumePower += batteryDiffEntry.mConsumePower;
202                 systemAppsDiffEntry.mForegroundUsageTimeInMs +=
203                         batteryDiffEntry.mForegroundUsageTimeInMs;
204                 systemAppsDiffEntry.setTotalConsumePower(batteryDiffEntry.getTotalConsumePower());
205                 appListIterator.remove();
206             }
207         }
208         if (systemAppsDiffEntry != null) {
209             appEntries.add(systemAppsDiffEntry);
210         }
211     }
212 
combineIntoUninstalledApps( final Context context, final @NonNull List<BatteryDiffEntry> appEntries)213     private static void combineIntoUninstalledApps(
214             final Context context, final @NonNull List<BatteryDiffEntry> appEntries) {
215         BatteryDiffEntry uninstalledAppDiffEntry = null;
216         final Iterator<BatteryDiffEntry> appListIterator = appEntries.iterator();
217         while (appListIterator.hasNext()) {
218             final BatteryDiffEntry batteryDiffEntry = appListIterator.next();
219             if (!batteryDiffEntry.isUninstalledEntry()) {
220                 continue;
221             }
222 
223             if (uninstalledAppDiffEntry == null) {
224                 uninstalledAppDiffEntry =
225                         new BatteryDiffEntry(
226                                 context,
227                                 BatteryDiffEntry.UNINSTALLED_APPS_KEY,
228                                 BatteryDiffEntry.UNINSTALLED_APPS_KEY,
229                                 ConvertUtils.CONSUMER_TYPE_UID_BATTERY);
230             }
231             uninstalledAppDiffEntry.mConsumePower += batteryDiffEntry.mConsumePower;
232             uninstalledAppDiffEntry.mForegroundUsageTimeInMs +=
233                     batteryDiffEntry.mForegroundUsageTimeInMs;
234             uninstalledAppDiffEntry.setTotalConsumePower(batteryDiffEntry.getTotalConsumePower());
235             appListIterator.remove();
236         }
237         if (uninstalledAppDiffEntry != null) {
238             appEntries.add(uninstalledAppDiffEntry);
239         }
240     }
241 
combineSystemItemsIntoOthers( final Context context, final PowerUsageFeatureProvider featureProvider, final List<BatteryDiffEntry> systemEntries)242     private static void combineSystemItemsIntoOthers(
243             final Context context,
244             final PowerUsageFeatureProvider featureProvider,
245             final List<BatteryDiffEntry> systemEntries) {
246         final Set<Integer> othersSystemComponentSet = featureProvider.getOthersSystemComponentSet();
247         final Set<String> othersCustomComponentNameSet =
248                 featureProvider.getOthersCustomComponentNameSet();
249         BatteryDiffEntry othersDiffEntry = null;
250         final Iterator<BatteryDiffEntry> systemListIterator = systemEntries.iterator();
251         while (systemListIterator.hasNext()) {
252             final BatteryDiffEntry batteryDiffEntry = systemListIterator.next();
253             final int componentId = batteryDiffEntry.mComponentId;
254             if (othersSystemComponentSet.contains(componentId)
255                     || (componentId >= BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID
256                             && othersCustomComponentNameSet.contains(
257                                     batteryDiffEntry.getAppLabel()))) {
258                 if (othersDiffEntry == null) {
259                     othersDiffEntry =
260                             new BatteryDiffEntry(
261                                     context,
262                                     BatteryDiffEntry.OTHERS_KEY,
263                                     BatteryDiffEntry.OTHERS_KEY,
264                                     ConvertUtils.CONSUMER_TYPE_SYSTEM_BATTERY);
265                 }
266                 othersDiffEntry.mConsumePower += batteryDiffEntry.mConsumePower;
267                 othersDiffEntry.setTotalConsumePower(batteryDiffEntry.getTotalConsumePower());
268                 systemListIterator.remove();
269             }
270         }
271         if (othersDiffEntry != null) {
272             systemEntries.add(othersDiffEntry);
273         }
274     }
275 
276     @VisibleForTesting
needsCombineInSystemApp( final BatteryDiffEntry batteryDiffEntry, final @NonNull List<String> systemAppsAllowlist, final @NonNull Set<String> systemAppsPackageNames, final @NonNull Set<Integer> systemAppsUids)277     static boolean needsCombineInSystemApp(
278             final BatteryDiffEntry batteryDiffEntry,
279             final @NonNull List<String> systemAppsAllowlist,
280             final @NonNull Set<String> systemAppsPackageNames,
281             final @NonNull Set<Integer> systemAppsUids) {
282         if (batteryDiffEntry.mIsHidden) {
283             return true;
284         }
285 
286         final String packageName = batteryDiffEntry.getPackageName();
287         if (packageName == null || packageName.isEmpty()) {
288             return false;
289         }
290 
291         if (systemAppsAllowlist.contains(packageName)) {
292             return true;
293         }
294 
295         int uid = (int) batteryDiffEntry.mUid;
296         return systemAppsPackageNames.contains(packageName) || systemAppsUids.contains(uid);
297     }
298 
299     /**
300      * Sets total consume power, and adjusts the percentages to ensure the total round percentage
301      * could be 100%, and then sorts entries based on the sorting key.
302      */
processAndSortEntries(final List<BatteryDiffEntry> batteryDiffEntries)303     public static void processAndSortEntries(final List<BatteryDiffEntry> batteryDiffEntries) {
304         if (batteryDiffEntries.isEmpty()) {
305             return;
306         }
307 
308         // Sets total consume power.
309         double totalConsumePower = 0.0;
310         for (BatteryDiffEntry batteryDiffEntry : batteryDiffEntries) {
311             totalConsumePower += batteryDiffEntry.mConsumePower;
312         }
313         for (BatteryDiffEntry batteryDiffEntry : batteryDiffEntries) {
314             batteryDiffEntry.setTotalConsumePower(totalConsumePower);
315         }
316 
317         // Adjusts percentages to show.
318         // The lower bound is treating all the small percentages as 0.
319         // The upper bound is treating all the small percentages as 1.
320         int totalLowerBound = 0;
321         int totalUpperBound = 0;
322         for (BatteryDiffEntry entry : batteryDiffEntries) {
323             if (entry.getPercentage() < SMALL_PERCENTAGE_THRESHOLD) {
324                 totalUpperBound += 1;
325             } else {
326                 int roundPercentage = Math.round((float) entry.getPercentage());
327                 totalLowerBound += roundPercentage;
328                 totalUpperBound += roundPercentage;
329             }
330         }
331         if (totalLowerBound > 100 || totalUpperBound < 100) {
332             Collections.sort(batteryDiffEntries, BatteryDiffEntry.COMPARATOR);
333             for (int i = 0; i < totalLowerBound - 100 && i < batteryDiffEntries.size(); i++) {
334                 batteryDiffEntries.get(i).setAdjustPercentageOffset(-1);
335             }
336             for (int i = 0; i < 100 - totalUpperBound && i < batteryDiffEntries.size(); i++) {
337                 batteryDiffEntries.get(i).setAdjustPercentageOffset(1);
338             }
339         }
340 
341         // Sorts entries.
342         Collections.sort(batteryDiffEntries, BatteryDiffEntry.COMPARATOR);
343     }
344 }
345