• 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.getFactory(context).getPowerUsageFeatureProvider(context);
71             purgeBatteryDiffData(featureProvider);
72             combineBatteryDiffEntry(
73                     context, featureProvider, systemAppsPackageNames, systemAppsUids);
74         }
75 
76         processAndSortEntries(mAppEntries);
77         processAndSortEntries(mSystemEntries);
78     }
79 
getStartTimestamp()80     long getStartTimestamp() {
81         return mStartTimestamp;
82     }
83 
getEndTimestamp()84     long getEndTimestamp() {
85         return mEndTimestamp;
86     }
87 
getStartBatteryLevel()88     int getStartBatteryLevel() {
89         return mStartBatteryLevel;
90     }
91 
getEndBatteryLevel()92     int getEndBatteryLevel() {
93         return mEndBatteryLevel;
94     }
95 
getScreenOnTime()96     long getScreenOnTime() {
97         return mScreenOnTime;
98     }
99 
getAppDiffEntryList()100     List<BatteryDiffEntry> getAppDiffEntryList() {
101         return mAppEntries;
102     }
103 
getSystemDiffEntryList()104     List<BatteryDiffEntry> getSystemDiffEntryList() {
105         return mSystemEntries;
106     }
107 
108     @Override
toString()109     public String toString() {
110         return new StringBuilder("BatteryDiffData{")
111                 .append("startTimestamp:" + utcToLocalTimeForLogging(mStartTimestamp))
112                 .append("|endTimestamp:" + utcToLocalTimeForLogging(mEndTimestamp))
113                 .append("|startLevel:" + mStartBatteryLevel)
114                 .append("|endLevel:" + mEndBatteryLevel)
115                 .append("|screenOnTime:" + mScreenOnTime)
116                 .append("|appEntries.size:" + mAppEntries.size())
117                 .append("|systemEntries.size:" + mSystemEntries.size())
118                 .append("}")
119                 .toString();
120     }
121 
122     /** Removes fake usage data and hidden packages. */
purgeBatteryDiffData(final PowerUsageFeatureProvider featureProvider)123     private void purgeBatteryDiffData(final PowerUsageFeatureProvider featureProvider) {
124         purgeBatteryDiffData(featureProvider, mAppEntries);
125         purgeBatteryDiffData(featureProvider, mSystemEntries);
126     }
127 
128     /** Combines into SystemAppsBatteryDiffEntry and OthersBatteryDiffEntry. */
combineBatteryDiffEntry( final Context context, final PowerUsageFeatureProvider featureProvider, final @NonNull Set<String> systemAppsPackageNames, final @NonNull Set<Integer> systemAppsUids)129     private void combineBatteryDiffEntry(
130             final Context context,
131             final PowerUsageFeatureProvider featureProvider,
132             final @NonNull Set<String> systemAppsPackageNames,
133             final @NonNull Set<Integer> systemAppsUids) {
134         combineIntoSystemApps(
135                 context, featureProvider, systemAppsPackageNames, systemAppsUids, mAppEntries);
136         combineSystemItemsIntoOthers(context, featureProvider, mSystemEntries);
137     }
138 
purgeBatteryDiffData( final PowerUsageFeatureProvider featureProvider, final List<BatteryDiffEntry> entries)139     private static void purgeBatteryDiffData(
140             final PowerUsageFeatureProvider featureProvider,
141             final List<BatteryDiffEntry> entries) {
142         final double screenOnTimeThresholdInMs =
143                 featureProvider.getBatteryUsageListScreenOnTimeThresholdInMs();
144         final double consumePowerThreshold =
145                 featureProvider.getBatteryUsageListConsumePowerThreshold();
146         final Set<Integer> hideSystemComponentSet = featureProvider.getHideSystemComponentSet();
147         final Set<String> hideBackgroundUsageTimeSet =
148                 featureProvider.getHideBackgroundUsageTimeSet();
149         final Set<String> hideApplicationSet = featureProvider.getHideApplicationSet();
150         final Iterator<BatteryDiffEntry> iterator = entries.iterator();
151         while (iterator.hasNext()) {
152             final BatteryDiffEntry entry = iterator.next();
153             final long screenOnTimeInMs = entry.mScreenOnTimeInMs;
154             final double comsumePower = entry.mConsumePower;
155             final String packageName = entry.getPackageName();
156             final Integer componentId = entry.mComponentId;
157             if ((screenOnTimeInMs < screenOnTimeThresholdInMs
158                     && comsumePower < consumePowerThreshold)
159                     || ConvertUtils.FAKE_PACKAGE_NAME.equals(packageName)
160                     || hideSystemComponentSet.contains(componentId)
161                     || (packageName != null && hideApplicationSet.contains(packageName))) {
162                 iterator.remove();
163             }
164             if (packageName != null && hideBackgroundUsageTimeSet.contains(packageName)) {
165                 entry.mBackgroundUsageTimeInMs = 0;
166             }
167         }
168     }
169 
combineIntoSystemApps( final Context context, final PowerUsageFeatureProvider featureProvider, final @NonNull Set<String> systemAppsPackageNames, final @NonNull Set<Integer> systemAppsUids, final @NonNull List<BatteryDiffEntry> appEntries)170     private static void combineIntoSystemApps(
171             final Context context,
172             final PowerUsageFeatureProvider featureProvider,
173             final @NonNull Set<String> systemAppsPackageNames,
174             final @NonNull Set<Integer> systemAppsUids,
175             final @NonNull List<BatteryDiffEntry> appEntries) {
176         final List<String> systemAppsAllowlist = featureProvider.getSystemAppsAllowlist();
177         BatteryDiffEntry systemAppsDiffEntry = null;
178         final Iterator<BatteryDiffEntry> appListIterator = appEntries.iterator();
179         while (appListIterator.hasNext()) {
180             final BatteryDiffEntry batteryDiffEntry = appListIterator.next();
181             if (needsCombineInSystemApp(batteryDiffEntry, systemAppsAllowlist,
182                     systemAppsPackageNames, systemAppsUids)) {
183                 if (systemAppsDiffEntry == null) {
184                     systemAppsDiffEntry = new BatteryDiffEntry(context,
185                             BatteryDiffEntry.SYSTEM_APPS_KEY, BatteryDiffEntry.SYSTEM_APPS_KEY,
186                             ConvertUtils.CONSUMER_TYPE_UID_BATTERY);
187                 }
188                 systemAppsDiffEntry.mConsumePower += batteryDiffEntry.mConsumePower;
189                 systemAppsDiffEntry.mForegroundUsageTimeInMs +=
190                         batteryDiffEntry.mForegroundUsageTimeInMs;
191                 systemAppsDiffEntry.setTotalConsumePower(
192                         batteryDiffEntry.getTotalConsumePower());
193                 appListIterator.remove();
194             }
195         }
196         if (systemAppsDiffEntry != null) {
197             appEntries.add(systemAppsDiffEntry);
198         }
199     }
200 
combineSystemItemsIntoOthers( final Context context, final PowerUsageFeatureProvider featureProvider, final List<BatteryDiffEntry> systemEntries)201     private static void combineSystemItemsIntoOthers(
202             final Context context,
203             final PowerUsageFeatureProvider featureProvider,
204             final List<BatteryDiffEntry> systemEntries) {
205         final Set<Integer> othersSystemComponentSet = featureProvider.getOthersSystemComponentSet();
206         final Set<String> othersCustomComponentNameSet =
207                 featureProvider.getOthersCustomComponentNameSet();
208         BatteryDiffEntry othersDiffEntry = null;
209         final Iterator<BatteryDiffEntry> systemListIterator = systemEntries.iterator();
210         while (systemListIterator.hasNext()) {
211             final BatteryDiffEntry batteryDiffEntry = systemListIterator.next();
212             final int componentId = batteryDiffEntry.mComponentId;
213             if (othersSystemComponentSet.contains(componentId) || (
214                     componentId >= BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID
215                             && othersCustomComponentNameSet.contains(
216                                     batteryDiffEntry.getAppLabel()))) {
217                 if (othersDiffEntry == null) {
218                     othersDiffEntry = new BatteryDiffEntry(context, BatteryDiffEntry.OTHERS_KEY,
219                             BatteryDiffEntry.OTHERS_KEY, ConvertUtils.CONSUMER_TYPE_SYSTEM_BATTERY);
220                 }
221                 othersDiffEntry.mConsumePower += batteryDiffEntry.mConsumePower;
222                 othersDiffEntry.setTotalConsumePower(
223                         batteryDiffEntry.getTotalConsumePower());
224                 systemListIterator.remove();
225             }
226         }
227         if (othersDiffEntry != null) {
228             systemEntries.add(othersDiffEntry);
229         }
230     }
231 
232     @VisibleForTesting
needsCombineInSystemApp( final BatteryDiffEntry batteryDiffEntry, final @NonNull List<String> systemAppsAllowlist, final @NonNull Set<String> systemAppsPackageNames, final @NonNull Set<Integer> systemAppsUids)233     static boolean needsCombineInSystemApp(
234             final BatteryDiffEntry batteryDiffEntry,
235             final @NonNull List<String> systemAppsAllowlist,
236             final @NonNull Set<String> systemAppsPackageNames,
237             final @NonNull Set<Integer> systemAppsUids) {
238         if (batteryDiffEntry.mIsHidden) {
239             return true;
240         }
241 
242         final String packageName = batteryDiffEntry.getPackageName();
243         if (packageName == null || packageName.isEmpty()) {
244             return false;
245         }
246 
247         if (systemAppsAllowlist.contains(packageName)) {
248             return true;
249         }
250 
251         int uid = (int) batteryDiffEntry.mUid;
252         return systemAppsPackageNames.contains(packageName) || systemAppsUids.contains(uid);
253     }
254 
255     /**
256      * Sets total consume power, and adjusts the percentages to ensure the total round percentage
257      * could be 100%, and then sorts entries based on the sorting key.
258      */
259     @VisibleForTesting
processAndSortEntries(final List<BatteryDiffEntry> batteryDiffEntries)260     static void processAndSortEntries(final List<BatteryDiffEntry> batteryDiffEntries) {
261         if (batteryDiffEntries.isEmpty()) {
262             return;
263         }
264 
265         // Sets total consume power.
266         double totalConsumePower = 0.0;
267         for (BatteryDiffEntry batteryDiffEntry : batteryDiffEntries) {
268             totalConsumePower += batteryDiffEntry.mConsumePower;
269         }
270         for (BatteryDiffEntry batteryDiffEntry : batteryDiffEntries) {
271             batteryDiffEntry.setTotalConsumePower(totalConsumePower);
272         }
273 
274         // Adjusts percentages to show.
275         // The lower bound is treating all the small percentages as 0.
276         // The upper bound is treating all the small percentages as 1.
277         int totalLowerBound = 0;
278         int totalUpperBound = 0;
279         for (BatteryDiffEntry entry : batteryDiffEntries) {
280             if (entry.getPercentage() < SMALL_PERCENTAGE_THRESHOLD) {
281                 totalUpperBound += 1;
282             } else {
283                 int roundPercentage = Math.round((float) entry.getPercentage());
284                 totalLowerBound += roundPercentage;
285                 totalUpperBound += roundPercentage;
286             }
287         }
288         if (totalLowerBound > 100 || totalUpperBound < 100) {
289             Collections.sort(batteryDiffEntries, BatteryDiffEntry.COMPARATOR);
290             for (int i = 0; i < totalLowerBound - 100 && i < batteryDiffEntries.size(); i++) {
291                 batteryDiffEntries.get(i).setAdjustPercentageOffset(-1);
292             }
293             for (int i = 0; i < 100 - totalUpperBound && i < batteryDiffEntries.size(); i++) {
294                 batteryDiffEntries.get(i).setAdjustPercentageOffset(1);
295             }
296         }
297 
298         // Sorts entries.
299         Collections.sort(batteryDiffEntries, BatteryDiffEntry.COMPARATOR);
300     }
301 }
302