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 } 173 } 174 } 175 combineIntoSystemApps( final Context context, final PowerUsageFeatureProvider featureProvider, final @NonNull Set<String> systemAppsPackageNames, final @NonNull Set<Integer> systemAppsUids, final @NonNull List<BatteryDiffEntry> appEntries)176 private static void combineIntoSystemApps( 177 final Context context, 178 final PowerUsageFeatureProvider featureProvider, 179 final @NonNull Set<String> systemAppsPackageNames, 180 final @NonNull Set<Integer> systemAppsUids, 181 final @NonNull List<BatteryDiffEntry> appEntries) { 182 final List<String> systemAppsAllowlist = featureProvider.getSystemAppsAllowlist(); 183 BatteryDiffEntry systemAppsDiffEntry = null; 184 final Iterator<BatteryDiffEntry> appListIterator = appEntries.iterator(); 185 while (appListIterator.hasNext()) { 186 final BatteryDiffEntry batteryDiffEntry = appListIterator.next(); 187 if (needsCombineInSystemApp( 188 batteryDiffEntry, 189 systemAppsAllowlist, 190 systemAppsPackageNames, 191 systemAppsUids)) { 192 if (systemAppsDiffEntry == null) { 193 systemAppsDiffEntry = 194 new BatteryDiffEntry( 195 context, 196 BatteryDiffEntry.SYSTEM_APPS_KEY, 197 BatteryDiffEntry.SYSTEM_APPS_KEY, 198 ConvertUtils.CONSUMER_TYPE_UID_BATTERY); 199 } 200 systemAppsDiffEntry.mConsumePower += batteryDiffEntry.mConsumePower; 201 systemAppsDiffEntry.mForegroundUsageTimeInMs += 202 batteryDiffEntry.mForegroundUsageTimeInMs; 203 systemAppsDiffEntry.setTotalConsumePower(batteryDiffEntry.getTotalConsumePower()); 204 appListIterator.remove(); 205 } 206 } 207 if (systemAppsDiffEntry != null) { 208 appEntries.add(systemAppsDiffEntry); 209 } 210 } 211 combineIntoUninstalledApps( final Context context, final @NonNull List<BatteryDiffEntry> appEntries)212 private static void combineIntoUninstalledApps( 213 final Context context, final @NonNull List<BatteryDiffEntry> appEntries) { 214 BatteryDiffEntry uninstalledAppDiffEntry = null; 215 final Iterator<BatteryDiffEntry> appListIterator = appEntries.iterator(); 216 while (appListIterator.hasNext()) { 217 final BatteryDiffEntry batteryDiffEntry = appListIterator.next(); 218 if (!batteryDiffEntry.isUninstalledEntry()) { 219 continue; 220 } 221 222 if (uninstalledAppDiffEntry == null) { 223 uninstalledAppDiffEntry = 224 new BatteryDiffEntry( 225 context, 226 BatteryDiffEntry.UNINSTALLED_APPS_KEY, 227 BatteryDiffEntry.UNINSTALLED_APPS_KEY, 228 ConvertUtils.CONSUMER_TYPE_UID_BATTERY); 229 } 230 uninstalledAppDiffEntry.mConsumePower += batteryDiffEntry.mConsumePower; 231 uninstalledAppDiffEntry.mForegroundUsageTimeInMs += 232 batteryDiffEntry.mForegroundUsageTimeInMs; 233 uninstalledAppDiffEntry.setTotalConsumePower(batteryDiffEntry.getTotalConsumePower()); 234 appListIterator.remove(); 235 } 236 if (uninstalledAppDiffEntry != null) { 237 appEntries.add(uninstalledAppDiffEntry); 238 } 239 } 240 combineSystemItemsIntoOthers( final Context context, final PowerUsageFeatureProvider featureProvider, final List<BatteryDiffEntry> systemEntries)241 private static void combineSystemItemsIntoOthers( 242 final Context context, 243 final PowerUsageFeatureProvider featureProvider, 244 final List<BatteryDiffEntry> systemEntries) { 245 final Set<Integer> othersSystemComponentSet = featureProvider.getOthersSystemComponentSet(); 246 final Set<String> othersCustomComponentNameSet = 247 featureProvider.getOthersCustomComponentNameSet(); 248 BatteryDiffEntry othersDiffEntry = null; 249 final Iterator<BatteryDiffEntry> systemListIterator = systemEntries.iterator(); 250 while (systemListIterator.hasNext()) { 251 final BatteryDiffEntry batteryDiffEntry = systemListIterator.next(); 252 final int componentId = batteryDiffEntry.mComponentId; 253 if (othersSystemComponentSet.contains(componentId) 254 || (componentId >= BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID 255 && othersCustomComponentNameSet.contains( 256 batteryDiffEntry.getAppLabel()))) { 257 if (othersDiffEntry == null) { 258 othersDiffEntry = 259 new BatteryDiffEntry( 260 context, 261 BatteryDiffEntry.OTHERS_KEY, 262 BatteryDiffEntry.OTHERS_KEY, 263 ConvertUtils.CONSUMER_TYPE_SYSTEM_BATTERY); 264 } 265 othersDiffEntry.mConsumePower += batteryDiffEntry.mConsumePower; 266 othersDiffEntry.setTotalConsumePower(batteryDiffEntry.getTotalConsumePower()); 267 systemListIterator.remove(); 268 } 269 } 270 if (othersDiffEntry != null) { 271 systemEntries.add(othersDiffEntry); 272 } 273 } 274 275 @VisibleForTesting needsCombineInSystemApp( final BatteryDiffEntry batteryDiffEntry, final @NonNull List<String> systemAppsAllowlist, final @NonNull Set<String> systemAppsPackageNames, final @NonNull Set<Integer> systemAppsUids)276 static boolean needsCombineInSystemApp( 277 final BatteryDiffEntry batteryDiffEntry, 278 final @NonNull List<String> systemAppsAllowlist, 279 final @NonNull Set<String> systemAppsPackageNames, 280 final @NonNull Set<Integer> systemAppsUids) { 281 if (batteryDiffEntry.mIsHidden) { 282 return true; 283 } 284 285 final String packageName = batteryDiffEntry.getPackageName(); 286 if (packageName == null || packageName.isEmpty()) { 287 return false; 288 } 289 290 if (systemAppsAllowlist.contains(packageName)) { 291 return true; 292 } 293 294 int uid = (int) batteryDiffEntry.mUid; 295 return systemAppsPackageNames.contains(packageName) || systemAppsUids.contains(uid); 296 } 297 298 /** 299 * Sets total consume power, and adjusts the percentages to ensure the total round percentage 300 * could be 100%, and then sorts entries based on the sorting key. 301 */ processAndSortEntries(final List<BatteryDiffEntry> batteryDiffEntries)302 public static void processAndSortEntries(final List<BatteryDiffEntry> batteryDiffEntries) { 303 if (batteryDiffEntries.isEmpty()) { 304 return; 305 } 306 307 // Sets total consume power. 308 double totalConsumePower = 0.0; 309 for (BatteryDiffEntry batteryDiffEntry : batteryDiffEntries) { 310 totalConsumePower += batteryDiffEntry.mConsumePower; 311 } 312 for (BatteryDiffEntry batteryDiffEntry : batteryDiffEntries) { 313 batteryDiffEntry.setTotalConsumePower(totalConsumePower); 314 } 315 316 // Adjusts percentages to show. 317 // The lower bound is treating all the small percentages as 0. 318 // The upper bound is treating all the small percentages as 1. 319 int totalLowerBound = 0; 320 int totalUpperBound = 0; 321 for (BatteryDiffEntry entry : batteryDiffEntries) { 322 if (entry.getPercentage() < SMALL_PERCENTAGE_THRESHOLD) { 323 totalUpperBound += 1; 324 } else { 325 int roundPercentage = Math.round((float) entry.getPercentage()); 326 totalLowerBound += roundPercentage; 327 totalUpperBound += roundPercentage; 328 } 329 } 330 if (totalLowerBound > 100 || totalUpperBound < 100) { 331 Collections.sort(batteryDiffEntries, BatteryDiffEntry.COMPARATOR); 332 for (int i = 0; i < totalLowerBound - 100 && i < batteryDiffEntries.size(); i++) { 333 batteryDiffEntries.get(i).setAdjustPercentageOffset(-1); 334 } 335 for (int i = 0; i < 100 - totalUpperBound && i < batteryDiffEntries.size(); i++) { 336 batteryDiffEntries.get(i).setAdjustPercentageOffset(1); 337 } 338 } 339 340 // Sorts entries. 341 Collections.sort(batteryDiffEntries, BatteryDiffEntry.COMPARATOR); 342 } 343 } 344