OSDN Git Service

Merge "Back-port fixes for b/62196835" into mnc-dev am: 093c7a8e56 am: 0ddd7e4714...
[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 (mEntriesMap) {
586                     mRebuildingSessions.add(this);
587                     mRebuildRequested = true;
588                     mRebuildAsync = false;
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                 // We will wait for .25s for the list to be built.
601                 long waitend = SystemClock.uptimeMillis()+250;
602
603                 while (mRebuildResult == null) {
604                     long now = SystemClock.uptimeMillis();
605                     if (now >= waitend) {
606                         break;
607                     }
608                     try {
609                         mRebuildSync.wait(waitend - now);
610                     } catch (InterruptedException e) {
611                     }
612                 }
613
614                 mRebuildAsync = true;
615
616                 return mRebuildResult;
617             }
618         }
619
620         void handleRebuildList() {
621             AppFilter filter;
622             Comparator<AppEntry> comparator;
623             synchronized (mRebuildSync) {
624                 if (!mRebuildRequested) {
625                     return;
626                 }
627
628                 filter = mRebuildFilter;
629                 comparator = mRebuildComparator;
630                 mRebuildRequested = false;
631                 mRebuildFilter = null;
632                 mRebuildComparator = null;
633                 if (mRebuildForeground) {
634                     Process.setThreadPriority(Process.THREAD_PRIORITY_FOREGROUND);
635                     mRebuildForeground = false;
636                 }
637             }
638
639             if (filter != null) {
640                 filter.init();
641             }
642
643             List<AppEntry> apps;
644             synchronized (mEntriesMap) {
645                 apps = new ArrayList<>(mAppEntries);
646             }
647
648             ArrayList<AppEntry> filteredApps = new ArrayList<AppEntry>();
649             if (DEBUG) Log.i(TAG, "Rebuilding...");
650             for (int i=0; i<apps.size(); i++) {
651                 AppEntry entry = apps.get(i);
652                 if (entry != null && (filter == null || filter.filterApp(entry))) {
653                     synchronized (mEntriesMap) {
654                         if (DEBUG_LOCKING) Log.v(TAG, "rebuild acquired lock");
655                         if (comparator != null) {
656                             // Only need the label if we are going to be sorting.
657                             entry.ensureLabel(mContext);
658                         }
659                         if (DEBUG) Log.i(TAG, "Using " + entry.info.packageName + ": " + entry);
660                         filteredApps.add(entry);
661                         if (DEBUG_LOCKING) Log.v(TAG, "rebuild releasing lock");
662                     }
663                 }
664             }
665
666             if (comparator != null) {
667                 Collections.sort(filteredApps, comparator);
668             }
669
670             synchronized (mRebuildSync) {
671                 if (!mRebuildRequested) {
672                     mLastAppList = filteredApps;
673                     if (!mRebuildAsync) {
674                         mRebuildResult = filteredApps;
675                         mRebuildSync.notifyAll();
676                     } else {
677                         if (!mMainHandler.hasMessages(MainHandler.MSG_REBUILD_COMPLETE, this)) {
678                             Message msg = mMainHandler.obtainMessage(
679                                     MainHandler.MSG_REBUILD_COMPLETE, this);
680                             mMainHandler.sendMessage(msg);
681                         }
682                     }
683                 }
684             }
685
686             Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
687         }
688
689         public void release() {
690             pause();
691             synchronized (mEntriesMap) {
692                 mSessions.remove(this);
693             }
694         }
695     }
696
697     class MainHandler extends Handler {
698         static final int MSG_REBUILD_COMPLETE = 1;
699         static final int MSG_PACKAGE_LIST_CHANGED = 2;
700         static final int MSG_PACKAGE_ICON_CHANGED = 3;
701         static final int MSG_PACKAGE_SIZE_CHANGED = 4;
702         static final int MSG_ALL_SIZES_COMPUTED = 5;
703         static final int MSG_RUNNING_STATE_CHANGED = 6;
704         static final int MSG_LAUNCHER_INFO_CHANGED = 7;
705         static final int MSG_LOAD_ENTRIES_COMPLETE = 8;
706
707         public MainHandler(Looper looper) {
708             super(looper);
709         }
710
711         @Override
712         public void handleMessage(Message msg) {
713             rebuildActiveSessions();
714             switch (msg.what) {
715                 case MSG_REBUILD_COMPLETE: {
716                     Session s = (Session)msg.obj;
717                     if (mActiveSessions.contains(s)) {
718                         s.mCallbacks.onRebuildComplete(s.mLastAppList);
719                     }
720                 } break;
721                 case MSG_PACKAGE_LIST_CHANGED: {
722                     for (int i=0; i<mActiveSessions.size(); i++) {
723                         mActiveSessions.get(i).mCallbacks.onPackageListChanged();
724                     }
725                 } break;
726                 case MSG_PACKAGE_ICON_CHANGED: {
727                     for (int i=0; i<mActiveSessions.size(); i++) {
728                         mActiveSessions.get(i).mCallbacks.onPackageIconChanged();
729                     }
730                 } break;
731                 case MSG_PACKAGE_SIZE_CHANGED: {
732                     for (int i=0; i<mActiveSessions.size(); i++) {
733                         mActiveSessions.get(i).mCallbacks.onPackageSizeChanged(
734                                 (String)msg.obj);
735                     }
736                 } break;
737                 case MSG_ALL_SIZES_COMPUTED: {
738                     for (int i=0; i<mActiveSessions.size(); i++) {
739                         mActiveSessions.get(i).mCallbacks.onAllSizesComputed();
740                     }
741                 } break;
742                 case MSG_RUNNING_STATE_CHANGED: {
743                     for (int i=0; i<mActiveSessions.size(); i++) {
744                         mActiveSessions.get(i).mCallbacks.onRunningStateChanged(
745                                 msg.arg1 != 0);
746                     }
747                 } break;
748                 case MSG_LAUNCHER_INFO_CHANGED: {
749                     for (int i=0; i<mActiveSessions.size(); i++) {
750                         mActiveSessions.get(i).mCallbacks.onLauncherInfoChanged();
751                     }
752                 } break;
753                 case MSG_LOAD_ENTRIES_COMPLETE: {
754                     for (int i=0; i<mActiveSessions.size(); i++) {
755                         mActiveSessions.get(i).mCallbacks.onLoadEntriesCompleted();
756                     }
757                 } break;
758             }
759         }
760     }
761
762     private class BackgroundHandler extends Handler {
763         static final int MSG_REBUILD_LIST = 1;
764         static final int MSG_LOAD_ENTRIES = 2;
765         static final int MSG_LOAD_ICONS = 3;
766         static final int MSG_LOAD_SIZES = 4;
767         static final int MSG_LOAD_LAUNCHER = 5;
768
769         boolean mRunning;
770
771         BackgroundHandler(Looper looper) {
772             super(looper);
773         }
774
775         @Override
776         public void handleMessage(Message msg) {
777             // Always try rebuilding list first thing, if needed.
778             ArrayList<Session> rebuildingSessions = null;
779             synchronized (mEntriesMap) {
780                 if (mRebuildingSessions.size() > 0) {
781                     rebuildingSessions = new ArrayList<Session>(mRebuildingSessions);
782                     mRebuildingSessions.clear();
783                 }
784             }
785             if (rebuildingSessions != null) {
786                 for (int i=0; i<rebuildingSessions.size(); i++) {
787                     rebuildingSessions.get(i).handleRebuildList();
788                 }
789             }
790
791             switch (msg.what) {
792                 case MSG_REBUILD_LIST: {
793                 } break;
794                 case MSG_LOAD_ENTRIES: {
795                     int numDone = 0;
796                     synchronized (mEntriesMap) {
797                         if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_ENTRIES acquired lock");
798                         for (int i = 0; i < mApplications.size() && numDone < 6; i++) {
799                             if (!mRunning) {
800                                 mRunning = true;
801                                 Message m = mMainHandler.obtainMessage(
802                                         MainHandler.MSG_RUNNING_STATE_CHANGED, 1);
803                                 mMainHandler.sendMessage(m);
804                             }
805                             ApplicationInfo info = mApplications.get(i);
806                             int userId = UserHandle.getUserId(info.uid);
807                             if (mEntriesMap.get(userId).get(info.packageName) == null) {
808                                 numDone++;
809                                 getEntryLocked(info);
810                             }
811                             if (userId != 0 && mEntriesMap.indexOfKey(0) >= 0) {
812                                 // If this app is for a profile and we are on the owner, remove
813                                 // the owner entry if it isn't installed.  This will prevent
814                                 // duplicates of work only apps showing up as 'not installed
815                                 // for this user'.
816                                 // Note: This depends on us traversing the users in order, which
817                                 // happens because of the way we generate the list in
818                                 // doResumeIfNeededLocked.
819                                 AppEntry entry = mEntriesMap.get(0).get(info.packageName);
820                                 if (entry != null &&
821                                         (entry.info.flags & ApplicationInfo.FLAG_INSTALLED) == 0) {
822                                     mEntriesMap.get(0).remove(info.packageName);
823                                     mAppEntries.remove(entry);
824                                 }
825                             }
826                         }
827                         if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_ENTRIES releasing lock");
828                     }
829
830                     if (numDone >= 6) {
831                         sendEmptyMessage(MSG_LOAD_ENTRIES);
832                     } else {
833                         if (!mMainHandler.hasMessages(MainHandler.MSG_LOAD_ENTRIES_COMPLETE)) {
834                             mMainHandler.sendEmptyMessage(MainHandler.MSG_LOAD_ENTRIES_COMPLETE);
835                         }
836                         sendEmptyMessage(MSG_LOAD_LAUNCHER);
837                     }
838                 } break;
839                 case MSG_LOAD_LAUNCHER: {
840                     Intent launchIntent = new Intent(Intent.ACTION_MAIN, null)
841                             .addCategory(Intent.CATEGORY_LAUNCHER);
842
843                     for (int i = 0; i < mEntriesMap.size(); i++) {
844                         int userId = mEntriesMap.keyAt(i);
845                         // If we do not specify MATCH_DIRECT_BOOT_AWARE or
846                         // MATCH_DIRECT_BOOT_UNAWARE, system will derive and update the flags
847                         // according to the user's lock state. When the user is locked, components
848                         // with ComponentInfo#directBootAware == false will be filtered. We should
849                         // explicitly include both direct boot aware and unaware components here.
850                         List<ResolveInfo> intents = mPm.queryIntentActivitiesAsUser(
851                                 launchIntent,
852                                 PackageManager.GET_DISABLED_COMPONENTS
853                                         | PackageManager.MATCH_DIRECT_BOOT_AWARE
854                                         | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
855                                 userId
856                         );
857                         synchronized (mEntriesMap) {
858                             if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_LAUNCHER acquired lock");
859                             HashMap<String, AppEntry> userEntries = mEntriesMap.valueAt(i);
860                             final int N = intents.size();
861                             for (int j = 0; j < N; j++) {
862                                 String packageName = intents.get(j).activityInfo.packageName;
863                                 AppEntry entry = userEntries.get(packageName);
864                                 if (entry != null) {
865                                     entry.hasLauncherEntry = true;
866                                 } else {
867                                     Log.w(TAG, "Cannot find pkg: " + packageName
868                                             + " on user " + userId);
869                                 }
870                             }
871                             if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_LAUNCHER releasing lock");
872                         }
873                     }
874
875                     if (!mMainHandler.hasMessages(MainHandler.MSG_LAUNCHER_INFO_CHANGED)) {
876                         mMainHandler.sendEmptyMessage(MainHandler.MSG_LAUNCHER_INFO_CHANGED);
877                     }
878                     sendEmptyMessage(MSG_LOAD_ICONS);
879                 } break;
880                 case MSG_LOAD_ICONS: {
881                     int numDone = 0;
882                     synchronized (mEntriesMap) {
883                         if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_ICONS acquired lock");
884                         for (int i=0; i<mAppEntries.size() && numDone<2; i++) {
885                             AppEntry entry = mAppEntries.get(i);
886                             if (entry.icon == null || !entry.mounted) {
887                                 synchronized (entry) {
888                                     if (entry.ensureIconLocked(mContext, mPm)) {
889                                         if (!mRunning) {
890                                             mRunning = true;
891                                             Message m = mMainHandler.obtainMessage(
892                                                     MainHandler.MSG_RUNNING_STATE_CHANGED, 1);
893                                             mMainHandler.sendMessage(m);
894                                         }
895                                         numDone++;
896                                     }
897                                 }
898                             }
899                         }
900                         if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_ICONS releasing lock");
901                     }
902                     if (numDone > 0) {
903                         if (!mMainHandler.hasMessages(MainHandler.MSG_PACKAGE_ICON_CHANGED)) {
904                             mMainHandler.sendEmptyMessage(MainHandler.MSG_PACKAGE_ICON_CHANGED);
905                         }
906                     }
907                     if (numDone >= 2) {
908                         sendEmptyMessage(MSG_LOAD_ICONS);
909                     } else {
910                         sendEmptyMessage(MSG_LOAD_SIZES);
911                     }
912                 } break;
913                 case MSG_LOAD_SIZES: {
914                     synchronized (mEntriesMap) {
915                         if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_SIZES acquired lock");
916                         if (mCurComputingSizePkg != null) {
917                             if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_SIZES releasing: currently computing");
918                             return;
919                         }
920
921                         long now = SystemClock.uptimeMillis();
922                         for (int i=0; i<mAppEntries.size(); i++) {
923                             AppEntry entry = mAppEntries.get(i);
924                             if (entry.size == SIZE_UNKNOWN || entry.sizeStale) {
925                                 if (entry.sizeLoadStart == 0 ||
926                                         (entry.sizeLoadStart < (now-20*1000))) {
927                                     if (!mRunning) {
928                                         mRunning = true;
929                                         Message m = mMainHandler.obtainMessage(
930                                                 MainHandler.MSG_RUNNING_STATE_CHANGED, 1);
931                                         mMainHandler.sendMessage(m);
932                                     }
933                                     entry.sizeLoadStart = now;
934                                     mCurComputingSizePkg = entry.info.packageName;
935                                     mCurComputingSizeUserId = UserHandle.getUserId(entry.info.uid);
936                                     mPm.getPackageSizeInfoAsUser(mCurComputingSizePkg,
937                                             mCurComputingSizeUserId, mStatsObserver);
938                                 }
939                                 if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_SIZES releasing: now computing");
940                                 return;
941                             }
942                         }
943                         if (!mMainHandler.hasMessages(MainHandler.MSG_ALL_SIZES_COMPUTED)) {
944                             mMainHandler.sendEmptyMessage(MainHandler.MSG_ALL_SIZES_COMPUTED);
945                             mRunning = false;
946                             Message m = mMainHandler.obtainMessage(
947                                     MainHandler.MSG_RUNNING_STATE_CHANGED, 0);
948                             mMainHandler.sendMessage(m);
949                         }
950                         if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_SIZES releasing lock");
951                     }
952                 } break;
953             }
954         }
955
956         final IPackageStatsObserver.Stub mStatsObserver = new IPackageStatsObserver.Stub() {
957             public void onGetStatsCompleted(PackageStats stats, boolean succeeded) {
958                 boolean sizeChanged = false;
959                 synchronized (mEntriesMap) {
960                     if (DEBUG_LOCKING) Log.v(TAG, "onGetStatsCompleted acquired lock");
961                     HashMap<String, AppEntry> userMap = mEntriesMap.get(stats.userHandle);
962                     if (userMap == null) {
963                         // The user must have been removed.
964                         return;
965                     }
966                     AppEntry entry = userMap.get(stats.packageName);
967                     if (entry != null) {
968                         synchronized (entry) {
969                             entry.sizeStale = false;
970                             entry.sizeLoadStart = 0;
971                             long externalCodeSize = stats.externalCodeSize
972                                     + stats.externalObbSize;
973                             long externalDataSize = stats.externalDataSize
974                                     + stats.externalMediaSize;
975                             long newSize = externalCodeSize + externalDataSize
976                                     + getTotalInternalSize(stats);
977                             if (entry.size != newSize ||
978                                     entry.cacheSize != stats.cacheSize ||
979                                     entry.codeSize != stats.codeSize ||
980                                     entry.dataSize != stats.dataSize ||
981                                     entry.externalCodeSize != externalCodeSize ||
982                                     entry.externalDataSize != externalDataSize ||
983                                     entry.externalCacheSize != stats.externalCacheSize) {
984                                 entry.size = newSize;
985                                 entry.cacheSize = stats.cacheSize;
986                                 entry.codeSize = stats.codeSize;
987                                 entry.dataSize = stats.dataSize;
988                                 entry.externalCodeSize = externalCodeSize;
989                                 entry.externalDataSize = externalDataSize;
990                                 entry.externalCacheSize = stats.externalCacheSize;
991                                 entry.sizeStr = getSizeStr(entry.size);
992                                 entry.internalSize = getTotalInternalSize(stats);
993                                 entry.internalSizeStr = getSizeStr(entry.internalSize);
994                                 entry.externalSize = getTotalExternalSize(stats);
995                                 entry.externalSizeStr = getSizeStr(entry.externalSize);
996                                 if (DEBUG) Log.i(TAG, "Set size of " + entry.label + " " + entry
997                                         + ": " + entry.sizeStr);
998                                 sizeChanged = true;
999                             }
1000                         }
1001                         if (sizeChanged) {
1002                             Message msg = mMainHandler.obtainMessage(
1003                                     MainHandler.MSG_PACKAGE_SIZE_CHANGED, stats.packageName);
1004                             mMainHandler.sendMessage(msg);
1005                         }
1006                     }
1007                     if (mCurComputingSizePkg != null
1008                             && (mCurComputingSizePkg.equals(stats.packageName)
1009                             && mCurComputingSizeUserId == stats.userHandle)) {
1010                         mCurComputingSizePkg = null;
1011                         sendEmptyMessage(MSG_LOAD_SIZES);
1012                     }
1013                     if (DEBUG_LOCKING) Log.v(TAG, "onGetStatsCompleted releasing lock");
1014                 }
1015             }
1016         };
1017     }
1018
1019     /**
1020      * Receives notifications when applications are added/removed.
1021      */
1022     private class PackageIntentReceiver extends BroadcastReceiver {
1023         void registerReceiver() {
1024             IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
1025             filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
1026             filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
1027             filter.addDataScheme("package");
1028             mContext.registerReceiver(this, filter);
1029             // Register for events related to sdcard installation.
1030             IntentFilter sdFilter = new IntentFilter();
1031             sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
1032             sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
1033             mContext.registerReceiver(this, sdFilter);
1034             // Register for events related to user creation/deletion.
1035             IntentFilter userFilter = new IntentFilter();
1036             userFilter.addAction(Intent.ACTION_USER_ADDED);
1037             userFilter.addAction(Intent.ACTION_USER_REMOVED);
1038             mContext.registerReceiver(this, userFilter);
1039         }
1040         void unregisterReceiver() {
1041             mContext.unregisterReceiver(this);
1042         }
1043         @Override
1044         public void onReceive(Context context, Intent intent) {
1045             String actionStr = intent.getAction();
1046             if (Intent.ACTION_PACKAGE_ADDED.equals(actionStr)) {
1047                 Uri data = intent.getData();
1048                 String pkgName = data.getEncodedSchemeSpecificPart();
1049                 for (int i = 0; i < mEntriesMap.size(); i++) {
1050                     addPackage(pkgName, mEntriesMap.keyAt(i));
1051                 }
1052             } else if (Intent.ACTION_PACKAGE_REMOVED.equals(actionStr)) {
1053                 Uri data = intent.getData();
1054                 String pkgName = data.getEncodedSchemeSpecificPart();
1055                 for (int i = 0; i < mEntriesMap.size(); i++) {
1056                     removePackage(pkgName, mEntriesMap.keyAt(i));
1057                 }
1058             } else if (Intent.ACTION_PACKAGE_CHANGED.equals(actionStr)) {
1059                 Uri data = intent.getData();
1060                 String pkgName = data.getEncodedSchemeSpecificPart();
1061                 for (int i = 0; i < mEntriesMap.size(); i++) {
1062                     invalidatePackage(pkgName, mEntriesMap.keyAt(i));
1063                 }
1064             } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(actionStr) ||
1065                     Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(actionStr)) {
1066                 // When applications become available or unavailable (perhaps because
1067                 // the SD card was inserted or ejected) we need to refresh the
1068                 // AppInfo with new label, icon and size information as appropriate
1069                 // given the newfound (un)availability of the application.
1070                 // A simple way to do that is to treat the refresh as a package
1071                 // removal followed by a package addition.
1072                 String pkgList[] = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
1073                 if (pkgList == null || pkgList.length == 0) {
1074                     // Ignore
1075                     return;
1076                 }
1077                 boolean avail = Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(actionStr);
1078                 if (avail) {
1079                     for (String pkgName : pkgList) {
1080                         for (int i = 0; i < mEntriesMap.size(); i++) {
1081                             invalidatePackage(pkgName, mEntriesMap.keyAt(i));
1082                         }
1083                     }
1084                 }
1085             } else if (Intent.ACTION_USER_ADDED.equals(actionStr)) {
1086                 addUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL));
1087             } else if (Intent.ACTION_USER_REMOVED.equals(actionStr)) {
1088                 removeUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL));
1089             }
1090         }
1091     }
1092
1093     public interface Callbacks {
1094         void onRunningStateChanged(boolean running);
1095         void onPackageListChanged();
1096         void onRebuildComplete(ArrayList<AppEntry> apps);
1097         void onPackageIconChanged();
1098         void onPackageSizeChanged(String packageName);
1099         void onAllSizesComputed();
1100         void onLauncherInfoChanged();
1101         void onLoadEntriesCompleted();
1102     }
1103
1104     public static class SizeInfo {
1105         public long cacheSize;
1106         public long codeSize;
1107         public long dataSize;
1108         public long externalCodeSize;
1109         public long externalDataSize;
1110
1111         // This is the part of externalDataSize that is in the cache
1112         // section of external storage.  Note that we don't just combine
1113         // this with cacheSize because currently the platform can't
1114         // automatically trim this data when needed, so it is something
1115         // the user may need to manage.  The externalDataSize also includes
1116         // this value, since what this is here is really the part of
1117         // externalDataSize that we can just consider to be "cache" files
1118         // for purposes of cleaning them up in the app details UI.
1119         public long externalCacheSize;
1120     }
1121
1122     public static class AppEntry extends SizeInfo {
1123         public final File apkFile;
1124         public final long id;
1125         public String label;
1126         public long size;
1127         public long internalSize;
1128         public long externalSize;
1129
1130         public boolean mounted;
1131
1132         /**
1133          * Setting this to {@code true} prevents the entry to be filtered by
1134          * {@link #FILTER_DOWNLOADED_AND_LAUNCHER}.
1135          */
1136         public boolean hasLauncherEntry;
1137
1138         public String getNormalizedLabel() {
1139             if (normalizedLabel != null) {
1140                 return normalizedLabel;
1141             }
1142             normalizedLabel = normalize(label);
1143             return normalizedLabel;
1144         }
1145
1146         // Need to synchronize on 'this' for the following.
1147         public ApplicationInfo info;
1148         public Drawable icon;
1149         public String sizeStr;
1150         public String internalSizeStr;
1151         public String externalSizeStr;
1152         public boolean sizeStale;
1153         public long sizeLoadStart;
1154
1155         public String normalizedLabel;
1156
1157         // A location where extra info can be placed to be used by custom filters.
1158         public Object extraInfo;
1159
1160         AppEntry(Context context, ApplicationInfo info, long id) {
1161             apkFile = new File(info.sourceDir);
1162             this.id = id;
1163             this.info = info;
1164             this.size = SIZE_UNKNOWN;
1165             this.sizeStale = true;
1166             ensureLabel(context);
1167         }
1168
1169         public void ensureLabel(Context context) {
1170             if (this.label == null || !this.mounted) {
1171                 if (!this.apkFile.exists()) {
1172                     this.mounted = false;
1173                     this.label = info.packageName;
1174                 } else {
1175                     this.mounted = true;
1176                     CharSequence label = info.loadLabel(context.getPackageManager());
1177                     this.label = label != null ? label.toString() : info.packageName;
1178                 }
1179             }
1180         }
1181
1182         boolean ensureIconLocked(Context context, PackageManager pm) {
1183             if (this.icon == null) {
1184                 if (this.apkFile.exists()) {
1185                     this.icon = getBadgedIcon(pm);
1186                     return true;
1187                 } else {
1188                     this.mounted = false;
1189                     this.icon = context.getDrawable(
1190                             com.android.internal.R.drawable.sym_app_on_sd_unavailable_icon);
1191                 }
1192             } else if (!this.mounted) {
1193                 // If the app wasn't mounted but is now mounted, reload
1194                 // its icon.
1195                 if (this.apkFile.exists()) {
1196                     this.mounted = true;
1197                     this.icon = getBadgedIcon(pm);
1198                     return true;
1199                 }
1200             }
1201             return false;
1202         }
1203
1204         private Drawable getBadgedIcon(PackageManager pm) {
1205             // Do badging ourself so that it comes from the user of the app not the current user.
1206             return pm.getUserBadgedIcon(pm.loadUnbadgedItemIcon(info, info),
1207                     new UserHandle(UserHandle.getUserId(info.uid)));
1208         }
1209
1210         public String getVersion(Context context) {
1211             try {
1212                 return context.getPackageManager().getPackageInfo(info.packageName, 0).versionName;
1213             } catch (PackageManager.NameNotFoundException e) {
1214                 return "";
1215             }
1216         }
1217     }
1218
1219     /**
1220      * Compare by label, then package name, then uid.
1221      */
1222     public static final Comparator<AppEntry> ALPHA_COMPARATOR = new Comparator<AppEntry>() {
1223         private final Collator sCollator = Collator.getInstance();
1224         @Override
1225         public int compare(AppEntry object1, AppEntry object2) {
1226             int compareResult = sCollator.compare(object1.label, object2.label);
1227             if (compareResult != 0) {
1228                 return compareResult;
1229             }
1230             if (object1.info != null && object2.info != null) {
1231                 compareResult =
1232                     sCollator.compare(object1.info.packageName, object2.info.packageName);
1233                 if (compareResult != 0) {
1234                     return compareResult;
1235                 }
1236             }
1237             return object1.info.uid - object2.info.uid;
1238         }
1239     };
1240
1241     public static final Comparator<AppEntry> SIZE_COMPARATOR
1242             = new Comparator<AppEntry>() {
1243         @Override
1244         public int compare(AppEntry object1, AppEntry object2) {
1245             if (object1.size < object2.size) return 1;
1246             if (object1.size > object2.size) return -1;
1247             return ALPHA_COMPARATOR.compare(object1, object2);
1248         }
1249     };
1250
1251     public static final Comparator<AppEntry> INTERNAL_SIZE_COMPARATOR
1252             = new Comparator<AppEntry>() {
1253         @Override
1254         public int compare(AppEntry object1, AppEntry object2) {
1255             if (object1.internalSize < object2.internalSize) return 1;
1256             if (object1.internalSize > object2.internalSize) return -1;
1257             return ALPHA_COMPARATOR.compare(object1, object2);
1258         }
1259     };
1260
1261     public static final Comparator<AppEntry> EXTERNAL_SIZE_COMPARATOR
1262             = new Comparator<AppEntry>() {
1263         @Override
1264         public int compare(AppEntry object1, AppEntry object2) {
1265             if (object1.externalSize < object2.externalSize) return 1;
1266             if (object1.externalSize > object2.externalSize) return -1;
1267             return ALPHA_COMPARATOR.compare(object1, object2);
1268         }
1269     };
1270
1271     public interface AppFilter {
1272         void init();
1273         boolean filterApp(AppEntry info);
1274     }
1275
1276     public static final AppFilter FILTER_PERSONAL = new AppFilter() {
1277         private int mCurrentUser;
1278
1279         public void init() {
1280             mCurrentUser = ActivityManager.getCurrentUser();
1281         }
1282
1283         @Override
1284         public boolean filterApp(AppEntry entry) {
1285             return UserHandle.getUserId(entry.info.uid) == mCurrentUser;
1286         }
1287     };
1288
1289     public static final AppFilter FILTER_WITHOUT_DISABLED_UNTIL_USED = new AppFilter() {
1290         public void init() {
1291             // do nothings
1292         }
1293
1294         @Override
1295         public boolean filterApp(AppEntry entry) {
1296             return entry.info.enabledSetting
1297                     != PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED;
1298         }
1299     };
1300
1301     public static final AppFilter FILTER_WORK = new AppFilter() {
1302         private int mCurrentUser;
1303
1304         public void init() {
1305             mCurrentUser = ActivityManager.getCurrentUser();
1306         }
1307
1308         @Override
1309         public boolean filterApp(AppEntry entry) {
1310             return UserHandle.getUserId(entry.info.uid) != mCurrentUser;
1311         }
1312     };
1313
1314     /**
1315      * Displays a combined list with "downloaded" and "visible in launcher" apps only.
1316      */
1317     public static final AppFilter FILTER_DOWNLOADED_AND_LAUNCHER = new AppFilter() {
1318         public void init() {
1319         }
1320
1321         @Override
1322         public boolean filterApp(AppEntry entry) {
1323             if ((entry.info.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) {
1324                 return true;
1325             } else if ((entry.info.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
1326                 return true;
1327             } else if (entry.hasLauncherEntry) {
1328                 return true;
1329             }
1330             return false;
1331         }
1332     };
1333
1334     public static final AppFilter FILTER_THIRD_PARTY = new AppFilter() {
1335         public void init() {
1336         }
1337
1338         @Override
1339         public boolean filterApp(AppEntry entry) {
1340             if ((entry.info.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) {
1341                 return true;
1342             } else if ((entry.info.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
1343                 return true;
1344             }
1345             return false;
1346         }
1347     };
1348
1349     public static final AppFilter FILTER_DISABLED = new AppFilter() {
1350         public void init() {
1351         }
1352
1353         @Override
1354         public boolean filterApp(AppEntry entry) {
1355             return !entry.info.enabled;
1356         }
1357     };
1358
1359     public static final AppFilter FILTER_ALL_ENABLED = new AppFilter() {
1360         public void init() {
1361         }
1362
1363         @Override
1364         public boolean filterApp(AppEntry entry) {
1365             return entry.info.enabled;
1366         }
1367     };
1368
1369     public static final AppFilter FILTER_EVERYTHING = new AppFilter() {
1370         public void init() {
1371         }
1372
1373         @Override
1374         public boolean filterApp(AppEntry entry) {
1375             return true;
1376         }
1377     };
1378
1379     public static final AppFilter FILTER_WITH_DOMAIN_URLS = new AppFilter() {
1380         public void init() {
1381         }
1382
1383         @Override
1384         public boolean filterApp(AppEntry entry) {
1385             return (entry.info.privateFlags & ApplicationInfo.PRIVATE_FLAG_HAS_DOMAIN_URLS) != 0;
1386         }
1387     };
1388
1389     public static class VolumeFilter implements AppFilter {
1390         private final String mVolumeUuid;
1391
1392         public VolumeFilter(String volumeUuid) {
1393             mVolumeUuid = volumeUuid;
1394         }
1395
1396         @Override
1397         public void init() {
1398         }
1399
1400         @Override
1401         public boolean filterApp(AppEntry info) {
1402             return Objects.equals(info.info.volumeUuid, mVolumeUuid);
1403         }
1404     }
1405
1406     public static class CompoundFilter implements AppFilter {
1407         private final AppFilter mFirstFilter;
1408         private final AppFilter mSecondFilter;
1409
1410         public CompoundFilter(AppFilter first, AppFilter second) {
1411             mFirstFilter = first;
1412             mSecondFilter = second;
1413         }
1414
1415         @Override
1416         public void init() {
1417             mFirstFilter.init();
1418             mSecondFilter.init();
1419         }
1420
1421         @Override
1422         public boolean filterApp(AppEntry info) {
1423             return mFirstFilter.filterApp(info) && mSecondFilter.filterApp(info);
1424         }
1425     }
1426 }