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