OSDN Git Service

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