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