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.pm.ApplicationInfo; 22 import android.content.pm.InstallSourceInfo; 23 import android.content.pm.PackageInfo; 24 import android.content.pm.PackageManager; 25 import android.content.pm.ResolveInfo; 26 import android.os.BatteryManager; 27 import android.os.BatteryStats; 28 import android.os.BatteryStatsManager; 29 import android.os.BatteryUsageStats; 30 import android.os.BatteryUsageStatsQuery; 31 import android.os.Build; 32 import android.os.Process; 33 import android.os.SystemClock; 34 import android.os.UidBatteryConsumer; 35 import android.os.UserHandle; 36 import android.provider.Settings; 37 import android.text.TextUtils; 38 import android.text.format.DateUtils; 39 import android.util.Base64; 40 import android.util.Log; 41 42 import androidx.annotation.IntDef; 43 import androidx.annotation.Nullable; 44 import androidx.annotation.VisibleForTesting; 45 import androidx.annotation.WorkerThread; 46 47 import com.android.internal.util.ArrayUtils; 48 import com.android.settings.R; 49 import com.android.settings.fuelgauge.batterytip.AnomalyDatabaseHelper; 50 import com.android.settings.fuelgauge.batterytip.AnomalyInfo; 51 import com.android.settings.fuelgauge.batterytip.BatteryDatabaseManager; 52 import com.android.settings.fuelgauge.batterytip.StatsManagerConfig; 53 import com.android.settings.overlay.FeatureFactory; 54 import com.android.settingslib.applications.AppUtils; 55 import com.android.settingslib.fuelgauge.Estimate; 56 import com.android.settingslib.fuelgauge.EstimateKt; 57 import com.android.settingslib.fuelgauge.PowerAllowlistBackend; 58 import com.android.settingslib.utils.PowerUtil; 59 import com.android.settingslib.utils.StringUtil; 60 import com.android.settingslib.utils.ThreadUtils; 61 62 import com.google.protobuf.InvalidProtocolBufferException; 63 import com.google.protobuf.MessageLite; 64 65 import java.lang.annotation.Retention; 66 import java.lang.annotation.RetentionPolicy; 67 import java.time.Instant; 68 import java.time.ZoneId; 69 import java.time.format.DateTimeFormatter; 70 import java.time.format.FormatStyle; 71 import java.util.List; 72 73 /** 74 * Utils for battery operation 75 */ 76 public class BatteryUtils { 77 public static final int UID_NULL = -1; 78 public static final int SDK_NULL = -1; 79 /** Special UID value for data usage by removed apps. */ 80 public static final int UID_REMOVED_APPS = -4; 81 /** Special UID value for data usage by tethering. */ 82 public static final int UID_TETHERING = -5; 83 84 /** Flag to check if the dock defender mode has been temporarily bypassed */ 85 public static final String SETTINGS_GLOBAL_DOCK_DEFENDER_BYPASS = "dock_defender_bypass"; 86 87 public static final String BYPASS_DOCK_DEFENDER_ACTION = "battery.dock.defender.bypass"; 88 89 private static final String GOOGLE_PLAY_STORE_PACKAGE = "com.android.vending"; 90 91 @Retention(RetentionPolicy.SOURCE) 92 @IntDef({StatusType.SCREEN_USAGE, 93 StatusType.FOREGROUND, 94 StatusType.BACKGROUND, 95 StatusType.ALL 96 }) 97 public @interface StatusType { 98 int SCREEN_USAGE = 0; 99 int FOREGROUND = 1; 100 int BACKGROUND = 2; 101 int ALL = 3; 102 } 103 104 @Retention(RetentionPolicy.SOURCE) 105 @IntDef({DockDefenderMode.FUTURE_BYPASS, 106 DockDefenderMode.ACTIVE, 107 DockDefenderMode.TEMPORARILY_BYPASSED, 108 DockDefenderMode.DISABLED}) 109 public @interface DockDefenderMode { 110 int FUTURE_BYPASS = 0; 111 int ACTIVE = 1; 112 int TEMPORARILY_BYPASSED = 2; 113 int DISABLED = 3; 114 } 115 116 private static final String TAG = "BatteryUtils"; 117 118 private static BatteryUtils sInstance; 119 private PackageManager mPackageManager; 120 121 private AppOpsManager mAppOpsManager; 122 private Context mContext; 123 @VisibleForTesting 124 PowerUsageFeatureProvider mPowerUsageFeatureProvider; 125 getInstance(Context context)126 public static BatteryUtils getInstance(Context context) { 127 if (sInstance == null || sInstance.isDataCorrupted()) { 128 sInstance = new BatteryUtils(context.getApplicationContext()); 129 } 130 return sInstance; 131 } 132 133 @VisibleForTesting BatteryUtils(Context context)134 public BatteryUtils(Context context) { 135 mContext = context; 136 mPackageManager = context.getPackageManager(); 137 mAppOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE); 138 mPowerUsageFeatureProvider = FeatureFactory.getFactory(context) 139 .getPowerUsageFeatureProvider(context); 140 } 141 getProcessTimeMs(@tatusType int type, @Nullable BatteryStats.Uid uid, int which)142 public long getProcessTimeMs(@StatusType int type, @Nullable BatteryStats.Uid uid, 143 int which) { 144 if (uid == null) { 145 return 0; 146 } 147 148 switch (type) { 149 case StatusType.SCREEN_USAGE: 150 return getScreenUsageTimeMs(uid, which); 151 case StatusType.FOREGROUND: 152 return getProcessForegroundTimeMs(uid, which); 153 case StatusType.BACKGROUND: 154 return getProcessBackgroundTimeMs(uid, which); 155 case StatusType.ALL: 156 return getProcessForegroundTimeMs(uid, which) 157 + getProcessBackgroundTimeMs(uid, which); 158 } 159 return 0; 160 } 161 getScreenUsageTimeMs(BatteryStats.Uid uid, int which, long rawRealTimeUs)162 private long getScreenUsageTimeMs(BatteryStats.Uid uid, int which, long rawRealTimeUs) { 163 final int foregroundTypes[] = {BatteryStats.Uid.PROCESS_STATE_TOP}; 164 Log.v(TAG, "package: " + mPackageManager.getNameForUid(uid.getUid())); 165 166 long timeUs = 0; 167 for (int type : foregroundTypes) { 168 final long localTime = uid.getProcessStateTime(type, rawRealTimeUs, which); 169 Log.v(TAG, "type: " + type + " time(us): " + localTime); 170 timeUs += localTime; 171 } 172 Log.v(TAG, "foreground time(us): " + timeUs); 173 174 // Return the min value of STATE_TOP time and foreground activity time, since both of these 175 // time have some errors 176 return PowerUtil.convertUsToMs( 177 Math.min(timeUs, getForegroundActivityTotalTimeUs(uid, rawRealTimeUs))); 178 } 179 getScreenUsageTimeMs(BatteryStats.Uid uid, int which)180 private long getScreenUsageTimeMs(BatteryStats.Uid uid, int which) { 181 final long rawRealTimeUs = PowerUtil.convertMsToUs(SystemClock.elapsedRealtime()); 182 return getScreenUsageTimeMs(uid, which, rawRealTimeUs); 183 } 184 getProcessBackgroundTimeMs(BatteryStats.Uid uid, int which)185 private long getProcessBackgroundTimeMs(BatteryStats.Uid uid, int which) { 186 final long rawRealTimeUs = PowerUtil.convertMsToUs(SystemClock.elapsedRealtime()); 187 final long timeUs = uid.getProcessStateTime( 188 BatteryStats.Uid.PROCESS_STATE_BACKGROUND, rawRealTimeUs, which); 189 190 Log.v(TAG, "package: " + mPackageManager.getNameForUid(uid.getUid())); 191 Log.v(TAG, "background time(us): " + timeUs); 192 return PowerUtil.convertUsToMs(timeUs); 193 } 194 getProcessForegroundTimeMs(BatteryStats.Uid uid, int which)195 private long getProcessForegroundTimeMs(BatteryStats.Uid uid, int which) { 196 final long rawRealTimeUs = PowerUtil.convertMsToUs(SystemClock.elapsedRealtime()); 197 return getScreenUsageTimeMs(uid, which, rawRealTimeUs) 198 + PowerUtil.convertUsToMs(getForegroundServiceTotalTimeUs(uid, rawRealTimeUs)); 199 } 200 201 /** 202 * Returns true if the specified battery consumer should be excluded from the summary 203 * battery consumption list. 204 */ shouldHideUidBatteryConsumer(UidBatteryConsumer consumer)205 public boolean shouldHideUidBatteryConsumer(UidBatteryConsumer consumer) { 206 return shouldHideUidBatteryConsumer(consumer, 207 mPackageManager.getPackagesForUid(consumer.getUid())); 208 } 209 210 /** 211 * Returns true if the specified battery consumer should be excluded from the summary 212 * battery consumption list. 213 */ shouldHideUidBatteryConsumer(UidBatteryConsumer consumer, String[] packages)214 public boolean shouldHideUidBatteryConsumer(UidBatteryConsumer consumer, String[] packages) { 215 return mPowerUsageFeatureProvider.isTypeSystem(consumer.getUid(), packages) 216 || shouldHideUidBatteryConsumerUnconditionally(consumer, packages); 217 } 218 219 /** 220 * Returns true if the specified battery consumer should be excluded from 221 * battery consumption lists, either short or full. 222 */ shouldHideUidBatteryConsumerUnconditionally(UidBatteryConsumer consumer, String[] packages)223 public boolean shouldHideUidBatteryConsumerUnconditionally(UidBatteryConsumer consumer, 224 String[] packages) { 225 final int uid = consumer.getUid(); 226 return uid == UID_TETHERING 227 ? false 228 : uid < 0 || isHiddenSystemModule(packages); 229 } 230 231 /** 232 * Returns true if one the specified packages belongs to a hidden system module. 233 */ isHiddenSystemModule(String[] packages)234 public boolean isHiddenSystemModule(String[] packages) { 235 if (packages != null) { 236 for (int i = 0, length = packages.length; i < length; i++) { 237 if (AppUtils.isHiddenSystemModule(mContext, packages[i])) { 238 return true; 239 } 240 } 241 } 242 return false; 243 } 244 245 /** 246 * Calculate the power usage percentage for an app 247 * 248 * @param powerUsageMah power used by the app 249 * @param totalPowerMah total power used in the system 250 * @param dischargeAmount The discharge amount calculated by {@link BatteryStats} 251 * @return A percentage value scaled by {@paramref dischargeAmount} 252 * @see BatteryStats#getDischargeAmount(int) 253 */ calculateBatteryPercent(double powerUsageMah, double totalPowerMah, int dischargeAmount)254 public double calculateBatteryPercent(double powerUsageMah, double totalPowerMah, 255 int dischargeAmount) { 256 if (totalPowerMah == 0) { 257 return 0; 258 } 259 260 return (powerUsageMah / totalPowerMah) * dischargeAmount; 261 } 262 263 /** 264 * Find the package name for a {@link android.os.BatteryStats.Uid} 265 * 266 * @param uid id to get the package name 267 * @return the package name. If there are multiple packages related to 268 * given id, return the first one. Or return null if there are no known 269 * packages with the given id 270 * @see PackageManager#getPackagesForUid(int) 271 */ getPackageName(int uid)272 public String getPackageName(int uid) { 273 final String[] packageNames = mPackageManager.getPackagesForUid(uid); 274 275 return ArrayUtils.isEmpty(packageNames) ? null : packageNames[0]; 276 } 277 278 /** 279 * Find the targetSdkVersion for package with name {@code packageName} 280 * 281 * @return the targetSdkVersion, or {@link #SDK_NULL} if {@code packageName} doesn't exist 282 */ getTargetSdkVersion(final String packageName)283 public int getTargetSdkVersion(final String packageName) { 284 try { 285 ApplicationInfo info = mPackageManager.getApplicationInfo(packageName, 286 PackageManager.GET_META_DATA); 287 288 return info.targetSdkVersion; 289 } catch (PackageManager.NameNotFoundException e) { 290 Log.e(TAG, "Cannot find package: " + packageName, e); 291 } 292 293 return SDK_NULL; 294 } 295 296 /** 297 * Check whether background restriction is enabled 298 */ isBackgroundRestrictionEnabled(final int targetSdkVersion, final int uid, final String packageName)299 public boolean isBackgroundRestrictionEnabled(final int targetSdkVersion, final int uid, 300 final String packageName) { 301 if (targetSdkVersion >= Build.VERSION_CODES.O) { 302 return true; 303 } 304 final int mode = mAppOpsManager 305 .checkOpNoThrow(AppOpsManager.OP_RUN_IN_BACKGROUND, uid, packageName); 306 return mode == AppOpsManager.MODE_IGNORED || mode == AppOpsManager.MODE_ERRORED; 307 } 308 309 /** 310 * Calculate the time since last full charge, including the device off time 311 * 312 * @param batteryUsageStats class that contains the data 313 * @param currentTimeMs current wall time 314 * @return time in millis 315 */ calculateLastFullChargeTime(BatteryUsageStats batteryUsageStats, long currentTimeMs)316 public long calculateLastFullChargeTime(BatteryUsageStats batteryUsageStats, 317 long currentTimeMs) { 318 return currentTimeMs - batteryUsageStats.getStatsStartTimestamp(); 319 } 320 logRuntime(String tag, String message, long startTime)321 public static void logRuntime(String tag, String message, long startTime) { 322 Log.d(tag, message + ": " + (System.currentTimeMillis() - startTime) + "ms"); 323 } 324 325 /** 326 * Return {@code true} if battery defender is on and charging. 327 */ isBatteryDefenderOn(BatteryInfo batteryInfo)328 public static boolean isBatteryDefenderOn(BatteryInfo batteryInfo) { 329 return batteryInfo.isBatteryDefender && !batteryInfo.discharging; 330 } 331 332 /** 333 * Find package uid from package name 334 * 335 * @param packageName used to find the uid 336 * @return uid for packageName, or {@link #UID_NULL} if exception happens or 337 * {@code packageName} is null 338 */ getPackageUid(String packageName)339 public int getPackageUid(String packageName) { 340 try { 341 return packageName == null ? UID_NULL : mPackageManager.getPackageUid(packageName, 342 PackageManager.GET_META_DATA); 343 } catch (PackageManager.NameNotFoundException e) { 344 return UID_NULL; 345 } 346 } 347 348 /** 349 * Parses proto object from string. 350 * 351 * @param serializedProto the serialized proto string 352 * @param protoClass class of the proto 353 * @return instance of the proto class parsed from the string 354 */ 355 @SuppressWarnings("unchecked") parseProtoFromString( String serializedProto, T protoClass)356 public static <T extends MessageLite> T parseProtoFromString( 357 String serializedProto, T protoClass) { 358 if (serializedProto == null || serializedProto.isEmpty()) { 359 return (T) protoClass.getDefaultInstanceForType(); 360 } 361 try { 362 return (T) protoClass.getParserForType() 363 .parseFrom(Base64.decode(serializedProto, Base64.DEFAULT)); 364 } catch (InvalidProtocolBufferException e) { 365 Log.e(TAG, "Failed to deserialize proto class", e); 366 return (T) protoClass.getDefaultInstanceForType(); 367 } 368 } 369 setForceAppStandby(int uid, String packageName, int mode)370 public void setForceAppStandby(int uid, String packageName, 371 int mode) { 372 final boolean isPreOApp = isPreOApp(packageName); 373 if (isPreOApp) { 374 // Control whether app could run in the background if it is pre O app 375 mAppOpsManager.setMode(AppOpsManager.OP_RUN_IN_BACKGROUND, uid, packageName, mode); 376 } 377 // Control whether app could run jobs in the background 378 mAppOpsManager.setMode(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, uid, packageName, mode); 379 380 ThreadUtils.postOnBackgroundThread(() -> { 381 final BatteryDatabaseManager batteryDatabaseManager = BatteryDatabaseManager 382 .getInstance(mContext); 383 if (mode == AppOpsManager.MODE_IGNORED) { 384 batteryDatabaseManager.insertAction(AnomalyDatabaseHelper.ActionType.RESTRICTION, 385 uid, packageName, System.currentTimeMillis()); 386 } else if (mode == AppOpsManager.MODE_ALLOWED) { 387 batteryDatabaseManager.deleteAction(AnomalyDatabaseHelper.ActionType.RESTRICTION, 388 uid, packageName); 389 } 390 }); 391 } 392 isForceAppStandbyEnabled(int uid, String packageName)393 public boolean isForceAppStandbyEnabled(int uid, String packageName) { 394 return mAppOpsManager.checkOpNoThrow(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, uid, 395 packageName) == AppOpsManager.MODE_IGNORED; 396 } 397 clearForceAppStandby(String packageName)398 public boolean clearForceAppStandby(String packageName) { 399 final int uid = getPackageUid(packageName); 400 if (uid != UID_NULL && isForceAppStandbyEnabled(uid, packageName)) { 401 setForceAppStandby(uid, packageName, AppOpsManager.MODE_ALLOWED); 402 return true; 403 } else { 404 return false; 405 } 406 } 407 408 @WorkerThread getBatteryInfo(final String tag)409 public BatteryInfo getBatteryInfo(final String tag) { 410 final BatteryStatsManager systemService = mContext.getSystemService( 411 BatteryStatsManager.class); 412 BatteryUsageStats batteryUsageStats; 413 try { 414 batteryUsageStats = systemService.getBatteryUsageStats( 415 new BatteryUsageStatsQuery.Builder().includeBatteryHistory().build()); 416 } catch (RuntimeException e) { 417 Log.e(TAG, "getBatteryInfo() error from getBatteryUsageStats()", e); 418 // Use default BatteryUsageStats. 419 batteryUsageStats = new BatteryUsageStats.Builder(new String[0]).build(); 420 } 421 422 final long startTime = System.currentTimeMillis(); 423 424 // Stuff we always need to get BatteryInfo 425 final Intent batteryBroadcast = getBatteryIntent(mContext); 426 427 final long elapsedRealtimeUs = PowerUtil.convertMsToUs( 428 SystemClock.elapsedRealtime()); 429 430 BatteryInfo batteryInfo; 431 Estimate estimate = getEnhancedEstimate(); 432 433 // couldn't get estimate from cache or provider, use fallback 434 if (estimate == null) { 435 estimate = new Estimate( 436 batteryUsageStats.getBatteryTimeRemainingMs(), 437 false /* isBasedOnUsage */, 438 EstimateKt.AVERAGE_TIME_TO_DISCHARGE_UNKNOWN); 439 } 440 441 BatteryUtils.logRuntime(tag, "BatteryInfoLoader post query", startTime); 442 batteryInfo = BatteryInfo.getBatteryInfo(mContext, batteryBroadcast, 443 batteryUsageStats, estimate, elapsedRealtimeUs, false /* shortString */); 444 BatteryUtils.logRuntime(tag, "BatteryInfoLoader.loadInBackground", startTime); 445 446 try { 447 batteryUsageStats.close(); 448 } catch (Exception e) { 449 Log.e(TAG, "BatteryUsageStats.close() failed", e); 450 } 451 return batteryInfo; 452 } 453 454 @VisibleForTesting getEnhancedEstimate()455 Estimate getEnhancedEstimate() { 456 // Align the same logic in the BatteryControllerImpl.updateEstimate() 457 Estimate estimate = Estimate.getCachedEstimateIfAvailable(mContext); 458 if (estimate == null && 459 mPowerUsageFeatureProvider != null && 460 mPowerUsageFeatureProvider.isEnhancedBatteryPredictionEnabled(mContext)) { 461 estimate = mPowerUsageFeatureProvider.getEnhancedBatteryPrediction(mContext); 462 if (estimate != null) { 463 Estimate.storeCachedEstimate(mContext, estimate); 464 } 465 } 466 return estimate; 467 } 468 isDataCorrupted()469 private boolean isDataCorrupted() { 470 return mPackageManager == null || mAppOpsManager == null; 471 } 472 473 @VisibleForTesting getForegroundActivityTotalTimeUs(BatteryStats.Uid uid, long rawRealtimeUs)474 long getForegroundActivityTotalTimeUs(BatteryStats.Uid uid, long rawRealtimeUs) { 475 final BatteryStats.Timer timer = uid.getForegroundActivityTimer(); 476 if (timer != null) { 477 return timer.getTotalTimeLocked(rawRealtimeUs, BatteryStats.STATS_SINCE_CHARGED); 478 } 479 480 return 0; 481 } 482 483 @VisibleForTesting getForegroundServiceTotalTimeUs(BatteryStats.Uid uid, long rawRealtimeUs)484 long getForegroundServiceTotalTimeUs(BatteryStats.Uid uid, long rawRealtimeUs) { 485 final BatteryStats.Timer timer = uid.getForegroundServiceTimer(); 486 if (timer != null) { 487 return timer.getTotalTimeLocked(rawRealtimeUs, BatteryStats.STATS_SINCE_CHARGED); 488 } 489 490 return 0; 491 } 492 isPreOApp(final String packageName)493 public boolean isPreOApp(final String packageName) { 494 try { 495 ApplicationInfo info = mPackageManager.getApplicationInfo(packageName, 496 PackageManager.GET_META_DATA); 497 498 return info.targetSdkVersion < Build.VERSION_CODES.O; 499 } catch (PackageManager.NameNotFoundException e) { 500 Log.e(TAG, "Cannot find package: " + packageName, e); 501 } 502 503 return false; 504 } 505 isPreOApp(final String[] packageNames)506 public boolean isPreOApp(final String[] packageNames) { 507 if (ArrayUtils.isEmpty(packageNames)) { 508 return false; 509 } 510 511 for (String packageName : packageNames) { 512 if (isPreOApp(packageName)) { 513 return true; 514 } 515 } 516 517 return false; 518 } 519 520 /** 521 * Return {@code true} if we should hide anomaly app represented by {@code uid} 522 */ shouldHideAnomaly(PowerAllowlistBackend powerAllowlistBackend, int uid, AnomalyInfo anomalyInfo)523 public boolean shouldHideAnomaly(PowerAllowlistBackend powerAllowlistBackend, int uid, 524 AnomalyInfo anomalyInfo) { 525 final String[] packageNames = mPackageManager.getPackagesForUid(uid); 526 if (ArrayUtils.isEmpty(packageNames)) { 527 // Don't show it if app has been uninstalled 528 return true; 529 } 530 531 return isSystemUid(uid) || powerAllowlistBackend.isAllowlisted(packageNames, uid) 532 || (isSystemApp(mPackageManager, packageNames) && !hasLauncherEntry(packageNames)) 533 || (isExcessiveBackgroundAnomaly(anomalyInfo) && !isPreOApp(packageNames)); 534 } 535 isExcessiveBackgroundAnomaly(AnomalyInfo anomalyInfo)536 private boolean isExcessiveBackgroundAnomaly(AnomalyInfo anomalyInfo) { 537 return anomalyInfo.anomalyType 538 == StatsManagerConfig.AnomalyType.EXCESSIVE_BACKGROUND_SERVICE; 539 } 540 isSystemUid(int uid)541 private boolean isSystemUid(int uid) { 542 final int appUid = UserHandle.getAppId(uid); 543 return appUid >= Process.ROOT_UID && appUid < Process.FIRST_APPLICATION_UID; 544 } 545 isSystemApp(PackageManager packageManager, String[] packageNames)546 private boolean isSystemApp(PackageManager packageManager, String[] packageNames) { 547 for (String packageName : packageNames) { 548 try { 549 final ApplicationInfo info = packageManager.getApplicationInfo(packageName, 550 0 /* flags */); 551 if ((info.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { 552 return true; 553 } 554 } catch (PackageManager.NameNotFoundException e) { 555 Log.e(TAG, "Package not found: " + packageName, e); 556 } 557 } 558 559 return false; 560 } 561 hasLauncherEntry(String[] packageNames)562 private boolean hasLauncherEntry(String[] packageNames) { 563 final Intent launchIntent = new Intent(Intent.ACTION_MAIN, null); 564 launchIntent.addCategory(Intent.CATEGORY_LAUNCHER); 565 566 // If we do not specify MATCH_DIRECT_BOOT_AWARE or 567 // MATCH_DIRECT_BOOT_UNAWARE, system will derive and update the flags 568 // according to the user's lock state. When the user is locked, 569 // components 570 // with ComponentInfo#directBootAware == false will be filtered. We should 571 // explicitly include both direct boot aware and unaware components here. 572 final List<ResolveInfo> resolveInfos = mPackageManager.queryIntentActivities(launchIntent, 573 PackageManager.MATCH_DISABLED_COMPONENTS 574 | PackageManager.MATCH_DIRECT_BOOT_AWARE 575 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE 576 | PackageManager.MATCH_SYSTEM_ONLY); 577 for (int i = 0, size = resolveInfos.size(); i < size; i++) { 578 final ResolveInfo resolveInfo = resolveInfos.get(i); 579 if (ArrayUtils.contains(packageNames, resolveInfo.activityInfo.packageName)) { 580 return true; 581 } 582 } 583 584 return false; 585 } 586 587 /** 588 * Return version number of an app represented by {@code packageName}, and return -1 if not 589 * found. 590 */ getAppLongVersionCode(String packageName)591 public long getAppLongVersionCode(String packageName) { 592 try { 593 final PackageInfo packageInfo = mPackageManager.getPackageInfo(packageName, 594 0 /* flags */); 595 return packageInfo.getLongVersionCode(); 596 } catch (PackageManager.NameNotFoundException e) { 597 Log.e(TAG, "Cannot find package: " + packageName, e); 598 } 599 600 return -1L; 601 } 602 603 /** Whether the package is installed from Google Play Store or not */ isAppInstalledFromGooglePlayStore(Context context, String packageName)604 public static boolean isAppInstalledFromGooglePlayStore(Context context, String packageName) { 605 if (TextUtils.isEmpty(packageName)) { 606 return false; 607 } 608 InstallSourceInfo installSourceInfo; 609 try { 610 installSourceInfo = context.getPackageManager().getInstallSourceInfo(packageName); 611 } catch (PackageManager.NameNotFoundException e) { 612 return false; 613 } 614 return installSourceInfo != null 615 && GOOGLE_PLAY_STORE_PACKAGE.equals(installSourceInfo.getInitiatingPackageName()); 616 } 617 618 /** Gets the latest sticky battery intent from the Android system. */ getBatteryIntent(Context context)619 public static Intent getBatteryIntent(Context context) { 620 return com.android.settingslib.fuelgauge.BatteryUtils.getBatteryIntent(context); 621 } 622 623 /** Gets the current dock defender mode */ getCurrentDockDefenderMode(Context context, BatteryInfo batteryInfo)624 public static int getCurrentDockDefenderMode(Context context, BatteryInfo batteryInfo) { 625 if (batteryInfo.pluggedStatus == BatteryManager.BATTERY_PLUGGED_DOCK) { 626 if (Settings.Global.getInt(context.getContentResolver(), 627 SETTINGS_GLOBAL_DOCK_DEFENDER_BYPASS, 0) == 1) { 628 return DockDefenderMode.TEMPORARILY_BYPASSED; 629 } else if (batteryInfo.isBatteryDefender && FeatureFactory.getFactory(context) 630 .getPowerUsageFeatureProvider(context) 631 .isExtraDefend()) { 632 return DockDefenderMode.ACTIVE; 633 } else if (!batteryInfo.isBatteryDefender) { 634 return DockDefenderMode.FUTURE_BYPASS; 635 } 636 } 637 return DockDefenderMode.DISABLED; 638 } 639 640 /** Formats elapsed time without commas in between. */ formatElapsedTimeWithoutComma( Context context, double millis, boolean withSeconds, boolean collapseTimeUnit)641 public static CharSequence formatElapsedTimeWithoutComma( 642 Context context, double millis, boolean withSeconds, boolean collapseTimeUnit) { 643 return StringUtil.formatElapsedTime(context, millis, withSeconds, collapseTimeUnit) 644 .toString().replaceAll(",", ""); 645 } 646 647 /** Builds the battery usage time summary. */ buildBatteryUsageTimeSummary(final Context context, final boolean isSystem, final long foregroundUsageTimeInMs, final long backgroundUsageTimeInMs, final long screenOnTimeInMs)648 public static String buildBatteryUsageTimeSummary(final Context context, final boolean isSystem, 649 final long foregroundUsageTimeInMs, final long backgroundUsageTimeInMs, 650 final long screenOnTimeInMs) { 651 StringBuilder summary = new StringBuilder(); 652 if (isSystem) { 653 final long totalUsageTimeInMs = foregroundUsageTimeInMs + backgroundUsageTimeInMs; 654 if (totalUsageTimeInMs != 0) { 655 summary.append(buildBatteryUsageTimeInfo(context, totalUsageTimeInMs, 656 R.string.battery_usage_total_less_than_one_minute, 657 R.string.battery_usage_for_total_time)); 658 } 659 } else { 660 if (screenOnTimeInMs != 0) { 661 summary.append(buildBatteryUsageTimeInfo(context, screenOnTimeInMs, 662 R.string.battery_usage_screen_time_less_than_one_minute, 663 R.string.battery_usage_screen_time)); 664 } 665 if (screenOnTimeInMs != 0 && backgroundUsageTimeInMs != 0) { 666 summary.append('\n'); 667 } 668 if (backgroundUsageTimeInMs != 0) { 669 summary.append(buildBatteryUsageTimeInfo(context, backgroundUsageTimeInMs, 670 R.string.battery_usage_background_less_than_one_minute, 671 R.string.battery_usage_for_background_time)); 672 } 673 } 674 return summary.toString(); 675 } 676 /** Format the date of battery related info */ getBatteryInfoFormattedDate(long dateInMs)677 public static CharSequence getBatteryInfoFormattedDate(long dateInMs) { 678 final Instant instant = Instant.ofEpochMilli(dateInMs); 679 final String localDate = instant.atZone(ZoneId.systemDefault()).toLocalDate().format( 680 DateTimeFormatter.ofLocalizedDate(FormatStyle.LONG)); 681 682 return localDate; 683 } 684 685 /** Builds the battery usage time information for one timestamp. */ buildBatteryUsageTimeInfo(final Context context, long timeInMs, final int lessThanOneMinuteResId, final int normalResId)686 private static String buildBatteryUsageTimeInfo(final Context context, long timeInMs, 687 final int lessThanOneMinuteResId, final int normalResId) { 688 if (timeInMs < DateUtils.MINUTE_IN_MILLIS) { 689 return context.getString(lessThanOneMinuteResId); 690 } 691 final CharSequence timeSequence = formatElapsedTimeWithoutComma( 692 context, (double) timeInMs, /*withSeconds=*/ false, /*collapseTimeUnit=*/ false); 693 return context.getString(normalResId, timeSequence); 694 } 695 } 696