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 if (android.content.pm.Flags.removeHiddenModuleUsage()) { 227 return uid == UID_TETHERING ? false : uid < 0; 228 } 229 return uid == UID_TETHERING ? false : uid < 0 || isHiddenSystemModule(packages); 230 } 231 232 /** 233 * Returns true if one the specified packages belongs to a hidden system module. 234 * TODO(b/382016780): to be removed after flag cleanup. 235 */ isHiddenSystemModule(String[] packages)236 public boolean isHiddenSystemModule(String[] packages) { 237 if (packages != null) { 238 for (int i = 0, length = packages.length; i < length; i++) { 239 if (AppUtils.isHiddenSystemModule(mContext, packages[i])) { 240 return true; 241 } 242 } 243 } 244 return false; 245 } 246 247 /** 248 * Calculate the power usage percentage for an app 249 * 250 * @param powerUsageMah power used by the app 251 * @param totalPowerMah total power used in the system 252 * @param dischargeAmount The discharge amount calculated by {@link BatteryStats} 253 * @return A percentage value scaled by {@paramref dischargeAmount} 254 * @see BatteryStats#getDischargeAmount(int) 255 */ calculateBatteryPercent( double powerUsageMah, double totalPowerMah, int dischargeAmount)256 public double calculateBatteryPercent( 257 double powerUsageMah, double totalPowerMah, int dischargeAmount) { 258 if (totalPowerMah == 0) { 259 return 0; 260 } 261 262 return (powerUsageMah / totalPowerMah) * dischargeAmount; 263 } 264 265 /** 266 * Find the package name for a {@link android.os.BatteryStats.Uid} 267 * 268 * @param uid id to get the package name 269 * @return the package name. If there are multiple packages related to given id, return the 270 * first one. Or return null if there are no known packages with the given id 271 * @see PackageManager#getPackagesForUid(int) 272 */ getPackageName(int uid)273 public String getPackageName(int uid) { 274 final String[] packageNames = mPackageManager.getPackagesForUid(uid); 275 276 return ArrayUtils.isEmpty(packageNames) ? null : packageNames[0]; 277 } 278 279 /** 280 * Find the targetSdkVersion for package with name {@code packageName} 281 * 282 * @return the targetSdkVersion, or {@link #SDK_NULL} if {@code packageName} doesn't exist 283 */ getTargetSdkVersion(final String packageName)284 public int getTargetSdkVersion(final String packageName) { 285 try { 286 ApplicationInfo info = 287 mPackageManager.getApplicationInfo(packageName, PackageManager.GET_META_DATA); 288 289 return info.targetSdkVersion; 290 } catch (PackageManager.NameNotFoundException e) { 291 Log.e(TAG, "Cannot find package: " + packageName, e); 292 } 293 294 return SDK_NULL; 295 } 296 297 /** Check whether background restriction is enabled */ isBackgroundRestrictionEnabled( final int targetSdkVersion, final int uid, final String packageName)298 public boolean isBackgroundRestrictionEnabled( 299 final int targetSdkVersion, final int uid, final String packageName) { 300 if (targetSdkVersion >= Build.VERSION_CODES.O) { 301 return true; 302 } 303 final int mode = 304 mAppOpsManager.checkOpNoThrow(AppOpsManager.OP_RUN_IN_BACKGROUND, uid, packageName); 305 return mode == AppOpsManager.MODE_IGNORED || mode == AppOpsManager.MODE_ERRORED; 306 } 307 308 /** 309 * Calculate the time since last full charge, including the device off time 310 * 311 * @param batteryUsageStats class that contains the data 312 * @param currentTimeMs current wall time 313 * @return time in millis 314 */ calculateLastFullChargeTime( BatteryUsageStats batteryUsageStats, long currentTimeMs)315 public long calculateLastFullChargeTime( 316 BatteryUsageStats batteryUsageStats, long currentTimeMs) { 317 return currentTimeMs - batteryUsageStats.getStatsStartTimestamp(); 318 } 319 logRuntime(String tag, String message, long startTime)320 public static void logRuntime(String tag, String message, long startTime) { 321 Log.d(tag, message + ": " + (System.currentTimeMillis() - startTime) + "ms"); 322 } 323 324 /** Return {@code true} if battery defender is on and charging. */ isBatteryDefenderOn(BatteryInfo batteryInfo)325 public static boolean isBatteryDefenderOn(BatteryInfo batteryInfo) { 326 return batteryInfo.isBatteryDefender && !batteryInfo.discharging; 327 } 328 329 /** 330 * Find package uid from package name 331 * 332 * @param packageName used to find the uid 333 * @return uid for packageName, or {@link #UID_NULL} if exception happens or {@code packageName} 334 * is null 335 */ getPackageUid(String packageName)336 public int getPackageUid(String packageName) { 337 try { 338 return packageName == null 339 ? UID_NULL 340 : mPackageManager.getPackageUid(packageName, PackageManager.GET_META_DATA); 341 } catch (PackageManager.NameNotFoundException e) { 342 return UID_NULL; 343 } 344 } 345 346 /** 347 * Find package uid from package name 348 * 349 * @param packageName used to find the uid 350 * @param userId The user handle identifier to look up the package under 351 * @return uid for packageName, or {@link #UID_NULL} if exception happens or {@code packageName} 352 * is null 353 */ getPackageUidAsUser(String packageName, int userId)354 public int getPackageUidAsUser(String packageName, int userId) { 355 try { 356 return packageName == null 357 ? UID_NULL 358 : mPackageManager.getPackageUidAsUser( 359 packageName, PackageManager.GET_META_DATA, userId); 360 } catch (PackageManager.NameNotFoundException e) { 361 return UID_NULL; 362 } 363 } 364 365 /** 366 * Parses proto object from string. 367 * 368 * @param serializedProto the serialized proto string 369 * @param protoClass class of the proto 370 * @return instance of the proto class parsed from the string 371 */ 372 @SuppressWarnings("unchecked") parseProtoFromString( String serializedProto, T protoClass)373 public static <T extends MessageLite> T parseProtoFromString( 374 String serializedProto, T protoClass) { 375 if (serializedProto == null || serializedProto.isEmpty()) { 376 return (T) protoClass.getDefaultInstanceForType(); 377 } 378 try { 379 return (T) 380 protoClass 381 .getParserForType() 382 .parseFrom(Base64.decode(serializedProto, Base64.DEFAULT)); 383 } catch (InvalidProtocolBufferException e) { 384 Log.e(TAG, "Failed to deserialize proto class", e); 385 return (T) protoClass.getDefaultInstanceForType(); 386 } 387 } 388 389 /** Sets force app standby mode */ setForceAppStandby(int uid, String packageName, int mode)390 public void setForceAppStandby(int uid, String packageName, int mode) { 391 final boolean isPreOApp = isPreOApp(packageName); 392 if (isPreOApp) { 393 // Control whether app could run in the background if it is pre O app 394 mAppOpsManager.setMode(AppOpsManager.OP_RUN_IN_BACKGROUND, uid, packageName, mode); 395 } 396 // Notify system of reason for change 397 if (isForceAppStandbyEnabled(uid, packageName) != (mode == AppOpsManager.MODE_IGNORED)) { 398 mContext.getSystemService(ActivityManager.class).noteAppRestrictionEnabled( 399 packageName, uid, ActivityManager.RESTRICTION_LEVEL_BACKGROUND_RESTRICTED, 400 mode == AppOpsManager.MODE_IGNORED, 401 ActivityManager.RESTRICTION_REASON_USER, 402 "settings", ActivityManager.RESTRICTION_SOURCE_USER, 0L); 403 } 404 // Control whether app could run jobs in the background 405 mAppOpsManager.setMode(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, uid, packageName, mode); 406 407 ThreadUtils.postOnBackgroundThread( 408 () -> { 409 final BatteryDatabaseManager batteryDatabaseManager = 410 BatteryDatabaseManager.getInstance(mContext); 411 if (mode == AppOpsManager.MODE_IGNORED) { 412 batteryDatabaseManager.insertAction( 413 AnomalyDatabaseHelper.ActionType.RESTRICTION, 414 uid, 415 packageName, 416 System.currentTimeMillis()); 417 } else if (mode == AppOpsManager.MODE_ALLOWED) { 418 batteryDatabaseManager.deleteAction( 419 AnomalyDatabaseHelper.ActionType.RESTRICTION, uid, packageName); 420 } 421 }); 422 } 423 isForceAppStandbyEnabled(int uid, String packageName)424 public boolean isForceAppStandbyEnabled(int uid, String packageName) { 425 return mAppOpsManager.checkOpNoThrow( 426 AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, uid, packageName) 427 == AppOpsManager.MODE_IGNORED; 428 } 429 clearForceAppStandby(String packageName)430 public boolean clearForceAppStandby(String packageName) { 431 final int uid = getPackageUid(packageName); 432 if (uid != UID_NULL && isForceAppStandbyEnabled(uid, packageName)) { 433 setForceAppStandby(uid, packageName, AppOpsManager.MODE_ALLOWED); 434 return true; 435 } else { 436 return false; 437 } 438 } 439 440 @WorkerThread getBatteryInfo(final String tag)441 public BatteryInfo getBatteryInfo(final String tag) { 442 final BatteryStatsManager systemService = 443 mContext.getSystemService(BatteryStatsManager.class); 444 BatteryUsageStats batteryUsageStats; 445 try { 446 batteryUsageStats = 447 systemService.getBatteryUsageStats( 448 new BatteryUsageStatsQuery.Builder().includeBatteryHistory().build()); 449 } catch (RuntimeException e) { 450 Log.e(TAG, "getBatteryInfo() error from getBatteryUsageStats()", e); 451 // Use default BatteryUsageStats. 452 batteryUsageStats = new BatteryUsageStats.Builder(new String[0]).build(); 453 } 454 455 final long startTime = System.currentTimeMillis(); 456 457 // Stuff we always need to get BatteryInfo 458 final Intent batteryBroadcast = getBatteryIntent(mContext); 459 460 final long elapsedRealtimeUs = PowerUtil.convertMsToUs(SystemClock.elapsedRealtime()); 461 462 BatteryInfo batteryInfo; 463 Estimate estimate = getEnhancedEstimate(); 464 465 // couldn't get estimate from cache or provider, use fallback 466 if (estimate == null) { 467 estimate = 468 new Estimate( 469 batteryUsageStats.getBatteryTimeRemainingMs(), 470 false /* isBasedOnUsage */, 471 EstimateKt.AVERAGE_TIME_TO_DISCHARGE_UNKNOWN); 472 } 473 474 BatteryUtils.logRuntime(tag, "BatteryInfoLoader post query", startTime); 475 batteryInfo = 476 BatteryInfo.getBatteryInfo( 477 mContext, 478 batteryBroadcast, 479 batteryUsageStats, 480 estimate, 481 elapsedRealtimeUs, 482 false /* shortString */); 483 BatteryUtils.logRuntime(tag, "BatteryInfoLoader.loadInBackground", startTime); 484 485 try { 486 batteryUsageStats.close(); 487 } catch (Exception e) { 488 Log.e(TAG, "BatteryUsageStats.close() failed", e); 489 } 490 return batteryInfo; 491 } 492 493 @VisibleForTesting getEnhancedEstimate()494 Estimate getEnhancedEstimate() { 495 // Align the same logic in the BatteryControllerImpl.updateEstimate() 496 Estimate estimate = Estimate.getCachedEstimateIfAvailable(mContext); 497 if (estimate == null 498 && mPowerUsageFeatureProvider != null 499 && mPowerUsageFeatureProvider.isEnhancedBatteryPredictionEnabled(mContext)) { 500 estimate = mPowerUsageFeatureProvider.getEnhancedBatteryPrediction(mContext); 501 if (estimate != null) { 502 Estimate.storeCachedEstimate(mContext, estimate); 503 } 504 } 505 return estimate; 506 } 507 isDataCorrupted()508 private boolean isDataCorrupted() { 509 return mPackageManager == null || mAppOpsManager == null; 510 } 511 512 @VisibleForTesting getForegroundActivityTotalTimeUs(BatteryStats.Uid uid, long rawRealtimeUs)513 long getForegroundActivityTotalTimeUs(BatteryStats.Uid uid, long rawRealtimeUs) { 514 final BatteryStats.Timer timer = uid.getForegroundActivityTimer(); 515 if (timer != null) { 516 return timer.getTotalTimeLocked(rawRealtimeUs, BatteryStats.STATS_SINCE_CHARGED); 517 } 518 519 return 0; 520 } 521 522 @VisibleForTesting getForegroundServiceTotalTimeUs(BatteryStats.Uid uid, long rawRealtimeUs)523 long getForegroundServiceTotalTimeUs(BatteryStats.Uid uid, long rawRealtimeUs) { 524 final BatteryStats.Timer timer = uid.getForegroundServiceTimer(); 525 if (timer != null) { 526 return timer.getTotalTimeLocked(rawRealtimeUs, BatteryStats.STATS_SINCE_CHARGED); 527 } 528 529 return 0; 530 } 531 isPreOApp(final String packageName)532 public boolean isPreOApp(final String packageName) { 533 try { 534 ApplicationInfo info = 535 mPackageManager.getApplicationInfo(packageName, PackageManager.GET_META_DATA); 536 537 return info.targetSdkVersion < Build.VERSION_CODES.O; 538 } catch (PackageManager.NameNotFoundException e) { 539 Log.e(TAG, "Cannot find package: " + packageName, e); 540 } 541 542 return false; 543 } 544 isPreOApp(final String[] packageNames)545 public boolean isPreOApp(final String[] packageNames) { 546 if (ArrayUtils.isEmpty(packageNames)) { 547 return false; 548 } 549 550 for (String packageName : packageNames) { 551 if (isPreOApp(packageName)) { 552 return true; 553 } 554 } 555 556 return false; 557 } 558 559 /** 560 * Return version number of an app represented by {@code packageName}, and return -1 if not 561 * found. 562 */ getAppLongVersionCode(String packageName)563 public long getAppLongVersionCode(String packageName) { 564 try { 565 final PackageInfo packageInfo = 566 mPackageManager.getPackageInfo(packageName, 0 /* flags */); 567 return packageInfo.getLongVersionCode(); 568 } catch (PackageManager.NameNotFoundException e) { 569 Log.e(TAG, "Cannot find package: " + packageName, e); 570 } 571 572 return -1L; 573 } 574 575 /** Whether the package is installed from Google Play Store or not */ isAppInstalledFromGooglePlayStore(Context context, String packageName)576 public static boolean isAppInstalledFromGooglePlayStore(Context context, String packageName) { 577 if (TextUtils.isEmpty(packageName)) { 578 return false; 579 } 580 InstallSourceInfo installSourceInfo; 581 try { 582 installSourceInfo = context.getPackageManager().getInstallSourceInfo(packageName); 583 } catch (PackageManager.NameNotFoundException e) { 584 return false; 585 } 586 return installSourceInfo != null 587 && GOOGLE_PLAY_STORE_PACKAGE.equals(installSourceInfo.getInitiatingPackageName()); 588 } 589 590 /** Gets the logging package name. */ getLoggingPackageName(Context context, String originalPackingName)591 public static String getLoggingPackageName(Context context, String originalPackingName) { 592 return BatteryUtils.isAppInstalledFromGooglePlayStore(context, originalPackingName) 593 ? originalPackingName 594 : PACKAGE_NAME_NONE; 595 } 596 597 /** Gets the latest sticky battery intent from the Android system. */ getBatteryIntent(Context context)598 public static Intent getBatteryIntent(Context context) { 599 return com.android.settingslib.fuelgauge.BatteryUtils.getBatteryIntent(context); 600 } 601 602 /** Gets the current dock defender mode */ getCurrentDockDefenderMode(Context context, BatteryInfo batteryInfo)603 public static int getCurrentDockDefenderMode(Context context, BatteryInfo batteryInfo) { 604 if (batteryInfo.pluggedStatus == BatteryManager.BATTERY_PLUGGED_DOCK) { 605 if (Settings.Global.getInt( 606 context.getContentResolver(), SETTINGS_GLOBAL_DOCK_DEFENDER_BYPASS, 0) 607 == 1) { 608 return DockDefenderMode.TEMPORARILY_BYPASSED; 609 } else if (batteryInfo.isLongLife 610 && FeatureFactory.getFeatureFactory() 611 .getPowerUsageFeatureProvider() 612 .isExtraDefend()) { 613 return DockDefenderMode.ACTIVE; 614 } else if (!batteryInfo.isLongLife) { 615 return DockDefenderMode.FUTURE_BYPASS; 616 } 617 } 618 return DockDefenderMode.DISABLED; 619 } 620 621 /** Formats elapsed time without commas in between. */ formatElapsedTimeWithoutComma( Context context, double millis, boolean withSeconds, boolean collapseTimeUnit)622 public static CharSequence formatElapsedTimeWithoutComma( 623 Context context, double millis, boolean withSeconds, boolean collapseTimeUnit) { 624 return StringUtil.formatElapsedTime(context, millis, withSeconds, collapseTimeUnit) 625 .toString() 626 .replaceAll(",", ""); 627 } 628 629 /** Builds the battery usage time summary. */ buildBatteryUsageTimeSummary( final Context context, final boolean isSystem, final long foregroundUsageTimeInMs, final long backgroundUsageTimeInMs, final long screenOnTimeInMs)630 public static String buildBatteryUsageTimeSummary( 631 final Context context, 632 final boolean isSystem, 633 final long foregroundUsageTimeInMs, 634 final long backgroundUsageTimeInMs, 635 final long screenOnTimeInMs) { 636 StringBuilder summary = new StringBuilder(); 637 if (isSystem) { 638 final long totalUsageTimeInMs = foregroundUsageTimeInMs + backgroundUsageTimeInMs; 639 if (totalUsageTimeInMs != 0) { 640 summary.append( 641 buildBatteryUsageTimeInfo( 642 context, 643 totalUsageTimeInMs, 644 R.string.battery_usage_total_less_than_one_minute, 645 R.string.battery_usage_for_total_time)); 646 } 647 } else { 648 if (screenOnTimeInMs != 0) { 649 summary.append( 650 buildBatteryUsageTimeInfo( 651 context, 652 screenOnTimeInMs, 653 R.string.battery_usage_screen_time_less_than_one_minute, 654 R.string.battery_usage_screen_time)); 655 } 656 if (screenOnTimeInMs != 0 && backgroundUsageTimeInMs != 0) { 657 summary.append('\n'); 658 } 659 if (backgroundUsageTimeInMs != 0) { 660 summary.append( 661 buildBatteryUsageTimeInfo( 662 context, 663 backgroundUsageTimeInMs, 664 R.string.battery_usage_background_less_than_one_minute, 665 R.string.battery_usage_for_background_time)); 666 } 667 } 668 return summary.toString(); 669 } 670 671 /** Format the date of battery related info */ getBatteryInfoFormattedDate(long dateInMs)672 public static CharSequence getBatteryInfoFormattedDate(long dateInMs) { 673 final Instant instant = Instant.ofEpochMilli(dateInMs); 674 final String localDate = 675 instant.atZone(ZoneId.systemDefault()) 676 .toLocalDate() 677 .format(DateTimeFormatter.ofLocalizedDate(FormatStyle.LONG)); 678 679 return localDate; 680 } 681 682 /** Builds the battery usage time information for one timestamp. */ buildBatteryUsageTimeInfo( final Context context, long timeInMs, final int lessThanOneMinuteResId, final int normalResId)683 private static String buildBatteryUsageTimeInfo( 684 final Context context, 685 long timeInMs, 686 final int lessThanOneMinuteResId, 687 final int normalResId) { 688 if (timeInMs <= DateUtils.MINUTE_IN_MILLIS / 2) { 689 return context.getString(lessThanOneMinuteResId); 690 } 691 final CharSequence timeSequence = 692 formatElapsedTimeWithoutComma( 693 context, 694 (double) timeInMs, 695 /* withSeconds= */ false, 696 /* collapseTimeUnit= */ false); 697 return context.getString(normalResId, timeSequence); 698 } 699 } 700