• 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.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      * Return {@code true} if battery is overheated and charging.
408      */
isBatteryDefenderOn(BatteryInfo batteryInfo)409     public static boolean isBatteryDefenderOn(BatteryInfo batteryInfo) {
410         return batteryInfo.isOverheated && !batteryInfo.discharging;
411     }
412 
413     /**
414      * Find package uid from package name
415      *
416      * @param packageName used to find the uid
417      * @return uid for packageName, or {@link #UID_NULL} if exception happens or
418      * {@code packageName} is null
419      */
getPackageUid(String packageName)420     public int getPackageUid(String packageName) {
421         try {
422             return packageName == null ? UID_NULL : mPackageManager.getPackageUid(packageName,
423                     PackageManager.GET_META_DATA);
424         } catch (PackageManager.NameNotFoundException e) {
425             return UID_NULL;
426         }
427     }
428 
setForceAppStandby(int uid, String packageName, int mode)429     public void setForceAppStandby(int uid, String packageName,
430             int mode) {
431         final boolean isPreOApp = isPreOApp(packageName);
432         if (isPreOApp) {
433             // Control whether app could run in the background if it is pre O app
434             mAppOpsManager.setMode(AppOpsManager.OP_RUN_IN_BACKGROUND, uid, packageName, mode);
435         }
436         // Control whether app could run jobs in the background
437         mAppOpsManager.setMode(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, uid, packageName, mode);
438 
439         ThreadUtils.postOnBackgroundThread(() -> {
440             final BatteryDatabaseManager batteryDatabaseManager = BatteryDatabaseManager
441                     .getInstance(mContext);
442             if (mode == AppOpsManager.MODE_IGNORED) {
443                 batteryDatabaseManager.insertAction(AnomalyDatabaseHelper.ActionType.RESTRICTION,
444                         uid, packageName, System.currentTimeMillis());
445             } else if (mode == AppOpsManager.MODE_ALLOWED) {
446                 batteryDatabaseManager.deleteAction(AnomalyDatabaseHelper.ActionType.RESTRICTION,
447                         uid, packageName);
448             }
449         });
450     }
451 
isForceAppStandbyEnabled(int uid, String packageName)452     public boolean isForceAppStandbyEnabled(int uid, String packageName) {
453         return mAppOpsManager.checkOpNoThrow(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, uid,
454                 packageName) == AppOpsManager.MODE_IGNORED;
455     }
456 
clearForceAppStandby(String packageName)457     public boolean clearForceAppStandby(String packageName) {
458         final int uid = getPackageUid(packageName);
459         if (uid != UID_NULL && isForceAppStandbyEnabled(uid, packageName)) {
460             setForceAppStandby(uid, packageName, AppOpsManager.MODE_ALLOWED);
461             return true;
462         } else {
463             return false;
464         }
465     }
466 
initBatteryStatsHelper(BatteryStatsHelper statsHelper, Bundle bundle, UserManager userManager)467     public void initBatteryStatsHelper(BatteryStatsHelper statsHelper, Bundle bundle,
468             UserManager userManager) {
469         statsHelper.create(bundle);
470         statsHelper.clearStats();
471         statsHelper.refreshStats(BatteryStats.STATS_SINCE_CHARGED, userManager.getUserProfiles());
472     }
473 
474     @WorkerThread
getBatteryInfo(final BatteryStatsHelper statsHelper, final String tag)475     public BatteryInfo getBatteryInfo(final BatteryStatsHelper statsHelper, final String tag) {
476         final long startTime = System.currentTimeMillis();
477 
478         // Stuff we always need to get BatteryInfo
479         final Intent batteryBroadcast = mContext.registerReceiver(null,
480                 new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
481         final long elapsedRealtimeUs = PowerUtil.convertMsToUs(
482                 SystemClock.elapsedRealtime());
483         final BatteryStats stats = statsHelper.getStats();
484         BatteryInfo batteryInfo;
485         Estimate estimate = getEnhancedEstimate();
486 
487         // couldn't get estimate from cache or provider, use fallback
488         if (estimate == null) {
489             estimate = new Estimate(
490                     PowerUtil.convertUsToMs(stats.computeBatteryTimeRemaining(elapsedRealtimeUs)),
491                     false /* isBasedOnUsage */,
492                     EstimateKt.AVERAGE_TIME_TO_DISCHARGE_UNKNOWN);
493         }
494 
495         BatteryUtils.logRuntime(tag, "BatteryInfoLoader post query", startTime);
496         batteryInfo = BatteryInfo.getBatteryInfo(mContext, batteryBroadcast, stats,
497                 estimate, elapsedRealtimeUs, false /* shortString */);
498         BatteryUtils.logRuntime(tag, "BatteryInfoLoader.loadInBackground", startTime);
499 
500         return batteryInfo;
501     }
502 
503     @VisibleForTesting
getEnhancedEstimate()504     Estimate getEnhancedEstimate() {
505         Estimate estimate = null;
506         // Get enhanced prediction if available
507         if (Duration.between(Estimate.getLastCacheUpdateTime(mContext), Instant.now())
508                 .compareTo(Duration.ofSeconds(10)) < 0) {
509             estimate = Estimate.getCachedEstimateIfAvailable(mContext);
510         } else if (mPowerUsageFeatureProvider != null &&
511                 mPowerUsageFeatureProvider.isEnhancedBatteryPredictionEnabled(mContext)) {
512             estimate = mPowerUsageFeatureProvider.getEnhancedBatteryPrediction(mContext);
513             if (estimate != null) {
514                 Estimate.storeCachedEstimate(mContext, estimate);
515             }
516         }
517         return estimate;
518     }
519 
520     /**
521      * Find the {@link BatterySipper} with the corresponding {@link BatterySipper.DrainType}
522      */
findBatterySipperByType(List<BatterySipper> usageList, BatterySipper.DrainType type)523     public BatterySipper findBatterySipperByType(List<BatterySipper> usageList,
524             BatterySipper.DrainType type) {
525         for (int i = 0, size = usageList.size(); i < size; i++) {
526             final BatterySipper sipper = usageList.get(i);
527             if (sipper.drainType == type) {
528                 return sipper;
529             }
530         }
531         return null;
532     }
533 
isDataCorrupted()534     private boolean isDataCorrupted() {
535         return mPackageManager == null || mAppOpsManager == null;
536     }
537 
538     @VisibleForTesting
getForegroundActivityTotalTimeUs(BatteryStats.Uid uid, long rawRealtimeUs)539     long getForegroundActivityTotalTimeUs(BatteryStats.Uid uid, long rawRealtimeUs) {
540         final BatteryStats.Timer timer = uid.getForegroundActivityTimer();
541         if (timer != null) {
542             return timer.getTotalTimeLocked(rawRealtimeUs, BatteryStats.STATS_SINCE_CHARGED);
543         }
544 
545         return 0;
546     }
547 
548     @VisibleForTesting
getForegroundServiceTotalTimeUs(BatteryStats.Uid uid, long rawRealtimeUs)549     long getForegroundServiceTotalTimeUs(BatteryStats.Uid uid, long rawRealtimeUs) {
550         final BatteryStats.Timer timer = uid.getForegroundServiceTimer();
551         if (timer != null) {
552             return timer.getTotalTimeLocked(rawRealtimeUs, BatteryStats.STATS_SINCE_CHARGED);
553         }
554 
555         return 0;
556     }
557 
isPreOApp(final String packageName)558     public boolean isPreOApp(final String packageName) {
559         try {
560             ApplicationInfo info = mPackageManager.getApplicationInfo(packageName,
561                     PackageManager.GET_META_DATA);
562 
563             return info.targetSdkVersion < Build.VERSION_CODES.O;
564         } catch (PackageManager.NameNotFoundException e) {
565             Log.e(TAG, "Cannot find package: " + packageName, e);
566         }
567 
568         return false;
569     }
570 
isPreOApp(final String[] packageNames)571     public boolean isPreOApp(final String[] packageNames) {
572         if (ArrayUtils.isEmpty(packageNames)) {
573             return false;
574         }
575 
576         for (String packageName : packageNames) {
577             if (isPreOApp(packageName)) {
578                 return true;
579             }
580         }
581 
582         return false;
583     }
584 
585     /**
586      * Return {@code true} if we should hide anomaly app represented by {@code uid}
587      */
shouldHideAnomaly(PowerWhitelistBackend powerWhitelistBackend, int uid, AnomalyInfo anomalyInfo)588     public boolean shouldHideAnomaly(PowerWhitelistBackend powerWhitelistBackend, int uid,
589             AnomalyInfo anomalyInfo) {
590         final String[] packageNames = mPackageManager.getPackagesForUid(uid);
591         if (ArrayUtils.isEmpty(packageNames)) {
592             // Don't show it if app has been uninstalled
593             return true;
594         }
595 
596         return isSystemUid(uid) || powerWhitelistBackend.isWhitelisted(packageNames)
597                 || (isSystemApp(mPackageManager, packageNames) && !hasLauncherEntry(packageNames))
598                 || (isExcessiveBackgroundAnomaly(anomalyInfo) && !isPreOApp(packageNames));
599     }
600 
isExcessiveBackgroundAnomaly(AnomalyInfo anomalyInfo)601     private boolean isExcessiveBackgroundAnomaly(AnomalyInfo anomalyInfo) {
602         return anomalyInfo.anomalyType
603                 == StatsManagerConfig.AnomalyType.EXCESSIVE_BACKGROUND_SERVICE;
604     }
605 
isSystemUid(int uid)606     private boolean isSystemUid(int uid) {
607         final int appUid = UserHandle.getAppId(uid);
608         return appUid >= Process.ROOT_UID && appUid < Process.FIRST_APPLICATION_UID;
609     }
610 
isSystemApp(PackageManager packageManager, String[] packageNames)611     private boolean isSystemApp(PackageManager packageManager, String[] packageNames) {
612         for (String packageName : packageNames) {
613             try {
614                 final ApplicationInfo info = packageManager.getApplicationInfo(packageName,
615                         0 /* flags */);
616                 if ((info.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
617                     return true;
618                 }
619             } catch (PackageManager.NameNotFoundException e) {
620                 Log.e(TAG, "Package not found: " + packageName, e);
621             }
622         }
623 
624         return false;
625     }
626 
hasLauncherEntry(String[] packageNames)627     private boolean hasLauncherEntry(String[] packageNames) {
628         final Intent launchIntent = new Intent(Intent.ACTION_MAIN, null);
629         launchIntent.addCategory(Intent.CATEGORY_LAUNCHER);
630 
631         // If we do not specify MATCH_DIRECT_BOOT_AWARE or
632         // MATCH_DIRECT_BOOT_UNAWARE, system will derive and update the flags
633         // according to the user's lock state. When the user is locked,
634         // components
635         // with ComponentInfo#directBootAware == false will be filtered. We should
636         // explicitly include both direct boot aware and unaware components here.
637         final List<ResolveInfo> resolveInfos = mPackageManager.queryIntentActivities(launchIntent,
638                 PackageManager.MATCH_DISABLED_COMPONENTS
639                         | PackageManager.MATCH_DIRECT_BOOT_AWARE
640                         | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
641                         | PackageManager.MATCH_SYSTEM_ONLY);
642         for (int i = 0, size = resolveInfos.size(); i < size; i++) {
643             final ResolveInfo resolveInfo = resolveInfos.get(i);
644             if (ArrayUtils.contains(packageNames, resolveInfo.activityInfo.packageName)) {
645                 return true;
646             }
647         }
648 
649         return false;
650     }
651 
652     /**
653      * Return version number of an app represented by {@code packageName}, and return -1 if not
654      * found.
655      */
getAppLongVersionCode(String packageName)656     public long getAppLongVersionCode(String packageName) {
657         try {
658             final PackageInfo packageInfo = mPackageManager.getPackageInfo(packageName,
659                     0 /* flags */);
660             return packageInfo.getLongVersionCode();
661         } catch (PackageManager.NameNotFoundException e) {
662             Log.e(TAG, "Cannot find package: " + packageName, e);
663         }
664 
665         return -1L;
666     }
667 }
668 
669