Home | History | Annotate | Download | only in fuelgauge
      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 
     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
    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 
    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 
    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 
    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 
    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 
    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      */
    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
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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 
    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      */
    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
    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 
    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 
    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 
    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
    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      */
    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 
    479     private boolean isDataCorrupted() {
    480         return mPackageManager == null || mAppOpsManager == null;
    481     }
    482 
    483     @VisibleForTesting
    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
    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 
    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 
    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      */
    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 
    546     private boolean isExcessiveBackgroundAnomaly(AnomalyInfo anomalyInfo) {
    547         return anomalyInfo.anomalyType
    548                 == StatsManagerConfig.AnomalyType.EXCESSIVE_BACKGROUND_SERVICE;
    549     }
    550 
    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 
    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 
    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      */
    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