1 /* 2 * Copyright (C) 2017 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 package com.android.settings.fuelgauge; 17 18 import android.app.AppOpsManager; 19 import android.content.Context; 20 import android.content.Intent; 21 import android.content.IntentFilter; 22 import android.content.pm.ApplicationInfo; 23 import android.content.pm.PackageInfo; 24 import android.content.pm.PackageManager; 25 import android.content.pm.ResolveInfo; 26 import android.os.BatteryStats; 27 import android.os.Build; 28 import android.os.Bundle; 29 import android.os.Process; 30 import android.os.SystemClock; 31 import android.os.UserHandle; 32 import android.os.UserManager; 33 import android.text.format.DateUtils; 34 import android.util.Log; 35 import android.util.SparseLongArray; 36 37 import androidx.annotation.IntDef; 38 import androidx.annotation.Nullable; 39 import androidx.annotation.VisibleForTesting; 40 import androidx.annotation.WorkerThread; 41 42 import com.android.internal.os.BatterySipper; 43 import com.android.internal.os.BatteryStatsHelper; 44 import com.android.internal.util.ArrayUtils; 45 import com.android.settings.fuelgauge.batterytip.AnomalyDatabaseHelper; 46 import com.android.settings.fuelgauge.batterytip.AnomalyInfo; 47 import com.android.settings.fuelgauge.batterytip.BatteryDatabaseManager; 48 import com.android.settings.fuelgauge.batterytip.StatsManagerConfig; 49 import com.android.settings.overlay.FeatureFactory; 50 import com.android.settingslib.applications.AppUtils; 51 import com.android.settingslib.fuelgauge.Estimate; 52 import com.android.settingslib.fuelgauge.EstimateKt; 53 import com.android.settingslib.fuelgauge.PowerWhitelistBackend; 54 import com.android.settingslib.utils.PowerUtil; 55 import com.android.settingslib.utils.ThreadUtils; 56 57 import java.lang.annotation.Retention; 58 import java.lang.annotation.RetentionPolicy; 59 import java.time.Duration; 60 import java.time.Instant; 61 import java.util.Collections; 62 import java.util.Comparator; 63 import java.util.List; 64 65 /** 66 * Utils for battery operation 67 */ 68 public class BatteryUtils { 69 public static final int UID_NULL = -1; 70 public static final int SDK_NULL = -1; 71 72 @Retention(RetentionPolicy.SOURCE) 73 @IntDef({StatusType.SCREEN_USAGE, 74 StatusType.FOREGROUND, 75 StatusType.BACKGROUND, 76 StatusType.ALL 77 }) 78 public @interface StatusType { 79 int SCREEN_USAGE = 0; 80 int FOREGROUND = 1; 81 int BACKGROUND = 2; 82 int ALL = 3; 83 } 84 85 private static final String TAG = "BatteryUtils"; 86 87 private static final int MIN_POWER_THRESHOLD_MILLI_AMP = 5; 88 89 private static final int SECONDS_IN_HOUR = 60 * 60; 90 private static BatteryUtils sInstance; 91 private PackageManager mPackageManager; 92 93 private AppOpsManager mAppOpsManager; 94 private Context mContext; 95 @VisibleForTesting 96 PowerUsageFeatureProvider mPowerUsageFeatureProvider; 97 getInstance(Context context)98 public static BatteryUtils getInstance(Context context) { 99 if (sInstance == null || sInstance.isDataCorrupted()) { 100 sInstance = new BatteryUtils(context.getApplicationContext()); 101 } 102 return sInstance; 103 } 104 105 @VisibleForTesting BatteryUtils(Context context)106 BatteryUtils(Context context) { 107 mContext = context; 108 mPackageManager = context.getPackageManager(); 109 mAppOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE); 110 mPowerUsageFeatureProvider = FeatureFactory.getFactory(context) 111 .getPowerUsageFeatureProvider(context); 112 } 113 getProcessTimeMs(@tatusType int type, @Nullable BatteryStats.Uid uid, int which)114 public long getProcessTimeMs(@StatusType int type, @Nullable BatteryStats.Uid uid, 115 int which) { 116 if (uid == null) { 117 return 0; 118 } 119 120 switch (type) { 121 case StatusType.SCREEN_USAGE: 122 return getScreenUsageTimeMs(uid, which); 123 case StatusType.FOREGROUND: 124 return getProcessForegroundTimeMs(uid, which); 125 case StatusType.BACKGROUND: 126 return getProcessBackgroundTimeMs(uid, which); 127 case StatusType.ALL: 128 return getProcessForegroundTimeMs(uid, which) 129 + getProcessBackgroundTimeMs(uid, which); 130 } 131 return 0; 132 } 133 getScreenUsageTimeMs(BatteryStats.Uid uid, int which, long rawRealTimeUs)134 private long getScreenUsageTimeMs(BatteryStats.Uid uid, int which, long rawRealTimeUs) { 135 final int foregroundTypes[] = {BatteryStats.Uid.PROCESS_STATE_TOP}; 136 Log.v(TAG, "package: " + mPackageManager.getNameForUid(uid.getUid())); 137 138 long timeUs = 0; 139 for (int type : foregroundTypes) { 140 final long localTime = uid.getProcessStateTime(type, rawRealTimeUs, which); 141 Log.v(TAG, "type: " + type + " time(us): " + localTime); 142 timeUs += localTime; 143 } 144 Log.v(TAG, "foreground time(us): " + timeUs); 145 146 // Return the min value of STATE_TOP time and foreground activity time, since both of these 147 // time have some errors 148 return PowerUtil.convertUsToMs( 149 Math.min(timeUs, getForegroundActivityTotalTimeUs(uid, rawRealTimeUs))); 150 } 151 getScreenUsageTimeMs(BatteryStats.Uid uid, int which)152 private long getScreenUsageTimeMs(BatteryStats.Uid uid, int which) { 153 final long rawRealTimeUs = PowerUtil.convertMsToUs(SystemClock.elapsedRealtime()); 154 return getScreenUsageTimeMs(uid, which, rawRealTimeUs); 155 } 156 getProcessBackgroundTimeMs(BatteryStats.Uid uid, int which)157 private long getProcessBackgroundTimeMs(BatteryStats.Uid uid, int which) { 158 final long rawRealTimeUs = PowerUtil.convertMsToUs(SystemClock.elapsedRealtime()); 159 final long timeUs = uid.getProcessStateTime( 160 BatteryStats.Uid.PROCESS_STATE_BACKGROUND, rawRealTimeUs, which); 161 162 Log.v(TAG, "package: " + mPackageManager.getNameForUid(uid.getUid())); 163 Log.v(TAG, "background time(us): " + timeUs); 164 return PowerUtil.convertUsToMs(timeUs); 165 } 166 getProcessForegroundTimeMs(BatteryStats.Uid uid, int which)167 private long getProcessForegroundTimeMs(BatteryStats.Uid uid, int which) { 168 final long rawRealTimeUs = PowerUtil.convertMsToUs(SystemClock.elapsedRealtime()); 169 return getScreenUsageTimeMs(uid, which, rawRealTimeUs) 170 + PowerUtil.convertUsToMs(getForegroundServiceTotalTimeUs(uid, rawRealTimeUs)); 171 } 172 173 /** 174 * Remove the {@link BatterySipper} that we should hide and smear the screen usage based on 175 * foreground activity time. 176 * 177 * @param sippers sipper list that need to check and remove 178 * @return the total power of the hidden items of {@link BatterySipper} 179 * for proportional smearing 180 */ removeHiddenBatterySippers(List<BatterySipper> sippers)181 public double removeHiddenBatterySippers(List<BatterySipper> sippers) { 182 double proportionalSmearPowerMah = 0; 183 BatterySipper screenSipper = null; 184 for (int i = sippers.size() - 1; i >= 0; i--) { 185 final BatterySipper sipper = sippers.get(i); 186 if (shouldHideSipper(sipper)) { 187 sippers.remove(i); 188 if (sipper.drainType != BatterySipper.DrainType.OVERCOUNTED 189 && sipper.drainType != BatterySipper.DrainType.SCREEN 190 && sipper.drainType != BatterySipper.DrainType.UNACCOUNTED 191 && sipper.drainType != BatterySipper.DrainType.BLUETOOTH 192 && sipper.drainType != BatterySipper.DrainType.WIFI 193 && sipper.drainType != BatterySipper.DrainType.IDLE 194 && !isHiddenSystemModule(sipper)) { 195 // Don't add it if it is overcounted, unaccounted, wifi, bluetooth, screen 196 // or hidden system modules 197 proportionalSmearPowerMah += sipper.totalPowerMah; 198 } 199 } 200 201 if (sipper.drainType == BatterySipper.DrainType.SCREEN) { 202 screenSipper = sipper; 203 } 204 } 205 206 smearScreenBatterySipper(sippers, screenSipper); 207 208 return proportionalSmearPowerMah; 209 } 210 211 /** 212 * Smear the screen on power usage among {@code sippers}, based on ratio of foreground activity 213 * time. 214 */ 215 @VisibleForTesting smearScreenBatterySipper(List<BatterySipper> sippers, BatterySipper screenSipper)216 void smearScreenBatterySipper(List<BatterySipper> sippers, BatterySipper screenSipper) { 217 long totalActivityTimeMs = 0; 218 final SparseLongArray activityTimeArray = new SparseLongArray(); 219 for (int i = 0, size = sippers.size(); i < size; i++) { 220 final BatteryStats.Uid uid = sippers.get(i).uidObj; 221 if (uid != null) { 222 final long timeMs = getProcessTimeMs(StatusType.SCREEN_USAGE, uid, 223 BatteryStats.STATS_SINCE_CHARGED); 224 activityTimeArray.put(uid.getUid(), timeMs); 225 totalActivityTimeMs += timeMs; 226 } 227 } 228 229 if (totalActivityTimeMs >= 10 * DateUtils.MINUTE_IN_MILLIS) { 230 if (screenSipper == null) { 231 Log.e(TAG, "screen sipper is null even when app screen time is not zero"); 232 return; 233 } 234 235 final double screenPowerMah = screenSipper.totalPowerMah; 236 for (int i = 0, size = sippers.size(); i < size; i++) { 237 final BatterySipper sipper = sippers.get(i); 238 sipper.totalPowerMah += screenPowerMah * activityTimeArray.get(sipper.getUid(), 0) 239 / totalActivityTimeMs; 240 } 241 } 242 } 243 244 /** 245 * Check whether we should hide the battery sipper. 246 */ shouldHideSipper(BatterySipper sipper)247 public boolean shouldHideSipper(BatterySipper sipper) { 248 final BatterySipper.DrainType drainType = sipper.drainType; 249 250 return drainType == BatterySipper.DrainType.IDLE 251 || drainType == BatterySipper.DrainType.CELL 252 || drainType == BatterySipper.DrainType.SCREEN 253 || drainType == BatterySipper.DrainType.UNACCOUNTED 254 || drainType == BatterySipper.DrainType.OVERCOUNTED 255 || drainType == BatterySipper.DrainType.BLUETOOTH 256 || drainType == BatterySipper.DrainType.WIFI 257 || (sipper.totalPowerMah * SECONDS_IN_HOUR) < MIN_POWER_THRESHOLD_MILLI_AMP 258 || mPowerUsageFeatureProvider.isTypeService(sipper) 259 || mPowerUsageFeatureProvider.isTypeSystem(sipper) 260 || isHiddenSystemModule(sipper); 261 } 262 263 /** 264 * Return {@code true} if one of packages in {@code sipper} is hidden system modules 265 */ isHiddenSystemModule(BatterySipper sipper)266 public boolean isHiddenSystemModule(BatterySipper sipper) { 267 if (sipper.uidObj == null) { 268 return false; 269 } 270 sipper.mPackages = mPackageManager.getPackagesForUid(sipper.getUid()); 271 if (sipper.mPackages != null) { 272 for (int i = 0, length = sipper.mPackages.length; i < length; i++) { 273 if (AppUtils.isHiddenSystemModule(mContext, sipper.mPackages[i])) { 274 return true; 275 } 276 } 277 } 278 279 return false; 280 } 281 282 /** 283 * Calculate the power usage percentage for an app 284 * 285 * @param powerUsageMah power used by the app 286 * @param totalPowerMah total power used in the system 287 * @param hiddenPowerMah power used by no-actionable app that we want to hide, i.e. Screen, 288 * Android OS. 289 * @param dischargeAmount The discharge amount calculated by {@link BatteryStats} 290 * @return A percentage value scaled by {@paramref dischargeAmount} 291 * @see BatteryStats#getDischargeAmount(int) 292 */ calculateBatteryPercent(double powerUsageMah, double totalPowerMah, double hiddenPowerMah, int dischargeAmount)293 public double calculateBatteryPercent(double powerUsageMah, double totalPowerMah, 294 double hiddenPowerMah, int dischargeAmount) { 295 if (totalPowerMah == 0) { 296 return 0; 297 } 298 299 return (powerUsageMah / (totalPowerMah - hiddenPowerMah)) * dischargeAmount; 300 } 301 302 /** 303 * Calculate the whole running time in the state {@code statsType} 304 * 305 * @param batteryStatsHelper utility class that contains the data 306 * @param statsType state that we want to calculate the time for 307 * @return the running time in millis 308 */ calculateRunningTimeBasedOnStatsType(BatteryStatsHelper batteryStatsHelper, int statsType)309 public long calculateRunningTimeBasedOnStatsType(BatteryStatsHelper batteryStatsHelper, 310 int statsType) { 311 final long elapsedRealtimeUs = PowerUtil.convertMsToUs( 312 SystemClock.elapsedRealtime()); 313 // Return the battery time (millisecond) on status mStatsType 314 return PowerUtil.convertUsToMs( 315 batteryStatsHelper.getStats().computeBatteryRealtime(elapsedRealtimeUs, statsType)); 316 317 } 318 319 /** 320 * Find the package name for a {@link android.os.BatteryStats.Uid} 321 * 322 * @param uid id to get the package name 323 * @return the package name. If there are multiple packages related to 324 * given id, return the first one. Or return null if there are no known 325 * packages with the given id 326 * @see PackageManager#getPackagesForUid(int) 327 */ getPackageName(int uid)328 public String getPackageName(int uid) { 329 final String[] packageNames = mPackageManager.getPackagesForUid(uid); 330 331 return ArrayUtils.isEmpty(packageNames) ? null : packageNames[0]; 332 } 333 334 /** 335 * Find the targetSdkVersion for package with name {@code packageName} 336 * 337 * @return the targetSdkVersion, or {@link #SDK_NULL} if {@code packageName} doesn't exist 338 */ getTargetSdkVersion(final String packageName)339 public int getTargetSdkVersion(final String packageName) { 340 try { 341 ApplicationInfo info = mPackageManager.getApplicationInfo(packageName, 342 PackageManager.GET_META_DATA); 343 344 return info.targetSdkVersion; 345 } catch (PackageManager.NameNotFoundException e) { 346 Log.e(TAG, "Cannot find package: " + packageName, e); 347 } 348 349 return SDK_NULL; 350 } 351 352 /** 353 * Check whether background restriction is enabled 354 */ isBackgroundRestrictionEnabled(final int targetSdkVersion, final int uid, final String packageName)355 public boolean isBackgroundRestrictionEnabled(final int targetSdkVersion, final int uid, 356 final String packageName) { 357 if (targetSdkVersion >= Build.VERSION_CODES.O) { 358 return true; 359 } 360 final int mode = mAppOpsManager 361 .checkOpNoThrow(AppOpsManager.OP_RUN_IN_BACKGROUND, uid, packageName); 362 return mode == AppOpsManager.MODE_IGNORED || mode == AppOpsManager.MODE_ERRORED; 363 } 364 365 /** 366 * Sort the {@code usageList} based on {@link BatterySipper#totalPowerMah} 367 */ sortUsageList(List<BatterySipper> usageList)368 public void sortUsageList(List<BatterySipper> usageList) { 369 Collections.sort(usageList, new Comparator<BatterySipper>() { 370 @Override 371 public int compare(BatterySipper a, BatterySipper b) { 372 return Double.compare(b.totalPowerMah, a.totalPowerMah); 373 } 374 }); 375 } 376 377 /** 378 * Calculate the time since last full charge, including the device off time 379 * 380 * @param batteryStatsHelper utility class that contains the data 381 * @param currentTimeMs current wall time 382 * @return time in millis 383 */ calculateLastFullChargeTime(BatteryStatsHelper batteryStatsHelper, long currentTimeMs)384 public long calculateLastFullChargeTime(BatteryStatsHelper batteryStatsHelper, 385 long currentTimeMs) { 386 return currentTimeMs - batteryStatsHelper.getStats().getStartClockTime(); 387 388 } 389 390 /** 391 * Calculate the screen usage time since last full charge. 392 * 393 * @param batteryStatsHelper utility class that contains the screen usage data 394 * @return time in millis 395 */ calculateScreenUsageTime(BatteryStatsHelper batteryStatsHelper)396 public long calculateScreenUsageTime(BatteryStatsHelper batteryStatsHelper) { 397 final BatterySipper sipper = findBatterySipperByType( 398 batteryStatsHelper.getUsageList(), BatterySipper.DrainType.SCREEN); 399 return sipper != null ? sipper.usageTimeMs : 0; 400 } 401 logRuntime(String tag, String message, long startTime)402 public static void logRuntime(String tag, String message, long startTime) { 403 Log.d(tag, message + ": " + (System.currentTimeMillis() - startTime) + "ms"); 404 } 405 406 /** 407 * Return {@code true} if battery is overheated and charging. 408 */ isBatteryDefenderOn(BatteryInfo batteryInfo)409 public static boolean isBatteryDefenderOn(BatteryInfo batteryInfo) { 410 return batteryInfo.isOverheated && !batteryInfo.discharging; 411 } 412 413 /** 414 * Find package uid from package name 415 * 416 * @param packageName used to find the uid 417 * @return uid for packageName, or {@link #UID_NULL} if exception happens or 418 * {@code packageName} is null 419 */ getPackageUid(String packageName)420 public int getPackageUid(String packageName) { 421 try { 422 return packageName == null ? UID_NULL : mPackageManager.getPackageUid(packageName, 423 PackageManager.GET_META_DATA); 424 } catch (PackageManager.NameNotFoundException e) { 425 return UID_NULL; 426 } 427 } 428 setForceAppStandby(int uid, String packageName, int mode)429 public void setForceAppStandby(int uid, String packageName, 430 int mode) { 431 final boolean isPreOApp = isPreOApp(packageName); 432 if (isPreOApp) { 433 // Control whether app could run in the background if it is pre O app 434 mAppOpsManager.setMode(AppOpsManager.OP_RUN_IN_BACKGROUND, uid, packageName, mode); 435 } 436 // Control whether app could run jobs in the background 437 mAppOpsManager.setMode(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, uid, packageName, mode); 438 439 ThreadUtils.postOnBackgroundThread(() -> { 440 final BatteryDatabaseManager batteryDatabaseManager = BatteryDatabaseManager 441 .getInstance(mContext); 442 if (mode == AppOpsManager.MODE_IGNORED) { 443 batteryDatabaseManager.insertAction(AnomalyDatabaseHelper.ActionType.RESTRICTION, 444 uid, packageName, System.currentTimeMillis()); 445 } else if (mode == AppOpsManager.MODE_ALLOWED) { 446 batteryDatabaseManager.deleteAction(AnomalyDatabaseHelper.ActionType.RESTRICTION, 447 uid, packageName); 448 } 449 }); 450 } 451 isForceAppStandbyEnabled(int uid, String packageName)452 public boolean isForceAppStandbyEnabled(int uid, String packageName) { 453 return mAppOpsManager.checkOpNoThrow(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, uid, 454 packageName) == AppOpsManager.MODE_IGNORED; 455 } 456 clearForceAppStandby(String packageName)457 public boolean clearForceAppStandby(String packageName) { 458 final int uid = getPackageUid(packageName); 459 if (uid != UID_NULL && isForceAppStandbyEnabled(uid, packageName)) { 460 setForceAppStandby(uid, packageName, AppOpsManager.MODE_ALLOWED); 461 return true; 462 } else { 463 return false; 464 } 465 } 466 initBatteryStatsHelper(BatteryStatsHelper statsHelper, Bundle bundle, UserManager userManager)467 public void initBatteryStatsHelper(BatteryStatsHelper statsHelper, Bundle bundle, 468 UserManager userManager) { 469 statsHelper.create(bundle); 470 statsHelper.clearStats(); 471 statsHelper.refreshStats(BatteryStats.STATS_SINCE_CHARGED, userManager.getUserProfiles()); 472 } 473 474 @WorkerThread getBatteryInfo(final BatteryStatsHelper statsHelper, final String tag)475 public BatteryInfo getBatteryInfo(final BatteryStatsHelper statsHelper, final String tag) { 476 final long startTime = System.currentTimeMillis(); 477 478 // Stuff we always need to get BatteryInfo 479 final Intent batteryBroadcast = mContext.registerReceiver(null, 480 new IntentFilter(Intent.ACTION_BATTERY_CHANGED)); 481 final long elapsedRealtimeUs = PowerUtil.convertMsToUs( 482 SystemClock.elapsedRealtime()); 483 final BatteryStats stats = statsHelper.getStats(); 484 BatteryInfo batteryInfo; 485 Estimate estimate = getEnhancedEstimate(); 486 487 // couldn't get estimate from cache or provider, use fallback 488 if (estimate == null) { 489 estimate = new Estimate( 490 PowerUtil.convertUsToMs(stats.computeBatteryTimeRemaining(elapsedRealtimeUs)), 491 false /* isBasedOnUsage */, 492 EstimateKt.AVERAGE_TIME_TO_DISCHARGE_UNKNOWN); 493 } 494 495 BatteryUtils.logRuntime(tag, "BatteryInfoLoader post query", startTime); 496 batteryInfo = BatteryInfo.getBatteryInfo(mContext, batteryBroadcast, stats, 497 estimate, elapsedRealtimeUs, false /* shortString */); 498 BatteryUtils.logRuntime(tag, "BatteryInfoLoader.loadInBackground", startTime); 499 500 return batteryInfo; 501 } 502 503 @VisibleForTesting getEnhancedEstimate()504 Estimate getEnhancedEstimate() { 505 Estimate estimate = null; 506 // Get enhanced prediction if available 507 if (Duration.between(Estimate.getLastCacheUpdateTime(mContext), Instant.now()) 508 .compareTo(Duration.ofSeconds(10)) < 0) { 509 estimate = Estimate.getCachedEstimateIfAvailable(mContext); 510 } else if (mPowerUsageFeatureProvider != null && 511 mPowerUsageFeatureProvider.isEnhancedBatteryPredictionEnabled(mContext)) { 512 estimate = mPowerUsageFeatureProvider.getEnhancedBatteryPrediction(mContext); 513 if (estimate != null) { 514 Estimate.storeCachedEstimate(mContext, estimate); 515 } 516 } 517 return estimate; 518 } 519 520 /** 521 * Find the {@link BatterySipper} with the corresponding {@link BatterySipper.DrainType} 522 */ findBatterySipperByType(List<BatterySipper> usageList, BatterySipper.DrainType type)523 public BatterySipper findBatterySipperByType(List<BatterySipper> usageList, 524 BatterySipper.DrainType type) { 525 for (int i = 0, size = usageList.size(); i < size; i++) { 526 final BatterySipper sipper = usageList.get(i); 527 if (sipper.drainType == type) { 528 return sipper; 529 } 530 } 531 return null; 532 } 533 isDataCorrupted()534 private boolean isDataCorrupted() { 535 return mPackageManager == null || mAppOpsManager == null; 536 } 537 538 @VisibleForTesting getForegroundActivityTotalTimeUs(BatteryStats.Uid uid, long rawRealtimeUs)539 long getForegroundActivityTotalTimeUs(BatteryStats.Uid uid, long rawRealtimeUs) { 540 final BatteryStats.Timer timer = uid.getForegroundActivityTimer(); 541 if (timer != null) { 542 return timer.getTotalTimeLocked(rawRealtimeUs, BatteryStats.STATS_SINCE_CHARGED); 543 } 544 545 return 0; 546 } 547 548 @VisibleForTesting getForegroundServiceTotalTimeUs(BatteryStats.Uid uid, long rawRealtimeUs)549 long getForegroundServiceTotalTimeUs(BatteryStats.Uid uid, long rawRealtimeUs) { 550 final BatteryStats.Timer timer = uid.getForegroundServiceTimer(); 551 if (timer != null) { 552 return timer.getTotalTimeLocked(rawRealtimeUs, BatteryStats.STATS_SINCE_CHARGED); 553 } 554 555 return 0; 556 } 557 isPreOApp(final String packageName)558 public boolean isPreOApp(final String packageName) { 559 try { 560 ApplicationInfo info = mPackageManager.getApplicationInfo(packageName, 561 PackageManager.GET_META_DATA); 562 563 return info.targetSdkVersion < Build.VERSION_CODES.O; 564 } catch (PackageManager.NameNotFoundException e) { 565 Log.e(TAG, "Cannot find package: " + packageName, e); 566 } 567 568 return false; 569 } 570 isPreOApp(final String[] packageNames)571 public boolean isPreOApp(final String[] packageNames) { 572 if (ArrayUtils.isEmpty(packageNames)) { 573 return false; 574 } 575 576 for (String packageName : packageNames) { 577 if (isPreOApp(packageName)) { 578 return true; 579 } 580 } 581 582 return false; 583 } 584 585 /** 586 * Return {@code true} if we should hide anomaly app represented by {@code uid} 587 */ shouldHideAnomaly(PowerWhitelistBackend powerWhitelistBackend, int uid, AnomalyInfo anomalyInfo)588 public boolean shouldHideAnomaly(PowerWhitelistBackend powerWhitelistBackend, int uid, 589 AnomalyInfo anomalyInfo) { 590 final String[] packageNames = mPackageManager.getPackagesForUid(uid); 591 if (ArrayUtils.isEmpty(packageNames)) { 592 // Don't show it if app has been uninstalled 593 return true; 594 } 595 596 return isSystemUid(uid) || powerWhitelistBackend.isWhitelisted(packageNames) 597 || (isSystemApp(mPackageManager, packageNames) && !hasLauncherEntry(packageNames)) 598 || (isExcessiveBackgroundAnomaly(anomalyInfo) && !isPreOApp(packageNames)); 599 } 600 isExcessiveBackgroundAnomaly(AnomalyInfo anomalyInfo)601 private boolean isExcessiveBackgroundAnomaly(AnomalyInfo anomalyInfo) { 602 return anomalyInfo.anomalyType 603 == StatsManagerConfig.AnomalyType.EXCESSIVE_BACKGROUND_SERVICE; 604 } 605 isSystemUid(int uid)606 private boolean isSystemUid(int uid) { 607 final int appUid = UserHandle.getAppId(uid); 608 return appUid >= Process.ROOT_UID && appUid < Process.FIRST_APPLICATION_UID; 609 } 610 isSystemApp(PackageManager packageManager, String[] packageNames)611 private boolean isSystemApp(PackageManager packageManager, String[] packageNames) { 612 for (String packageName : packageNames) { 613 try { 614 final ApplicationInfo info = packageManager.getApplicationInfo(packageName, 615 0 /* flags */); 616 if ((info.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { 617 return true; 618 } 619 } catch (PackageManager.NameNotFoundException e) { 620 Log.e(TAG, "Package not found: " + packageName, e); 621 } 622 } 623 624 return false; 625 } 626 hasLauncherEntry(String[] packageNames)627 private boolean hasLauncherEntry(String[] packageNames) { 628 final Intent launchIntent = new Intent(Intent.ACTION_MAIN, null); 629 launchIntent.addCategory(Intent.CATEGORY_LAUNCHER); 630 631 // If we do not specify MATCH_DIRECT_BOOT_AWARE or 632 // MATCH_DIRECT_BOOT_UNAWARE, system will derive and update the flags 633 // according to the user's lock state. When the user is locked, 634 // components 635 // with ComponentInfo#directBootAware == false will be filtered. We should 636 // explicitly include both direct boot aware and unaware components here. 637 final List<ResolveInfo> resolveInfos = mPackageManager.queryIntentActivities(launchIntent, 638 PackageManager.MATCH_DISABLED_COMPONENTS 639 | PackageManager.MATCH_DIRECT_BOOT_AWARE 640 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE 641 | PackageManager.MATCH_SYSTEM_ONLY); 642 for (int i = 0, size = resolveInfos.size(); i < size; i++) { 643 final ResolveInfo resolveInfo = resolveInfos.get(i); 644 if (ArrayUtils.contains(packageNames, resolveInfo.activityInfo.packageName)) { 645 return true; 646 } 647 } 648 649 return false; 650 } 651 652 /** 653 * Return version number of an app represented by {@code packageName}, and return -1 if not 654 * found. 655 */ getAppLongVersionCode(String packageName)656 public long getAppLongVersionCode(String packageName) { 657 try { 658 final PackageInfo packageInfo = mPackageManager.getPackageInfo(packageName, 659 0 /* flags */); 660 return packageInfo.getLongVersionCode(); 661 } catch (PackageManager.NameNotFoundException e) { 662 Log.e(TAG, "Cannot find package: " + packageName, e); 663 } 664 665 return -1L; 666 } 667 } 668 669