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