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 * Find package uid from package name 408 * 409 * @param packageName used to find the uid 410 * @return uid for packageName, or {@link #UID_NULL} if exception happens or 411 * {@code packageName} is null 412 */ getPackageUid(String packageName)413 public int getPackageUid(String packageName) { 414 try { 415 return packageName == null ? UID_NULL : mPackageManager.getPackageUid(packageName, 416 PackageManager.GET_META_DATA); 417 } catch (PackageManager.NameNotFoundException e) { 418 return UID_NULL; 419 } 420 } 421 setForceAppStandby(int uid, String packageName, int mode)422 public void setForceAppStandby(int uid, String packageName, 423 int mode) { 424 final boolean isPreOApp = isPreOApp(packageName); 425 if (isPreOApp) { 426 // Control whether app could run in the background if it is pre O app 427 mAppOpsManager.setMode(AppOpsManager.OP_RUN_IN_BACKGROUND, uid, packageName, mode); 428 } 429 // Control whether app could run jobs in the background 430 mAppOpsManager.setMode(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, uid, packageName, mode); 431 432 ThreadUtils.postOnBackgroundThread(() -> { 433 final BatteryDatabaseManager batteryDatabaseManager = BatteryDatabaseManager 434 .getInstance(mContext); 435 if (mode == AppOpsManager.MODE_IGNORED) { 436 batteryDatabaseManager.insertAction(AnomalyDatabaseHelper.ActionType.RESTRICTION, 437 uid, packageName, System.currentTimeMillis()); 438 } else if (mode == AppOpsManager.MODE_ALLOWED) { 439 batteryDatabaseManager.deleteAction(AnomalyDatabaseHelper.ActionType.RESTRICTION, 440 uid, packageName); 441 } 442 }); 443 } 444 isForceAppStandbyEnabled(int uid, String packageName)445 public boolean isForceAppStandbyEnabled(int uid, String packageName) { 446 return mAppOpsManager.checkOpNoThrow(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, uid, 447 packageName) == AppOpsManager.MODE_IGNORED; 448 } 449 clearForceAppStandby(String packageName)450 public boolean clearForceAppStandby(String packageName) { 451 final int uid = getPackageUid(packageName); 452 if (uid != UID_NULL && isForceAppStandbyEnabled(uid, packageName)) { 453 setForceAppStandby(uid, packageName, AppOpsManager.MODE_ALLOWED); 454 return true; 455 } else { 456 return false; 457 } 458 } 459 initBatteryStatsHelper(BatteryStatsHelper statsHelper, Bundle bundle, UserManager userManager)460 public void initBatteryStatsHelper(BatteryStatsHelper statsHelper, Bundle bundle, 461 UserManager userManager) { 462 statsHelper.create(bundle); 463 statsHelper.clearStats(); 464 statsHelper.refreshStats(BatteryStats.STATS_SINCE_CHARGED, userManager.getUserProfiles()); 465 } 466 467 @WorkerThread getBatteryInfo(final BatteryStatsHelper statsHelper, final String tag)468 public BatteryInfo getBatteryInfo(final BatteryStatsHelper statsHelper, final String tag) { 469 final long startTime = System.currentTimeMillis(); 470 471 // Stuff we always need to get BatteryInfo 472 final Intent batteryBroadcast = mContext.registerReceiver(null, 473 new IntentFilter(Intent.ACTION_BATTERY_CHANGED)); 474 final long elapsedRealtimeUs = PowerUtil.convertMsToUs( 475 SystemClock.elapsedRealtime()); 476 final BatteryStats stats = statsHelper.getStats(); 477 BatteryInfo batteryInfo; 478 Estimate estimate = getEnhancedEstimate(); 479 480 // couldn't get estimate from cache or provider, use fallback 481 if (estimate == null) { 482 estimate = new Estimate( 483 PowerUtil.convertUsToMs(stats.computeBatteryTimeRemaining(elapsedRealtimeUs)), 484 false /* isBasedOnUsage */, 485 EstimateKt.AVERAGE_TIME_TO_DISCHARGE_UNKNOWN); 486 } 487 488 BatteryUtils.logRuntime(tag, "BatteryInfoLoader post query", startTime); 489 batteryInfo = BatteryInfo.getBatteryInfo(mContext, batteryBroadcast, stats, 490 estimate, elapsedRealtimeUs, false /* shortString */); 491 BatteryUtils.logRuntime(tag, "BatteryInfoLoader.loadInBackground", startTime); 492 493 return batteryInfo; 494 } 495 496 @VisibleForTesting getEnhancedEstimate()497 Estimate getEnhancedEstimate() { 498 Estimate estimate = null; 499 // Get enhanced prediction if available 500 if (Duration.between(Estimate.getLastCacheUpdateTime(mContext), Instant.now()) 501 .compareTo(Duration.ofSeconds(10)) < 0) { 502 estimate = Estimate.getCachedEstimateIfAvailable(mContext); 503 } else if (mPowerUsageFeatureProvider != null && 504 mPowerUsageFeatureProvider.isEnhancedBatteryPredictionEnabled(mContext)) { 505 estimate = mPowerUsageFeatureProvider.getEnhancedBatteryPrediction(mContext); 506 if (estimate != null) { 507 Estimate.storeCachedEstimate(mContext, estimate); 508 } 509 } 510 return estimate; 511 } 512 513 /** 514 * Find the {@link BatterySipper} with the corresponding {@link BatterySipper.DrainType} 515 */ findBatterySipperByType(List<BatterySipper> usageList, BatterySipper.DrainType type)516 public BatterySipper findBatterySipperByType(List<BatterySipper> usageList, 517 BatterySipper.DrainType type) { 518 for (int i = 0, size = usageList.size(); i < size; i++) { 519 final BatterySipper sipper = usageList.get(i); 520 if (sipper.drainType == type) { 521 return sipper; 522 } 523 } 524 return null; 525 } 526 isDataCorrupted()527 private boolean isDataCorrupted() { 528 return mPackageManager == null || mAppOpsManager == null; 529 } 530 531 @VisibleForTesting getForegroundActivityTotalTimeUs(BatteryStats.Uid uid, long rawRealtimeUs)532 long getForegroundActivityTotalTimeUs(BatteryStats.Uid uid, long rawRealtimeUs) { 533 final BatteryStats.Timer timer = uid.getForegroundActivityTimer(); 534 if (timer != null) { 535 return timer.getTotalTimeLocked(rawRealtimeUs, BatteryStats.STATS_SINCE_CHARGED); 536 } 537 538 return 0; 539 } 540 541 @VisibleForTesting getForegroundServiceTotalTimeUs(BatteryStats.Uid uid, long rawRealtimeUs)542 long getForegroundServiceTotalTimeUs(BatteryStats.Uid uid, long rawRealtimeUs) { 543 final BatteryStats.Timer timer = uid.getForegroundServiceTimer(); 544 if (timer != null) { 545 return timer.getTotalTimeLocked(rawRealtimeUs, BatteryStats.STATS_SINCE_CHARGED); 546 } 547 548 return 0; 549 } 550 isPreOApp(final String packageName)551 public boolean isPreOApp(final String packageName) { 552 try { 553 ApplicationInfo info = mPackageManager.getApplicationInfo(packageName, 554 PackageManager.GET_META_DATA); 555 556 return info.targetSdkVersion < Build.VERSION_CODES.O; 557 } catch (PackageManager.NameNotFoundException e) { 558 Log.e(TAG, "Cannot find package: " + packageName, e); 559 } 560 561 return false; 562 } 563 isPreOApp(final String[] packageNames)564 public boolean isPreOApp(final String[] packageNames) { 565 if (ArrayUtils.isEmpty(packageNames)) { 566 return false; 567 } 568 569 for (String packageName : packageNames) { 570 if (isPreOApp(packageName)) { 571 return true; 572 } 573 } 574 575 return false; 576 } 577 578 /** 579 * Return {@code true} if we should hide anomaly app represented by {@code uid} 580 */ shouldHideAnomaly(PowerWhitelistBackend powerWhitelistBackend, int uid, AnomalyInfo anomalyInfo)581 public boolean shouldHideAnomaly(PowerWhitelistBackend powerWhitelistBackend, int uid, 582 AnomalyInfo anomalyInfo) { 583 final String[] packageNames = mPackageManager.getPackagesForUid(uid); 584 if (ArrayUtils.isEmpty(packageNames)) { 585 // Don't show it if app has been uninstalled 586 return true; 587 } 588 589 return isSystemUid(uid) || powerWhitelistBackend.isWhitelisted(packageNames) 590 || (isSystemApp(mPackageManager, packageNames) && !hasLauncherEntry(packageNames)) 591 || (isExcessiveBackgroundAnomaly(anomalyInfo) && !isPreOApp(packageNames)); 592 } 593 isExcessiveBackgroundAnomaly(AnomalyInfo anomalyInfo)594 private boolean isExcessiveBackgroundAnomaly(AnomalyInfo anomalyInfo) { 595 return anomalyInfo.anomalyType 596 == StatsManagerConfig.AnomalyType.EXCESSIVE_BACKGROUND_SERVICE; 597 } 598 isSystemUid(int uid)599 private boolean isSystemUid(int uid) { 600 final int appUid = UserHandle.getAppId(uid); 601 return appUid >= Process.ROOT_UID && appUid < Process.FIRST_APPLICATION_UID; 602 } 603 isSystemApp(PackageManager packageManager, String[] packageNames)604 private boolean isSystemApp(PackageManager packageManager, String[] packageNames) { 605 for (String packageName : packageNames) { 606 try { 607 final ApplicationInfo info = packageManager.getApplicationInfo(packageName, 608 0 /* flags */); 609 if ((info.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { 610 return true; 611 } 612 } catch (PackageManager.NameNotFoundException e) { 613 Log.e(TAG, "Package not found: " + packageName, e); 614 } 615 } 616 617 return false; 618 } 619 hasLauncherEntry(String[] packageNames)620 private boolean hasLauncherEntry(String[] packageNames) { 621 final Intent launchIntent = new Intent(Intent.ACTION_MAIN, null); 622 launchIntent.addCategory(Intent.CATEGORY_LAUNCHER); 623 624 // If we do not specify MATCH_DIRECT_BOOT_AWARE or 625 // MATCH_DIRECT_BOOT_UNAWARE, system will derive and update the flags 626 // according to the user's lock state. When the user is locked, 627 // components 628 // with ComponentInfo#directBootAware == false will be filtered. We should 629 // explicitly include both direct boot aware and unaware components here. 630 final List<ResolveInfo> resolveInfos = mPackageManager.queryIntentActivities(launchIntent, 631 PackageManager.MATCH_DISABLED_COMPONENTS 632 | PackageManager.MATCH_DIRECT_BOOT_AWARE 633 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE 634 | PackageManager.MATCH_SYSTEM_ONLY); 635 for (int i = 0, size = resolveInfos.size(); i < size; i++) { 636 final ResolveInfo resolveInfo = resolveInfos.get(i); 637 if (ArrayUtils.contains(packageNames, resolveInfo.activityInfo.packageName)) { 638 return true; 639 } 640 } 641 642 return false; 643 } 644 645 /** 646 * Return version number of an app represented by {@code packageName}, and return -1 if not 647 * found. 648 */ getAppLongVersionCode(String packageName)649 public long getAppLongVersionCode(String packageName) { 650 try { 651 final PackageInfo packageInfo = mPackageManager.getPackageInfo(packageName, 652 0 /* flags */); 653 return packageInfo.getLongVersionCode(); 654 } catch (PackageManager.NameNotFoundException e) { 655 Log.e(TAG, "Cannot find package: " + packageName, e); 656 } 657 658 return -1L; 659 } 660 } 661 662