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