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