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