• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.android.settings.fuelgauge;
17 
18 import android.app.AppOpsManager;
19 import android.content.Context;
20 import android.content.Intent;
21 import android.content.IntentFilter;
22 import android.content.pm.ApplicationInfo;
23 import android.content.pm.PackageInfo;
24 import android.content.pm.PackageManager;
25 import android.content.pm.ResolveInfo;
26 import android.os.BatteryStats;
27 import android.os.Bundle;
28 import android.os.Build;
29 import android.os.Process;
30 import android.os.SystemClock;
31 import android.os.UserHandle;
32 import android.os.UserManager;
33 import android.support.annotation.IntDef;
34 import android.support.annotation.Nullable;
35 import android.support.annotation.StringRes;
36 import android.support.annotation.VisibleForTesting;
37 import android.support.annotation.WorkerThread;
38 import android.text.TextUtils;
39 import android.text.format.DateUtils;
40 import android.util.Log;
41 import android.util.SparseLongArray;
42 
43 import com.android.internal.os.BatterySipper;
44 import com.android.internal.os.BatteryStatsHelper;
45 import com.android.internal.util.ArrayUtils;
46 import com.android.settings.R;
47 import com.android.settings.fuelgauge.anomaly.Anomaly;
48 import com.android.settings.fuelgauge.batterytip.AnomalyInfo;
49 import com.android.settings.fuelgauge.batterytip.StatsManagerConfig;
50 import com.android.settings.overlay.FeatureFactory;
51 
52 import com.android.settingslib.fuelgauge.PowerWhitelistBackend;
53 import com.android.settingslib.utils.PowerUtil;
54 
55 import java.lang.annotation.Retention;
56 import java.lang.annotation.RetentionPolicy;
57 import java.util.Collections;
58 import java.util.Comparator;
59 import java.util.List;
60 
61 /**
62  * Utils for battery operation
63  */
64 public class BatteryUtils {
65     public static final int UID_NULL = -1;
66     public static final int SDK_NULL = -1;
67 
68     @Retention(RetentionPolicy.SOURCE)
69     @IntDef({StatusType.SCREEN_USAGE,
70             StatusType.FOREGROUND,
71             StatusType.BACKGROUND,
72             StatusType.ALL
73     })
74     public @interface StatusType {
75         int SCREEN_USAGE = 0;
76         int FOREGROUND = 1;
77         int BACKGROUND = 2;
78         int ALL = 3;
79     }
80 
81     private static final String TAG = "BatteryUtils";
82 
83     private static final int MIN_POWER_THRESHOLD_MILLI_AMP = 5;
84 
85     private static final int SECONDS_IN_HOUR = 60 * 60;
86     private static BatteryUtils sInstance;
87     private PackageManager mPackageManager;
88 
89     private AppOpsManager mAppOpsManager;
90     private Context mContext;
91     @VisibleForTesting
92     PowerUsageFeatureProvider mPowerUsageFeatureProvider;
93 
getInstance(Context context)94     public static BatteryUtils getInstance(Context context) {
95         if (sInstance == null || sInstance.isDataCorrupted()) {
96             sInstance = new BatteryUtils(context);
97         }
98         return sInstance;
99     }
100 
101     @VisibleForTesting
BatteryUtils(Context context)102     BatteryUtils(Context context) {
103         mContext = context.getApplicationContext();
104         mPackageManager = context.getPackageManager();
105         mAppOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
106         mPowerUsageFeatureProvider = FeatureFactory.getFactory(
107                 context).getPowerUsageFeatureProvider(context);
108     }
109 
getProcessTimeMs(@tatusType int type, @Nullable BatteryStats.Uid uid, int which)110     public long getProcessTimeMs(@StatusType int type, @Nullable BatteryStats.Uid uid,
111             int which) {
112         if (uid == null) {
113             return 0;
114         }
115 
116         switch (type) {
117             case StatusType.SCREEN_USAGE:
118                 return getScreenUsageTimeMs(uid, which);
119             case StatusType.FOREGROUND:
120                 return getProcessForegroundTimeMs(uid, which);
121             case StatusType.BACKGROUND:
122                 return getProcessBackgroundTimeMs(uid, which);
123             case StatusType.ALL:
124                 return getProcessForegroundTimeMs(uid, which)
125                         + getProcessBackgroundTimeMs(uid, which);
126         }
127         return 0;
128     }
129 
getScreenUsageTimeMs(BatteryStats.Uid uid, int which, long rawRealTimeUs)130     private long getScreenUsageTimeMs(BatteryStats.Uid uid, int which, long rawRealTimeUs) {
131         final int foregroundTypes[] = {BatteryStats.Uid.PROCESS_STATE_TOP};
132         Log.v(TAG, "package: " + mPackageManager.getNameForUid(uid.getUid()));
133 
134         long timeUs = 0;
135         for (int type : foregroundTypes) {
136             final long localTime = uid.getProcessStateTime(type, rawRealTimeUs, which);
137             Log.v(TAG, "type: " + type + " time(us): " + localTime);
138             timeUs += localTime;
139         }
140         Log.v(TAG, "foreground time(us): " + timeUs);
141 
142         // Return the min value of STATE_TOP time and foreground activity time, since both of these
143         // time have some errors
144         return PowerUtil.convertUsToMs(
145                 Math.min(timeUs, getForegroundActivityTotalTimeUs(uid, rawRealTimeUs)));
146     }
147 
getScreenUsageTimeMs(BatteryStats.Uid uid, int which)148     private long getScreenUsageTimeMs(BatteryStats.Uid uid, int which) {
149         final long rawRealTimeUs = PowerUtil.convertMsToUs(SystemClock.elapsedRealtime());
150         return getScreenUsageTimeMs(uid, which, rawRealTimeUs);
151     }
152 
getProcessBackgroundTimeMs(BatteryStats.Uid uid, int which)153     private long getProcessBackgroundTimeMs(BatteryStats.Uid uid, int which) {
154         final long rawRealTimeUs = PowerUtil.convertMsToUs(SystemClock.elapsedRealtime());
155         final long timeUs = uid.getProcessStateTime(
156                 BatteryStats.Uid.PROCESS_STATE_BACKGROUND, rawRealTimeUs, which);
157 
158         Log.v(TAG, "package: " + mPackageManager.getNameForUid(uid.getUid()));
159         Log.v(TAG, "background time(us): " + timeUs);
160         return PowerUtil.convertUsToMs(timeUs);
161     }
162 
getProcessForegroundTimeMs(BatteryStats.Uid uid, int which)163     private long getProcessForegroundTimeMs(BatteryStats.Uid uid, int which) {
164         final long rawRealTimeUs = PowerUtil.convertMsToUs(SystemClock.elapsedRealtime());
165         return getScreenUsageTimeMs(uid, which, rawRealTimeUs)
166                 + PowerUtil.convertUsToMs(getForegroundServiceTotalTimeUs(uid, rawRealTimeUs));
167     }
168 
169     /**
170      * Remove the {@link BatterySipper} that we should hide and smear the screen usage based on
171      * foreground activity time.
172      *
173      * @param sippers sipper list that need to check and remove
174      * @return the total power of the hidden items of {@link BatterySipper}
175      * for proportional smearing
176      */
removeHiddenBatterySippers(List<BatterySipper> sippers)177     public double removeHiddenBatterySippers(List<BatterySipper> sippers) {
178         double proportionalSmearPowerMah = 0;
179         BatterySipper screenSipper = null;
180         for (int i = sippers.size() - 1; i >= 0; i--) {
181             final BatterySipper sipper = sippers.get(i);
182             if (shouldHideSipper(sipper)) {
183                 sippers.remove(i);
184                 if (sipper.drainType != BatterySipper.DrainType.OVERCOUNTED
185                         && sipper.drainType != BatterySipper.DrainType.SCREEN
186                         && sipper.drainType != BatterySipper.DrainType.UNACCOUNTED
187                         && sipper.drainType != BatterySipper.DrainType.BLUETOOTH
188                         && sipper.drainType != BatterySipper.DrainType.WIFI
189                         && sipper.drainType != BatterySipper.DrainType.IDLE) {
190                     // Don't add it if it is overcounted, unaccounted, wifi, bluetooth, or screen
191                     proportionalSmearPowerMah += sipper.totalPowerMah;
192                 }
193             }
194 
195             if (sipper.drainType == BatterySipper.DrainType.SCREEN) {
196                 screenSipper = sipper;
197             }
198         }
199 
200         smearScreenBatterySipper(sippers, screenSipper);
201 
202         return proportionalSmearPowerMah;
203     }
204 
205     /**
206      * Smear the screen on power usage among {@code sippers}, based on ratio of foreground activity
207      * time.
208      */
209     @VisibleForTesting
smearScreenBatterySipper(List<BatterySipper> sippers, BatterySipper screenSipper)210     void smearScreenBatterySipper(List<BatterySipper> sippers, BatterySipper screenSipper) {
211         long totalActivityTimeMs = 0;
212         final SparseLongArray activityTimeArray = new SparseLongArray();
213         for (int i = 0, size = sippers.size(); i < size; i++) {
214             final BatteryStats.Uid uid = sippers.get(i).uidObj;
215             if (uid != null) {
216                 final long timeMs = getProcessTimeMs(StatusType.SCREEN_USAGE, uid,
217                         BatteryStats.STATS_SINCE_CHARGED);
218                 activityTimeArray.put(uid.getUid(), timeMs);
219                 totalActivityTimeMs += timeMs;
220             }
221         }
222 
223         if (totalActivityTimeMs >= 10 * DateUtils.MINUTE_IN_MILLIS) {
224             if (screenSipper == null) {
225                 Log.e(TAG, "screen sipper is null even when app screen time is not zero");
226                 return;
227             }
228 
229             final double screenPowerMah = screenSipper.totalPowerMah;
230             for (int i = 0, size = sippers.size(); i < size; i++) {
231                 final BatterySipper sipper = sippers.get(i);
232                 sipper.totalPowerMah += screenPowerMah * activityTimeArray.get(sipper.getUid(), 0)
233                         / totalActivityTimeMs;
234             }
235         }
236     }
237 
238     /**
239      * Check whether we should hide the battery sipper.
240      */
shouldHideSipper(BatterySipper sipper)241     public boolean shouldHideSipper(BatterySipper sipper) {
242         final BatterySipper.DrainType drainType = sipper.drainType;
243 
244         return drainType == BatterySipper.DrainType.IDLE
245                 || drainType == BatterySipper.DrainType.CELL
246                 || drainType == BatterySipper.DrainType.SCREEN
247                 || drainType == BatterySipper.DrainType.UNACCOUNTED
248                 || drainType == BatterySipper.DrainType.OVERCOUNTED
249                 || drainType == BatterySipper.DrainType.BLUETOOTH
250                 || drainType == BatterySipper.DrainType.WIFI
251                 || (sipper.totalPowerMah * SECONDS_IN_HOUR) < MIN_POWER_THRESHOLD_MILLI_AMP
252                 || mPowerUsageFeatureProvider.isTypeService(sipper)
253                 || mPowerUsageFeatureProvider.isTypeSystem(sipper);
254     }
255 
256     /**
257      * Calculate the power usage percentage for an app
258      *
259      * @param powerUsageMah   power used by the app
260      * @param totalPowerMah   total power used in the system
261      * @param hiddenPowerMah  power used by no-actionable app that we want to hide, i.e. Screen,
262      *                        Android OS.
263      * @param dischargeAmount The discharge amount calculated by {@link BatteryStats}
264      * @return A percentage value scaled by {@paramref dischargeAmount}
265      * @see BatteryStats#getDischargeAmount(int)
266      */
calculateBatteryPercent(double powerUsageMah, double totalPowerMah, double hiddenPowerMah, int dischargeAmount)267     public double calculateBatteryPercent(double powerUsageMah, double totalPowerMah,
268             double hiddenPowerMah, int dischargeAmount) {
269         if (totalPowerMah == 0) {
270             return 0;
271         }
272 
273         return (powerUsageMah / (totalPowerMah - hiddenPowerMah)) * dischargeAmount;
274     }
275 
276     /**
277      * Calculate the whole running time in the state {@code statsType}
278      *
279      * @param batteryStatsHelper utility class that contains the data
280      * @param statsType          state that we want to calculate the time for
281      * @return the running time in millis
282      */
calculateRunningTimeBasedOnStatsType(BatteryStatsHelper batteryStatsHelper, int statsType)283     public long calculateRunningTimeBasedOnStatsType(BatteryStatsHelper batteryStatsHelper,
284             int statsType) {
285         final long elapsedRealtimeUs = PowerUtil.convertMsToUs(
286                 SystemClock.elapsedRealtime());
287         // Return the battery time (millisecond) on status mStatsType
288         return PowerUtil.convertUsToMs(
289                 batteryStatsHelper.getStats().computeBatteryRealtime(elapsedRealtimeUs, statsType));
290 
291     }
292 
293     /**
294      * Find the package name for a {@link android.os.BatteryStats.Uid}
295      *
296      * @param uid id to get the package name
297      * @return the package name. If there are multiple packages related to
298      * given id, return the first one. Or return null if there are no known
299      * packages with the given id
300      * @see PackageManager#getPackagesForUid(int)
301      */
getPackageName(int uid)302     public String getPackageName(int uid) {
303         final String[] packageNames = mPackageManager.getPackagesForUid(uid);
304 
305         return ArrayUtils.isEmpty(packageNames) ? null : packageNames[0];
306     }
307 
308     /**
309      * Find the targetSdkVersion for package with name {@code packageName}
310      *
311      * @return the targetSdkVersion, or {@link #SDK_NULL} if {@code packageName} doesn't exist
312      */
getTargetSdkVersion(final String packageName)313     public int getTargetSdkVersion(final String packageName) {
314         try {
315             ApplicationInfo info = mPackageManager.getApplicationInfo(packageName,
316                     PackageManager.GET_META_DATA);
317 
318             return info.targetSdkVersion;
319         } catch (PackageManager.NameNotFoundException e) {
320             Log.e(TAG, "Cannot find package: " + packageName, e);
321         }
322 
323         return SDK_NULL;
324     }
325 
326     /**
327      * Check whether background restriction is enabled
328      */
isBackgroundRestrictionEnabled(final int targetSdkVersion, final int uid, final String packageName)329     public boolean isBackgroundRestrictionEnabled(final int targetSdkVersion, final int uid,
330             final String packageName) {
331         if (targetSdkVersion >= Build.VERSION_CODES.O) {
332             return true;
333         }
334         final int mode = mAppOpsManager
335                 .checkOpNoThrow(AppOpsManager.OP_RUN_IN_BACKGROUND, uid, packageName);
336         return mode == AppOpsManager.MODE_IGNORED || mode == AppOpsManager.MODE_ERRORED;
337     }
338 
339     /**
340      * Sort the {@code usageList} based on {@link BatterySipper#totalPowerMah}
341      */
sortUsageList(List<BatterySipper> usageList)342     public void sortUsageList(List<BatterySipper> usageList) {
343         Collections.sort(usageList, new Comparator<BatterySipper>() {
344             @Override
345             public int compare(BatterySipper a, BatterySipper b) {
346                 return Double.compare(b.totalPowerMah, a.totalPowerMah);
347             }
348         });
349     }
350 
351     /**
352      * Calculate the time since last full charge, including the device off time
353      *
354      * @param batteryStatsHelper utility class that contains the data
355      * @param currentTimeMs      current wall time
356      * @return time in millis
357      */
calculateLastFullChargeTime(BatteryStatsHelper batteryStatsHelper, long currentTimeMs)358     public long calculateLastFullChargeTime(BatteryStatsHelper batteryStatsHelper,
359             long currentTimeMs) {
360         return currentTimeMs - batteryStatsHelper.getStats().getStartClockTime();
361 
362     }
363 
364     /**
365      * Calculate the screen usage time since last full charge.
366      *
367      * @param batteryStatsHelper utility class that contains the screen usage data
368      * @return time in millis
369      */
calculateScreenUsageTime(BatteryStatsHelper batteryStatsHelper)370     public long calculateScreenUsageTime(BatteryStatsHelper batteryStatsHelper) {
371         final BatterySipper sipper = findBatterySipperByType(
372                 batteryStatsHelper.getUsageList(), BatterySipper.DrainType.SCREEN);
373         return sipper != null ? sipper.usageTimeMs : 0;
374     }
375 
logRuntime(String tag, String message, long startTime)376     public static void logRuntime(String tag, String message, long startTime) {
377         Log.d(tag, message + ": " + (System.currentTimeMillis() - startTime) + "ms");
378     }
379 
380     /**
381      * Find package uid from package name
382      *
383      * @param packageName used to find the uid
384      * @return uid for packageName, or {@link #UID_NULL} if exception happens or
385      * {@code packageName} is null
386      */
getPackageUid(String packageName)387     public int getPackageUid(String packageName) {
388         try {
389             return packageName == null ? UID_NULL : mPackageManager.getPackageUid(packageName,
390                     PackageManager.GET_META_DATA);
391         } catch (PackageManager.NameNotFoundException e) {
392             return UID_NULL;
393         }
394     }
395 
396     @StringRes
getSummaryResIdFromAnomalyType(@nomaly.AnomalyType int type)397     public int getSummaryResIdFromAnomalyType(@Anomaly.AnomalyType int type) {
398         switch (type) {
399             case Anomaly.AnomalyType.WAKE_LOCK:
400                 return R.string.battery_abnormal_wakelock_summary;
401             case Anomaly.AnomalyType.WAKEUP_ALARM:
402                 return R.string.battery_abnormal_wakeup_alarm_summary;
403             case Anomaly.AnomalyType.BLUETOOTH_SCAN:
404                 return R.string.battery_abnormal_location_summary;
405             default:
406                 throw new IllegalArgumentException("Incorrect anomaly type: " + type);
407         }
408     }
409 
setForceAppStandby(int uid, String packageName, int mode)410     public void setForceAppStandby(int uid, String packageName,
411             int mode) {
412         final boolean isPreOApp = isPreOApp(packageName);
413         if (isPreOApp) {
414             // Control whether app could run in the background if it is pre O app
415             mAppOpsManager.setMode(AppOpsManager.OP_RUN_IN_BACKGROUND, uid, packageName, mode);
416         }
417         // Control whether app could run jobs in the background
418         mAppOpsManager.setMode(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, uid, packageName, mode);
419     }
420 
isForceAppStandbyEnabled(int uid, String packageName)421     public boolean isForceAppStandbyEnabled(int uid, String packageName) {
422         return mAppOpsManager.checkOpNoThrow(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, uid,
423                 packageName) == AppOpsManager.MODE_IGNORED;
424     }
425 
initBatteryStatsHelper(BatteryStatsHelper statsHelper, Bundle bundle, UserManager userManager)426     public void initBatteryStatsHelper(BatteryStatsHelper statsHelper, Bundle bundle,
427             UserManager userManager) {
428         statsHelper.create(bundle);
429         statsHelper.clearStats();
430         statsHelper.refreshStats(BatteryStats.STATS_SINCE_CHARGED, userManager.getUserProfiles());
431     }
432 
433     @WorkerThread
getBatteryInfo(final BatteryStatsHelper statsHelper, final String tag)434     public BatteryInfo getBatteryInfo(final BatteryStatsHelper statsHelper, final String tag) {
435         final long startTime = System.currentTimeMillis();
436 
437         // Stuff we always need to get BatteryInfo
438         final Intent batteryBroadcast = mContext.registerReceiver(null,
439                 new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
440         final long elapsedRealtimeUs = PowerUtil.convertMsToUs(
441                 SystemClock.elapsedRealtime());
442         final BatteryStats stats = statsHelper.getStats();
443         BatteryInfo batteryInfo;
444 
445         final Estimate estimate;
446         // Get enhanced prediction if available
447         if (mPowerUsageFeatureProvider != null &&
448                 mPowerUsageFeatureProvider.isEnhancedBatteryPredictionEnabled(mContext)) {
449             estimate = mPowerUsageFeatureProvider.getEnhancedBatteryPrediction(mContext);
450         } else {
451             estimate = new Estimate(
452                     PowerUtil.convertUsToMs(stats.computeBatteryTimeRemaining(elapsedRealtimeUs)),
453                     false /* isBasedOnUsage */,
454                     Estimate.AVERAGE_TIME_TO_DISCHARGE_UNKNOWN);
455         }
456 
457         BatteryUtils.logRuntime(tag, "BatteryInfoLoader post query", startTime);
458         batteryInfo = BatteryInfo.getBatteryInfo(mContext, batteryBroadcast, stats,
459                 estimate, elapsedRealtimeUs, false /* shortString */);
460         BatteryUtils.logRuntime(tag, "BatteryInfoLoader.loadInBackground", startTime);
461 
462         return batteryInfo;
463     }
464 
465     /**
466      * Find the {@link BatterySipper} with the corresponding {@link BatterySipper.DrainType}
467      */
findBatterySipperByType(List<BatterySipper> usageList, BatterySipper.DrainType type)468     public BatterySipper findBatterySipperByType(List<BatterySipper> usageList,
469             BatterySipper.DrainType type) {
470         for (int i = 0, size = usageList.size(); i < size; i++) {
471             final BatterySipper sipper = usageList.get(i);
472             if (sipper.drainType == type) {
473                 return sipper;
474             }
475         }
476         return null;
477     }
478 
isDataCorrupted()479     private boolean isDataCorrupted() {
480         return mPackageManager == null || mAppOpsManager == null;
481     }
482 
483     @VisibleForTesting
getForegroundActivityTotalTimeUs(BatteryStats.Uid uid, long rawRealtimeUs)484     long getForegroundActivityTotalTimeUs(BatteryStats.Uid uid, long rawRealtimeUs) {
485         final BatteryStats.Timer timer = uid.getForegroundActivityTimer();
486         if (timer != null) {
487             return timer.getTotalTimeLocked(rawRealtimeUs, BatteryStats.STATS_SINCE_CHARGED);
488         }
489 
490         return 0;
491     }
492 
493     @VisibleForTesting
getForegroundServiceTotalTimeUs(BatteryStats.Uid uid, long rawRealtimeUs)494     long getForegroundServiceTotalTimeUs(BatteryStats.Uid uid, long rawRealtimeUs) {
495         final BatteryStats.Timer timer = uid.getForegroundServiceTimer();
496         if (timer != null) {
497             return timer.getTotalTimeLocked(rawRealtimeUs, BatteryStats.STATS_SINCE_CHARGED);
498         }
499 
500         return 0;
501     }
502 
isPreOApp(final String packageName)503     public boolean isPreOApp(final String packageName) {
504         try {
505             ApplicationInfo info = mPackageManager.getApplicationInfo(packageName,
506                     PackageManager.GET_META_DATA);
507 
508             return info.targetSdkVersion < Build.VERSION_CODES.O;
509         } catch (PackageManager.NameNotFoundException e) {
510             Log.e(TAG, "Cannot find package: " + packageName, e);
511         }
512 
513         return false;
514     }
515 
isPreOApp(final String[] packageNames)516     public boolean isPreOApp(final String[] packageNames) {
517         if (ArrayUtils.isEmpty(packageNames)) {
518             return false;
519         }
520 
521         for (String packageName : packageNames) {
522             if (isPreOApp(packageName)) {
523                 return true;
524             }
525         }
526 
527         return false;
528     }
529 
530     /**
531      * Return {@code true} if we should hide anomaly app represented by {@code uid}
532      */
shouldHideAnomaly(PowerWhitelistBackend powerWhitelistBackend, int uid, AnomalyInfo anomalyInfo)533     public boolean shouldHideAnomaly(PowerWhitelistBackend powerWhitelistBackend, int uid,
534             AnomalyInfo anomalyInfo) {
535         final String[] packageNames = mPackageManager.getPackagesForUid(uid);
536         if (ArrayUtils.isEmpty(packageNames)) {
537             // Don't show it if app has been uninstalled
538             return true;
539         }
540 
541         return isSystemUid(uid) || powerWhitelistBackend.isWhitelisted(packageNames)
542                 || (isSystemApp(mPackageManager, packageNames) && !hasLauncherEntry(packageNames))
543                 || (isExcessiveBackgroundAnomaly(anomalyInfo) && !isPreOApp(packageNames));
544     }
545 
isExcessiveBackgroundAnomaly(AnomalyInfo anomalyInfo)546     private boolean isExcessiveBackgroundAnomaly(AnomalyInfo anomalyInfo) {
547         return anomalyInfo.anomalyType
548                 == StatsManagerConfig.AnomalyType.EXCESSIVE_BACKGROUND_SERVICE;
549     }
550 
isSystemUid(int uid)551     private boolean isSystemUid(int uid) {
552         final int appUid = UserHandle.getAppId(uid);
553         return appUid >= Process.ROOT_UID && appUid < Process.FIRST_APPLICATION_UID;
554     }
555 
isSystemApp(PackageManager packageManager, String[] packageNames)556     private boolean isSystemApp(PackageManager packageManager, String[] packageNames) {
557         for (String packageName : packageNames) {
558             try {
559                 final ApplicationInfo info = packageManager.getApplicationInfo(packageName,
560                         0 /* flags */);
561                 if ((info.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
562                     return true;
563                 }
564             } catch (PackageManager.NameNotFoundException e) {
565                 Log.e(TAG, "Package not found: " + packageName, e);
566             }
567         }
568 
569         return false;
570     }
571 
hasLauncherEntry(String[] packageNames)572     private boolean hasLauncherEntry(String[] packageNames) {
573         final Intent launchIntent = new Intent(Intent.ACTION_MAIN, null);
574         launchIntent.addCategory(Intent.CATEGORY_LAUNCHER);
575 
576         // If we do not specify MATCH_DIRECT_BOOT_AWARE or
577         // MATCH_DIRECT_BOOT_UNAWARE, system will derive and update the flags
578         // according to the user's lock state. When the user is locked,
579         // components
580         // with ComponentInfo#directBootAware == false will be filtered. We should
581         // explicitly include both direct boot aware and unaware components here.
582         final List<ResolveInfo> resolveInfos = mPackageManager.queryIntentActivities(launchIntent,
583                 PackageManager.MATCH_DISABLED_COMPONENTS
584                         | PackageManager.MATCH_DIRECT_BOOT_AWARE
585                         | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
586                         | PackageManager.MATCH_SYSTEM_ONLY);
587         for (int i = 0, size = resolveInfos.size(); i < size; i++) {
588             final ResolveInfo resolveInfo = resolveInfos.get(i);
589             if (ArrayUtils.contains(packageNames, resolveInfo.activityInfo.packageName)) {
590                 return true;
591             }
592         }
593 
594         return false;
595     }
596 
597     /**
598      * Return version number of an app represented by {@code packageName}, and return -1 if not
599      * found.
600      */
getAppLongVersionCode(String packageName)601     public long getAppLongVersionCode(String packageName) {
602         try {
603             final PackageInfo packageInfo = mPackageManager.getPackageInfo(packageName,
604                     0 /* flags */);
605             return packageInfo.getLongVersionCode();
606         } catch (PackageManager.NameNotFoundException e) {
607             Log.e(TAG, "Cannot find package: " + packageName, e);
608         }
609 
610         return -1L;
611     }
612 }
613 
614