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                         () -> {
    344                             try {
    345                                 final StorageStats stats =
    346                                         mStats.queryStatsForPackage(
    347                                                 entry.info.storageUuid,
    348                                                 packageName,
    349                                                 UserHandle.of(userId));
    350                                 final long cacheQuota =
    351                                         mStats.getCacheQuotaBytes(
    352                                                 entry.info.storageUuid.toString(), entry.info.uid);
    353                                 final PackageStats legacy = new PackageStats(packageName, userId);
    354                                 legacy.codeSize = stats.getCodeBytes();
    355                                 legacy.dataSize = stats.getDataBytes();
    356                                 legacy.cacheSize = Math.min(stats.getCacheBytes(), cacheQuota);
    357                                 try {
    358                                     mBackgroundHandler.mStatsObserver.onGetStatsCompleted(
    359                                             legacy, true);
    360                                 } catch (RemoteException ignored) {
    361                                 }
    362                             } catch (NameNotFoundException | IOException e) {
    363                                 Log.w(TAG, "Failed to query stats: " + e);
    364                                 try {
    365                                     mBackgroundHandler.mStatsObserver.onGetStatsCompleted(
    366                                             null, false);
    367                                 } catch (RemoteException ignored) {
    368                                 }
    369                             }
    370                         });
    371             }
    372             if (DEBUG_LOCKING) Log.v(TAG, "...requestSize releasing lock");
    373         }
    374     }
    375 
    376     long sumCacheSizes() {
    377         long sum = 0;
    378         if (DEBUG_LOCKING) Log.v(TAG, "sumCacheSizes about to acquire lock...");
    379         synchronized (mEntriesMap) {
    380             if (DEBUG_LOCKING) Log.v(TAG, "-> sumCacheSizes now has lock");
    381             for (int i=mAppEntries.size()-1; i>=0; i--) {
    382                 sum += mAppEntries.get(i).cacheSize;
    383             }
    384             if (DEBUG_LOCKING) Log.v(TAG, "...sumCacheSizes releasing lock");
    385         }
    386         return sum;
    387     }
    388 
    389     int indexOfApplicationInfoLocked(String pkgName, int userId) {
    390         for (int i=mApplications.size()-1; i>=0; i--) {
    391             ApplicationInfo appInfo = mApplications.get(i);
    392             if (appInfo.packageName.equals(pkgName)
    393                     && UserHandle.getUserId(appInfo.uid) == userId) {
    394                 return i;
    395             }
    396         }
    397         return -1;
    398     }
    399 
    400     void addPackage(String pkgName, int userId) {
    401         try {
    402             synchronized (mEntriesMap) {
    403                 if (DEBUG_LOCKING) Log.v(TAG, "addPackage acquired lock");
    404                 if (DEBUG) Log.i(TAG, "Adding package " + pkgName);
    405                 if (!mResumed) {
    406                     // If we are not resumed, we will do a full query the
    407                     // next time we resume, so there is no reason to do work
    408                     // here.
    409                     if (DEBUG_LOCKING) Log.v(TAG, "addPackage release lock: not resumed");
    410                     return;
    411                 }
    412                 if (indexOfApplicationInfoLocked(pkgName, userId) >= 0) {
    413                     if (DEBUG) Log.i(TAG, "Package already exists!");
    414                     if (DEBUG_LOCKING) Log.v(TAG, "addPackage release lock: already exists");
    415                     return;
    416                 }
    417                 ApplicationInfo info = mIpm.getApplicationInfo(pkgName,
    418                         mUm.isUserAdmin(userId) ? mAdminRetrieveFlags : mRetrieveFlags,
    419                         userId);
    420                 if (info == null) {
    421                     return;
    422                 }
    423                 if (!info.enabled) {
    424                     if (info.enabledSetting
    425                             != PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) {
    426                         return;
    427                     }
    428                     mHaveDisabledApps = true;
    429                 }
    430                 if (AppUtils.isInstant(info)) {
    431                     mHaveInstantApps = true;
    432                 }
    433                 mApplications.add(info);
    434                 if (!mBackgroundHandler.hasMessages(BackgroundHandler.MSG_LOAD_ENTRIES)) {
    435                     mBackgroundHandler.sendEmptyMessage(BackgroundHandler.MSG_LOAD_ENTRIES);
    436                 }
    437                 if (!mMainHandler.hasMessages(MainHandler.MSG_PACKAGE_LIST_CHANGED)) {
    438                     mMainHandler.sendEmptyMessage(MainHandler.MSG_PACKAGE_LIST_CHANGED);
    439                 }
    440                 if (DEBUG_LOCKING) Log.v(TAG, "addPackage releasing lock");
    441             }
    442         } catch (RemoteException e) {
    443         }
    444     }
    445 
    446     public void removePackage(String pkgName, int userId) {
    447         synchronized (mEntriesMap) {
    448             if (DEBUG_LOCKING) Log.v(TAG, "removePackage acquired lock");
    449             int idx = indexOfApplicationInfoLocked(pkgName, userId);
    450             if (DEBUG) Log.i(TAG, "removePackage: " + pkgName + " @ " + idx);
    451             if (idx >= 0) {
    452                 AppEntry entry = mEntriesMap.get(userId).get(pkgName);
    453                 if (DEBUG) Log.i(TAG, "removePackage: " + entry);
    454                 if (entry != null) {
    455                     mEntriesMap.get(userId).remove(pkgName);
    456                     mAppEntries.remove(entry);
    457                 }
    458                 ApplicationInfo info = mApplications.get(idx);
    459                 mApplications.remove(idx);
    460                 if (!info.enabled) {
    461                     mHaveDisabledApps = false;
    462                     for (ApplicationInfo otherInfo : mApplications) {
    463                         if (!otherInfo.enabled) {
    464                             mHaveDisabledApps = true;
    465                             break;
    466                         }
    467                     }
    468                 }
    469                 if (AppUtils.isInstant(info)) {
    470                     mHaveInstantApps = false;
    471                     for (ApplicationInfo otherInfo : mApplications) {
    472                         if (AppUtils.isInstant(otherInfo)) {
    473                             mHaveInstantApps = true;
    474                             break;
    475                         }
    476                     }
    477                 }
    478                 if (!mMainHandler.hasMessages(MainHandler.MSG_PACKAGE_LIST_CHANGED)) {
    479                     mMainHandler.sendEmptyMessage(MainHandler.MSG_PACKAGE_LIST_CHANGED);
    480                 }
    481             }
    482             if (DEBUG_LOCKING) Log.v(TAG, "removePackage releasing lock");
    483         }
    484     }
    485 
    486     public void invalidatePackage(String pkgName, int userId) {
    487         removePackage(pkgName, userId);
    488         addPackage(pkgName, userId);
    489     }
    490 
    491     private void addUser(int userId) {
    492         final int profileIds[] = mUm.getProfileIdsWithDisabled(UserHandle.myUserId());
    493         if (ArrayUtils.contains(profileIds, userId)) {
    494             synchronized (mEntriesMap) {
    495                 mEntriesMap.put(userId, new HashMap<String, AppEntry>());
    496                 if (mResumed) {
    497                     // If resumed, Manually pause, then cause a resume to repopulate the app list.
    498                     // This is the simplest way to reload the packages so that the new user
    499                     // is included.  Otherwise the list will be repopulated on next resume.
    500                     doPauseLocked();
    501                     doResumeIfNeededLocked();
    502                 }
    503                 if (!mMainHandler.hasMessages(MainHandler.MSG_PACKAGE_LIST_CHANGED)) {
    504                     mMainHandler.sendEmptyMessage(MainHandler.MSG_PACKAGE_LIST_CHANGED);
    505                 }
    506             }
    507         }
    508     }
    509 
    510     private void removeUser(int userId) {
    511         synchronized (mEntriesMap) {
    512             HashMap<String, AppEntry> userMap = mEntriesMap.get(userId);
    513             if (userMap != null) {
    514                 for (AppEntry appEntry : userMap.values()) {
    515                     mAppEntries.remove(appEntry);
    516                     mApplications.remove(appEntry.info);
    517                 }
    518                 mEntriesMap.remove(userId);
    519                 if (!mMainHandler.hasMessages(MainHandler.MSG_PACKAGE_LIST_CHANGED)) {
    520                     mMainHandler.sendEmptyMessage(MainHandler.MSG_PACKAGE_LIST_CHANGED);
    521                 }
    522             }
    523         }
    524     }
    525 
    526     private AppEntry getEntryLocked(ApplicationInfo info) {
    527         int userId = UserHandle.getUserId(info.uid);
    528         AppEntry entry = mEntriesMap.get(userId).get(info.packageName);
    529         if (DEBUG) Log.i(TAG, "Looking up entry of pkg " + info.packageName + ": " + entry);
    530         if (entry == null) {
    531             if (DEBUG) Log.i(TAG, "Creating AppEntry for " + info.packageName);
    532             entry = new AppEntry(mContext, info, mCurId++);
    533             mEntriesMap.get(userId).put(info.packageName, entry);
    534             mAppEntries.add(entry);
    535         } else if (entry.info != info) {
    536             entry.info = info;
    537         }
    538         return entry;
    539     }
    540 
    541     // --------------------------------------------------------------
    542 
    543     private long getTotalInternalSize(PackageStats ps) {
    544         if (ps != null) {
    545             return ps.codeSize + ps.dataSize;
    546         }
    547         return SIZE_INVALID;
    548     }
    549 
    550     private long getTotalExternalSize(PackageStats ps) {
    551         if (ps != null) {
    552             // We also include the cache size here because for non-emulated
    553             // we don't automtically clean cache files.
    554             return ps.externalCodeSize + ps.externalDataSize
    555                     + ps.externalCacheSize
    556                     + ps.externalMediaSize + ps.externalObbSize;
    557         }
    558         return SIZE_INVALID;
    559     }
    560 
    561     private String getSizeStr(long size) {
    562         if (size >= 0) {
    563             return Formatter.formatFileSize(mContext, size);
    564         }
    565         return null;
    566     }
    567 
    568     void rebuildActiveSessions() {
    569         synchronized (mEntriesMap) {
    570             if (!mSessionsChanged) {
    571                 return;
    572             }
    573             mActiveSessions.clear();
    574             for (int i=0; i<mSessions.size(); i++) {
    575                 Session s = mSessions.get(i);
    576                 if (s.mResumed) {
    577                     mActiveSessions.add(s);
    578                 }
    579             }
    580         }
    581     }
    582 
    583     public static String normalize(String str) {
    584         String tmp = Normalizer.normalize(str, Form.NFD);
    585         return REMOVE_DIACRITICALS_PATTERN.matcher(tmp)
    586                 .replaceAll("").toLowerCase();
    587     }
    588 
    589     public class Session {
    590         final Callbacks mCallbacks;
    591         boolean mResumed;
    592 
    593         // Rebuilding of app list.  Synchronized on mRebuildSync.
    594         final Object mRebuildSync = new Object();
    595         boolean mRebuildRequested;
    596         boolean mRebuildAsync;
    597         AppFilter mRebuildFilter;
    598         Comparator<AppEntry> mRebuildComparator;
    599         ArrayList<AppEntry> mRebuildResult;
    600         ArrayList<AppEntry> mLastAppList;
    601         boolean mRebuildForeground;
    602 
    603         Session(Callbacks callbacks) {
    604             mCallbacks = callbacks;
    605         }
    606 
    607         public void resume() {
    608             if (DEBUG_LOCKING) Log.v(TAG, "resume about to acquire lock...");
    609             synchronized (mEntriesMap) {
    610                 if (!mResumed) {
    611                     mResumed = true;
    612                     mSessionsChanged = true;
    613                     doResumeIfNeededLocked();
    614                 }
    615             }
    616             if (DEBUG_LOCKING) Log.v(TAG, "...resume releasing lock");
    617         }
    618 
    619         public void pause() {
    620             if (DEBUG_LOCKING) Log.v(TAG, "pause about to acquire lock...");
    621             synchronized (mEntriesMap) {
    622                 if (mResumed) {
    623                     mResumed = false;
    624                     mSessionsChanged = true;
    625                     mBackgroundHandler.removeMessages(BackgroundHandler.MSG_REBUILD_LIST, this);
    626                     doPauseIfNeededLocked();
    627                 }
    628                 if (DEBUG_LOCKING) Log.v(TAG, "...pause releasing lock");
    629             }
    630         }
    631 
    632         public ArrayList<AppEntry> getAllApps() {
    633             synchronized (mEntriesMap) {
    634                 return new ArrayList<>(mAppEntries);
    635             }
    636         }
    637 
    638         // Creates a new list of app entries with the given filter and comparator.
    639         public ArrayList<AppEntry> rebuild(AppFilter filter, Comparator<AppEntry> comparator) {
    640             return rebuild(filter, comparator, true);
    641         }
    642 
    643         public ArrayList<AppEntry> rebuild(AppFilter filter, Comparator<AppEntry> comparator,
    644                 boolean foreground) {
    645             synchronized (mRebuildSync) {
    646                 synchronized (mRebuildingSessions) {
    647                     mRebuildingSessions.add(this);
    648                     mRebuildRequested = true;
    649                     mRebuildAsync = true;
    650                     mRebuildFilter = filter;
    651                     mRebuildComparator = comparator;
    652                     mRebuildForeground = foreground;
    653                     mRebuildResult = null;
    654                     if (!mBackgroundHandler.hasMessages(BackgroundHandler.MSG_REBUILD_LIST)) {
    655                         Message msg = mBackgroundHandler.obtainMessage(
    656                                 BackgroundHandler.MSG_REBUILD_LIST);
    657                         mBackgroundHandler.sendMessage(msg);
    658                     }
    659                 }
    660 
    661                 return null;
    662             }
    663         }
    664 
    665         void handleRebuildList() {
    666             AppFilter filter;
    667             Comparator<AppEntry> comparator;
    668             synchronized (mRebuildSync) {
    669                 if (!mRebuildRequested) {
    670                     return;
    671                 }
    672 
    673                 filter = mRebuildFilter;
    674                 comparator = mRebuildComparator;
    675                 mRebuildRequested = false;
    676                 mRebuildFilter = null;
    677                 mRebuildComparator = null;
    678                 if (mRebuildForeground) {
    679                     Process.setThreadPriority(Process.THREAD_PRIORITY_FOREGROUND);
    680                     mRebuildForeground = false;
    681                 }
    682             }
    683 
    684             if (filter != null) {
    685                 filter.init(mContext);
    686             }
    687 
    688             List<AppEntry> apps;
    689             synchronized (mEntriesMap) {
    690                 apps = new ArrayList<>(mAppEntries);
    691             }
    692 
    693             ArrayList<AppEntry> filteredApps = new ArrayList<AppEntry>();
    694             if (DEBUG) Log.i(TAG, "Rebuilding...");
    695             for (int i=0; i<apps.size(); i++) {
    696                 AppEntry entry = apps.get(i);
    697                 if (entry != null && (filter == null || filter.filterApp(entry))) {
    698                     synchronized (mEntriesMap) {
    699                         if (DEBUG_LOCKING) Log.v(TAG, "rebuild acquired lock");
    700                         if (comparator != null) {
    701                             // Only need the label if we are going to be sorting.
    702                             entry.ensureLabel(mContext);
    703                         }
    704                         if (DEBUG) Log.i(TAG, "Using " + entry.info.packageName + ": " + entry);
    705                         filteredApps.add(entry);
    706                         if (DEBUG_LOCKING) Log.v(TAG, "rebuild releasing lock");
    707                     }
    708                 }
    709             }
    710 
    711             if (comparator != null) {
    712                 synchronized (mEntriesMap) {
    713                     // Locking to ensure that the background handler does not mutate
    714                     // the size of AppEntries used for ordering while sorting.
    715                     Collections.sort(filteredApps, comparator);
    716                 }
    717             }
    718 
    719             synchronized (mRebuildSync) {
    720                 if (!mRebuildRequested) {
    721                     mLastAppList = filteredApps;
    722                     if (!mRebuildAsync) {
    723                         mRebuildResult = filteredApps;
    724                         mRebuildSync.notifyAll();
    725                     } else {
    726                         if (!mMainHandler.hasMessages(MainHandler.MSG_REBUILD_COMPLETE, this)) {
    727                             Message msg = mMainHandler.obtainMessage(
    728                                     MainHandler.MSG_REBUILD_COMPLETE, this);
    729                             mMainHandler.sendMessage(msg);
    730                         }
    731                     }
    732                 }
    733             }
    734 
    735             Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
    736         }
    737 
    738         public void release() {
    739             pause();
    740             synchronized (mEntriesMap) {
    741                 mSessions.remove(this);
    742             }
    743         }
    744     }
    745 
    746     class MainHandler extends Handler {
    747         static final int MSG_REBUILD_COMPLETE = 1;
    748         static final int MSG_PACKAGE_LIST_CHANGED = 2;
    749         static final int MSG_PACKAGE_ICON_CHANGED = 3;
    750         static final int MSG_PACKAGE_SIZE_CHANGED = 4;
    751         static final int MSG_ALL_SIZES_COMPUTED = 5;
    752         static final int MSG_RUNNING_STATE_CHANGED = 6;
    753         static final int MSG_LAUNCHER_INFO_CHANGED = 7;
    754         static final int MSG_LOAD_ENTRIES_COMPLETE = 8;
    755 
    756         public MainHandler(Looper looper) {
    757             super(looper);
    758         }
    759 
    760         @Override
    761         public void handleMessage(Message msg) {
    762             rebuildActiveSessions();
    763             switch (msg.what) {
    764                 case MSG_REBUILD_COMPLETE: {
    765                     Session s = (Session)msg.obj;
    766                     if (mActiveSessions.contains(s)) {
    767                         s.mCallbacks.onRebuildComplete(s.mLastAppList);
    768                     }
    769                 } break;
    770                 case MSG_PACKAGE_LIST_CHANGED: {
    771                     for (int i=0; i<mActiveSessions.size(); i++) {
    772                         mActiveSessions.get(i).mCallbacks.onPackageListChanged();
    773                     }
    774                 } break;
    775                 case MSG_PACKAGE_ICON_CHANGED: {
    776                     for (int i=0; i<mActiveSessions.size(); i++) {
    777                         mActiveSessions.get(i).mCallbacks.onPackageIconChanged();
    778                     }
    779                 } break;
    780                 case MSG_PACKAGE_SIZE_CHANGED: {
    781                     for (int i=0; i<mActiveSessions.size(); i++) {
    782                         mActiveSessions.get(i).mCallbacks.onPackageSizeChanged(
    783                                 (String)msg.obj);
    784                     }
    785                 } break;
    786                 case MSG_ALL_SIZES_COMPUTED: {
    787                     for (int i=0; i<mActiveSessions.size(); i++) {
    788                         mActiveSessions.get(i).mCallbacks.onAllSizesComputed();
    789                     }
    790                 } break;
    791                 case MSG_RUNNING_STATE_CHANGED: {
    792                     for (int i=0; i<mActiveSessions.size(); i++) {
    793                         mActiveSessions.get(i).mCallbacks.onRunningStateChanged(
    794                                 msg.arg1 != 0);
    795                     }
    796                 } break;
    797                 case MSG_LAUNCHER_INFO_CHANGED: {
    798                     for (int i=0; i<mActiveSessions.size(); i++) {
    799                         mActiveSessions.get(i).mCallbacks.onLauncherInfoChanged();
    800                     }
    801                 } break;
    802                 case MSG_LOAD_ENTRIES_COMPLETE: {
    803                     for (int i=0; i<mActiveSessions.size(); i++) {
    804                         mActiveSessions.get(i).mCallbacks.onLoadEntriesCompleted();
    805                     }
    806                 } break;
    807             }
    808         }
    809     }
    810 
    811     private class BackgroundHandler extends Handler {
    812         static final int MSG_REBUILD_LIST = 1;
    813         static final int MSG_LOAD_ENTRIES = 2;
    814         static final int MSG_LOAD_ICONS = 3;
    815         static final int MSG_LOAD_SIZES = 4;
    816         static final int MSG_LOAD_LAUNCHER = 5;
    817         static final int MSG_LOAD_HOME_APP = 6;
    818 
    819         boolean mRunning;
    820 
    821         BackgroundHandler(Looper looper) {
    822             super(looper);
    823         }
    824 
    825         @Override
    826         public void handleMessage(Message msg) {
    827             // Always try rebuilding list first thing, if needed.
    828             ArrayList<Session> rebuildingSessions = null;
    829             synchronized (mRebuildingSessions) {
    830                 if (mRebuildingSessions.size() > 0) {
    831                     rebuildingSessions = new ArrayList<Session>(mRebuildingSessions);
    832                     mRebuildingSessions.clear();
    833                 }
    834             }
    835             if (rebuildingSessions != null) {
    836                 for (int i=0; i<rebuildingSessions.size(); i++) {
    837                     rebuildingSessions.get(i).handleRebuildList();
    838                 }
    839             }
    840 
    841             switch (msg.what) {
    842                 case MSG_REBUILD_LIST: {
    843                 } break;
    844                 case MSG_LOAD_ENTRIES: {
    845                     int numDone = 0;
    846                     synchronized (mEntriesMap) {
    847                         if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_ENTRIES acquired lock");
    848                         for (int i = 0; i < mApplications.size() && numDone < 6; i++) {
    849                             if (!mRunning) {
    850                                 mRunning = true;
    851                                 Message m = mMainHandler.obtainMessage(
    852                                         MainHandler.MSG_RUNNING_STATE_CHANGED, 1);
    853                                 mMainHandler.sendMessage(m);
    854                             }
    855                             ApplicationInfo info = mApplications.get(i);
    856                             int userId = UserHandle.getUserId(info.uid);
    857                             if (mEntriesMap.get(userId).get(info.packageName) == null) {
    858                                 numDone++;
    859                                 getEntryLocked(info);
    860                             }
    861                             if (userId != 0 && mEntriesMap.indexOfKey(0) >= 0) {
    862                                 // If this app is for a profile and we are on the owner, remove
    863                                 // the owner entry if it isn't installed.  This will prevent
    864                                 // duplicates of work only apps showing up as 'not installed
    865                                 // for this user'.
    866                                 // Note: This depends on us traversing the users in order, which
    867                                 // happens because of the way we generate the list in
    868                                 // doResumeIfNeededLocked.
    869                                 AppEntry entry = mEntriesMap.get(0).get(info.packageName);
    870                                 if (entry != null &&
    871                                         (entry.info.flags & ApplicationInfo.FLAG_INSTALLED) == 0) {
    872                                     mEntriesMap.get(0).remove(info.packageName);
    873                                     mAppEntries.remove(entry);
    874                                 }
    875                             }
    876                         }
    877                         if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_ENTRIES releasing lock");
    878                     }
    879 
    880                     if (numDone >= 6) {
    881                         sendEmptyMessage(MSG_LOAD_ENTRIES);
    882                     } else {
    883                         if (!mMainHandler.hasMessages(MainHandler.MSG_LOAD_ENTRIES_COMPLETE)) {
    884                             mMainHandler.sendEmptyMessage(MainHandler.MSG_LOAD_ENTRIES_COMPLETE);
    885                         }
    886                         sendEmptyMessage(MSG_LOAD_HOME_APP);
    887                     }
    888                 } break;
    889                 case MSG_LOAD_HOME_APP: {
    890                     final List<ResolveInfo> homeActivities = new ArrayList<>();
    891                     mPm.getHomeActivities(homeActivities);
    892                     synchronized (mEntriesMap) {
    893                         final int entryCount = mEntriesMap.size();
    894                         for (int i = 0; i < entryCount; i++) {
    895                             if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_HOME_APP acquired lock");
    896                             final HashMap<String, AppEntry> userEntries = mEntriesMap.valueAt(i);
    897                             for (ResolveInfo activity : homeActivities) {
    898                                 String packageName = activity.activityInfo.packageName;
    899                                 AppEntry entry = userEntries.get(packageName);
    900                                 if (entry != null) {
    901                                     entry.isHomeApp = true;
    902                                 }
    903                             }
    904                             if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_HOME_APP releasing lock");
    905                         }
    906                     }
    907                     sendEmptyMessage(MSG_LOAD_LAUNCHER);
    908                 }
    909                 break;
    910                 case MSG_LOAD_LAUNCHER: {
    911                     Intent launchIntent = new Intent(Intent.ACTION_MAIN, null)
    912                             .addCategory(Intent.CATEGORY_LAUNCHER);
    913                     for (int i = 0; i < mEntriesMap.size(); i++) {
    914                         int userId = mEntriesMap.keyAt(i);
    915                         // If we do not specify MATCH_DIRECT_BOOT_AWARE or
    916                         // MATCH_DIRECT_BOOT_UNAWARE, system will derive and update the flags
    917                         // according to the user's lock state. When the user is locked, components
    918                         // with ComponentInfo#directBootAware == false will be filtered. We should
    919                         // explicitly include both direct boot aware and unaware components here.
    920                         List<ResolveInfo> intents = mPm.queryIntentActivitiesAsUser(
    921                                 launchIntent,
    922                                 PackageManager.MATCH_DISABLED_COMPONENTS
    923                                         | PackageManager.MATCH_DIRECT_BOOT_AWARE
    924                                         | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
    925                                 userId
    926                         );
    927                         synchronized (mEntriesMap) {
    928                             if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_LAUNCHER acquired lock");
    929                             HashMap<String, AppEntry> userEntries = mEntriesMap.valueAt(i);
    930                             final int N = intents.size();
    931                             for (int j = 0; j < N; j++) {
    932                                 String packageName = intents.get(j).activityInfo.packageName;
    933                                 AppEntry entry = userEntries.get(packageName);
    934                                 if (entry != null) {
    935                                     entry.hasLauncherEntry = true;
    936                                 } else {
    937                                     Log.w(TAG, "Cannot find pkg: " + packageName
    938                                             + " on user " + userId);
    939                                 }
    940                             }
    941                             if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_LAUNCHER releasing lock");
    942                         }
    943                     }
    944 
    945                     if (!mMainHandler.hasMessages(MainHandler.MSG_LAUNCHER_INFO_CHANGED)) {
    946                         mMainHandler.sendEmptyMessage(MainHandler.MSG_LAUNCHER_INFO_CHANGED);
    947                     }
    948                     sendEmptyMessage(MSG_LOAD_ICONS);
    949                 } break;
    950                 case MSG_LOAD_ICONS: {
    951                     int numDone = 0;
    952                     synchronized (mEntriesMap) {
    953                         if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_ICONS acquired lock");
    954                         for (int i=0; i<mAppEntries.size() && numDone<2; i++) {
    955                             AppEntry entry = mAppEntries.get(i);
    956                             if (entry.icon == null || !entry.mounted) {
    957                                 synchronized (entry) {
    958                                     if (entry.ensureIconLocked(mContext, mDrawableFactory)) {
    959                                         if (!mRunning) {
    960                                             mRunning = true;
    961                                             Message m = mMainHandler.obtainMessage(
    962                                                     MainHandler.MSG_RUNNING_STATE_CHANGED, 1);
    963                                             mMainHandler.sendMessage(m);
    964                                         }
    965                                         numDone++;
    966                                     }
    967                                 }
    968                             }
    969                         }
    970                         if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_ICONS releasing lock");
    971                     }
    972                     if (numDone > 0) {
    973                         if (!mMainHandler.hasMessages(MainHandler.MSG_PACKAGE_ICON_CHANGED)) {
    974                             mMainHandler.sendEmptyMessage(MainHandler.MSG_PACKAGE_ICON_CHANGED);
    975                         }
    976                     }
    977                     if (numDone >= 2) {
    978                         sendEmptyMessage(MSG_LOAD_ICONS);
    979                     } else {
    980                         sendEmptyMessage(MSG_LOAD_SIZES);
    981                     }
    982                 } break;
    983                 case MSG_LOAD_SIZES: {
    984                     synchronized (mEntriesMap) {
    985                         if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_SIZES acquired lock");
    986                         if (mCurComputingSizePkg != null) {
    987                             if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_SIZES releasing: currently computing");
    988                             return;
    989                         }
    990 
    991                         long now = SystemClock.uptimeMillis();
    992                         for (int i=0; i<mAppEntries.size(); i++) {
    993                             AppEntry entry = mAppEntries.get(i);
    994                             if ((entry.info.flags & ApplicationInfo.FLAG_INSTALLED) != 0
    995                                     && (entry.size == SIZE_UNKNOWN || entry.sizeStale)) {
    996                                 if (entry.sizeLoadStart == 0 ||
    997                                         (entry.sizeLoadStart < (now-20*1000))) {
    998                                     if (!mRunning) {
    999                                         mRunning = true;
   1000                                         Message m = mMainHandler.obtainMessage(
   1001                                                 MainHandler.MSG_RUNNING_STATE_CHANGED, 1);
   1002                                         mMainHandler.sendMessage(m);
   1003                                     }
   1004                                     entry.sizeLoadStart = now;
   1005                                     mCurComputingSizeUuid = entry.info.storageUuid;
   1006                                     mCurComputingSizePkg = entry.info.packageName;
   1007                                     mCurComputingSizeUserId = UserHandle.getUserId(entry.info.uid);
   1008 
   1009                                     mBackgroundHandler.post(() -> {
   1010                                         try {
   1011                                             final StorageStats stats = mStats.queryStatsForPackage(
   1012                                                     mCurComputingSizeUuid, mCurComputingSizePkg,
   1013                                                     UserHandle.of(mCurComputingSizeUserId));
   1014                                             final PackageStats legacy = new PackageStats(
   1015                                                     mCurComputingSizePkg, mCurComputingSizeUserId);
   1016                                             legacy.codeSize = stats.getCodeBytes();
   1017                                             legacy.dataSize = stats.getDataBytes();
   1018                                             legacy.cacheSize = stats.getCacheBytes();
   1019                                             try {
   1020                                                 mStatsObserver.onGetStatsCompleted(legacy, true);
   1021                                             } catch (RemoteException ignored) {
   1022                                             }
   1023                                         } catch (NameNotFoundException | IOException e) {
   1024                                             Log.w(TAG, "Failed to query stats: " + e);
   1025                                             try {
   1026                                                 mStatsObserver.onGetStatsCompleted(null, false);
   1027                                             } catch (RemoteException ignored) {
   1028                                             }
   1029                                         }
   1030 
   1031                                     });
   1032                                 }
   1033                                 if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_SIZES releasing: now computing");
   1034                                 return;
   1035                             }
   1036                         }
   1037                         if (!mMainHandler.hasMessages(MainHandler.MSG_ALL_SIZES_COMPUTED)) {
   1038                             mMainHandler.sendEmptyMessage(MainHandler.MSG_ALL_SIZES_COMPUTED);
   1039                             mRunning = false;
   1040                             Message m = mMainHandler.obtainMessage(
   1041                                     MainHandler.MSG_RUNNING_STATE_CHANGED, 0);
   1042                             mMainHandler.sendMessage(m);
   1043                         }
   1044                         if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_SIZES releasing lock");
   1045                     }
   1046                 } break;
   1047             }
   1048         }
   1049 
   1050         final IPackageStatsObserver.Stub mStatsObserver = new IPackageStatsObserver.Stub() {
   1051             public void onGetStatsCompleted(PackageStats stats, boolean succeeded) {
   1052                 if (!succeeded) {
   1053                     // There is no meaningful information in stats if the call failed.
   1054                     return;
   1055                 }
   1056 
   1057                 boolean sizeChanged = false;
   1058                 synchronized (mEntriesMap) {
   1059                     if (DEBUG_LOCKING) Log.v(TAG, "onGetStatsCompleted acquired lock");
   1060                     HashMap<String, AppEntry> userMap = mEntriesMap.get(stats.userHandle);
   1061                     if (userMap == null) {
   1062                         // The user must have been removed.
   1063                         return;
   1064                     }
   1065                     AppEntry entry = userMap.get(stats.packageName);
   1066                     if (entry != null) {
   1067                         synchronized (entry) {
   1068                             entry.sizeStale = false;
   1069                             entry.sizeLoadStart = 0;
   1070                             long externalCodeSize = stats.externalCodeSize
   1071                                     + stats.externalObbSize;
   1072                             long externalDataSize = stats.externalDataSize
   1073                                     + stats.externalMediaSize;
   1074                             long newSize = externalCodeSize + externalDataSize
   1075                                     + getTotalInternalSize(stats);
   1076                             if (entry.size != newSize ||
   1077                                     entry.cacheSize != stats.cacheSize ||
   1078                                     entry.codeSize != stats.codeSize ||
   1079                                     entry.dataSize != stats.dataSize ||
   1080                                     entry.externalCodeSize != externalCodeSize ||
   1081                                     entry.externalDataSize != externalDataSize ||
   1082                                     entry.externalCacheSize != stats.externalCacheSize) {
   1083                                 entry.size = newSize;
   1084                                 entry.cacheSize = stats.cacheSize;
   1085                                 entry.codeSize = stats.codeSize;
   1086                                 entry.dataSize = stats.dataSize;
   1087                                 entry.externalCodeSize = externalCodeSize;
   1088                                 entry.externalDataSize = externalDataSize;
   1089                                 entry.externalCacheSize = stats.externalCacheSize;
   1090                                 entry.sizeStr = getSizeStr(entry.size);
   1091                                 entry.internalSize = getTotalInternalSize(stats);
   1092                                 entry.internalSizeStr = getSizeStr(entry.internalSize);
   1093                                 entry.externalSize = getTotalExternalSize(stats);
   1094                                 entry.externalSizeStr = getSizeStr(entry.externalSize);
   1095                                 if (DEBUG) Log.i(TAG, "Set size of " + entry.label + " " + entry
   1096                                         + ": " + entry.sizeStr);
   1097                                 sizeChanged = true;
   1098                             }
   1099                         }
   1100                         if (sizeChanged) {
   1101                             Message msg = mMainHandler.obtainMessage(
   1102                                     MainHandler.MSG_PACKAGE_SIZE_CHANGED, stats.packageName);
   1103                             mMainHandler.sendMessage(msg);
   1104                         }
   1105                     }
   1106                     if (mCurComputingSizePkg != null
   1107                             && (mCurComputingSizePkg.equals(stats.packageName)
   1108                             && mCurComputingSizeUserId == stats.userHandle)) {
   1109                         mCurComputingSizePkg = null;
   1110                         sendEmptyMessage(MSG_LOAD_SIZES);
   1111                     }
   1112                     if (DEBUG_LOCKING) Log.v(TAG, "onGetStatsCompleted releasing lock");
   1113                 }
   1114             }
   1115         };
   1116     }
   1117 
   1118     /**
   1119      * Receives notifications when applications are added/removed.
   1120      */
   1121     private class PackageIntentReceiver extends BroadcastReceiver {
   1122         void registerReceiver() {
   1123             IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
   1124             filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
   1125             filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
   1126             filter.addDataScheme("package");
   1127             mContext.registerReceiver(this, filter);
   1128             // Register for events related to sdcard installation.
   1129             IntentFilter sdFilter = new IntentFilter();
   1130             sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
   1131             sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
   1132             mContext.registerReceiver(this, sdFilter);
   1133             // Register for events related to user creation/deletion.
   1134             IntentFilter userFilter = new IntentFilter();
   1135             userFilter.addAction(Intent.ACTION_USER_ADDED);
   1136             userFilter.addAction(Intent.ACTION_USER_REMOVED);
   1137             mContext.registerReceiver(this, userFilter);
   1138         }
   1139         void unregisterReceiver() {
   1140             mContext.unregisterReceiver(this);
   1141         }
   1142         @Override
   1143         public void onReceive(Context context, Intent intent) {
   1144             String actionStr = intent.getAction();
   1145             if (Intent.ACTION_PACKAGE_ADDED.equals(actionStr)) {
   1146                 Uri data = intent.getData();
   1147                 String pkgName = data.getEncodedSchemeSpecificPart();
   1148                 for (int i = 0; i < mEntriesMap.size(); i++) {
   1149                     addPackage(pkgName, mEntriesMap.keyAt(i));
   1150                 }
   1151             } else if (Intent.ACTION_PACKAGE_REMOVED.equals(actionStr)) {
   1152                 Uri data = intent.getData();
   1153                 String pkgName = data.getEncodedSchemeSpecificPart();
   1154                 for (int i = 0; i < mEntriesMap.size(); i++) {
   1155                     removePackage(pkgName, mEntriesMap.keyAt(i));
   1156                 }
   1157             } else if (Intent.ACTION_PACKAGE_CHANGED.equals(actionStr)) {
   1158                 Uri data = intent.getData();
   1159                 String pkgName = data.getEncodedSchemeSpecificPart();
   1160                 for (int i = 0; i < mEntriesMap.size(); i++) {
   1161                     invalidatePackage(pkgName, mEntriesMap.keyAt(i));
   1162                 }
   1163             } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(actionStr) ||
   1164                     Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(actionStr)) {
   1165                 // When applications become available or unavailable (perhaps because
   1166                 // the SD card was inserted or ejected) we need to refresh the
   1167                 // AppInfo with new label, icon and size information as appropriate
   1168                 // given the newfound (un)availability of the application.
   1169                 // A simple way to do that is to treat the refresh as a package
   1170                 // removal followed by a package addition.
   1171                 String pkgList[] = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
   1172                 if (pkgList == null || pkgList.length == 0) {
   1173                     // Ignore
   1174                     return;
   1175                 }
   1176                 boolean avail = Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(actionStr);
   1177                 if (avail) {
   1178                     for (String pkgName : pkgList) {
   1179                         for (int i = 0; i < mEntriesMap.size(); i++) {
   1180                             invalidatePackage(pkgName, mEntriesMap.keyAt(i));
   1181                         }
   1182                     }
   1183                 }
   1184             } else if (Intent.ACTION_USER_ADDED.equals(actionStr)) {
   1185                 addUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL));
   1186             } else if (Intent.ACTION_USER_REMOVED.equals(actionStr)) {
   1187                 removeUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL));
   1188             }
   1189         }
   1190     }
   1191 
   1192     public interface Callbacks {
   1193         void onRunningStateChanged(boolean running);
   1194         void onPackageListChanged();
   1195         void onRebuildComplete(ArrayList<AppEntry> apps);
   1196         void onPackageIconChanged();
   1197         void onPackageSizeChanged(String packageName);
   1198         void onAllSizesComputed();
   1199         void onLauncherInfoChanged();
   1200         void onLoadEntriesCompleted();
   1201     }
   1202 
   1203     public static class SizeInfo {
   1204         public long cacheSize;
   1205         public long codeSize;
   1206         public long dataSize;
   1207         public long externalCodeSize;
   1208         public long externalDataSize;
   1209 
   1210         // This is the part of externalDataSize that is in the cache
   1211         // section of external storage.  Note that we don't just combine
   1212         // this with cacheSize because currently the platform can't
   1213         // automatically trim this data when needed, so it is something
   1214         // the user may need to manage.  The externalDataSize also includes
   1215         // this value, since what this is here is really the part of
   1216         // externalDataSize that we can just consider to be "cache" files
   1217         // for purposes of cleaning them up in the app details UI.
   1218         public long externalCacheSize;
   1219     }
   1220 
   1221     public static class AppEntry extends SizeInfo {
   1222         public final File apkFile;
   1223         public final long id;
   1224         public String label;
   1225         public long size;
   1226         public long internalSize;
   1227         public long externalSize;
   1228 
   1229         public boolean mounted;
   1230 
   1231         /**
   1232          * Setting this to {@code true} prevents the entry to be filtered by
   1233          * {@link #FILTER_DOWNLOADED_AND_LAUNCHER}.
   1234          */
   1235         public boolean hasLauncherEntry;
   1236 
   1237         /**
   1238          * Whether or not it's a Home app.
   1239          */
   1240         public boolean isHomeApp;
   1241 
   1242         public String getNormalizedLabel() {
   1243             if (normalizedLabel != null) {
   1244                 return normalizedLabel;
   1245             }
   1246             normalizedLabel = normalize(label);
   1247             return normalizedLabel;
   1248         }
   1249 
   1250         // Need to synchronize on 'this' for the following.
   1251         public ApplicationInfo info;
   1252         public Drawable icon;
   1253         public String sizeStr;
   1254         public String internalSizeStr;
   1255         public String externalSizeStr;
   1256         public boolean sizeStale;
   1257         public long sizeLoadStart;
   1258 
   1259         public String normalizedLabel;
   1260 
   1261         // A location where extra info can be placed to be used by custom filters.
   1262         public Object extraInfo;
   1263 
   1264         AppEntry(Context context, ApplicationInfo info, long id) {
   1265             apkFile = new File(info.sourceDir);
   1266             this.id = id;
   1267             this.info = info;
   1268             this.size = SIZE_UNKNOWN;
   1269             this.sizeStale = true;
   1270             ensureLabel(context);
   1271         }
   1272 
   1273         public void ensureLabel(Context context) {
   1274             if (this.label == null || !this.mounted) {
   1275                 if (!this.apkFile.exists()) {
   1276                     this.mounted = false;
   1277                     this.label = info.packageName;
   1278                 } else {
   1279                     this.mounted = true;
   1280                     CharSequence label = info.loadLabel(context.getPackageManager());
   1281                     this.label = label != null ? label.toString() : info.packageName;
   1282                 }
   1283             }
   1284         }
   1285 
   1286         boolean ensureIconLocked(Context context, IconDrawableFactory drawableFactory) {
   1287             if (this.icon == null) {
   1288                 if (this.apkFile.exists()) {
   1289                     this.icon = drawableFactory.getBadgedIcon(info);
   1290                     return true;
   1291                 } else {
   1292                     this.mounted = false;
   1293                     this.icon = context.getDrawable(R.drawable.sym_app_on_sd_unavailable_icon);
   1294                 }
   1295             } else if (!this.mounted) {
   1296                 // If the app wasn't mounted but is now mounted, reload
   1297                 // its icon.
   1298                 if (this.apkFile.exists()) {
   1299                     this.mounted = true;
   1300                     this.icon = drawableFactory.getBadgedIcon(info);
   1301                     return true;
   1302                 }
   1303             }
   1304             return false;
   1305         }
   1306 
   1307         public String getVersion(Context context) {
   1308             try {
   1309                 return context.getPackageManager().getPackageInfo(info.packageName, 0).versionName;
   1310             } catch (PackageManager.NameNotFoundException e) {
   1311                 return "";
   1312             }
   1313         }
   1314     }
   1315 
   1316     /**
   1317      * Compare by label, then package name, then uid.
   1318      */
   1319     public static final Comparator<AppEntry> ALPHA_COMPARATOR = new Comparator<AppEntry>() {
   1320         private final Collator sCollator = Collator.getInstance();
   1321         @Override
   1322         public int compare(AppEntry object1, AppEntry object2) {
   1323             int compareResult = sCollator.compare(object1.label, object2.label);
   1324             if (compareResult != 0) {
   1325                 return compareResult;
   1326             }
   1327             if (object1.info != null && object2.info != null) {
   1328                 compareResult =
   1329                     sCollator.compare(object1.info.packageName, object2.info.packageName);
   1330                 if (compareResult != 0) {
   1331                     return compareResult;
   1332                 }
   1333             }
   1334             return object1.info.uid - object2.info.uid;
   1335         }
   1336     };
   1337 
   1338     public static final Comparator<AppEntry> SIZE_COMPARATOR
   1339             = new Comparator<AppEntry>() {
   1340         @Override
   1341         public int compare(AppEntry object1, AppEntry object2) {
   1342             if (object1.size < object2.size) return 1;
   1343             if (object1.size > object2.size) return -1;
   1344             return ALPHA_COMPARATOR.compare(object1, object2);
   1345         }
   1346     };
   1347 
   1348     public static final Comparator<AppEntry> INTERNAL_SIZE_COMPARATOR
   1349             = new Comparator<AppEntry>() {
   1350         @Override
   1351         public int compare(AppEntry object1, AppEntry object2) {
   1352             if (object1.internalSize < object2.internalSize) return 1;
   1353             if (object1.internalSize > object2.internalSize) return -1;
   1354             return ALPHA_COMPARATOR.compare(object1, object2);
   1355         }
   1356     };
   1357 
   1358     public static final Comparator<AppEntry> EXTERNAL_SIZE_COMPARATOR
   1359             = new Comparator<AppEntry>() {
   1360         @Override
   1361         public int compare(AppEntry object1, AppEntry object2) {
   1362             if (object1.externalSize < object2.externalSize) return 1;
   1363             if (object1.externalSize > object2.externalSize) return -1;
   1364             return ALPHA_COMPARATOR.compare(object1, object2);
   1365         }
   1366     };
   1367 
   1368     public interface AppFilter {
   1369         void init();
   1370         default void init(Context context) {
   1371             init();
   1372         }
   1373         boolean filterApp(AppEntry info);
   1374     }
   1375 
   1376     public static final AppFilter FILTER_PERSONAL = new AppFilter() {
   1377         private int mCurrentUser;
   1378 
   1379         @Override
   1380         public void init() {
   1381             mCurrentUser = ActivityManager.getCurrentUser();
   1382         }
   1383 
   1384         @Override
   1385         public boolean filterApp(AppEntry entry) {
   1386             return UserHandle.getUserId(entry.info.uid) == mCurrentUser;
   1387         }
   1388     };
   1389 
   1390     public static final AppFilter FILTER_WITHOUT_DISABLED_UNTIL_USED = new AppFilter() {
   1391         @Override
   1392         public void init() {
   1393             // do nothing
   1394         }
   1395 
   1396         @Override
   1397         public boolean filterApp(AppEntry entry) {
   1398             return entry.info.enabledSetting
   1399                     != PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED;
   1400         }
   1401     };
   1402 
   1403     public static final AppFilter FILTER_WORK = new AppFilter() {
   1404         private int mCurrentUser;
   1405 
   1406         @Override
   1407         public void init() {
   1408             mCurrentUser = ActivityManager.getCurrentUser();
   1409         }
   1410 
   1411         @Override
   1412         public boolean filterApp(AppEntry entry) {
   1413             return UserHandle.getUserId(entry.info.uid) != mCurrentUser;
   1414         }
   1415     };
   1416 
   1417     /**
   1418      * Displays a combined list with "downloaded" and "visible in launcher" apps only.
   1419      */
   1420     public static final AppFilter FILTER_DOWNLOADED_AND_LAUNCHER = new AppFilter() {
   1421         @Override
   1422         public void init() {
   1423         }
   1424 
   1425         @Override
   1426         public boolean filterApp(AppEntry entry) {
   1427             if (AppUtils.isInstant(entry.info)) {
   1428                 return false;
   1429             } else if ((entry.info.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) {
   1430                 return true;
   1431             } else if ((entry.info.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
   1432                 return true;
   1433             } else if (entry.hasLauncherEntry) {
   1434                 return true;
   1435             } else if ((entry.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0 && entry.isHomeApp) {
   1436                 return true;
   1437             }
   1438             return false;
   1439         }
   1440     };
   1441 
   1442     /**
   1443      * Displays a combined list with "downloaded" and "visible in launcher" apps only.
   1444      */
   1445     public static final AppFilter FILTER_DOWNLOADED_AND_LAUNCHER_AND_INSTANT = new AppFilter() {
   1446 
   1447         @Override
   1448         public void init() {
   1449         }
   1450 
   1451         @Override
   1452         public boolean filterApp(AppEntry entry) {
   1453             return AppUtils.isInstant(entry.info)
   1454                     || FILTER_DOWNLOADED_AND_LAUNCHER.filterApp(entry);
   1455         }
   1456 
   1457     };
   1458 
   1459     public static final AppFilter FILTER_THIRD_PARTY = new AppFilter() {
   1460         @Override
   1461         public void init() {
   1462         }
   1463 
   1464         @Override
   1465         public boolean filterApp(AppEntry entry) {
   1466             if ((entry.info.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) {
   1467                 return true;
   1468             } else if ((entry.info.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
   1469                 return true;
   1470             }
   1471             return false;
   1472         }
   1473     };
   1474 
   1475     public static final AppFilter FILTER_DISABLED = new AppFilter() {
   1476         @Override
   1477         public void init() {
   1478         }
   1479 
   1480         @Override
   1481         public boolean filterApp(AppEntry entry) {
   1482             return !entry.info.enabled && !AppUtils.isInstant(entry.info);
   1483         }
   1484     };
   1485 
   1486     public static final AppFilter FILTER_INSTANT = new AppFilter() {
   1487         @Override
   1488         public void init() {
   1489         }
   1490 
   1491         @Override
   1492         public boolean filterApp(AppEntry entry) {
   1493             return AppUtils.isInstant(entry.info);
   1494         }
   1495     };
   1496 
   1497     public static final AppFilter FILTER_ALL_ENABLED = new AppFilter() {
   1498         @Override
   1499         public void init() {
   1500         }
   1501 
   1502         @Override
   1503         public boolean filterApp(AppEntry entry) {
   1504             return entry.info.enabled && !AppUtils.isInstant(entry.info);
   1505         }
   1506     };
   1507 
   1508     public static final AppFilter FILTER_EVERYTHING = new AppFilter() {
   1509         @Override
   1510         public void init() {
   1511         }
   1512 
   1513         @Override
   1514         public boolean filterApp(AppEntry entry) {
   1515             return true;
   1516         }
   1517     };
   1518 
   1519     public static final AppFilter FILTER_WITH_DOMAIN_URLS = new AppFilter() {
   1520         @Override
   1521         public void init() {
   1522         }
   1523 
   1524         @Override
   1525         public boolean filterApp(AppEntry entry) {
   1526             return !AppUtils.isInstant(entry.info)
   1527                 && (entry.info.privateFlags & ApplicationInfo.PRIVATE_FLAG_HAS_DOMAIN_URLS) != 0;
   1528         }
   1529     };
   1530 
   1531     public static final AppFilter FILTER_NOT_HIDE = new AppFilter() {
   1532         private String[] mHidePackageNames;
   1533 
   1534         @Override
   1535         public void init(Context context) {
   1536             mHidePackageNames = context.getResources()
   1537                 .getStringArray(R.array.config_hideWhenDisabled_packageNames);
   1538         }
   1539 
   1540         @Override
   1541         public void init() {
   1542         }
   1543 
   1544         @Override
   1545         public boolean filterApp(AppEntry entry) {
   1546             if (ArrayUtils.contains(mHidePackageNames, entry.info.packageName)) {
   1547                 if (!entry.info.enabled) {
   1548                     return false;
   1549                 } else if (entry.info.enabledSetting ==
   1550                     PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED) {
   1551                     return false;
   1552                 }
   1553             }
   1554 
   1555             return true;
   1556         }
   1557     };
   1558 
   1559     public static final AppFilter FILTER_GAMES = new AppFilter() {
   1560         @Override
   1561         public void init() {
   1562         }
   1563 
   1564         @Override
   1565         public boolean filterApp(ApplicationsState.AppEntry info) {
   1566             // TODO: Update for the new game category.
   1567             boolean isGame;
   1568             synchronized (info.info) {
   1569                 isGame = ((info.info.flags & ApplicationInfo.FLAG_IS_GAME) != 0)
   1570                         || info.info.category == ApplicationInfo.CATEGORY_GAME;
   1571             }
   1572             return isGame;
   1573         }
   1574     };
   1575 
   1576     public static class VolumeFilter implements AppFilter {
   1577         private final String mVolumeUuid;
   1578 
   1579         public VolumeFilter(String volumeUuid) {
   1580             mVolumeUuid = volumeUuid;
   1581         }
   1582 
   1583         @Override
   1584         public void init() {
   1585         }
   1586 
   1587         @Override
   1588         public boolean filterApp(AppEntry info) {
   1589             return Objects.equals(info.info.volumeUuid, mVolumeUuid);
   1590         }
   1591     }
   1592 
   1593     public static class CompoundFilter implements AppFilter {
   1594         private final AppFilter mFirstFilter;
   1595         private final AppFilter mSecondFilter;
   1596 
   1597         public CompoundFilter(AppFilter first, AppFilter second) {
   1598             mFirstFilter = first;
   1599             mSecondFilter = second;
   1600         }
   1601 
   1602         @Override
   1603         public void init(Context context) {
   1604             mFirstFilter.init(context);
   1605             mSecondFilter.init(context);
   1606         }
   1607 
   1608         @Override
   1609         public void init() {
   1610             mFirstFilter.init();
   1611             mSecondFilter.init();
   1612         }
   1613 
   1614         @Override
   1615         public boolean filterApp(AppEntry info) {
   1616             return mFirstFilter.filterApp(info) && mSecondFilter.filterApp(info);
   1617         }
   1618     }
   1619 
   1620     public static final AppFilter FILTER_AUDIO = new AppFilter() {
   1621         @Override
   1622         public void init() {
   1623         }
   1624 
   1625         @Override
   1626         public boolean filterApp(AppEntry entry) {
   1627             boolean isMusicApp;
   1628             synchronized(entry) {
   1629                 isMusicApp = entry.info.category == ApplicationInfo.CATEGORY_AUDIO;
   1630             }
   1631             return isMusicApp;
   1632         }
   1633     };
   1634 
   1635     public static final AppFilter FILTER_MOVIES = new AppFilter() {
   1636         @Override
   1637         public void init() {
   1638         }
   1639 
   1640         @Override
   1641         public boolean filterApp(AppEntry entry) {
   1642             boolean isMovieApp;
   1643             synchronized(entry) {
   1644                 isMovieApp = entry.info.category == ApplicationInfo.CATEGORY_VIDEO;
   1645             }
   1646             return isMovieApp;
   1647         }
   1648     };
   1649 
   1650     public static final AppFilter FILTER_PHOTOS =
   1651             new AppFilter() {
   1652                 @Override
   1653                 public void init() {}
   1654 
   1655                 @Override
   1656                 public boolean filterApp(AppEntry entry) {
   1657                     boolean isPhotosApp;
   1658                     synchronized (entry) {
   1659                         isPhotosApp = entry.info.category == ApplicationInfo.CATEGORY_IMAGE;
   1660                     }
   1661                     return isPhotosApp;
   1662                 }
   1663             };
   1664 
   1665     public static final AppFilter FILTER_OTHER_APPS =
   1666             new AppFilter() {
   1667                 @Override
   1668                 public void init() {}
   1669 
   1670                 @Override
   1671                 public boolean filterApp(AppEntry entry) {
   1672                     boolean isCategorized;
   1673                     synchronized (entry) {
   1674                         isCategorized =
   1675                                 FILTER_AUDIO.filterApp(entry)
   1676                                         || FILTER_GAMES.filterApp(entry)
   1677                                         || FILTER_MOVIES.filterApp(entry)
   1678                                         || FILTER_PHOTOS.filterApp(entry);
   1679                     }
   1680                     return !isCategorized;
   1681                 }
   1682             };
   1683 }
   1684