OSDN Git Service

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