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