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