Home | History | Annotate | Download | only in applications
      1 /*
      2  * Copyright (C) 2015 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 
     17 package com.android.settingslib.applications;
     18 
     19 import android.annotation.IntDef;
     20 import android.app.ActivityManager;
     21 import android.app.AppGlobals;
     22 import android.app.Application;
     23 import android.app.usage.StorageStats;
     24 import android.app.usage.StorageStatsManager;
     25 import android.content.BroadcastReceiver;
     26 import android.content.Context;
     27 import android.content.Intent;
     28 import android.content.IntentFilter;
     29 import android.content.pm.ApplicationInfo;
     30 import android.content.pm.IPackageManager;
     31 import android.content.pm.IPackageStatsObserver;
     32 import android.content.pm.ModuleInfo;
     33 import android.content.pm.PackageManager;
     34 import android.content.pm.PackageManager.NameNotFoundException;
     35 import android.content.pm.PackageStats;
     36 import android.content.pm.ParceledListSlice;
     37 import android.content.pm.ResolveInfo;
     38 import android.content.pm.UserInfo;
     39 import android.graphics.drawable.Drawable;
     40 import android.net.Uri;
     41 import android.os.Handler;
     42 import android.os.HandlerThread;
     43 import android.os.Looper;
     44 import android.os.Message;
     45 import android.os.Process;
     46 import android.os.RemoteException;
     47 import android.os.SystemClock;
     48 import android.os.UserHandle;
     49 import android.os.UserManager;
     50 import android.text.format.Formatter;
     51 import android.util.IconDrawableFactory;
     52 import android.util.Log;
     53 import android.util.SparseArray;
     54 
     55 import androidx.annotation.VisibleForTesting;
     56 import androidx.lifecycle.Lifecycle;
     57 import androidx.lifecycle.LifecycleObserver;
     58 import androidx.lifecycle.OnLifecycleEvent;
     59 
     60 import com.android.internal.R;
     61 import com.android.internal.util.ArrayUtils;
     62 
     63 import java.io.File;
     64 import java.io.IOException;
     65 import java.lang.annotation.Retention;
     66 import java.lang.annotation.RetentionPolicy;
     67 import java.lang.ref.WeakReference;
     68 import java.text.Collator;
     69 import java.text.Normalizer;
     70 import java.text.Normalizer.Form;
     71 import java.util.ArrayList;
     72 import java.util.Collections;
     73 import java.util.Comparator;
     74 import java.util.HashMap;
     75 import java.util.HashSet;
     76 import java.util.List;
     77 import java.util.Objects;
     78 import java.util.UUID;
     79 import java.util.regex.Pattern;
     80 
     81 /**
     82  * Keeps track of information about all installed applications, lazy-loading
     83  * as needed.
     84  */
     85 public class ApplicationsState {
     86     private static final String TAG = "ApplicationsState";
     87 
     88     public static final int SIZE_UNKNOWN = -1;
     89     public static final int SIZE_INVALID = -2;
     90 
     91     private static final boolean DEBUG = false;
     92     private static final boolean DEBUG_LOCKING = false;
     93     private static final Object sLock = new Object();
     94     private static final Pattern REMOVE_DIACRITICALS_PATTERN
     95             = Pattern.compile("\\p{InCombiningDiacriticalMarks}+");
     96 
     97     @VisibleForTesting
     98     static ApplicationsState sInstance;
     99 
    100     public static ApplicationsState getInstance(Application app) {
    101         return getInstance(app, AppGlobals.getPackageManager());
    102     }
    103 
    104     @VisibleForTesting
    105     static ApplicationsState getInstance(Application app, IPackageManager iPackageManager) {
    106         synchronized (sLock) {
    107             if (sInstance == null) {
    108                 sInstance = new ApplicationsState(app, iPackageManager);
    109             }
    110             return sInstance;
    111         }
    112     }
    113 
    114     final Context mContext;
    115     final PackageManager mPm;
    116     final IconDrawableFactory mDrawableFactory;
    117     final IPackageManager mIpm;
    118     final UserManager mUm;
    119     final StorageStatsManager mStats;
    120     final int mAdminRetrieveFlags;
    121     final int mRetrieveFlags;
    122     PackageIntentReceiver mPackageIntentReceiver;
    123 
    124     boolean mResumed;
    125     boolean mHaveDisabledApps;
    126     boolean mHaveInstantApps;
    127 
    128     // Information about all applications.  Synchronize on mEntriesMap
    129     // to protect access to these.
    130     final ArrayList<Session> mSessions = new ArrayList<>();
    131     final ArrayList<Session> mRebuildingSessions = new ArrayList<>();
    132     private InterestingConfigChanges mInterestingConfigChanges = new InterestingConfigChanges();
    133     // Map: userid => (Map: package name => AppEntry)
    134     final SparseArray<HashMap<String, AppEntry>> mEntriesMap = new SparseArray<>();
    135     final ArrayList<AppEntry> mAppEntries = new ArrayList<>();
    136     List<ApplicationInfo> mApplications = new ArrayList<>();
    137     long mCurId = 1;
    138     UUID mCurComputingSizeUuid;
    139     String mCurComputingSizePkg;
    140     int mCurComputingSizeUserId;
    141     boolean mSessionsChanged;
    142     // Maps all installed modules on the system to whether they're hidden or not.
    143     final HashMap<String, Boolean> mSystemModules = new HashMap<>();
    144 
    145     // Temporary for dispatching session callbacks.  Only touched by main thread.
    146     final ArrayList<WeakReference<Session>> mActiveSessions = new ArrayList<>();
    147 
    148     final HandlerThread mThread;
    149     final BackgroundHandler mBackgroundHandler;
    150     final MainHandler mMainHandler = new MainHandler(Looper.getMainLooper());
    151 
    152     /** Requests that the home app is loaded. */
    153     public static final int FLAG_SESSION_REQUEST_HOME_APP = 1 << 0;
    154 
    155     /** Requests that icons are loaded. */
    156     public static final int FLAG_SESSION_REQUEST_ICONS = 1 << 1;
    157 
    158     /** Requests that sizes are loaded. */
    159     public static final int FLAG_SESSION_REQUEST_SIZES = 1 << 2;
    160 
    161     /** Requests that launcher intents are resolved. */
    162     public static final int FLAG_SESSION_REQUEST_LAUNCHER = 1 << 3;
    163 
    164     /** Requests that leanback launcher intents are resolved. */
    165     public static final int FLAG_SESSION_REQUEST_LEANBACK_LAUNCHER = 1 << 4;
    166 
    167     /**
    168      * Flags to configure the session to request various types of info.
    169      */
    170     @IntDef(prefix = {"FLAG_SESSION_"}, value = {
    171             FLAG_SESSION_REQUEST_HOME_APP,
    172             FLAG_SESSION_REQUEST_ICONS,
    173             FLAG_SESSION_REQUEST_SIZES,
    174             FLAG_SESSION_REQUEST_LAUNCHER,
    175             FLAG_SESSION_REQUEST_LEANBACK_LAUNCHER
    176     })
    177     @Retention(RetentionPolicy.SOURCE)
    178     public @interface SessionFlags {
    179     }
    180 
    181     @VisibleForTesting
    182     void setInterestingConfigChanges(InterestingConfigChanges interestingConfigChanges) {
    183         mInterestingConfigChanges = interestingConfigChanges;
    184     }
    185 
    186     @SessionFlags
    187     public static final int DEFAULT_SESSION_FLAGS =
    188             FLAG_SESSION_REQUEST_HOME_APP | FLAG_SESSION_REQUEST_ICONS |
    189                     FLAG_SESSION_REQUEST_SIZES | FLAG_SESSION_REQUEST_LAUNCHER;
    190 
    191     private ApplicationsState(Application app, IPackageManager iPackageManager) {
    192         mContext = app;
    193         mPm = mContext.getPackageManager();
    194         mDrawableFactory = IconDrawableFactory.newInstance(mContext);
    195         mIpm = iPackageManager;
    196         mUm = mContext.getSystemService(UserManager.class);
    197         mStats = mContext.getSystemService(StorageStatsManager.class);
    198         for (int userId : mUm.getProfileIdsWithDisabled(UserHandle.myUserId())) {
    199             mEntriesMap.put(userId, new HashMap<>());
    200         }
    201 
    202         mThread = new HandlerThread("ApplicationsState.Loader",
    203                 Process.THREAD_PRIORITY_BACKGROUND);
    204         mThread.start();
    205         mBackgroundHandler = new BackgroundHandler(mThread.getLooper());
    206 
    207         // Only the owner can see all apps.
    208         mAdminRetrieveFlags = PackageManager.MATCH_ANY_USER |
    209                 PackageManager.MATCH_DISABLED_COMPONENTS |
    210                 PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS;
    211         mRetrieveFlags = PackageManager.MATCH_DISABLED_COMPONENTS |
    212                 PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS;
    213 
    214         final List<ModuleInfo> moduleInfos = mPm.getInstalledModules(0 /* flags */);
    215         for (ModuleInfo info : moduleInfos) {
    216             mSystemModules.put(info.getPackageName(), info.isHidden());
    217         }
    218 
    219         /**
    220          * This is a trick to prevent the foreground thread from being delayed.
    221          * The problem is that Dalvik monitors are initially spin locks, to keep
    222          * them lightweight.  This leads to unfair contention -- Even though the
    223          * background thread only holds the lock for a short amount of time, if
    224          * it keeps running and locking again it can prevent the main thread from
    225          * acquiring its lock for a long time...  sometimes even > 5 seconds
    226          * (leading to an ANR).
    227          *
    228          * Dalvik will promote a monitor to a "real" lock if it detects enough
    229          * contention on it.  It doesn't figure this out fast enough for us
    230          * here, though, so this little trick will force it to turn into a real
    231          * lock immediately.
    232          */
    233         synchronized (mEntriesMap) {
    234             try {
    235                 mEntriesMap.wait(1);
    236             } catch (InterruptedException e) {
    237             }
    238         }
    239     }
    240 
    241     public Looper getBackgroundLooper() {
    242         return mThread.getLooper();
    243     }
    244 
    245     public Session newSession(Callbacks callbacks) {
    246         return newSession(callbacks, null);
    247     }
    248 
    249     public Session newSession(Callbacks callbacks, Lifecycle lifecycle) {
    250         Session s = new Session(callbacks, lifecycle);
    251         synchronized (mEntriesMap) {
    252             mSessions.add(s);
    253         }
    254         return s;
    255     }
    256 
    257     void doResumeIfNeededLocked() {
    258         if (mResumed) {
    259             return;
    260         }
    261         mResumed = true;
    262         if (mPackageIntentReceiver == null) {
    263             mPackageIntentReceiver = new PackageIntentReceiver();
    264             mPackageIntentReceiver.registerReceiver();
    265         }
    266 
    267         final List<ApplicationInfo> prevApplications = mApplications;
    268         mApplications = new ArrayList<>();
    269         for (UserInfo user : mUm.getProfiles(UserHandle.myUserId())) {
    270             try {
    271                 // If this user is new, it needs a map created.
    272                 if (mEntriesMap.indexOfKey(user.id) < 0) {
    273                     mEntriesMap.put(user.id, new HashMap<>());
    274                 }
    275                 @SuppressWarnings("unchecked")
    276                 ParceledListSlice<ApplicationInfo> list =
    277                         mIpm.getInstalledApplications(
    278                                 user.isAdmin() ? mAdminRetrieveFlags : mRetrieveFlags,
    279                                 user.id);
    280                 mApplications.addAll(list.getList());
    281             } catch (Exception e) {
    282                 Log.e(TAG, "Error during doResumeIfNeededLocked", e);
    283             }
    284         }
    285 
    286         if (mInterestingConfigChanges.applyNewConfig(mContext.getResources())) {
    287             // If an interesting part of the configuration has changed, we
    288             // should completely reload the app entries.
    289             clearEntries();
    290         } else {
    291             for (int i = 0; i < mAppEntries.size(); i++) {
    292                 mAppEntries.get(i).sizeStale = true;
    293             }
    294         }
    295 
    296         mHaveDisabledApps = false;
    297         mHaveInstantApps = false;
    298         for (int i = 0; i < mApplications.size(); i++) {
    299             final ApplicationInfo info = mApplications.get(i);
    300             // Need to trim out any applications that are disabled by
    301             // something different than the user.
    302             if (!info.enabled) {
    303                 if (info.enabledSetting != PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) {
    304                     mApplications.remove(i);
    305                     i--;
    306                     continue;
    307                 }
    308                 mHaveDisabledApps = true;
    309             }
    310             if (isHiddenModule(info.packageName)) {
    311                 mApplications.remove(i--);
    312                 continue;
    313             }
    314             if (!mHaveInstantApps && AppUtils.isInstant(info)) {
    315                 mHaveInstantApps = true;
    316             }
    317 
    318             int userId = UserHandle.getUserId(info.uid);
    319             final AppEntry entry = mEntriesMap.get(userId).get(info.packageName);
    320             if (entry != null) {
    321                 entry.info = info;
    322             }
    323         }
    324 
    325         if (anyAppIsRemoved(prevApplications, mApplications)) {
    326             // some apps have been uninstalled.
    327             clearEntries();
    328         }
    329         mCurComputingSizePkg = null;
    330         if (!mBackgroundHandler.hasMessages(BackgroundHandler.MSG_LOAD_ENTRIES)) {
    331             mBackgroundHandler.sendEmptyMessage(BackgroundHandler.MSG_LOAD_ENTRIES);
    332         }
    333     }
    334 
    335     /* The original design is mAppEntries.size() > mApplications.size().
    336        It's correct if there is only the owner user and only one app is removed.
    337        Problem 1:
    338        If there is a user profile, the size of mAppEntries < mApplications is normal because
    339        the number of app entries on UI (mAppEntries) should be equal to the number of apps got
    340        from PMS (mApplications).
    341 
    342        owner only case:
    343        mApplications: user 0: 191
    344        mAppEntries  : user 0: 191
    345        total mAppEntries: 191, mApplications: 191
    346        If an app is removed, cached mAppEntries: 191 , mApplications: 191 -> 190, it is detected
    347        as the number of apps becomes less.
    348 
    349        If there is a work profile, mAppEntries removes some apps that are not installed for the
    350        owner user.
    351 
    352        For example, in the following case, 6 apps are removed from mAppEntries for the owner.
    353        mApplications: user 0: 197, user 10: 189 => total 386
    354        mAppEntries  : user 0: 191, user 10: 189 => total 380
    355        If an app is removed, cached mAppEntries: 380 , mApplications: 386 -> 385, the size of
    356        mAppEntries is still not larger than mApplications, then does not clear mAppEntries.
    357 
    358        Problem 2:
    359        If remove an app and add another app outside Settings (e.g. Play Store) and back to
    360        Settings, the amount of apps are not changed, it causes the entries keep the removed app.
    361 
    362        Another case, if adding more apps than removing apps (e.g. add 2 apps and remove 1 app),
    363        the final number of apps (mApplications) is even increased,
    364 
    365        Therefore, should not only count on number of apps to determine any app is removed.
    366        Compare the change of applications instead.
    367     */
    368     private static boolean anyAppIsRemoved(List<ApplicationInfo> prevApplications,
    369             List<ApplicationInfo> applications) {
    370 
    371         // No cache
    372         if (prevApplications.size() == 0) {
    373             return false;
    374         }
    375 
    376         if (applications.size() < prevApplications.size()) {
    377             return true;
    378         }
    379 
    380         // build package sets of all applications <userId, HashSet of packages>
    381         final HashMap<String, HashSet<String>> packageMap = new HashMap<>();
    382         for (ApplicationInfo application : applications) {
    383             final String userId = String.valueOf(UserHandle.getUserId(application.uid));
    384 
    385             HashSet<String> appPackages = packageMap.get(userId);
    386             if (appPackages == null) {
    387                 appPackages = new HashSet<>();
    388                 packageMap.put(userId, appPackages);
    389             }
    390             if (hasFlag(application.flags, ApplicationInfo.FLAG_INSTALLED)) {
    391                 appPackages.add(application.packageName);
    392             }
    393         }
    394 
    395         // detect any previous app is removed
    396         for (ApplicationInfo prevApplication : prevApplications) {
    397             if (!hasFlag(prevApplication.flags, ApplicationInfo.FLAG_INSTALLED)) {
    398                 continue;
    399             }
    400             final String userId = String.valueOf(UserHandle.getUserId(prevApplication.uid));
    401 
    402             final HashSet<String> packagesSet = packageMap.get(userId);
    403             if (packagesSet == null || !packagesSet.remove(prevApplication.packageName)) {
    404                 return true;
    405             }
    406         }
    407 
    408         return false;
    409     }
    410 
    411     @VisibleForTesting
    412     void clearEntries() {
    413         for (int i = 0; i < mEntriesMap.size(); i++) {
    414             mEntriesMap.valueAt(i).clear();
    415         }
    416         mAppEntries.clear();
    417     }
    418 
    419     public boolean haveDisabledApps() {
    420         return mHaveDisabledApps;
    421     }
    422 
    423     public boolean haveInstantApps() {
    424         return mHaveInstantApps;
    425     }
    426 
    427     boolean isHiddenModule(String packageName) {
    428         Boolean isHidden = mSystemModules.get(packageName);
    429         if (isHidden == null) {
    430             return false;
    431         }
    432 
    433         return isHidden;
    434     }
    435 
    436     boolean isSystemModule(String packageName) {
    437         return mSystemModules.containsKey(packageName);
    438     }
    439 
    440     void doPauseIfNeededLocked() {
    441         if (!mResumed) {
    442             return;
    443         }
    444         for (int i = 0; i < mSessions.size(); i++) {
    445             if (mSessions.get(i).mResumed) {
    446                 return;
    447             }
    448         }
    449         doPauseLocked();
    450     }
    451 
    452     void doPauseLocked() {
    453         mResumed = false;
    454         if (mPackageIntentReceiver != null) {
    455             mPackageIntentReceiver.unregisterReceiver();
    456             mPackageIntentReceiver = null;
    457         }
    458     }
    459 
    460     public AppEntry getEntry(String packageName, int userId) {
    461         if (DEBUG_LOCKING) Log.v(TAG, "getEntry about to acquire lock...");
    462         synchronized (mEntriesMap) {
    463             AppEntry entry = mEntriesMap.get(userId).get(packageName);
    464             if (entry == null) {
    465                 ApplicationInfo info = getAppInfoLocked(packageName, userId);
    466                 if (info == null) {
    467                     try {
    468                         info = mIpm.getApplicationInfo(packageName, 0, userId);
    469                     } catch (RemoteException e) {
    470                         Log.w(TAG, "getEntry couldn't reach PackageManager", e);
    471                         return null;
    472                     }
    473                 }
    474                 if (info != null) {
    475                     entry = getEntryLocked(info);
    476                 }
    477             }
    478             if (DEBUG_LOCKING) Log.v(TAG, "...getEntry releasing lock");
    479             return entry;
    480         }
    481     }
    482 
    483     private ApplicationInfo getAppInfoLocked(String pkg, int userId) {
    484         for (int i = 0; i < mApplications.size(); i++) {
    485             ApplicationInfo info = mApplications.get(i);
    486             if (pkg.equals(info.packageName)
    487                     && userId == UserHandle.getUserId(info.uid)) {
    488                 return info;
    489             }
    490         }
    491         return null;
    492     }
    493 
    494     public void ensureIcon(AppEntry entry) {
    495         if (entry.icon != null) {
    496             return;
    497         }
    498         synchronized (entry) {
    499             entry.ensureIconLocked(mContext, mDrawableFactory);
    500         }
    501     }
    502 
    503     public void requestSize(String packageName, int userId) {
    504         if (DEBUG_LOCKING) Log.v(TAG, "requestSize about to acquire lock...");
    505         synchronized (mEntriesMap) {
    506             AppEntry entry = mEntriesMap.get(userId).get(packageName);
    507             if (entry != null && hasFlag(entry.info.flags, ApplicationInfo.FLAG_INSTALLED)) {
    508                 mBackgroundHandler.post(
    509                         () -> {
    510                             try {
    511                                 final StorageStats stats =
    512                                         mStats.queryStatsForPackage(
    513                                                 entry.info.storageUuid,
    514                                                 packageName,
    515                                                 UserHandle.of(userId));
    516                                 final long cacheQuota =
    517                                         mStats.getCacheQuotaBytes(
    518                                                 entry.info.storageUuid.toString(), entry.info.uid);
    519                                 final PackageStats legacy = new PackageStats(packageName, userId);
    520                                 legacy.codeSize = stats.getCodeBytes();
    521                                 legacy.dataSize = stats.getDataBytes();
    522                                 legacy.cacheSize = Math.min(stats.getCacheBytes(), cacheQuota);
    523                                 try {
    524                                     mBackgroundHandler.mStatsObserver.onGetStatsCompleted(
    525                                             legacy, true);
    526                                 } catch (RemoteException ignored) {
    527                                 }
    528                             } catch (NameNotFoundException | IOException e) {
    529                                 Log.w(TAG, "Failed to query stats: " + e);
    530                                 try {
    531                                     mBackgroundHandler.mStatsObserver.onGetStatsCompleted(
    532                                             null, false);
    533                                 } catch (RemoteException ignored) {
    534                                 }
    535                             }
    536                         });
    537             }
    538             if (DEBUG_LOCKING) Log.v(TAG, "...requestSize releasing lock");
    539         }
    540     }
    541 
    542     long sumCacheSizes() {
    543         long sum = 0;
    544         if (DEBUG_LOCKING) Log.v(TAG, "sumCacheSizes about to acquire lock...");
    545         synchronized (mEntriesMap) {
    546             if (DEBUG_LOCKING) Log.v(TAG, "-> sumCacheSizes now has lock");
    547             for (int i = mAppEntries.size() - 1; i >= 0; i--) {
    548                 sum += mAppEntries.get(i).cacheSize;
    549             }
    550             if (DEBUG_LOCKING) Log.v(TAG, "...sumCacheSizes releasing lock");
    551         }
    552         return sum;
    553     }
    554 
    555     int indexOfApplicationInfoLocked(String pkgName, int userId) {
    556         for (int i = mApplications.size() - 1; i >= 0; i--) {
    557             ApplicationInfo appInfo = mApplications.get(i);
    558             if (appInfo.packageName.equals(pkgName)
    559                     && UserHandle.getUserId(appInfo.uid) == userId) {
    560                 return i;
    561             }
    562         }
    563         return -1;
    564     }
    565 
    566     void addPackage(String pkgName, int userId) {
    567         try {
    568             synchronized (mEntriesMap) {
    569                 if (DEBUG_LOCKING) Log.v(TAG, "addPackage acquired lock");
    570                 if (DEBUG) Log.i(TAG, "Adding package " + pkgName);
    571                 if (!mResumed) {
    572                     // If we are not resumed, we will do a full query the
    573                     // next time we resume, so there is no reason to do work
    574                     // here.
    575                     if (DEBUG_LOCKING) Log.v(TAG, "addPackage release lock: not resumed");
    576                     return;
    577                 }
    578                 if (indexOfApplicationInfoLocked(pkgName, userId) >= 0) {
    579                     if (DEBUG) Log.i(TAG, "Package already exists!");
    580                     if (DEBUG_LOCKING) Log.v(TAG, "addPackage release lock: already exists");
    581                     return;
    582                 }
    583                 ApplicationInfo info = mIpm.getApplicationInfo(pkgName,
    584                         mUm.isUserAdmin(userId) ? mAdminRetrieveFlags : mRetrieveFlags,
    585                         userId);
    586                 if (info == null) {
    587                     return;
    588                 }
    589                 if (!info.enabled) {
    590                     if (info.enabledSetting
    591                             != PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) {
    592                         return;
    593                     }
    594                     mHaveDisabledApps = true;
    595                 }
    596                 if (AppUtils.isInstant(info)) {
    597                     mHaveInstantApps = true;
    598                 }
    599                 mApplications.add(info);
    600                 if (!mBackgroundHandler.hasMessages(BackgroundHandler.MSG_LOAD_ENTRIES)) {
    601                     mBackgroundHandler.sendEmptyMessage(BackgroundHandler.MSG_LOAD_ENTRIES);
    602                 }
    603                 if (!mMainHandler.hasMessages(MainHandler.MSG_PACKAGE_LIST_CHANGED)) {
    604                     mMainHandler.sendEmptyMessage(MainHandler.MSG_PACKAGE_LIST_CHANGED);
    605                 }
    606                 if (DEBUG_LOCKING) Log.v(TAG, "addPackage releasing lock");
    607             }
    608         } catch (RemoteException e) {
    609         }
    610     }
    611 
    612     public void removePackage(String pkgName, int userId) {
    613         synchronized (mEntriesMap) {
    614             if (DEBUG_LOCKING) Log.v(TAG, "removePackage acquired lock");
    615             int idx = indexOfApplicationInfoLocked(pkgName, userId);
    616             if (DEBUG) Log.i(TAG, "removePackage: " + pkgName + " @ " + idx);
    617             if (idx >= 0) {
    618                 AppEntry entry = mEntriesMap.get(userId).get(pkgName);
    619                 if (DEBUG) Log.i(TAG, "removePackage: " + entry);
    620                 if (entry != null) {
    621                     mEntriesMap.get(userId).remove(pkgName);
    622                     mAppEntries.remove(entry);
    623                 }
    624                 ApplicationInfo info = mApplications.get(idx);
    625                 mApplications.remove(idx);
    626                 if (!info.enabled) {
    627                     mHaveDisabledApps = false;
    628                     for (ApplicationInfo otherInfo : mApplications) {
    629                         if (!otherInfo.enabled) {
    630                             mHaveDisabledApps = true;
    631                             break;
    632                         }
    633                     }
    634                 }
    635                 if (AppUtils.isInstant(info)) {
    636                     mHaveInstantApps = false;
    637                     for (ApplicationInfo otherInfo : mApplications) {
    638                         if (AppUtils.isInstant(otherInfo)) {
    639                             mHaveInstantApps = true;
    640                             break;
    641                         }
    642                     }
    643                 }
    644                 if (!mMainHandler.hasMessages(MainHandler.MSG_PACKAGE_LIST_CHANGED)) {
    645                     mMainHandler.sendEmptyMessage(MainHandler.MSG_PACKAGE_LIST_CHANGED);
    646                 }
    647             }
    648             if (DEBUG_LOCKING) Log.v(TAG, "removePackage releasing lock");
    649         }
    650     }
    651 
    652     public void invalidatePackage(String pkgName, int userId) {
    653         removePackage(pkgName, userId);
    654         addPackage(pkgName, userId);
    655     }
    656 
    657     private void addUser(int userId) {
    658         final int profileIds[] = mUm.getProfileIdsWithDisabled(UserHandle.myUserId());
    659         if (ArrayUtils.contains(profileIds, userId)) {
    660             synchronized (mEntriesMap) {
    661                 mEntriesMap.put(userId, new HashMap<String, AppEntry>());
    662                 if (mResumed) {
    663                     // If resumed, Manually pause, then cause a resume to repopulate the app list.
    664                     // This is the simplest way to reload the packages so that the new user
    665                     // is included.  Otherwise the list will be repopulated on next resume.
    666                     doPauseLocked();
    667                     doResumeIfNeededLocked();
    668                 }
    669                 if (!mMainHandler.hasMessages(MainHandler.MSG_PACKAGE_LIST_CHANGED)) {
    670                     mMainHandler.sendEmptyMessage(MainHandler.MSG_PACKAGE_LIST_CHANGED);
    671                 }
    672             }
    673         }
    674     }
    675 
    676     private void removeUser(int userId) {
    677         synchronized (mEntriesMap) {
    678             HashMap<String, AppEntry> userMap = mEntriesMap.get(userId);
    679             if (userMap != null) {
    680                 for (AppEntry appEntry : userMap.values()) {
    681                     mAppEntries.remove(appEntry);
    682                     mApplications.remove(appEntry.info);
    683                 }
    684                 mEntriesMap.remove(userId);
    685                 if (!mMainHandler.hasMessages(MainHandler.MSG_PACKAGE_LIST_CHANGED)) {
    686                     mMainHandler.sendEmptyMessage(MainHandler.MSG_PACKAGE_LIST_CHANGED);
    687                 }
    688             }
    689         }
    690     }
    691 
    692     private AppEntry getEntryLocked(ApplicationInfo info) {
    693         int userId = UserHandle.getUserId(info.uid);
    694         AppEntry entry = mEntriesMap.get(userId).get(info.packageName);
    695         if (DEBUG) {
    696             Log.i(TAG, "Looking up entry of pkg " + info.packageName + ": " + entry);
    697         }
    698         if (entry == null) {
    699             if (isHiddenModule(info.packageName)) {
    700                 if (DEBUG) {
    701                     Log.i(TAG, "No AppEntry for " + info.packageName + " (hidden module)");
    702                 }
    703                 return null;
    704             }
    705             if (DEBUG) {
    706                 Log.i(TAG, "Creating AppEntry for " + info.packageName);
    707             }
    708             entry = new AppEntry(mContext, info, mCurId++);
    709             mEntriesMap.get(userId).put(info.packageName, entry);
    710             mAppEntries.add(entry);
    711         } else if (entry.info != info) {
    712             entry.info = info;
    713         }
    714         return entry;
    715     }
    716 
    717     // --------------------------------------------------------------
    718 
    719     private long getTotalInternalSize(PackageStats ps) {
    720         if (ps != null) {
    721             // We subtract the cache size because the system can clear it automatically and
    722             // |dataSize| is a superset of |cacheSize|.
    723             return ps.codeSize + ps.dataSize - ps.cacheSize;
    724         }
    725         return SIZE_INVALID;
    726     }
    727 
    728     private long getTotalExternalSize(PackageStats ps) {
    729         if (ps != null) {
    730             // We also include the cache size here because for non-emulated
    731             // we don't automatically clean cache files.
    732             return ps.externalCodeSize + ps.externalDataSize
    733                     + ps.externalCacheSize
    734                     + ps.externalMediaSize + ps.externalObbSize;
    735         }
    736         return SIZE_INVALID;
    737     }
    738 
    739     private String getSizeStr(long size) {
    740         if (size >= 0) {
    741             return Formatter.formatFileSize(mContext, size);
    742         }
    743         return null;
    744     }
    745 
    746     void rebuildActiveSessions() {
    747         synchronized (mEntriesMap) {
    748             if (!mSessionsChanged) {
    749                 return;
    750             }
    751             mActiveSessions.clear();
    752             for (int i = 0; i < mSessions.size(); i++) {
    753                 Session s = mSessions.get(i);
    754                 if (s.mResumed) {
    755                     mActiveSessions.add(new WeakReference<>(s));
    756                 }
    757             }
    758         }
    759     }
    760 
    761     public static String normalize(String str) {
    762         String tmp = Normalizer.normalize(str, Form.NFD);
    763         return REMOVE_DIACRITICALS_PATTERN.matcher(tmp)
    764                 .replaceAll("").toLowerCase();
    765     }
    766 
    767     public class Session implements LifecycleObserver {
    768 
    769         final Callbacks mCallbacks;
    770         boolean mResumed;
    771 
    772         // Rebuilding of app list.  Synchronized on mRebuildSync.
    773         final Object mRebuildSync = new Object();
    774         boolean mRebuildRequested;
    775         boolean mRebuildAsync;
    776         AppFilter mRebuildFilter;
    777         Comparator<AppEntry> mRebuildComparator;
    778         ArrayList<AppEntry> mRebuildResult;
    779         ArrayList<AppEntry> mLastAppList;
    780         boolean mRebuildForeground;
    781 
    782         private final boolean mHasLifecycle;
    783         @SessionFlags
    784         private int mFlags = DEFAULT_SESSION_FLAGS;
    785 
    786         Session(Callbacks callbacks, Lifecycle lifecycle) {
    787             mCallbacks = callbacks;
    788             if (lifecycle != null) {
    789                 lifecycle.addObserver(this);
    790                 mHasLifecycle = true;
    791             } else {
    792                 mHasLifecycle = false;
    793             }
    794         }
    795 
    796         @SessionFlags
    797         public int getSessionFlags() {
    798             return mFlags;
    799         }
    800 
    801         public void setSessionFlags(@SessionFlags int flags) {
    802             mFlags = flags;
    803         }
    804 
    805         @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
    806         public void onResume() {
    807             if (DEBUG_LOCKING) Log.v(TAG, "resume about to acquire lock...");
    808             synchronized (mEntriesMap) {
    809                 if (!mResumed) {
    810                     mResumed = true;
    811                     mSessionsChanged = true;
    812                     doPauseLocked();
    813                     doResumeIfNeededLocked();
    814                 }
    815             }
    816             if (DEBUG_LOCKING) Log.v(TAG, "...resume releasing lock");
    817         }
    818 
    819         @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
    820         public void onPause() {
    821             if (DEBUG_LOCKING) Log.v(TAG, "pause about to acquire lock...");
    822             synchronized (mEntriesMap) {
    823                 if (mResumed) {
    824                     mResumed = false;
    825                     mSessionsChanged = true;
    826                     mBackgroundHandler.removeMessages(BackgroundHandler.MSG_REBUILD_LIST, this);
    827                     doPauseIfNeededLocked();
    828                 }
    829                 if (DEBUG_LOCKING) Log.v(TAG, "...pause releasing lock");
    830             }
    831         }
    832 
    833         public ArrayList<AppEntry> getAllApps() {
    834             synchronized (mEntriesMap) {
    835                 return new ArrayList<>(mAppEntries);
    836             }
    837         }
    838 
    839         // Creates a new list of app entries with the given filter and comparator.
    840         public ArrayList<AppEntry> rebuild(AppFilter filter, Comparator<AppEntry> comparator) {
    841             return rebuild(filter, comparator, true);
    842         }
    843 
    844         public ArrayList<AppEntry> rebuild(AppFilter filter, Comparator<AppEntry> comparator,
    845                 boolean foreground) {
    846             synchronized (mRebuildSync) {
    847                 synchronized (mRebuildingSessions) {
    848                     mRebuildingSessions.add(this);
    849                     mRebuildRequested = true;
    850                     mRebuildAsync = true;
    851                     mRebuildFilter = filter;
    852                     mRebuildComparator = comparator;
    853                     mRebuildForeground = foreground;
    854                     mRebuildResult = null;
    855                     if (!mBackgroundHandler.hasMessages(BackgroundHandler.MSG_REBUILD_LIST)) {
    856                         Message msg = mBackgroundHandler.obtainMessage(
    857                                 BackgroundHandler.MSG_REBUILD_LIST);
    858                         mBackgroundHandler.sendMessage(msg);
    859                     }
    860                 }
    861 
    862                 return null;
    863             }
    864         }
    865 
    866         void handleRebuildList() {
    867             AppFilter filter;
    868             Comparator<AppEntry> comparator;
    869             synchronized (mRebuildSync) {
    870                 if (!mRebuildRequested) {
    871                     return;
    872                 }
    873 
    874                 filter = mRebuildFilter;
    875                 comparator = mRebuildComparator;
    876                 mRebuildRequested = false;
    877                 mRebuildFilter = null;
    878                 mRebuildComparator = null;
    879                 if (mRebuildForeground) {
    880                     Process.setThreadPriority(Process.THREAD_PRIORITY_FOREGROUND);
    881                     mRebuildForeground = false;
    882                 }
    883             }
    884 
    885             if (filter != null) {
    886                 filter.init(mContext);
    887             }
    888 
    889             final List<AppEntry> apps;
    890             synchronized (mEntriesMap) {
    891                 apps = new ArrayList<>(mAppEntries);
    892             }
    893 
    894             ArrayList<AppEntry> filteredApps = new ArrayList<>();
    895             if (DEBUG) {
    896                 Log.i(TAG, "Rebuilding...");
    897             }
    898             for (AppEntry entry : apps) {
    899                 if (entry != null && (filter == null || filter.filterApp(entry))) {
    900                     synchronized (mEntriesMap) {
    901                         if (DEBUG_LOCKING) {
    902                             Log.v(TAG, "rebuild acquired lock");
    903                         }
    904                         if (comparator != null) {
    905                             // Only need the label if we are going to be sorting.
    906                             entry.ensureLabel(mContext);
    907                         }
    908                         if (DEBUG) {
    909                             Log.i(TAG, "Using " + entry.info.packageName + ": " + entry);
    910                         }
    911                         filteredApps.add(entry);
    912                         if (DEBUG_LOCKING) {
    913                             Log.v(TAG, "rebuild releasing lock");
    914                         }
    915                     }
    916                 }
    917             }
    918 
    919             if (comparator != null) {
    920                 synchronized (mEntriesMap) {
    921                     // Locking to ensure that the background handler does not mutate
    922                     // the size of AppEntries used for ordering while sorting.
    923                     Collections.sort(filteredApps, comparator);
    924                 }
    925             }
    926 
    927             synchronized (mRebuildSync) {
    928                 if (!mRebuildRequested) {
    929                     mLastAppList = filteredApps;
    930                     if (!mRebuildAsync) {
    931                         mRebuildResult = filteredApps;
    932                         mRebuildSync.notifyAll();
    933                     } else {
    934                         if (!mMainHandler.hasMessages(MainHandler.MSG_REBUILD_COMPLETE, this)) {
    935                             Message msg = mMainHandler.obtainMessage(
    936                                     MainHandler.MSG_REBUILD_COMPLETE, this);
    937                             mMainHandler.sendMessage(msg);
    938                         }
    939                     }
    940                 }
    941             }
    942 
    943             Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
    944         }
    945 
    946         @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
    947         public void onDestroy() {
    948             if (!mHasLifecycle) {
    949                 // TODO: Legacy, remove this later once all usages are switched to Lifecycle
    950                 onPause();
    951             }
    952             synchronized (mEntriesMap) {
    953                 mSessions.remove(this);
    954             }
    955         }
    956     }
    957 
    958     class MainHandler extends Handler {
    959         static final int MSG_REBUILD_COMPLETE = 1;
    960         static final int MSG_PACKAGE_LIST_CHANGED = 2;
    961         static final int MSG_PACKAGE_ICON_CHANGED = 3;
    962         static final int MSG_PACKAGE_SIZE_CHANGED = 4;
    963         static final int MSG_ALL_SIZES_COMPUTED = 5;
    964         static final int MSG_RUNNING_STATE_CHANGED = 6;
    965         static final int MSG_LAUNCHER_INFO_CHANGED = 7;
    966         static final int MSG_LOAD_ENTRIES_COMPLETE = 8;
    967 
    968         public MainHandler(Looper looper) {
    969             super(looper);
    970         }
    971 
    972         @Override
    973         public void handleMessage(Message msg) {
    974             rebuildActiveSessions();
    975             switch (msg.what) {
    976                 case MSG_REBUILD_COMPLETE: {
    977                     Session s = (Session) msg.obj;
    978                     for (WeakReference<Session> sessionRef : mActiveSessions) {
    979                         final Session session = sessionRef.get();
    980                         if (session != null && session == s) {
    981                             s.mCallbacks.onRebuildComplete(s.mLastAppList);
    982                         }
    983                     }
    984                 } break;
    985                 case MSG_PACKAGE_LIST_CHANGED: {
    986                     for (WeakReference<Session> sessionRef : mActiveSessions) {
    987                         final Session session = sessionRef.get();
    988                         if (session != null) {
    989                             session.mCallbacks.onPackageListChanged();
    990                         }
    991                     }
    992                 } break;
    993                 case MSG_PACKAGE_ICON_CHANGED: {
    994                     for (WeakReference<Session> sessionRef : mActiveSessions) {
    995                         final Session session = sessionRef.get();
    996                         if (session != null) {
    997                             session.mCallbacks.onPackageIconChanged();
    998                         }
    999                     }
   1000                 } break;
   1001                 case MSG_PACKAGE_SIZE_CHANGED: {
   1002                     for (WeakReference<Session> sessionRef : mActiveSessions) {
   1003                         final Session session = sessionRef.get();
   1004                         if (session != null) {
   1005                             session.mCallbacks.onPackageSizeChanged(
   1006                                     (String) msg.obj);
   1007                         }
   1008                     }
   1009                 } break;
   1010                 case MSG_ALL_SIZES_COMPUTED: {
   1011                     for (WeakReference<Session> sessionRef : mActiveSessions) {
   1012                         final Session session = sessionRef.get();
   1013                         if (session != null) {
   1014                             session.mCallbacks.onAllSizesComputed();
   1015                         }
   1016                     }
   1017                 } break;
   1018                 case MSG_RUNNING_STATE_CHANGED: {
   1019                     for (WeakReference<Session> sessionRef : mActiveSessions) {
   1020                         final Session session = sessionRef.get();
   1021                         if (session != null) {
   1022                             session.mCallbacks.onRunningStateChanged(
   1023                                     msg.arg1 != 0);
   1024                         }
   1025                     }
   1026                 } break;
   1027                 case MSG_LAUNCHER_INFO_CHANGED: {
   1028                     for (WeakReference<Session> sessionRef : mActiveSessions) {
   1029                         final Session session = sessionRef.get();
   1030                         if (session != null) {
   1031                             session.mCallbacks.onLauncherInfoChanged();
   1032                         }
   1033                     }
   1034                 } break;
   1035                 case MSG_LOAD_ENTRIES_COMPLETE: {
   1036                     for (WeakReference<Session> sessionRef : mActiveSessions) {
   1037                         final Session session = sessionRef.get();
   1038                         if (session != null) {
   1039                             session.mCallbacks.onLoadEntriesCompleted();
   1040                         }
   1041                     }
   1042                 } break;
   1043             }
   1044         }
   1045     }
   1046 
   1047     private class BackgroundHandler extends Handler {
   1048         static final int MSG_REBUILD_LIST = 1;
   1049         static final int MSG_LOAD_ENTRIES = 2;
   1050         static final int MSG_LOAD_HOME_APP = 3;
   1051         static final int MSG_LOAD_LAUNCHER = 4;
   1052         static final int MSG_LOAD_LEANBACK_LAUNCHER = 5;
   1053         static final int MSG_LOAD_ICONS = 6;
   1054         static final int MSG_LOAD_SIZES = 7;
   1055 
   1056         boolean mRunning;
   1057 
   1058         BackgroundHandler(Looper looper) {
   1059             super(looper);
   1060         }
   1061 
   1062         @Override
   1063         public void handleMessage(Message msg) {
   1064             // Always try rebuilding list first thing, if needed.
   1065             ArrayList<Session> rebuildingSessions = null;
   1066             synchronized (mRebuildingSessions) {
   1067                 if (mRebuildingSessions.size() > 0) {
   1068                     rebuildingSessions = new ArrayList<Session>(mRebuildingSessions);
   1069                     mRebuildingSessions.clear();
   1070                 }
   1071             }
   1072             if (rebuildingSessions != null) {
   1073                 for (int i = 0; i < rebuildingSessions.size(); i++) {
   1074                     rebuildingSessions.get(i).handleRebuildList();
   1075                 }
   1076             }
   1077 
   1078             int flags = getCombinedSessionFlags(mSessions);
   1079 
   1080             switch (msg.what) {
   1081                 case MSG_REBUILD_LIST: {
   1082                 } break;
   1083                 case MSG_LOAD_ENTRIES: {
   1084                     int numDone = 0;
   1085                     synchronized (mEntriesMap) {
   1086                         if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_ENTRIES acquired lock");
   1087                         for (int i = 0; i < mApplications.size() && numDone < 6; i++) {
   1088                             if (!mRunning) {
   1089                                 mRunning = true;
   1090                                 Message m = mMainHandler.obtainMessage(
   1091                                         MainHandler.MSG_RUNNING_STATE_CHANGED, 1);
   1092                                 mMainHandler.sendMessage(m);
   1093                             }
   1094                             ApplicationInfo info = mApplications.get(i);
   1095                             int userId = UserHandle.getUserId(info.uid);
   1096                             if (mEntriesMap.get(userId).get(info.packageName) == null) {
   1097                                 numDone++;
   1098                                 getEntryLocked(info);
   1099                             }
   1100                             if (userId != 0 && mEntriesMap.indexOfKey(0) >= 0) {
   1101                                 // If this app is for a profile and we are on the owner, remove
   1102                                 // the owner entry if it isn't installed.  This will prevent
   1103                                 // duplicates of work only apps showing up as 'not installed
   1104                                 // for this user'.
   1105                                 // Note: This depends on us traversing the users in order, which
   1106                                 // happens because of the way we generate the list in
   1107                                 // doResumeIfNeededLocked.
   1108                                 AppEntry entry = mEntriesMap.get(0).get(info.packageName);
   1109                                 if (entry != null && !hasFlag(entry.info.flags,
   1110                                         ApplicationInfo.FLAG_INSTALLED)) {
   1111                                     mEntriesMap.get(0).remove(info.packageName);
   1112                                     mAppEntries.remove(entry);
   1113                                 }
   1114                             }
   1115                         }
   1116                         if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_ENTRIES releasing lock");
   1117                     }
   1118 
   1119                     if (numDone >= 6) {
   1120                         sendEmptyMessage(MSG_LOAD_ENTRIES);
   1121                     } else {
   1122                         if (!mMainHandler.hasMessages(MainHandler.MSG_LOAD_ENTRIES_COMPLETE)) {
   1123                             mMainHandler.sendEmptyMessage(MainHandler.MSG_LOAD_ENTRIES_COMPLETE);
   1124                         }
   1125                         sendEmptyMessage(MSG_LOAD_HOME_APP);
   1126                     }
   1127                 } break;
   1128                 case MSG_LOAD_HOME_APP: {
   1129                     if (hasFlag(flags, FLAG_SESSION_REQUEST_HOME_APP)) {
   1130                         final List<ResolveInfo> homeActivities = new ArrayList<>();
   1131                         mPm.getHomeActivities(homeActivities);
   1132                         synchronized (mEntriesMap) {
   1133                             final int entryCount = mEntriesMap.size();
   1134                             for (int i = 0; i < entryCount; i++) {
   1135                                 if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_HOME_APP acquired lock");
   1136                                 final HashMap<String, AppEntry> userEntries = mEntriesMap.valueAt(
   1137                                         i);
   1138                                 for (ResolveInfo activity : homeActivities) {
   1139                                     String packageName = activity.activityInfo.packageName;
   1140                                     AppEntry entry = userEntries.get(packageName);
   1141                                     if (entry != null) {
   1142                                         entry.isHomeApp = true;
   1143                                     }
   1144                                 }
   1145                                 if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_HOME_APP releasing lock");
   1146                             }
   1147                         }
   1148                     }
   1149                     sendEmptyMessage(MSG_LOAD_LAUNCHER);
   1150                 } break;
   1151                 case MSG_LOAD_LAUNCHER:
   1152                 case MSG_LOAD_LEANBACK_LAUNCHER: {
   1153                     if ((msg.what == MSG_LOAD_LAUNCHER &&
   1154                             hasFlag(flags, FLAG_SESSION_REQUEST_LAUNCHER))
   1155                             || (msg.what == MSG_LOAD_LEANBACK_LAUNCHER &&
   1156                             hasFlag(flags, FLAG_SESSION_REQUEST_LEANBACK_LAUNCHER))) {
   1157 
   1158                         Intent launchIntent = new Intent(Intent.ACTION_MAIN, null);
   1159                         launchIntent.addCategory(msg.what == MSG_LOAD_LAUNCHER
   1160                                 ? Intent.CATEGORY_LAUNCHER : Intent.CATEGORY_LEANBACK_LAUNCHER);
   1161                         for (int i = 0; i < mEntriesMap.size(); i++) {
   1162                             int userId = mEntriesMap.keyAt(i);
   1163                             // If we do not specify MATCH_DIRECT_BOOT_AWARE or
   1164                             // MATCH_DIRECT_BOOT_UNAWARE, system will derive and update the flags
   1165                             // according to the user's lock state. When the user is locked,
   1166                             // components with ComponentInfo#directBootAware == false will be
   1167                             // filtered. W should explicitly include both direct boot aware and
   1168                             // unaware component here.
   1169                             List<ResolveInfo> intents = mPm.queryIntentActivitiesAsUser(
   1170                                     launchIntent,
   1171                                     PackageManager.MATCH_DISABLED_COMPONENTS
   1172                                             | PackageManager.MATCH_DIRECT_BOOT_AWARE
   1173                                             | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
   1174                                     userId
   1175                             );
   1176                             synchronized (mEntriesMap) {
   1177                                 if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_LAUNCHER acquired lock");
   1178                                 HashMap<String, AppEntry> userEntries = mEntriesMap.valueAt(i);
   1179                                 final int N = intents.size();
   1180                                 for (int j = 0; j < N; j++) {
   1181                                     ResolveInfo resolveInfo = intents.get(j);
   1182                                     String packageName = resolveInfo.activityInfo.packageName;
   1183                                     AppEntry entry = userEntries.get(packageName);
   1184                                     if (entry != null) {
   1185                                         entry.hasLauncherEntry = true;
   1186                                         entry.launcherEntryEnabled |=
   1187                                                 resolveInfo.activityInfo.enabled;
   1188                                     } else {
   1189                                         Log.w(TAG, "Cannot find pkg: " + packageName
   1190                                                 + " on user " + userId);
   1191                                     }
   1192                                 }
   1193                                 if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_LAUNCHER releasing lock");
   1194                             }
   1195                         }
   1196 
   1197                         if (!mMainHandler.hasMessages(MainHandler.MSG_LAUNCHER_INFO_CHANGED)) {
   1198                             mMainHandler.sendEmptyMessage(MainHandler.MSG_LAUNCHER_INFO_CHANGED);
   1199                         }
   1200                     }
   1201                     if (msg.what == MSG_LOAD_LAUNCHER) {
   1202                         sendEmptyMessage(MSG_LOAD_LEANBACK_LAUNCHER);
   1203                     } else {
   1204                         sendEmptyMessage(MSG_LOAD_ICONS);
   1205                     }
   1206                 } break;
   1207                 case MSG_LOAD_ICONS: {
   1208                     if (hasFlag(flags, FLAG_SESSION_REQUEST_ICONS)) {
   1209                         int numDone = 0;
   1210                         synchronized (mEntriesMap) {
   1211                             if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_ICONS acquired lock");
   1212                             for (int i = 0; i < mAppEntries.size() && numDone < 2; i++) {
   1213                                 AppEntry entry = mAppEntries.get(i);
   1214                                 if (entry.icon == null || !entry.mounted) {
   1215                                     synchronized (entry) {
   1216                                         if (entry.ensureIconLocked(mContext, mDrawableFactory)) {
   1217                                             if (!mRunning) {
   1218                                                 mRunning = true;
   1219                                                 Message m = mMainHandler.obtainMessage(
   1220                                                         MainHandler.MSG_RUNNING_STATE_CHANGED, 1);
   1221                                                 mMainHandler.sendMessage(m);
   1222                                             }
   1223                                             numDone++;
   1224                                         }
   1225                                     }
   1226                                 }
   1227                             }
   1228                             if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_ICONS releasing lock");
   1229                         }
   1230                         if (numDone > 0) {
   1231                             if (!mMainHandler.hasMessages(MainHandler.MSG_PACKAGE_ICON_CHANGED)) {
   1232                                 mMainHandler.sendEmptyMessage(MainHandler.MSG_PACKAGE_ICON_CHANGED);
   1233                             }
   1234                         }
   1235                         if (numDone >= 2) {
   1236                             sendEmptyMessage(MSG_LOAD_ICONS);
   1237                             break;
   1238                         }
   1239                     }
   1240                     sendEmptyMessage(MSG_LOAD_SIZES);
   1241                 } break;
   1242                 case MSG_LOAD_SIZES: {
   1243                     if (hasFlag(flags, FLAG_SESSION_REQUEST_SIZES)) {
   1244                         synchronized (mEntriesMap) {
   1245                             if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_SIZES acquired lock");
   1246                             if (mCurComputingSizePkg != null) {
   1247                                 if (DEBUG_LOCKING) {
   1248                                     Log.v(TAG,
   1249                                             "MSG_LOAD_SIZES releasing: currently computing");
   1250                                 }
   1251                                 return;
   1252                             }
   1253 
   1254                             long now = SystemClock.uptimeMillis();
   1255                             for (int i = 0; i < mAppEntries.size(); i++) {
   1256                                 AppEntry entry = mAppEntries.get(i);
   1257                                 if (hasFlag(entry.info.flags, ApplicationInfo.FLAG_INSTALLED)
   1258                                         && (entry.size == SIZE_UNKNOWN || entry.sizeStale)) {
   1259                                     if (entry.sizeLoadStart == 0 ||
   1260                                             (entry.sizeLoadStart < (now - 20 * 1000))) {
   1261                                         if (!mRunning) {
   1262                                             mRunning = true;
   1263                                             Message m = mMainHandler.obtainMessage(
   1264                                                     MainHandler.MSG_RUNNING_STATE_CHANGED, 1);
   1265                                             mMainHandler.sendMessage(m);
   1266                                         }
   1267                                         entry.sizeLoadStart = now;
   1268                                         mCurComputingSizeUuid = entry.info.storageUuid;
   1269                                         mCurComputingSizePkg = entry.info.packageName;
   1270                                         mCurComputingSizeUserId = UserHandle.getUserId(
   1271                                                 entry.info.uid);
   1272 
   1273                                         mBackgroundHandler.post(() -> {
   1274                                             try {
   1275                                                 final StorageStats stats =
   1276                                                         mStats.queryStatsForPackage(
   1277                                                                 mCurComputingSizeUuid,
   1278                                                                 mCurComputingSizePkg,
   1279                                                                 UserHandle.of(
   1280                                                                         mCurComputingSizeUserId));
   1281                                                 final PackageStats legacy = new PackageStats(
   1282                                                         mCurComputingSizePkg,
   1283                                                         mCurComputingSizeUserId);
   1284                                                 legacy.codeSize = stats.getCodeBytes();
   1285                                                 legacy.dataSize = stats.getDataBytes();
   1286                                                 legacy.cacheSize = stats.getCacheBytes();
   1287                                                 try {
   1288                                                     mStatsObserver.onGetStatsCompleted(legacy,
   1289                                                             true);
   1290                                                 } catch (RemoteException ignored) {
   1291                                                 }
   1292                                             } catch (NameNotFoundException | IOException e) {
   1293                                                 Log.w(TAG, "Failed to query stats: " + e);
   1294                                                 try {
   1295                                                     mStatsObserver.onGetStatsCompleted(null, false);
   1296                                                 } catch (RemoteException ignored) {
   1297                                                 }
   1298                                             }
   1299 
   1300                                         });
   1301                                     }
   1302                                     if (DEBUG_LOCKING) {
   1303                                         Log.v(TAG,
   1304                                                 "MSG_LOAD_SIZES releasing: now computing");
   1305                                     }
   1306                                     return;
   1307                                 }
   1308                             }
   1309                             if (!mMainHandler.hasMessages(MainHandler.MSG_ALL_SIZES_COMPUTED)) {
   1310                                 mMainHandler.sendEmptyMessage(MainHandler.MSG_ALL_SIZES_COMPUTED);
   1311                                 mRunning = false;
   1312                                 Message m = mMainHandler.obtainMessage(
   1313                                         MainHandler.MSG_RUNNING_STATE_CHANGED, 0);
   1314                                 mMainHandler.sendMessage(m);
   1315                             }
   1316                             if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_SIZES releasing lock");
   1317                         }
   1318                     }
   1319                 } break;
   1320             }
   1321         }
   1322 
   1323         @SessionFlags
   1324         private int getCombinedSessionFlags(List<Session> sessions) {
   1325             synchronized (mEntriesMap) {
   1326                 int flags = 0;
   1327                 for (Session session : sessions) {
   1328                     flags |= session.mFlags;
   1329                 }
   1330                 return flags;
   1331             }
   1332         }
   1333 
   1334         final IPackageStatsObserver.Stub mStatsObserver = new IPackageStatsObserver.Stub() {
   1335             public void onGetStatsCompleted(PackageStats stats, boolean succeeded) {
   1336                 if (!succeeded) {
   1337                     // There is no meaningful information in stats if the call failed.
   1338                     return;
   1339                 }
   1340 
   1341                 boolean sizeChanged = false;
   1342                 synchronized (mEntriesMap) {
   1343                     if (DEBUG_LOCKING) Log.v(TAG, "onGetStatsCompleted acquired lock");
   1344                     HashMap<String, AppEntry> userMap = mEntriesMap.get(stats.userHandle);
   1345                     if (userMap == null) {
   1346                         // The user must have been removed.
   1347                         return;
   1348                     }
   1349                     AppEntry entry = userMap.get(stats.packageName);
   1350                     if (entry != null) {
   1351                         synchronized (entry) {
   1352                             entry.sizeStale = false;
   1353                             entry.sizeLoadStart = 0;
   1354                             long externalCodeSize = stats.externalCodeSize
   1355                                     + stats.externalObbSize;
   1356                             long externalDataSize = stats.externalDataSize
   1357                                     + stats.externalMediaSize;
   1358                             long newSize = externalCodeSize + externalDataSize
   1359                                     + getTotalInternalSize(stats);
   1360                             if (entry.size != newSize ||
   1361                                     entry.cacheSize != stats.cacheSize ||
   1362                                     entry.codeSize != stats.codeSize ||
   1363                                     entry.dataSize != stats.dataSize ||
   1364                                     entry.externalCodeSize != externalCodeSize ||
   1365                                     entry.externalDataSize != externalDataSize ||
   1366                                     entry.externalCacheSize != stats.externalCacheSize) {
   1367                                 entry.size = newSize;
   1368                                 entry.cacheSize = stats.cacheSize;
   1369                                 entry.codeSize = stats.codeSize;
   1370                                 entry.dataSize = stats.dataSize;
   1371                                 entry.externalCodeSize = externalCodeSize;
   1372                                 entry.externalDataSize = externalDataSize;
   1373                                 entry.externalCacheSize = stats.externalCacheSize;
   1374                                 entry.sizeStr = getSizeStr(entry.size);
   1375                                 entry.internalSize = getTotalInternalSize(stats);
   1376                                 entry.internalSizeStr = getSizeStr(entry.internalSize);
   1377                                 entry.externalSize = getTotalExternalSize(stats);
   1378                                 entry.externalSizeStr = getSizeStr(entry.externalSize);
   1379                                 if (DEBUG) {
   1380                                     Log.i(TAG, "Set size of " + entry.label + " " + entry
   1381                                             + ": " + entry.sizeStr);
   1382                                 }
   1383                                 sizeChanged = true;
   1384                             }
   1385                         }
   1386                         if (sizeChanged) {
   1387                             Message msg = mMainHandler.obtainMessage(
   1388                                     MainHandler.MSG_PACKAGE_SIZE_CHANGED, stats.packageName);
   1389                             mMainHandler.sendMessage(msg);
   1390                         }
   1391                     }
   1392                     if (mCurComputingSizePkg != null
   1393                             && (mCurComputingSizePkg.equals(stats.packageName)
   1394                             && mCurComputingSizeUserId == stats.userHandle)) {
   1395                         mCurComputingSizePkg = null;
   1396                         sendEmptyMessage(MSG_LOAD_SIZES);
   1397                     }
   1398                     if (DEBUG_LOCKING) Log.v(TAG, "onGetStatsCompleted releasing lock");
   1399                 }
   1400             }
   1401         };
   1402     }
   1403 
   1404     /**
   1405      * Receives notifications when applications are added/removed.
   1406      */
   1407     private class PackageIntentReceiver extends BroadcastReceiver {
   1408         void registerReceiver() {
   1409             IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
   1410             filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
   1411             filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
   1412             filter.addDataScheme("package");
   1413             mContext.registerReceiver(this, filter);
   1414             // Register for events related to sdcard installation.
   1415             IntentFilter sdFilter = new IntentFilter();
   1416             sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
   1417             sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
   1418             mContext.registerReceiver(this, sdFilter);
   1419             // Register for events related to user creation/deletion.
   1420             IntentFilter userFilter = new IntentFilter();
   1421             userFilter.addAction(Intent.ACTION_USER_ADDED);
   1422             userFilter.addAction(Intent.ACTION_USER_REMOVED);
   1423             mContext.registerReceiver(this, userFilter);
   1424         }
   1425 
   1426         void unregisterReceiver() {
   1427             mContext.unregisterReceiver(this);
   1428         }
   1429 
   1430         @Override
   1431         public void onReceive(Context context, Intent intent) {
   1432             String actionStr = intent.getAction();
   1433             if (Intent.ACTION_PACKAGE_ADDED.equals(actionStr)) {
   1434                 Uri data = intent.getData();
   1435                 String pkgName = data.getEncodedSchemeSpecificPart();
   1436                 for (int i = 0; i < mEntriesMap.size(); i++) {
   1437                     addPackage(pkgName, mEntriesMap.keyAt(i));
   1438                 }
   1439             } else if (Intent.ACTION_PACKAGE_REMOVED.equals(actionStr)) {
   1440                 Uri data = intent.getData();
   1441                 String pkgName = data.getEncodedSchemeSpecificPart();
   1442                 for (int i = 0; i < mEntriesMap.size(); i++) {
   1443                     removePackage(pkgName, mEntriesMap.keyAt(i));
   1444                 }
   1445             } else if (Intent.ACTION_PACKAGE_CHANGED.equals(actionStr)) {
   1446                 Uri data = intent.getData();
   1447                 String pkgName = data.getEncodedSchemeSpecificPart();
   1448                 for (int i = 0; i < mEntriesMap.size(); i++) {
   1449                     invalidatePackage(pkgName, mEntriesMap.keyAt(i));
   1450                 }
   1451             } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(actionStr) ||
   1452                     Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(actionStr)) {
   1453                 // When applications become available or unavailable (perhaps because
   1454                 // the SD card was inserted or ejected) we need to refresh the
   1455                 // AppInfo with new label, icon and size information as appropriate
   1456                 // given the newfound (un)availability of the application.
   1457                 // A simple way to do that is to treat the refresh as a package
   1458                 // removal followed by a package addition.
   1459                 String pkgList[] = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
   1460                 if (pkgList == null || pkgList.length == 0) {
   1461                     // Ignore
   1462                     return;
   1463                 }
   1464                 boolean avail = Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(actionStr);
   1465                 if (avail) {
   1466                     for (String pkgName : pkgList) {
   1467                         for (int i = 0; i < mEntriesMap.size(); i++) {
   1468                             invalidatePackage(pkgName, mEntriesMap.keyAt(i));
   1469                         }
   1470                     }
   1471                 }
   1472             } else if (Intent.ACTION_USER_ADDED.equals(actionStr)) {
   1473                 addUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL));
   1474             } else if (Intent.ACTION_USER_REMOVED.equals(actionStr)) {
   1475                 removeUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL));
   1476             }
   1477         }
   1478     }
   1479 
   1480     public interface Callbacks {
   1481         void onRunningStateChanged(boolean running);
   1482 
   1483         void onPackageListChanged();
   1484 
   1485         void onRebuildComplete(ArrayList<AppEntry> apps);
   1486 
   1487         void onPackageIconChanged();
   1488 
   1489         void onPackageSizeChanged(String packageName);
   1490 
   1491         void onAllSizesComputed();
   1492 
   1493         void onLauncherInfoChanged();
   1494 
   1495         void onLoadEntriesCompleted();
   1496     }
   1497 
   1498     public static class SizeInfo {
   1499         public long cacheSize;
   1500         public long codeSize;
   1501         public long dataSize;
   1502         public long externalCodeSize;
   1503         public long externalDataSize;
   1504 
   1505         // This is the part of externalDataSize that is in the cache
   1506         // section of external storage.  Note that we don't just combine
   1507         // this with cacheSize because currently the platform can't
   1508         // automatically trim this data when needed, so it is something
   1509         // the user may need to manage.  The externalDataSize also includes
   1510         // this value, since what this is here is really the part of
   1511         // externalDataSize that we can just consider to be "cache" files
   1512         // for purposes of cleaning them up in the app details UI.
   1513         public long externalCacheSize;
   1514     }
   1515 
   1516     public static class AppEntry extends SizeInfo {
   1517         public final File apkFile;
   1518         public final long id;
   1519         public String label;
   1520         public long size;
   1521         public long internalSize;
   1522         public long externalSize;
   1523 
   1524         public boolean mounted;
   1525 
   1526         /**
   1527          * Setting this to {@code true} prevents the entry to be filtered by
   1528          * {@link #FILTER_DOWNLOADED_AND_LAUNCHER}.
   1529          */
   1530         public boolean hasLauncherEntry;
   1531 
   1532         /**
   1533          * Whether the component that has a launcher intent filter is enabled.
   1534          */
   1535         public boolean launcherEntryEnabled;
   1536 
   1537         /**
   1538          * Whether or not it's a Home app.
   1539          */
   1540         public boolean isHomeApp;
   1541 
   1542         public String getNormalizedLabel() {
   1543             if (normalizedLabel != null) {
   1544                 return normalizedLabel;
   1545             }
   1546             normalizedLabel = normalize(label);
   1547             return normalizedLabel;
   1548         }
   1549 
   1550         // Need to synchronize on 'this' for the following.
   1551         public ApplicationInfo info;
   1552         public Drawable icon;
   1553         public String sizeStr;
   1554         public String internalSizeStr;
   1555         public String externalSizeStr;
   1556         public boolean sizeStale;
   1557         public long sizeLoadStart;
   1558 
   1559         public String normalizedLabel;
   1560 
   1561         // A location where extra info can be placed to be used by custom filters.
   1562         public Object extraInfo;
   1563 
   1564         @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
   1565         public AppEntry(Context context, ApplicationInfo info, long id) {
   1566             apkFile = new File(info.sourceDir);
   1567             this.id = id;
   1568             this.info = info;
   1569             this.size = SIZE_UNKNOWN;
   1570             this.sizeStale = true;
   1571             ensureLabel(context);
   1572         }
   1573 
   1574         public void ensureLabel(Context context) {
   1575             if (this.label == null || !this.mounted) {
   1576                 if (!this.apkFile.exists()) {
   1577                     this.mounted = false;
   1578                     this.label = info.packageName;
   1579                 } else {
   1580                     this.mounted = true;
   1581                     CharSequence label = info.loadLabel(context.getPackageManager());
   1582                     this.label = label != null ? label.toString() : info.packageName;
   1583                 }
   1584             }
   1585         }
   1586 
   1587         boolean ensureIconLocked(Context context, IconDrawableFactory drawableFactory) {
   1588             if (this.icon == null) {
   1589                 if (this.apkFile.exists()) {
   1590                     this.icon = drawableFactory.getBadgedIcon(info);
   1591                     return true;
   1592                 } else {
   1593                     this.mounted = false;
   1594                     this.icon = context.getDrawable(R.drawable.sym_app_on_sd_unavailable_icon);
   1595                 }
   1596             } else if (!this.mounted) {
   1597                 // If the app wasn't mounted but is now mounted, reload
   1598                 // its icon.
   1599                 if (this.apkFile.exists()) {
   1600                     this.mounted = true;
   1601                     this.icon = drawableFactory.getBadgedIcon(info);
   1602                     return true;
   1603                 }
   1604             }
   1605             return false;
   1606         }
   1607 
   1608         public String getVersion(Context context) {
   1609             try {
   1610                 return context.getPackageManager().getPackageInfo(info.packageName, 0).versionName;
   1611             } catch (PackageManager.NameNotFoundException e) {
   1612                 return "";
   1613             }
   1614         }
   1615     }
   1616 
   1617     private static boolean hasFlag(int flags, int flag) {
   1618         return (flags & flag) != 0;
   1619     }
   1620 
   1621     /**
   1622      * Compare by label, then package name, then uid.
   1623      */
   1624     public static final Comparator<AppEntry> ALPHA_COMPARATOR = new Comparator<AppEntry>() {
   1625         private final Collator sCollator = Collator.getInstance();
   1626 
   1627         @Override
   1628         public int compare(AppEntry object1, AppEntry object2) {
   1629             int compareResult = sCollator.compare(object1.label, object2.label);
   1630             if (compareResult != 0) {
   1631                 return compareResult;
   1632             }
   1633             if (object1.info != null && object2.info != null) {
   1634                 compareResult =
   1635                         sCollator.compare(object1.info.packageName, object2.info.packageName);
   1636                 if (compareResult != 0) {
   1637                     return compareResult;
   1638                 }
   1639             }
   1640 
   1641             return object1.info.uid - object2.info.uid;
   1642         }
   1643     };
   1644 
   1645     public static final Comparator<AppEntry> SIZE_COMPARATOR
   1646             = new Comparator<AppEntry>() {
   1647         @Override
   1648         public int compare(AppEntry object1, AppEntry object2) {
   1649             if (object1.size < object2.size) return 1;
   1650             if (object1.size > object2.size) return -1;
   1651             return ALPHA_COMPARATOR.compare(object1, object2);
   1652         }
   1653     };
   1654 
   1655     public static final Comparator<AppEntry> INTERNAL_SIZE_COMPARATOR
   1656             = new Comparator<AppEntry>() {
   1657         @Override
   1658         public int compare(AppEntry object1, AppEntry object2) {
   1659             if (object1.internalSize < object2.internalSize) return 1;
   1660             if (object1.internalSize > object2.internalSize) return -1;
   1661             return ALPHA_COMPARATOR.compare(object1, object2);
   1662         }
   1663     };
   1664 
   1665     public static final Comparator<AppEntry> EXTERNAL_SIZE_COMPARATOR
   1666             = new Comparator<AppEntry>() {
   1667         @Override
   1668         public int compare(AppEntry object1, AppEntry object2) {
   1669             if (object1.externalSize < object2.externalSize) return 1;
   1670             if (object1.externalSize > object2.externalSize) return -1;
   1671             return ALPHA_COMPARATOR.compare(object1, object2);
   1672         }
   1673     };
   1674 
   1675     public interface AppFilter {
   1676         void init();
   1677 
   1678         default void init(Context context) {
   1679             init();
   1680         }
   1681 
   1682         boolean filterApp(AppEntry info);
   1683     }
   1684 
   1685     public static final AppFilter FILTER_PERSONAL = new AppFilter() {
   1686         private int mCurrentUser;
   1687 
   1688         @Override
   1689         public void init() {
   1690             mCurrentUser = ActivityManager.getCurrentUser();
   1691         }
   1692 
   1693         @Override
   1694         public boolean filterApp(AppEntry entry) {
   1695             return UserHandle.getUserId(entry.info.uid) == mCurrentUser;
   1696         }
   1697     };
   1698 
   1699     public static final AppFilter FILTER_WITHOUT_DISABLED_UNTIL_USED = new AppFilter() {
   1700         @Override
   1701         public void init() {
   1702             // do nothing
   1703         }
   1704 
   1705         @Override
   1706         public boolean filterApp(AppEntry entry) {
   1707             return entry.info.enabledSetting
   1708                     != PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED;
   1709         }
   1710     };
   1711 
   1712     public static final AppFilter FILTER_WORK = new AppFilter() {
   1713         private int mCurrentUser;
   1714 
   1715         @Override
   1716         public void init() {
   1717             mCurrentUser = ActivityManager.getCurrentUser();
   1718         }
   1719 
   1720         @Override
   1721         public boolean filterApp(AppEntry entry) {
   1722             return UserHandle.getUserId(entry.info.uid) != mCurrentUser;
   1723         }
   1724     };
   1725 
   1726     /**
   1727      * Displays a combined list with "downloaded" and "visible in launcher" apps only.
   1728      */
   1729     public static final AppFilter FILTER_DOWNLOADED_AND_LAUNCHER = new AppFilter() {
   1730         @Override
   1731         public void init() {
   1732         }
   1733 
   1734         @Override
   1735         public boolean filterApp(AppEntry entry) {
   1736             if (AppUtils.isInstant(entry.info)) {
   1737                 return false;
   1738             } else if (hasFlag(entry.info.flags, ApplicationInfo.FLAG_UPDATED_SYSTEM_APP)) {
   1739                 return true;
   1740             } else if (!hasFlag(entry.info.flags, ApplicationInfo.FLAG_SYSTEM)) {
   1741                 return true;
   1742             } else if (entry.hasLauncherEntry) {
   1743                 return true;
   1744             } else if (hasFlag(entry.info.flags, ApplicationInfo.FLAG_SYSTEM) && entry.isHomeApp) {
   1745                 return true;
   1746             }
   1747             return false;
   1748         }
   1749     };
   1750 
   1751     /**
   1752      * Displays a combined list with "downloaded" and "visible in launcher" apps only.
   1753      */
   1754     public static final AppFilter FILTER_DOWNLOADED_AND_LAUNCHER_AND_INSTANT = new AppFilter() {
   1755 
   1756         @Override
   1757         public void init() {
   1758         }
   1759 
   1760         @Override
   1761         public boolean filterApp(AppEntry entry) {
   1762             return AppUtils.isInstant(entry.info)
   1763                     || FILTER_DOWNLOADED_AND_LAUNCHER.filterApp(entry);
   1764         }
   1765 
   1766     };
   1767 
   1768     public static final AppFilter FILTER_THIRD_PARTY = new AppFilter() {
   1769         @Override
   1770         public void init() {
   1771         }
   1772 
   1773         @Override
   1774         public boolean filterApp(AppEntry entry) {
   1775             if (hasFlag(entry.info.flags, ApplicationInfo.FLAG_UPDATED_SYSTEM_APP)) {
   1776                 return true;
   1777             } else if (!hasFlag(entry.info.flags, ApplicationInfo.FLAG_SYSTEM)) {
   1778                 return true;
   1779             }
   1780             return false;
   1781         }
   1782     };
   1783 
   1784     public static final AppFilter FILTER_DISABLED = new AppFilter() {
   1785         @Override
   1786         public void init() {
   1787         }
   1788 
   1789         @Override
   1790         public boolean filterApp(AppEntry entry) {
   1791             return !entry.info.enabled && !AppUtils.isInstant(entry.info);
   1792         }
   1793     };
   1794 
   1795     public static final AppFilter FILTER_INSTANT = new AppFilter() {
   1796         @Override
   1797         public void init() {
   1798         }
   1799 
   1800         @Override
   1801         public boolean filterApp(AppEntry entry) {
   1802             return AppUtils.isInstant(entry.info);
   1803         }
   1804     };
   1805 
   1806     public static final AppFilter FILTER_ALL_ENABLED = new AppFilter() {
   1807         @Override
   1808         public void init() {
   1809         }
   1810 
   1811         @Override
   1812         public boolean filterApp(AppEntry entry) {
   1813             return entry.info.enabled && !AppUtils.isInstant(entry.info);
   1814         }
   1815     };
   1816 
   1817     public static final AppFilter FILTER_EVERYTHING = new AppFilter() {
   1818         @Override
   1819         public void init() {
   1820         }
   1821 
   1822         @Override
   1823         public boolean filterApp(AppEntry entry) {
   1824             return true;
   1825         }
   1826     };
   1827 
   1828     public static final AppFilter FILTER_WITH_DOMAIN_URLS = new AppFilter() {
   1829         @Override
   1830         public void init() {
   1831         }
   1832 
   1833         @Override
   1834         public boolean filterApp(AppEntry entry) {
   1835             return !AppUtils.isInstant(entry.info)
   1836                     && hasFlag(entry.info.privateFlags,
   1837                     ApplicationInfo.PRIVATE_FLAG_HAS_DOMAIN_URLS);
   1838         }
   1839     };
   1840 
   1841     public static final AppFilter FILTER_NOT_HIDE = new AppFilter() {
   1842         private String[] mHidePackageNames;
   1843 
   1844         @Override
   1845         public void init(Context context) {
   1846             mHidePackageNames = context.getResources()
   1847                     .getStringArray(R.array.config_hideWhenDisabled_packageNames);
   1848         }
   1849 
   1850         @Override
   1851         public void init() {
   1852         }
   1853 
   1854         @Override
   1855         public boolean filterApp(AppEntry entry) {
   1856             if (ArrayUtils.contains(mHidePackageNames, entry.info.packageName)) {
   1857                 if (!entry.info.enabled) {
   1858                     return false;
   1859                 } else if (entry.info.enabledSetting ==
   1860                         PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED) {
   1861                     return false;
   1862                 }
   1863             }
   1864 
   1865             return true;
   1866         }
   1867     };
   1868 
   1869     public static final AppFilter FILTER_GAMES = new AppFilter() {
   1870         @Override
   1871         public void init() {
   1872         }
   1873 
   1874         @Override
   1875         public boolean filterApp(ApplicationsState.AppEntry info) {
   1876             // TODO: Update for the new game category.
   1877             boolean isGame;
   1878             synchronized (info.info) {
   1879                 isGame = hasFlag(info.info.flags, ApplicationInfo.FLAG_IS_GAME)
   1880                         || info.info.category == ApplicationInfo.CATEGORY_GAME;
   1881             }
   1882             return isGame;
   1883         }
   1884     };
   1885 
   1886     public static class VolumeFilter implements AppFilter {
   1887         private final String mVolumeUuid;
   1888 
   1889         public VolumeFilter(String volumeUuid) {
   1890             mVolumeUuid = volumeUuid;
   1891         }
   1892 
   1893         @Override
   1894         public void init() {
   1895         }
   1896 
   1897         @Override
   1898         public boolean filterApp(AppEntry info) {
   1899             return Objects.equals(info.info.volumeUuid, mVolumeUuid);
   1900         }
   1901     }
   1902 
   1903     public static class CompoundFilter implements AppFilter {
   1904         private final AppFilter mFirstFilter;
   1905         private final AppFilter mSecondFilter;
   1906 
   1907         public CompoundFilter(AppFilter first, AppFilter second) {
   1908             mFirstFilter = first;
   1909             mSecondFilter = second;
   1910         }
   1911 
   1912         @Override
   1913         public void init(Context context) {
   1914             mFirstFilter.init(context);
   1915             mSecondFilter.init(context);
   1916         }
   1917 
   1918         @Override
   1919         public void init() {
   1920             mFirstFilter.init();
   1921             mSecondFilter.init();
   1922         }
   1923 
   1924         @Override
   1925         public boolean filterApp(AppEntry info) {
   1926             return mFirstFilter.filterApp(info) && mSecondFilter.filterApp(info);
   1927         }
   1928     }
   1929 
   1930     public static final AppFilter FILTER_AUDIO = new AppFilter() {
   1931         @Override
   1932         public void init() {
   1933         }
   1934 
   1935         @Override
   1936         public boolean filterApp(AppEntry entry) {
   1937             boolean isMusicApp;
   1938             synchronized (entry) {
   1939                 isMusicApp = entry.info.category == ApplicationInfo.CATEGORY_AUDIO;
   1940             }
   1941             return isMusicApp;
   1942         }
   1943     };
   1944 
   1945     public static final AppFilter FILTER_MOVIES = new AppFilter() {
   1946         @Override
   1947         public void init() {
   1948         }
   1949 
   1950         @Override
   1951         public boolean filterApp(AppEntry entry) {
   1952             boolean isMovieApp;
   1953             synchronized (entry) {
   1954                 isMovieApp = entry.info.category == ApplicationInfo.CATEGORY_VIDEO;
   1955             }
   1956             return isMovieApp;
   1957         }
   1958     };
   1959 
   1960     public static final AppFilter FILTER_PHOTOS =
   1961             new AppFilter() {
   1962                 @Override
   1963                 public void init() {
   1964                 }
   1965 
   1966                 @Override
   1967                 public boolean filterApp(AppEntry entry) {
   1968                     boolean isPhotosApp;
   1969                     synchronized (entry) {
   1970                         isPhotosApp = entry.info.category == ApplicationInfo.CATEGORY_IMAGE;
   1971                     }
   1972                     return isPhotosApp;
   1973                 }
   1974             };
   1975 
   1976     public static final AppFilter FILTER_OTHER_APPS =
   1977             new AppFilter() {
   1978                 @Override
   1979                 public void init() {
   1980                 }
   1981 
   1982                 @Override
   1983                 public boolean filterApp(AppEntry entry) {
   1984                     boolean isCategorized;
   1985                     synchronized (entry) {
   1986                         isCategorized =
   1987                                 FILTER_AUDIO.filterApp(entry)
   1988                                         || FILTER_GAMES.filterApp(entry)
   1989                                         || FILTER_MOVIES.filterApp(entry)
   1990                                         || FILTER_PHOTOS.filterApp(entry);
   1991                     }
   1992                     return !isCategorized;
   1993                 }
   1994             };
   1995 }
   1996