1 /*
2  * Copyright (C) 2021 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.tv.settings.library.device.apps;
18 
19 import android.annotation.IntDef;
20 import android.app.ActivityManager;
21 import android.app.AppGlobals;
22 import android.app.Application;
23 import android.app.usage.StorageStats;
24 import android.app.usage.StorageStatsManager;
25 import android.content.BroadcastReceiver;
26 import android.content.Context;
27 import android.content.Intent;
28 import android.content.IntentFilter;
29 import android.content.pm.ApplicationInfo;
30 import android.content.pm.IPackageManager;
31 import android.content.pm.IPackageStatsObserver;
32 import android.content.pm.ModuleInfo;
33 import android.content.pm.PackageManager;
34 import android.content.pm.PackageManager.NameNotFoundException;
35 import android.content.pm.PackageStats;
36 import android.content.pm.ParceledListSlice;
37 import android.content.pm.ResolveInfo;
38 import android.content.pm.UserInfo;
39 import android.graphics.drawable.Drawable;
40 import android.net.Uri;
41 import android.os.Handler;
42 import android.os.HandlerThread;
43 import android.os.Looper;
44 import android.os.Message;
45 import android.os.Process;
46 import android.os.RemoteException;
47 import android.os.SystemClock;
48 import android.os.UserHandle;
49 import android.os.UserManager;
50 import android.text.format.Formatter;
51 import android.util.Log;
52 import android.util.SparseArray;
53 
54 import com.android.internal.annotations.VisibleForTesting;
55 import com.android.internal.util.ArrayUtils;
56 import com.android.tv.settings.library.util.LibUtils;
57 import com.android.tv.settings.library.util.ThreadUtils;
58 import com.android.tv.settings.library.util.lifecycle.Lifecycle;
59 import com.android.tv.settings.library.util.lifecycle.LifecycleObserver;
60 import com.android.tv.settings.library.util.lifecycle.events.OnDestroy;
61 import com.android.tv.settings.library.util.lifecycle.events.OnPause;
62 import com.android.tv.settings.library.util.lifecycle.events.OnResume;
63 
64 import java.io.File;
65 import java.io.IOException;
66 import java.lang.annotation.Retention;
67 import java.lang.annotation.RetentionPolicy;
68 import java.lang.ref.WeakReference;
69 import java.text.Collator;
70 import java.text.Normalizer;
71 import java.text.Normalizer.Form;
72 import java.util.ArrayList;
73 import java.util.Collections;
74 import java.util.Comparator;
75 import java.util.HashMap;
76 import java.util.HashSet;
77 import java.util.List;
78 import java.util.Objects;
79 import java.util.UUID;
80 import java.util.regex.Pattern;
81 
82 /**
83  * Keeps track of information about all installed applications, lazy-loading
84  * as needed.
85  */
86 public class ApplicationsState {
87     private static final String TAG = "ApplicationsState";
88 
89     public static final int SIZE_UNKNOWN = -1;
90     public static final int SIZE_INVALID = -2;
91 
92     // TODO(b/187728742): Migrate to use one flag.
93     private static final boolean DEBUG = false;
94     private static final boolean DEBUG_LOCKING = false;
95     private static final Object sLock = new Object();
96     private static final Pattern REMOVE_DIACRITICALS_PATTERN
97             = Pattern.compile("\\p{InCombiningDiacriticalMarks}+");
98 
99     @VisibleForTesting
100     static ApplicationsState sInstance;
101 
getInstance(Application app)102     public static ApplicationsState getInstance(Application app) {
103         return getInstance(app, AppGlobals.getPackageManager());
104     }
105 
106     @VisibleForTesting
getInstance(Application app, IPackageManager iPackageManager)107     static ApplicationsState getInstance(Application app, IPackageManager iPackageManager) {
108         synchronized (sLock) {
109             if (sInstance == null) {
110                 sInstance = new ApplicationsState(app, iPackageManager);
111             }
112             return sInstance;
113         }
114     }
115 
116     final Context mContext;
117     final PackageManager mPm;
118     final IPackageManager mIpm;
119     final UserManager mUm;
120     final StorageStatsManager mStats;
121     final int mAdminRetrieveFlags;
122     final int mRetrieveFlags;
123     ApplicationsState.PackageIntentReceiver
124             mPackageIntentReceiver;
125 
126     boolean mResumed;
127     boolean mHaveDisabledApps;
128     boolean mHaveInstantApps;
129 
130     // Information about all applications.  Synchronize on mEntriesMap
131     // to protect access to these.
132     final ArrayList<ApplicationsState.Session> mSessions = new ArrayList<>();
133     final ArrayList<ApplicationsState.Session> mRebuildingSessions = new ArrayList<>();
134     private InterestingConfigChanges mInterestingConfigChanges = new InterestingConfigChanges();
135     // Map: userid => (Map: package name => AppEntry)
136     final SparseArray<HashMap<String, ApplicationsState.AppEntry>> mEntriesMap =
137             new SparseArray<>();
138     final ArrayList<ApplicationsState.AppEntry> mAppEntries = new ArrayList<>();
139     List<ApplicationInfo> mApplications = new ArrayList<>();
140     long mCurId = 1;
141     UUID mCurComputingSizeUuid;
142     String mCurComputingSizePkg;
143     int mCurComputingSizeUserId;
144     boolean mSessionsChanged;
145     // Maps all installed modules on the system to whether they're hidden or not.
146     final HashMap<String, Boolean> mSystemModules = new HashMap<>();
147 
148     // Temporary for dispatching session callbacks.  Only touched by main thread.
149     final ArrayList<WeakReference<ApplicationsState.Session>> mActiveSessions = new ArrayList<>();
150 
151     final HandlerThread mThread;
152     final ApplicationsState.BackgroundHandler
153             mBackgroundHandler;
154     final ApplicationsState.MainHandler
155             mMainHandler = new ApplicationsState.MainHandler(Looper.getMainLooper());
156 
157     /** Requests that the home app is loaded. */
158     public static final int FLAG_SESSION_REQUEST_HOME_APP = 1 << 0;
159 
160     /** Requests that icons are loaded. */
161     public static final int FLAG_SESSION_REQUEST_ICONS = 1 << 1;
162 
163     /** Requests that sizes are loaded. */
164     public static final int FLAG_SESSION_REQUEST_SIZES = 1 << 2;
165 
166     /** Requests that launcher intents are resolved. */
167     public static final int FLAG_SESSION_REQUEST_LAUNCHER = 1 << 3;
168 
169     /** Requests that leanback launcher intents are resolved. */
170     public static final int FLAG_SESSION_REQUEST_LEANBACK_LAUNCHER = 1 << 4;
171 
172     /**
173      * Flags to configure the session to request various types of info.
174      */
175     @IntDef( value = {
176             FLAG_SESSION_REQUEST_HOME_APP,
177             FLAG_SESSION_REQUEST_ICONS,
178             FLAG_SESSION_REQUEST_SIZES,
179             FLAG_SESSION_REQUEST_LAUNCHER,
180             FLAG_SESSION_REQUEST_LEANBACK_LAUNCHER
181     })
182     @Retention(RetentionPolicy.SOURCE)
183     public @interface SessionFlags {
184     }
185 
186     @VisibleForTesting
setInterestingConfigChanges(InterestingConfigChanges interestingConfigChanges)187     void setInterestingConfigChanges(InterestingConfigChanges interestingConfigChanges) {
188         mInterestingConfigChanges = interestingConfigChanges;
189     }
190 
191     @ApplicationsState.SessionFlags
192     public static final int DEFAULT_SESSION_FLAGS =
193             FLAG_SESSION_REQUEST_HOME_APP | FLAG_SESSION_REQUEST_ICONS |
194                     FLAG_SESSION_REQUEST_SIZES | FLAG_SESSION_REQUEST_LAUNCHER;
195 
ApplicationsState(Application app, IPackageManager iPackageManager)196     private ApplicationsState(Application app, IPackageManager iPackageManager) {
197         mContext = app;
198         mPm = mContext.getPackageManager();
199         mIpm = iPackageManager;
200         mUm = mContext.getSystemService(UserManager.class);
201         mStats = mContext.getSystemService(StorageStatsManager.class);
202         for (int userId : mUm.getProfileIdsWithDisabled(UserHandle.myUserId())) {
203             mEntriesMap.put(userId, new HashMap<>());
204         }
205 
206         mThread = new HandlerThread("ApplicationsState.Loader");
207         mThread.start();
208         mBackgroundHandler = new ApplicationsState.BackgroundHandler(mThread.getLooper());
209 
210         // Only the owner can see all apps.
211         mAdminRetrieveFlags = PackageManager.MATCH_ANY_USER |
212                 PackageManager.MATCH_DISABLED_COMPONENTS |
213                 PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS;
214         mRetrieveFlags = PackageManager.MATCH_DISABLED_COMPONENTS |
215                 PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS;
216 
217         final List<ModuleInfo> moduleInfos = mPm.getInstalledModules(0 /* flags */);
218         for (ModuleInfo info : moduleInfos) {
219             mSystemModules.put(info.getPackageName(), info.isHidden());
220         }
221 
222         /**
223          * This is a trick to prevent the foreground thread from being delayed.
224          * The problem is that Dalvik monitors are initially spin locks, to keep
225          * them lightweight.  This leads to unfair contention -- Even though the
226          * background thread only holds the lock for a short amount of time, if
227          * it keeps running and locking again it can prevent the main thread from
228          * acquiring its lock for a long time...  sometimes even > 5 seconds
229          * (leading to an ANR).
230          *
231          * Dalvik will promote a monitor to a "real" lock if it detects enough
232          * contention on it.  It doesn't figure this out fast enough for us
233          * here, though, so this little trick will force it to turn into a real
234          * lock immediately.
235          */
236         synchronized (mEntriesMap) {
237             try {
238                 mEntriesMap.wait(1);
239             } catch (InterruptedException e) {
240             }
241         }
242     }
243 
getBackgroundLooper()244     public Looper getBackgroundLooper() {
245         return mThread.getLooper();
246     }
247 
newSession( ApplicationsState.Callbacks callbacks)248     public ApplicationsState.Session newSession(
249             ApplicationsState.Callbacks callbacks) {
250         return newSession(callbacks, null);
251     }
252 
newSession( ApplicationsState.Callbacks callbacks, Lifecycle lifecycle)253     public ApplicationsState.Session newSession(
254             ApplicationsState.Callbacks callbacks, Lifecycle lifecycle) {
255         ApplicationsState.Session
256                 s = new ApplicationsState.Session(callbacks, lifecycle);
257         synchronized (mEntriesMap) {
258             mSessions.add(s);
259         }
260         return s;
261     }
262 
doResumeIfNeededLocked()263     void doResumeIfNeededLocked() {
264         if (mResumed) {
265             return;
266         }
267         mResumed = true;
268         if (mPackageIntentReceiver == null) {
269             mPackageIntentReceiver = new ApplicationsState.PackageIntentReceiver();
270             mPackageIntentReceiver.registerReceiver();
271         }
272 
273         final List<ApplicationInfo> prevApplications = mApplications;
274         mApplications = new ArrayList<>();
275         for (UserInfo user : mUm.getProfiles(UserHandle.myUserId())) {
276             try {
277                 // If this user is new, it needs a map created.
278                 if (mEntriesMap.indexOfKey(user.id) < 0) {
279                     mEntriesMap.put(user.id, new HashMap<>());
280                 }
281                 @SuppressWarnings("unchecked")
282                 ParceledListSlice<ApplicationInfo> list =
283                         mIpm.getInstalledApplications(
284                                 user.isAdmin() ? mAdminRetrieveFlags : mRetrieveFlags,
285                                 user.id);
286                 mApplications.addAll(list.getList());
287             } catch (Exception e) {
288                 Log.e(TAG, "Error during doResumeIfNeededLocked", e);
289             }
290         }
291 
292         if (mInterestingConfigChanges.applyNewConfig(mContext.getResources())) {
293             // If an interesting part of the configuration has changed, we
294             // should completely reload the app entries.
295             clearEntries();
296         } else {
297             for (int i = 0; i < mAppEntries.size(); i++) {
298                 mAppEntries.get(i).sizeStale = true;
299             }
300         }
301 
302         mHaveDisabledApps = false;
303         mHaveInstantApps = false;
304         for (int i = 0; i < mApplications.size(); i++) {
305             final ApplicationInfo info = mApplications.get(i);
306             // Need to trim out any applications that are disabled by
307             // something different than the user.
308             if (!info.enabled) {
309                 if (info.enabledSetting != PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) {
310                     mApplications.remove(i);
311                     i--;
312                     continue;
313                 }
314                 mHaveDisabledApps = true;
315             }
316             if (isHiddenModule(info.packageName)) {
317                 mApplications.remove(i--);
318                 continue;
319             }
320             if (!mHaveInstantApps && AppUtils.isInstant(info)) {
321                 mHaveInstantApps = true;
322             }
323 
324             int userId = UserHandle.getUserId(info.uid);
325             final ApplicationsState.AppEntry entry = mEntriesMap.get(userId).get(info.packageName);
326             if (entry != null) {
327                 entry.info = info;
328             }
329         }
330 
331         if (anyAppIsRemoved(prevApplications, mApplications)) {
332             // some apps have been uninstalled.
333             clearEntries();
334         }
335         mCurComputingSizePkg = null;
336         if (!mBackgroundHandler.hasMessages(
337                 ApplicationsState.BackgroundHandler.MSG_LOAD_ENTRIES)) {
338             mBackgroundHandler.sendEmptyMessage(
339                     ApplicationsState.BackgroundHandler.MSG_LOAD_ENTRIES);
340         }
341     }
342 
343     /* The original design is mAppEntries.size() > mApplications.size().
344        It's correct if there is only the owner user and only one app is removed.
345        Problem 1:
346        If there is a user profile, the size of mAppEntries < mApplications is normal because
347        the number of app entries on UI (mAppEntries) should be equal to the number of apps got
348        from PMS (mApplications).
349 
350        owner only case:
351        mApplications: user 0: 191
352        mAppEntries  : user 0: 191
353        total mAppEntries: 191, mApplications: 191
354        If an app is removed, cached mAppEntries: 191 , mApplications: 191 -> 190, it is detected
355        as the number of apps becomes less.
356 
357        If there is a work profile, mAppEntries removes some apps that are not installed for the
358        owner user.
359 
360        For example, in the following case, 6 apps are removed from mAppEntries for the owner.
361        mApplications: user 0: 197, user 10: 189 => total 386
362        mAppEntries  : user 0: 191, user 10: 189 => total 380
363        If an app is removed, cached mAppEntries: 380 , mApplications: 386 -> 385, the size of
364        mAppEntries is still not larger than mApplications, then does not clear mAppEntries.
365 
366        Problem 2:
367        If remove an app and add another app outside Settings (e.g. Play Store) and back to
368        Settings, the amount of apps are not changed, it causes the entries keep the removed app.
369 
370        Another case, if adding more apps than removing apps (e.g. add 2 apps and remove 1 app),
371        the final number of apps (mApplications) is even increased,
372 
373        Therefore, should not only count on number of apps to determine any app is removed.
374        Compare the change of applications instead.
375     */
anyAppIsRemoved(List<ApplicationInfo> prevApplications, List<ApplicationInfo> applications)376     private static boolean anyAppIsRemoved(List<ApplicationInfo> prevApplications,
377             List<ApplicationInfo> applications) {
378 
379         // No cache
380         if (prevApplications.size() == 0) {
381             return false;
382         }
383 
384         if (applications.size() < prevApplications.size()) {
385             return true;
386         }
387 
388         // build package sets of all applications <userId, HashSet of packages>
389         final HashMap<String, HashSet<String>> packageMap = new HashMap<>();
390         for (ApplicationInfo application : applications) {
391             final String userId = String.valueOf(UserHandle.getUserId(application.uid));
392 
393             HashSet<String> appPackages = packageMap.get(userId);
394             if (appPackages == null) {
395                 appPackages = new HashSet<>();
396                 packageMap.put(userId, appPackages);
397             }
398             if (hasFlag(application.flags, ApplicationInfo.FLAG_INSTALLED)) {
399                 appPackages.add(application.packageName);
400             }
401         }
402 
403         // detect any previous app is removed
404         for (ApplicationInfo prevApplication : prevApplications) {
405             if (!hasFlag(prevApplication.flags, ApplicationInfo.FLAG_INSTALLED)) {
406                 continue;
407             }
408             final String userId = String.valueOf(UserHandle.getUserId(prevApplication.uid));
409 
410             final HashSet<String> packagesSet = packageMap.get(userId);
411             if (packagesSet == null || !packagesSet.remove(prevApplication.packageName)) {
412                 return true;
413             }
414         }
415 
416         return false;
417     }
418 
419     @VisibleForTesting
clearEntries()420     void clearEntries() {
421         for (int i = 0; i < mEntriesMap.size(); i++) {
422             mEntriesMap.valueAt(i).clear();
423         }
424         mAppEntries.clear();
425     }
426 
haveDisabledApps()427     public boolean haveDisabledApps() {
428         return mHaveDisabledApps;
429     }
430 
haveInstantApps()431     public boolean haveInstantApps() {
432         return mHaveInstantApps;
433     }
434 
isHiddenModule(String packageName)435     boolean isHiddenModule(String packageName) {
436         Boolean isHidden = mSystemModules.get(packageName);
437         if (isHidden == null) {
438             return false;
439         }
440 
441         return isHidden;
442     }
443 
isSystemModule(String packageName)444     boolean isSystemModule(String packageName) {
445         return mSystemModules.containsKey(packageName);
446     }
447 
doPauseIfNeededLocked()448     void doPauseIfNeededLocked() {
449         if (!mResumed) {
450             return;
451         }
452         for (int i = 0; i < mSessions.size(); i++) {
453             if (mSessions.get(i).mResumed) {
454                 return;
455             }
456         }
457         doPauseLocked();
458     }
459 
doPauseLocked()460     void doPauseLocked() {
461         mResumed = false;
462         if (mPackageIntentReceiver != null) {
463             mPackageIntentReceiver.unregisterReceiver();
464             mPackageIntentReceiver = null;
465         }
466     }
467 
getEntry(String packageName, int userId)468     public ApplicationsState.AppEntry getEntry(String packageName, int userId) {
469         if (DEBUG_LOCKING) Log.v(TAG, "getEntry about to acquire lock...");
470         synchronized (mEntriesMap) {
471             ApplicationsState.AppEntry entry = mEntriesMap.get(userId).get(packageName);
472             if (entry == null) {
473                 ApplicationInfo info = getAppInfoLocked(packageName, userId);
474                 if (info == null) {
475                     try {
476                         info = mIpm.getApplicationInfo(packageName, 0, userId);
477                     } catch (RemoteException e) {
478                         Log.w(TAG, "getEntry couldn't reach PackageManager", e);
479                         return null;
480                     }
481                 }
482                 if (info != null) {
483                     entry = getEntryLocked(info);
484                 }
485             }
486             if (DEBUG_LOCKING) Log.v(TAG, "...getEntry releasing lock");
487             return entry;
488         }
489     }
490 
getAppInfoLocked(String pkg, int userId)491     private ApplicationInfo getAppInfoLocked(String pkg, int userId) {
492         for (int i = 0; i < mApplications.size(); i++) {
493             ApplicationInfo info = mApplications.get(i);
494             if (pkg.equals(info.packageName)
495                     && userId == UserHandle.getUserId(info.uid)) {
496                 return info;
497             }
498         }
499         return null;
500     }
501 
ensureIcon(ApplicationsState.AppEntry entry)502     public void ensureIcon(ApplicationsState.AppEntry entry) {
503         if (entry.icon != null) {
504             return;
505         }
506         synchronized (entry) {
507             entry.ensureIconLocked(mContext);
508         }
509     }
510 
511     /**
512      * To generate and cache the label description.
513      *
514      * @param entry contain the entries of an app
515      */
ensureLabelDescription( ApplicationsState.AppEntry entry)516     public void ensureLabelDescription(
517             ApplicationsState.AppEntry entry) {
518         if (entry.labelDescription != null) {
519             return;
520         }
521         synchronized (entry) {
522             entry.ensureLabelDescriptionLocked(mContext);
523         }
524     }
525 
requestSize(String packageName, int userId)526     public void requestSize(String packageName, int userId) {
527         if (DEBUG_LOCKING) Log.v(TAG, "requestSize about to acquire lock...");
528         synchronized (mEntriesMap) {
529             ApplicationsState.AppEntry entry = mEntriesMap.get(userId).get(packageName);
530             if (entry != null && hasFlag(entry.info.flags, ApplicationInfo.FLAG_INSTALLED)) {
531                 mBackgroundHandler.post(
532                         () -> {
533                             try {
534                                 final StorageStats stats =
535                                         mStats.queryStatsForPackage(
536                                                 entry.info.storageUuid,
537                                                 packageName,
538                                                 UserHandle.of(userId));
539                                 final long cacheQuota =
540                                         mStats.getCacheQuotaBytes(
541                                                 entry.info.storageUuid.toString(), entry.info.uid);
542                                 final PackageStats legacy = new PackageStats(packageName, userId);
543                                 legacy.codeSize = stats.getAppBytes();
544                                 legacy.dataSize = stats.getDataBytes();
545                                 legacy.cacheSize = Math.min(stats.getCacheBytes(), cacheQuota);
546                                 try {
547                                     mBackgroundHandler.mStatsObserver.onGetStatsCompleted(
548                                             legacy, true);
549                                 } catch (RemoteException ignored) {
550                                 }
551                             } catch (NameNotFoundException | IOException e) {
552                                 Log.w(TAG, "Failed to query stats: " + e);
553                                 try {
554                                     mBackgroundHandler.mStatsObserver.onGetStatsCompleted(
555                                             null, false);
556                                 } catch (RemoteException ignored) {
557                                 }
558                             }
559                         });
560             }
561             if (DEBUG_LOCKING) Log.v(TAG, "...requestSize releasing lock");
562         }
563     }
564 
sumCacheSizes()565     long sumCacheSizes() {
566         long sum = 0;
567         if (DEBUG_LOCKING) Log.v(TAG, "sumCacheSizes about to acquire lock...");
568         synchronized (mEntriesMap) {
569             if (DEBUG_LOCKING) Log.v(TAG, "-> sumCacheSizes now has lock");
570             for (int i = mAppEntries.size() - 1; i >= 0; i--) {
571                 sum += mAppEntries.get(i).cacheSize;
572             }
573             if (DEBUG_LOCKING) Log.v(TAG, "...sumCacheSizes releasing lock");
574         }
575         return sum;
576     }
577 
indexOfApplicationInfoLocked(String pkgName, int userId)578     int indexOfApplicationInfoLocked(String pkgName, int userId) {
579         for (int i = mApplications.size() - 1; i >= 0; i--) {
580             ApplicationInfo appInfo = mApplications.get(i);
581             if (appInfo.packageName.equals(pkgName)
582                     && UserHandle.getUserId(appInfo.uid) == userId) {
583                 return i;
584             }
585         }
586         return -1;
587     }
588 
addPackage(String pkgName, int userId)589     void addPackage(String pkgName, int userId) {
590         try {
591             synchronized (mEntriesMap) {
592                 if (DEBUG_LOCKING) Log.v(TAG, "addPackage acquired lock");
593                 if (DEBUG) Log.i(TAG, "Adding package " + pkgName);
594                 if (!mResumed) {
595                     // If we are not resumed, we will do a full query the
596                     // next time we resume, so there is no reason to do work
597                     // here.
598                     if (DEBUG_LOCKING) Log.v(TAG, "addPackage release lock: not resumed");
599                     return;
600                 }
601                 if (indexOfApplicationInfoLocked(pkgName, userId) >= 0) {
602                     if (DEBUG) Log.i(TAG, "Package already exists!");
603                     if (DEBUG_LOCKING) Log.v(TAG, "addPackage release lock: already exists");
604                     return;
605                 }
606                 ApplicationInfo info = mIpm.getApplicationInfo(pkgName,
607                         mUm.isUserAdmin(userId) ? mAdminRetrieveFlags : mRetrieveFlags,
608                         userId);
609                 if (info == null) {
610                     return;
611                 }
612                 if (!info.enabled) {
613                     if (info.enabledSetting
614                             != PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) {
615                         return;
616                     }
617                     mHaveDisabledApps = true;
618                 }
619                 if (AppUtils.isInstant(info)) {
620                     mHaveInstantApps = true;
621                 }
622                 mApplications.add(info);
623                 if (!mBackgroundHandler.hasMessages(
624                         ApplicationsState.BackgroundHandler.MSG_LOAD_ENTRIES)) {
625                     mBackgroundHandler.sendEmptyMessage(
626                             ApplicationsState.BackgroundHandler.MSG_LOAD_ENTRIES);
627                 }
628                 if (!mMainHandler.hasMessages(
629                         ApplicationsState.MainHandler.MSG_PACKAGE_LIST_CHANGED)) {
630                     mMainHandler.sendEmptyMessage(
631                             ApplicationsState.MainHandler.MSG_PACKAGE_LIST_CHANGED);
632                 }
633                 if (DEBUG_LOCKING) Log.v(TAG, "addPackage releasing lock");
634             }
635         } catch (RemoteException e) {
636         }
637     }
638 
removePackage(String pkgName, int userId)639     public void removePackage(String pkgName, int userId) {
640         synchronized (mEntriesMap) {
641             if (DEBUG_LOCKING) Log.v(TAG, "removePackage acquired lock");
642             int idx = indexOfApplicationInfoLocked(pkgName, userId);
643             if (DEBUG) Log.i(TAG, "removePackage: " + pkgName + " @ " + idx);
644             if (idx >= 0) {
645                 ApplicationsState.AppEntry entry = mEntriesMap.get(userId).get(pkgName);
646                 if (DEBUG) Log.i(TAG, "removePackage: " + entry);
647                 if (entry != null) {
648                     mEntriesMap.get(userId).remove(pkgName);
649                     mAppEntries.remove(entry);
650                 }
651                 ApplicationInfo info = mApplications.get(idx);
652                 mApplications.remove(idx);
653                 if (!info.enabled) {
654                     mHaveDisabledApps = false;
655                     for (ApplicationInfo otherInfo : mApplications) {
656                         if (!otherInfo.enabled) {
657                             mHaveDisabledApps = true;
658                             break;
659                         }
660                     }
661                 }
662                 if (AppUtils.isInstant(info)) {
663                     mHaveInstantApps = false;
664                     for (ApplicationInfo otherInfo : mApplications) {
665                         if (AppUtils.isInstant(otherInfo)) {
666                             mHaveInstantApps = true;
667                             break;
668                         }
669                     }
670                 }
671                 if (!mMainHandler.hasMessages(
672                         ApplicationsState.MainHandler.MSG_PACKAGE_LIST_CHANGED)) {
673                     mMainHandler.sendEmptyMessage(
674                             ApplicationsState.MainHandler.MSG_PACKAGE_LIST_CHANGED);
675                 }
676             }
677             if (DEBUG_LOCKING) Log.v(TAG, "removePackage releasing lock");
678         }
679     }
680 
invalidatePackage(String pkgName, int userId)681     public void invalidatePackage(String pkgName, int userId) {
682         removePackage(pkgName, userId);
683         addPackage(pkgName, userId);
684     }
685 
addUser(int userId)686     private void addUser(int userId) {
687         final int[] profileIds = mUm.getProfileIdsWithDisabled(UserHandle.myUserId());
688         if (ArrayUtils.contains(profileIds, userId)) {
689             synchronized (mEntriesMap) {
690                 mEntriesMap.put(userId, new HashMap<String, ApplicationsState.AppEntry>());
691                 if (mResumed) {
692                     // If resumed, Manually pause, then cause a resume to repopulate the app list.
693                     // This is the simplest way to reload the packages so that the new user
694                     // is included.  Otherwise the list will be repopulated on next resume.
695                     doPauseLocked();
696                     doResumeIfNeededLocked();
697                 }
698                 if (!mMainHandler.hasMessages(
699                         ApplicationsState.MainHandler.MSG_PACKAGE_LIST_CHANGED)) {
700                     mMainHandler.sendEmptyMessage(
701                             ApplicationsState.MainHandler.MSG_PACKAGE_LIST_CHANGED);
702                 }
703             }
704         }
705     }
706 
removeUser(int userId)707     private void removeUser(int userId) {
708         synchronized (mEntriesMap) {
709             HashMap<String, ApplicationsState.AppEntry> userMap = mEntriesMap.get(userId);
710             if (userMap != null) {
711                 for (ApplicationsState.AppEntry appEntry : userMap.values()) {
712                     mAppEntries.remove(appEntry);
713                     mApplications.remove(appEntry.info);
714                 }
715                 mEntriesMap.remove(userId);
716                 if (!mMainHandler.hasMessages(
717                         ApplicationsState.MainHandler.MSG_PACKAGE_LIST_CHANGED)) {
718                     mMainHandler.sendEmptyMessage(
719                             ApplicationsState.MainHandler.MSG_PACKAGE_LIST_CHANGED);
720                 }
721             }
722         }
723     }
724 
getEntryLocked(ApplicationInfo info)725     private ApplicationsState.AppEntry getEntryLocked(ApplicationInfo info) {
726         int userId = UserHandle.getUserId(info.uid);
727         ApplicationsState.AppEntry entry = mEntriesMap.get(userId).get(info.packageName);
728         if (DEBUG) {
729             Log.i(TAG, "Looking up entry of pkg " + info.packageName + ": " + entry);
730         }
731         if (entry == null) {
732             if (isHiddenModule(info.packageName)) {
733                 if (DEBUG) {
734                     Log.i(TAG, "No AppEntry for " + info.packageName + " (hidden module)");
735                 }
736                 return null;
737             }
738             if (DEBUG) {
739                 Log.i(TAG, "Creating AppEntry for " + info.packageName);
740             }
741             entry = new ApplicationsState.AppEntry(mContext, info, mCurId++);
742             mEntriesMap.get(userId).put(info.packageName, entry);
743             mAppEntries.add(entry);
744         } else if (entry.info != info) {
745             entry.info = info;
746         }
747         return entry;
748     }
749 
750     // --------------------------------------------------------------
751 
getTotalInternalSize(PackageStats ps)752     private long getTotalInternalSize(PackageStats ps) {
753         if (ps != null) {
754             // We subtract the cache size because the system can clear it automatically and
755             // |dataSize| is a superset of |cacheSize|.
756             return ps.codeSize + ps.dataSize - ps.cacheSize;
757         }
758         return SIZE_INVALID;
759     }
760 
getTotalExternalSize(PackageStats ps)761     private long getTotalExternalSize(PackageStats ps) {
762         if (ps != null) {
763             // We also include the cache size here because for non-emulated
764             // we don't automatically clean cache files.
765             return ps.externalCodeSize + ps.externalDataSize
766                     + ps.externalCacheSize
767                     + ps.externalMediaSize + ps.externalObbSize;
768         }
769         return SIZE_INVALID;
770     }
771 
getSizeStr(long size)772     private String getSizeStr(long size) {
773         if (size >= 0) {
774             return Formatter.formatFileSize(mContext, size);
775         }
776         return null;
777     }
778 
rebuildActiveSessions()779     void rebuildActiveSessions() {
780         synchronized (mEntriesMap) {
781             if (!mSessionsChanged) {
782                 return;
783             }
784             mActiveSessions.clear();
785             for (int i = 0; i < mSessions.size(); i++) {
786                 ApplicationsState.Session s = mSessions.get(i);
787                 if (s.mResumed) {
788                     mActiveSessions.add(new WeakReference<>(s));
789                 }
790             }
791         }
792     }
793 
normalize(String str)794     public static String normalize(String str) {
795         String tmp = Normalizer.normalize(str, Form.NFD);
796         return REMOVE_DIACRITICALS_PATTERN.matcher(tmp)
797                 .replaceAll("").toLowerCase();
798     }
799 
800     public class Session implements LifecycleObserver, OnResume, OnPause, OnDestroy {
801 
802         final ApplicationsState.Callbacks mCallbacks;
803         boolean mResumed;
804 
805         // Rebuilding of app list.  Synchronized on mRebuildSync.
806         final Object mRebuildSync = new Object();
807         boolean mRebuildRequested;
808         boolean mRebuildAsync;
809         ApplicationsState.AppFilter mRebuildFilter;
810         Comparator<ApplicationsState.AppEntry> mRebuildComparator;
811         ArrayList<ApplicationsState.AppEntry> mRebuildResult;
812         ArrayList<ApplicationsState.AppEntry> mLastAppList;
813         boolean mRebuildForeground;
814 
815         private final boolean mHasLifecycle;
816         @ApplicationsState.SessionFlags
817         private int mFlags = DEFAULT_SESSION_FLAGS;
818 
Session(ApplicationsState.Callbacks callbacks, Lifecycle lifecycle)819         Session(ApplicationsState.Callbacks callbacks, Lifecycle lifecycle) {
820             mCallbacks = callbacks;
821             if (lifecycle != null) {
822                 lifecycle.addObserver(this);
823                 mHasLifecycle = true;
824             } else {
825                 mHasLifecycle = false;
826             }
827         }
828 
829         @ApplicationsState.SessionFlags
getSessionFlags()830         public int getSessionFlags() {
831             return mFlags;
832         }
833 
setSessionFlags(@pplicationsState.SessionFlags int flags)834         public void setSessionFlags(@ApplicationsState.SessionFlags int flags) {
835             mFlags = flags;
836         }
837 
onResume()838         public void onResume() {
839             if (DEBUG_LOCKING) Log.v(TAG, "resume about to acquire lock...");
840             synchronized (mEntriesMap) {
841                 if (!mResumed) {
842                     mResumed = true;
843                     mSessionsChanged = true;
844                     doPauseLocked();
845                     doResumeIfNeededLocked();
846                 }
847             }
848             if (DEBUG_LOCKING) Log.v(TAG, "...resume releasing lock");
849         }
850 
onPause()851         public void onPause() {
852             if (DEBUG_LOCKING) Log.v(TAG, "pause about to acquire lock...");
853             synchronized (mEntriesMap) {
854                 if (mResumed) {
855                     mResumed = false;
856                     mSessionsChanged = true;
857                     mBackgroundHandler.removeMessages(
858                             ApplicationsState.BackgroundHandler.MSG_REBUILD_LIST, this);
859                     doPauseIfNeededLocked();
860                 }
861                 if (DEBUG_LOCKING) Log.v(TAG, "...pause releasing lock");
862             }
863         }
864 
getAllApps()865         public ArrayList<ApplicationsState.AppEntry> getAllApps() {
866             synchronized (mEntriesMap) {
867                 return new ArrayList<>(mAppEntries);
868             }
869         }
870 
871         // Creates a new list of app entries with the given filter and comparator.
rebuild( ApplicationsState.AppFilter filter, Comparator<ApplicationsState.AppEntry> comparator)872         public ArrayList<ApplicationsState.AppEntry> rebuild(
873                 ApplicationsState.AppFilter filter,
874                 Comparator<ApplicationsState.AppEntry> comparator) {
875             return rebuild(filter, comparator, true);
876         }
877 
rebuild( ApplicationsState.AppFilter filter, Comparator<ApplicationsState.AppEntry> comparator, boolean foreground)878         public ArrayList<ApplicationsState.AppEntry> rebuild(
879                 ApplicationsState.AppFilter filter,
880                 Comparator<ApplicationsState.AppEntry> comparator,
881                 boolean foreground) {
882             synchronized (mRebuildSync) {
883                 synchronized (mRebuildingSessions) {
884                     mRebuildingSessions.add(this);
885                     mRebuildRequested = true;
886                     mRebuildAsync = true;
887                     mRebuildFilter = filter;
888                     mRebuildComparator = comparator;
889                     mRebuildForeground = foreground;
890                     mRebuildResult = null;
891                     if (!mBackgroundHandler.hasMessages(
892                             ApplicationsState.BackgroundHandler.MSG_REBUILD_LIST)) {
893                         Message msg = mBackgroundHandler.obtainMessage(
894                                 ApplicationsState.BackgroundHandler.MSG_REBUILD_LIST);
895                         mBackgroundHandler.sendMessage(msg);
896                     }
897                 }
898 
899                 return null;
900             }
901         }
902 
handleRebuildList()903         void handleRebuildList() {
904             ApplicationsState.AppFilter filter;
905             Comparator<ApplicationsState.AppEntry> comparator;
906 
907             if (!mResumed) {
908                 return;
909             }
910             synchronized (mRebuildSync) {
911                 if (!mRebuildRequested) {
912                     return;
913                 }
914 
915                 filter = mRebuildFilter;
916                 comparator = mRebuildComparator;
917                 mRebuildRequested = false;
918                 mRebuildFilter = null;
919                 mRebuildComparator = null;
920                 if (mRebuildForeground) {
921                     Process.setThreadPriority(Process.THREAD_PRIORITY_FOREGROUND);
922                     mRebuildForeground = false;
923                 }
924             }
925 
926             if (filter != null) {
927                 filter.init(mContext);
928             }
929 
930             final List<ApplicationsState.AppEntry> apps;
931             synchronized (mEntriesMap) {
932                 apps = new ArrayList<>(mAppEntries);
933             }
934 
935             ArrayList<ApplicationsState.AppEntry> filteredApps = new ArrayList<>();
936             if (DEBUG) {
937                 Log.i(TAG, "Rebuilding...");
938             }
939             for (ApplicationsState.AppEntry entry : apps) {
940                 if (entry != null && (filter == null || filter.filterApp(entry))) {
941                     synchronized (mEntriesMap) {
942                         if (DEBUG_LOCKING) {
943                             Log.v(TAG, "rebuild acquired lock");
944                         }
945                         if (comparator != null) {
946                             // Only need the label if we are going to be sorting.
947                             entry.ensureLabel(mContext);
948                         }
949                         if (DEBUG) {
950                             Log.i(TAG, "Using " + entry.info.packageName + ": " + entry);
951                         }
952                         filteredApps.add(entry);
953                         if (DEBUG_LOCKING) {
954                             Log.v(TAG, "rebuild releasing lock");
955                         }
956                     }
957                 }
958             }
959 
960             if (comparator != null) {
961                 synchronized (mEntriesMap) {
962                     // Locking to ensure that the background handler does not mutate
963                     // the size of AppEntries used for ordering while sorting.
964                     Collections.sort(filteredApps, comparator);
965                 }
966             }
967 
968             synchronized (mRebuildSync) {
969                 if (!mRebuildRequested) {
970                     mLastAppList = filteredApps;
971                     if (!mRebuildAsync) {
972                         mRebuildResult = filteredApps;
973                         mRebuildSync.notifyAll();
974                     } else {
975                         if (!mMainHandler.hasMessages(
976                                 ApplicationsState.MainHandler.MSG_REBUILD_COMPLETE, this)) {
977                             Message msg = mMainHandler.obtainMessage(
978                                     ApplicationsState.MainHandler.MSG_REBUILD_COMPLETE, this);
979                             mMainHandler.sendMessage(msg);
980                         }
981                     }
982                 }
983             }
984 
985             Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
986         }
987 
onDestroy()988         public void onDestroy() {
989             if (!mHasLifecycle) {
990                 // TODO: Legacy, remove this later once all usages are switched to Lifecycle
991                 onPause();
992             }
993             synchronized (mEntriesMap) {
994                 mSessions.remove(this);
995             }
996         }
997     }
998 
999     class MainHandler extends Handler {
1000         static final int MSG_REBUILD_COMPLETE = 1;
1001         static final int MSG_PACKAGE_LIST_CHANGED = 2;
1002         static final int MSG_PACKAGE_ICON_CHANGED = 3;
1003         static final int MSG_PACKAGE_SIZE_CHANGED = 4;
1004         static final int MSG_ALL_SIZES_COMPUTED = 5;
1005         static final int MSG_RUNNING_STATE_CHANGED = 6;
1006         static final int MSG_LAUNCHER_INFO_CHANGED = 7;
1007         static final int MSG_LOAD_ENTRIES_COMPLETE = 8;
1008 
MainHandler(Looper looper)1009         public MainHandler(Looper looper) {
1010             super(looper);
1011         }
1012 
1013         @Override
handleMessage(Message msg)1014         public void handleMessage(Message msg) {
1015             rebuildActiveSessions();
1016             switch (msg.what) {
1017                 case MSG_REBUILD_COMPLETE: {
1018                     ApplicationsState.Session
1019                             s = (ApplicationsState.Session) msg.obj;
1020                     for (WeakReference<ApplicationsState.Session> sessionRef : mActiveSessions) {
1021                         final ApplicationsState.Session session = sessionRef.get();
1022                         if (session != null && session == s) {
1023                             s.mCallbacks.onRebuildComplete(s.mLastAppList);
1024                         }
1025                     }
1026                 }
1027                 break;
1028                 case MSG_PACKAGE_LIST_CHANGED: {
1029                     for (WeakReference<ApplicationsState.Session> sessionRef : mActiveSessions) {
1030                         final ApplicationsState.Session session = sessionRef.get();
1031                         if (session != null) {
1032                             session.mCallbacks.onPackageListChanged();
1033                         }
1034                     }
1035                 }
1036                 break;
1037                 case MSG_PACKAGE_ICON_CHANGED: {
1038                     for (WeakReference<ApplicationsState.Session> sessionRef : mActiveSessions) {
1039                         final ApplicationsState.Session session = sessionRef.get();
1040                         if (session != null) {
1041                             session.mCallbacks.onPackageIconChanged();
1042                         }
1043                     }
1044                 }
1045                 break;
1046                 case MSG_PACKAGE_SIZE_CHANGED: {
1047                     for (WeakReference<ApplicationsState.Session> sessionRef : mActiveSessions) {
1048                         final ApplicationsState.Session session = sessionRef.get();
1049                         if (session != null) {
1050                             session.mCallbacks.onPackageSizeChanged(
1051                                     (String) msg.obj);
1052                         }
1053                     }
1054                 }
1055                 break;
1056                 case MSG_ALL_SIZES_COMPUTED: {
1057                     for (WeakReference<ApplicationsState.Session> sessionRef : mActiveSessions) {
1058                         final ApplicationsState.Session session = sessionRef.get();
1059                         if (session != null) {
1060                             session.mCallbacks.onAllSizesComputed();
1061                         }
1062                     }
1063                 }
1064                 break;
1065                 case MSG_RUNNING_STATE_CHANGED: {
1066                     for (WeakReference<ApplicationsState.Session> sessionRef : mActiveSessions) {
1067                         final ApplicationsState.Session session = sessionRef.get();
1068                         if (session != null) {
1069                             session.mCallbacks.onRunningStateChanged(
1070                                     msg.arg1 != 0);
1071                         }
1072                     }
1073                 }
1074                 break;
1075                 case MSG_LAUNCHER_INFO_CHANGED: {
1076                     for (WeakReference<ApplicationsState.Session> sessionRef : mActiveSessions) {
1077                         final ApplicationsState.Session session = sessionRef.get();
1078                         if (session != null) {
1079                             session.mCallbacks.onLauncherInfoChanged();
1080                         }
1081                     }
1082                 }
1083                 break;
1084                 case MSG_LOAD_ENTRIES_COMPLETE: {
1085                     for (WeakReference<ApplicationsState.Session> sessionRef : mActiveSessions) {
1086                         final ApplicationsState.Session session = sessionRef.get();
1087                         if (session != null) {
1088                             session.mCallbacks.onLoadEntriesCompleted();
1089                         }
1090                     }
1091                 }
1092                 break;
1093             }
1094         }
1095     }
1096 
1097     private class BackgroundHandler extends Handler {
1098         static final int MSG_REBUILD_LIST = 1;
1099         static final int MSG_LOAD_ENTRIES = 2;
1100         static final int MSG_LOAD_HOME_APP = 3;
1101         static final int MSG_LOAD_LAUNCHER = 4;
1102         static final int MSG_LOAD_LEANBACK_LAUNCHER = 5;
1103         static final int MSG_LOAD_ICONS = 6;
1104         static final int MSG_LOAD_SIZES = 7;
1105 
1106         boolean mRunning;
1107 
BackgroundHandler(Looper looper)1108         BackgroundHandler(Looper looper) {
1109             super(looper);
1110         }
1111 
1112         @Override
handleMessage(Message msg)1113         public void handleMessage(Message msg) {
1114             // Always try rebuilding list first thing, if needed.
1115             ArrayList<ApplicationsState.Session> rebuildingSessions = null;
1116             synchronized (mRebuildingSessions) {
1117                 if (mRebuildingSessions.size() > 0) {
1118                     rebuildingSessions = new ArrayList<ApplicationsState.Session>(
1119                             mRebuildingSessions);
1120                     mRebuildingSessions.clear();
1121                 }
1122             }
1123             if (rebuildingSessions != null) {
1124                 for (ApplicationsState.Session session : rebuildingSessions) {
1125                     session.handleRebuildList();
1126                 }
1127             }
1128 
1129             int flags = getCombinedSessionFlags(mSessions);
1130 
1131             switch (msg.what) {
1132                 case MSG_REBUILD_LIST: {
1133                 }
1134                 break;
1135                 case MSG_LOAD_ENTRIES: {
1136                     int numDone = 0;
1137                     synchronized (mEntriesMap) {
1138                         if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_ENTRIES acquired lock");
1139                         for (int i = 0; i < mApplications.size() && numDone < 6; i++) {
1140                             if (!mRunning) {
1141                                 mRunning = true;
1142                                 Message m = mMainHandler.obtainMessage(
1143                                         ApplicationsState.MainHandler.MSG_RUNNING_STATE_CHANGED, 1);
1144                                 mMainHandler.sendMessage(m);
1145                             }
1146                             ApplicationInfo info = mApplications.get(i);
1147                             int userId = UserHandle.getUserId(info.uid);
1148                             if (mEntriesMap.get(userId).get(info.packageName) == null) {
1149                                 numDone++;
1150                                 getEntryLocked(info);
1151                             }
1152                             if (userId != 0 && mEntriesMap.indexOfKey(0) >= 0) {
1153                                 // If this app is for a profile and we are on the owner, remove
1154                                 // the owner entry if it isn't installed.  This will prevent
1155                                 // duplicates of work only apps showing up as 'not installed
1156                                 // for this user'.
1157                                 // Note: This depends on us traversing the users in order, which
1158                                 // happens because of the way we generate the list in
1159                                 // doResumeIfNeededLocked.
1160                                 ApplicationsState.AppEntry
1161                                         entry = mEntriesMap.get(0).get(info.packageName);
1162                                 if (entry != null && !hasFlag(entry.info.flags,
1163                                         ApplicationInfo.FLAG_INSTALLED)) {
1164                                     mEntriesMap.get(0).remove(info.packageName);
1165                                     mAppEntries.remove(entry);
1166                                 }
1167                             }
1168                         }
1169                         if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_ENTRIES releasing lock");
1170                     }
1171 
1172                     if (numDone >= 6) {
1173                         sendEmptyMessage(MSG_LOAD_ENTRIES);
1174                     } else {
1175                         if (!mMainHandler.hasMessages(
1176                                 ApplicationsState.MainHandler.MSG_LOAD_ENTRIES_COMPLETE)) {
1177                             mMainHandler.sendEmptyMessage(
1178                                     ApplicationsState.MainHandler.MSG_LOAD_ENTRIES_COMPLETE);
1179                         }
1180                         sendEmptyMessage(MSG_LOAD_HOME_APP);
1181                     }
1182                 }
1183                 break;
1184                 case MSG_LOAD_HOME_APP: {
1185                     if (hasFlag(flags, FLAG_SESSION_REQUEST_HOME_APP)) {
1186                         final List<ResolveInfo> homeActivities = new ArrayList<>();
1187                         mPm.getHomeActivities(homeActivities);
1188                         synchronized (mEntriesMap) {
1189                             final int entryCount = mEntriesMap.size();
1190                             for (int i = 0; i < entryCount; i++) {
1191                                 if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_HOME_APP acquired lock");
1192                                 final HashMap<String, ApplicationsState.AppEntry> userEntries =
1193                                         mEntriesMap.valueAt(
1194                                                 i);
1195                                 for (ResolveInfo activity : homeActivities) {
1196                                     String packageName = activity.activityInfo.packageName;
1197                                     ApplicationsState.AppEntry
1198                                             entry = userEntries.get(packageName);
1199                                     if (entry != null) {
1200                                         entry.isHomeApp = true;
1201                                     }
1202                                 }
1203                                 if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_HOME_APP releasing lock");
1204                             }
1205                         }
1206                     }
1207                     sendEmptyMessage(MSG_LOAD_LAUNCHER);
1208                 }
1209                 break;
1210                 case MSG_LOAD_LAUNCHER:
1211                 case MSG_LOAD_LEANBACK_LAUNCHER: {
1212                     if ((msg.what == MSG_LOAD_LAUNCHER &&
1213                             hasFlag(flags, FLAG_SESSION_REQUEST_LAUNCHER))
1214                             || (msg.what == MSG_LOAD_LEANBACK_LAUNCHER &&
1215                             hasFlag(flags, FLAG_SESSION_REQUEST_LEANBACK_LAUNCHER))) {
1216 
1217                         Intent launchIntent = new Intent(Intent.ACTION_MAIN, null);
1218                         launchIntent.addCategory(msg.what == MSG_LOAD_LAUNCHER
1219                                 ? Intent.CATEGORY_LAUNCHER : Intent.CATEGORY_LEANBACK_LAUNCHER);
1220                         for (int i = 0; i < mEntriesMap.size(); i++) {
1221                             int userId = mEntriesMap.keyAt(i);
1222                             // If we do not specify MATCH_DIRECT_BOOT_AWARE or
1223                             // MATCH_DIRECT_BOOT_UNAWARE, system will derive and update the flags
1224                             // according to the user's lock state. When the user is locked,
1225                             // components with ComponentInfo#directBootAware == false will be
1226                             // filtered. W should explicitly include both direct boot aware and
1227                             // unaware component here.
1228                             List<ResolveInfo> intents = mPm.queryIntentActivitiesAsUser(
1229                                     launchIntent,
1230                                     PackageManager.MATCH_DISABLED_COMPONENTS
1231                                             | PackageManager.MATCH_DIRECT_BOOT_AWARE
1232                                             | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
1233                                     userId
1234                             );
1235                             synchronized (mEntriesMap) {
1236                                 if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_LAUNCHER acquired lock");
1237                                 HashMap<String, ApplicationsState.AppEntry> userEntries =
1238                                         mEntriesMap.valueAt(i);
1239                                 final int N = intents.size();
1240                                 for (int j = 0; j < N; j++) {
1241                                     ResolveInfo resolveInfo = intents.get(j);
1242                                     String packageName = resolveInfo.activityInfo.packageName;
1243                                     ApplicationsState.AppEntry
1244                                             entry = userEntries.get(packageName);
1245                                     if (entry != null) {
1246                                         entry.hasLauncherEntry = true;
1247                                         entry.launcherEntryEnabled |=
1248                                                 resolveInfo.activityInfo.enabled;
1249                                     } else {
1250                                         Log.w(TAG, "Cannot find pkg: " + packageName
1251                                                 + " on user " + userId);
1252                                     }
1253                                 }
1254                                 if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_LAUNCHER releasing lock");
1255                             }
1256                         }
1257 
1258                         if (!mMainHandler.hasMessages(
1259                                 ApplicationsState.MainHandler.MSG_LAUNCHER_INFO_CHANGED)) {
1260                             mMainHandler.sendEmptyMessage(
1261                                     ApplicationsState.MainHandler.MSG_LAUNCHER_INFO_CHANGED);
1262                         }
1263                     }
1264                     if (msg.what == MSG_LOAD_LAUNCHER) {
1265                         sendEmptyMessage(MSG_LOAD_LEANBACK_LAUNCHER);
1266                     } else {
1267                         sendEmptyMessage(MSG_LOAD_ICONS);
1268                     }
1269                 }
1270                 break;
1271                 case MSG_LOAD_ICONS: {
1272                     if (hasFlag(flags, FLAG_SESSION_REQUEST_ICONS)) {
1273                         int numDone = 0;
1274                         synchronized (mEntriesMap) {
1275                             if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_ICONS acquired lock");
1276                             for (int i = 0; i < mAppEntries.size() && numDone < 2; i++) {
1277                                 ApplicationsState.AppEntry
1278                                         entry = mAppEntries.get(i);
1279                                 if (entry.icon == null || !entry.mounted) {
1280                                     synchronized (entry) {
1281                                         if (entry.ensureIconLocked(mContext)) {
1282                                             if (!mRunning) {
1283                                                 mRunning = true;
1284                                                 Message m = mMainHandler.obtainMessage(
1285                                                         ApplicationsState.MainHandler
1286                                                                 .MSG_RUNNING_STATE_CHANGED,
1287                                                         1);
1288                                                 mMainHandler.sendMessage(m);
1289                                             }
1290                                             numDone++;
1291                                         }
1292                                     }
1293                                 }
1294                             }
1295                             if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_ICONS releasing lock");
1296                         }
1297                         if (numDone > 0) {
1298                             if (!mMainHandler.hasMessages(
1299                                     ApplicationsState.MainHandler.MSG_PACKAGE_ICON_CHANGED)) {
1300                                 mMainHandler.sendEmptyMessage(
1301                                         ApplicationsState.MainHandler.MSG_PACKAGE_ICON_CHANGED);
1302                             }
1303                         }
1304                         if (numDone >= 2) {
1305                             sendEmptyMessage(MSG_LOAD_ICONS);
1306                             break;
1307                         }
1308                     }
1309                     sendEmptyMessage(MSG_LOAD_SIZES);
1310                 }
1311                 break;
1312                 case MSG_LOAD_SIZES: {
1313                     if (hasFlag(flags, FLAG_SESSION_REQUEST_SIZES)) {
1314                         synchronized (mEntriesMap) {
1315                             if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_SIZES acquired lock");
1316                             if (mCurComputingSizePkg != null) {
1317                                 if (DEBUG_LOCKING) {
1318                                     Log.v(TAG,
1319                                             "MSG_LOAD_SIZES releasing: currently computing");
1320                                 }
1321                                 return;
1322                             }
1323 
1324                             long now = SystemClock.uptimeMillis();
1325                             for (int i = 0; i < mAppEntries.size(); i++) {
1326                                 ApplicationsState.AppEntry
1327                                         entry = mAppEntries.get(i);
1328                                 if (hasFlag(entry.info.flags, ApplicationInfo.FLAG_INSTALLED)
1329                                         && (entry.size == SIZE_UNKNOWN || entry.sizeStale)) {
1330                                     if (entry.sizeLoadStart == 0 ||
1331                                             (entry.sizeLoadStart < (now - 20 * 1000))) {
1332                                         if (!mRunning) {
1333                                             mRunning = true;
1334                                             Message m = mMainHandler.obtainMessage(
1335                                                     ApplicationsState.MainHandler
1336                                                             .MSG_RUNNING_STATE_CHANGED,
1337                                                     1);
1338                                             mMainHandler.sendMessage(m);
1339                                         }
1340                                         entry.sizeLoadStart = now;
1341                                         mCurComputingSizeUuid = entry.info.storageUuid;
1342                                         mCurComputingSizePkg = entry.info.packageName;
1343                                         mCurComputingSizeUserId = UserHandle.getUserId(
1344                                                 entry.info.uid);
1345 
1346                                         mBackgroundHandler.post(() -> {
1347                                             try {
1348                                                 final StorageStats stats =
1349                                                         mStats.queryStatsForPackage(
1350                                                                 mCurComputingSizeUuid,
1351                                                                 mCurComputingSizePkg,
1352                                                                 UserHandle.of(
1353                                                                         mCurComputingSizeUserId));
1354                                                 final PackageStats legacy = new PackageStats(
1355                                                         mCurComputingSizePkg,
1356                                                         mCurComputingSizeUserId);
1357                                                 legacy.codeSize = stats.getAppBytes();
1358                                                 legacy.dataSize = stats.getDataBytes();
1359                                                 legacy.cacheSize = stats.getCacheBytes();
1360                                                 try {
1361                                                     mStatsObserver.onGetStatsCompleted(legacy,
1362                                                             true);
1363                                                 } catch (RemoteException ignored) {
1364                                                 }
1365                                             } catch (NameNotFoundException | IOException e) {
1366                                                 Log.w(TAG, "Failed to query stats: " + e);
1367                                                 try {
1368                                                     mStatsObserver.onGetStatsCompleted(null, false);
1369                                                 } catch (RemoteException ignored) {
1370                                                 }
1371                                             }
1372 
1373                                         });
1374                                     }
1375                                     if (DEBUG_LOCKING) {
1376                                         Log.v(TAG,
1377                                                 "MSG_LOAD_SIZES releasing: now computing");
1378                                     }
1379                                     return;
1380                                 }
1381                             }
1382                             if (!mMainHandler.hasMessages(
1383                                     ApplicationsState.MainHandler.MSG_ALL_SIZES_COMPUTED)) {
1384                                 mMainHandler.sendEmptyMessage(
1385                                         ApplicationsState.MainHandler.MSG_ALL_SIZES_COMPUTED);
1386                                 mRunning = false;
1387                                 Message m = mMainHandler.obtainMessage(
1388                                         ApplicationsState.MainHandler.MSG_RUNNING_STATE_CHANGED, 0);
1389                                 mMainHandler.sendMessage(m);
1390                             }
1391                             if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_SIZES releasing lock");
1392                         }
1393                     }
1394                 }
1395                 break;
1396             }
1397         }
1398 
1399         @ApplicationsState.SessionFlags
getCombinedSessionFlags(List<ApplicationsState.Session> sessions)1400         private int getCombinedSessionFlags(List<ApplicationsState.Session> sessions) {
1401             synchronized (mEntriesMap) {
1402                 int flags = 0;
1403                 for (ApplicationsState.Session session : sessions) {
1404                     flags |= session.mFlags;
1405                 }
1406                 return flags;
1407             }
1408         }
1409 
1410         final IPackageStatsObserver.Stub mStatsObserver = new IPackageStatsObserver.Stub() {
1411             public void onGetStatsCompleted(PackageStats stats, boolean succeeded) {
1412                 if (!succeeded) {
1413                     // There is no meaningful information in stats if the call failed.
1414                     return;
1415                 }
1416 
1417                 boolean sizeChanged = false;
1418                 synchronized (mEntriesMap) {
1419                     if (DEBUG_LOCKING) Log.v(TAG, "onGetStatsCompleted acquired lock");
1420                     HashMap<String, ApplicationsState.AppEntry> userMap = mEntriesMap.get(
1421                             stats.userHandle);
1422                     if (userMap == null) {
1423                         // The user must have been removed.
1424                         return;
1425                     }
1426                     ApplicationsState.AppEntry entry = userMap.get(stats.packageName);
1427                     if (entry != null) {
1428                         synchronized (entry) {
1429                             entry.sizeStale = false;
1430                             entry.sizeLoadStart = 0;
1431                             long externalCodeSize = stats.externalCodeSize
1432                                     + stats.externalObbSize;
1433                             long externalDataSize = stats.externalDataSize
1434                                     + stats.externalMediaSize;
1435                             long newSize = externalCodeSize + externalDataSize
1436                                     + getTotalInternalSize(stats);
1437                             if (entry.size != newSize ||
1438                                     entry.cacheSize != stats.cacheSize ||
1439                                     entry.codeSize != stats.codeSize ||
1440                                     entry.dataSize != stats.dataSize ||
1441                                     entry.externalCodeSize != externalCodeSize ||
1442                                     entry.externalDataSize != externalDataSize ||
1443                                     entry.externalCacheSize != stats.externalCacheSize) {
1444                                 entry.size = newSize;
1445                                 entry.cacheSize = stats.cacheSize;
1446                                 entry.codeSize = stats.codeSize;
1447                                 entry.dataSize = stats.dataSize;
1448                                 entry.externalCodeSize = externalCodeSize;
1449                                 entry.externalDataSize = externalDataSize;
1450                                 entry.externalCacheSize = stats.externalCacheSize;
1451                                 entry.sizeStr = getSizeStr(entry.size);
1452                                 entry.internalSize = getTotalInternalSize(stats);
1453                                 entry.internalSizeStr = getSizeStr(entry.internalSize);
1454                                 entry.externalSize = getTotalExternalSize(stats);
1455                                 entry.externalSizeStr = getSizeStr(entry.externalSize);
1456                                 if (DEBUG) {
1457                                     Log.i(TAG, "Set size of " + entry.label + " " + entry
1458                                             + ": " + entry.sizeStr);
1459                                 }
1460                                 sizeChanged = true;
1461                             }
1462                         }
1463                         if (sizeChanged) {
1464                             Message msg = mMainHandler.obtainMessage(
1465                                     ApplicationsState.MainHandler.MSG_PACKAGE_SIZE_CHANGED,
1466                                     stats.packageName);
1467                             mMainHandler.sendMessage(msg);
1468                         }
1469                     }
1470                     if (mCurComputingSizePkg != null
1471                             && (mCurComputingSizePkg.equals(stats.packageName)
1472                             && mCurComputingSizeUserId == stats.userHandle)) {
1473                         mCurComputingSizePkg = null;
1474                         sendEmptyMessage(MSG_LOAD_SIZES);
1475                     }
1476                     if (DEBUG_LOCKING) Log.v(TAG, "onGetStatsCompleted releasing lock");
1477                 }
1478             }
1479         };
1480     }
1481 
1482     /**
1483      * Receives notifications when applications are added/removed.
1484      */
1485     private class PackageIntentReceiver extends BroadcastReceiver {
registerReceiver()1486         void registerReceiver() {
1487             IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
1488             filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
1489             filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
1490             filter.addDataScheme("package");
1491             mContext.registerReceiver(this, filter);
1492             // Register for events related to sdcard installation.
1493             IntentFilter sdFilter = new IntentFilter();
1494             sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
1495             sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
1496             mContext.registerReceiver(this, sdFilter);
1497             // Register for events related to user creation/deletion.
1498             IntentFilter userFilter = new IntentFilter();
1499             userFilter.addAction(Intent.ACTION_USER_ADDED);
1500             userFilter.addAction(Intent.ACTION_USER_REMOVED);
1501             mContext.registerReceiver(this, userFilter);
1502         }
1503 
unregisterReceiver()1504         void unregisterReceiver() {
1505             mContext.unregisterReceiver(this);
1506         }
1507 
1508         @Override
onReceive(Context context, Intent intent)1509         public void onReceive(Context context, Intent intent) {
1510             String actionStr = intent.getAction();
1511             if (Intent.ACTION_PACKAGE_ADDED.equals(actionStr)) {
1512                 Uri data = intent.getData();
1513                 String pkgName = data.getEncodedSchemeSpecificPart();
1514                 for (int i = 0; i < mEntriesMap.size(); i++) {
1515                     addPackage(pkgName, mEntriesMap.keyAt(i));
1516                 }
1517             } else if (Intent.ACTION_PACKAGE_REMOVED.equals(actionStr)) {
1518                 Uri data = intent.getData();
1519                 String pkgName = data.getEncodedSchemeSpecificPart();
1520                 for (int i = 0; i < mEntriesMap.size(); i++) {
1521                     removePackage(pkgName, mEntriesMap.keyAt(i));
1522                 }
1523             } else if (Intent.ACTION_PACKAGE_CHANGED.equals(actionStr)) {
1524                 Uri data = intent.getData();
1525                 String pkgName = data.getEncodedSchemeSpecificPart();
1526                 for (int i = 0; i < mEntriesMap.size(); i++) {
1527                     invalidatePackage(pkgName, mEntriesMap.keyAt(i));
1528                 }
1529             } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(actionStr) ||
1530                     Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(actionStr)) {
1531                 // When applications become available or unavailable (perhaps because
1532                 // the SD card was inserted or ejected) we need to refresh the
1533                 // AppInfo with new label, icon and size information as appropriate
1534                 // given the newfound (un)availability of the application.
1535                 // A simple way to do that is to treat the refresh as a package
1536                 // removal followed by a package addition.
1537                 String[] pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
1538                 if (pkgList == null || pkgList.length == 0) {
1539                     // Ignore
1540                     return;
1541                 }
1542                 boolean avail = Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(actionStr);
1543                 if (avail) {
1544                     for (String pkgName : pkgList) {
1545                         for (int i = 0; i < mEntriesMap.size(); i++) {
1546                             invalidatePackage(pkgName, mEntriesMap.keyAt(i));
1547                         }
1548                     }
1549                 }
1550             } else if (Intent.ACTION_USER_ADDED.equals(actionStr)) {
1551                 addUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL));
1552             } else if (Intent.ACTION_USER_REMOVED.equals(actionStr)) {
1553                 removeUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL));
1554             }
1555         }
1556     }
1557 
1558     /**
1559      * Whether the packages for the  user have been initialized.
1560      */
isUserAdded(int userId)1561     public boolean isUserAdded(int userId) {
1562         return mEntriesMap.contains(userId);
1563     }
1564 
1565     public interface Callbacks {
onRunningStateChanged(boolean running)1566         void onRunningStateChanged(boolean running);
1567 
onPackageListChanged()1568         void onPackageListChanged();
1569 
onRebuildComplete(ArrayList<ApplicationsState.AppEntry> apps)1570         void onRebuildComplete(ArrayList<ApplicationsState.AppEntry> apps);
1571 
onPackageIconChanged()1572         void onPackageIconChanged();
1573 
onPackageSizeChanged(String packageName)1574         void onPackageSizeChanged(String packageName);
1575 
onAllSizesComputed()1576         void onAllSizesComputed();
1577 
onLauncherInfoChanged()1578         void onLauncherInfoChanged();
1579 
onLoadEntriesCompleted()1580         void onLoadEntriesCompleted();
1581     }
1582 
1583     public static class SizeInfo {
1584         public long cacheSize;
1585         public long codeSize;
1586         public long dataSize;
1587         public long externalCodeSize;
1588         public long externalDataSize;
1589 
1590         // This is the part of externalDataSize that is in the cache
1591         // section of external storage.  Note that we don't just combine
1592         // this with cacheSize because currently the platform can't
1593         // automatically trim this data when needed, so it is something
1594         // the user may need to manage.  The externalDataSize also includes
1595         // this value, since what this is here is really the part of
1596         // externalDataSize that we can just consider to be "cache" files
1597         // for purposes of cleaning them up in the app details UI.
1598         public long externalCacheSize;
1599     }
1600 
1601     public static class AppEntry extends
1602             ApplicationsState.SizeInfo {
1603         public final File apkFile;
1604         public final long id;
1605         public String label;
1606         public long size;
1607         public long internalSize;
1608         public long externalSize;
1609         public String labelDescription;
1610 
1611         public boolean mounted;
1612 
1613         /**
1614          * Setting this to {@code true} prevents the entry to be filtered by
1615          * {@link #FILTER_DOWNLOADED_AND_LAUNCHER}.
1616          */
1617         public boolean hasLauncherEntry;
1618 
1619         /**
1620          * Whether the component that has a launcher intent filter is enabled.
1621          */
1622         public boolean launcherEntryEnabled;
1623 
1624         /**
1625          * Whether or not it's a Home app.
1626          */
1627         public boolean isHomeApp;
1628 
getNormalizedLabel()1629         public String getNormalizedLabel() {
1630             if (normalizedLabel != null) {
1631                 return normalizedLabel;
1632             }
1633             normalizedLabel = normalize(label);
1634             return normalizedLabel;
1635         }
1636 
1637         // Need to synchronize on 'this' for the following.
1638         public ApplicationInfo info;
1639         public Drawable icon;
1640         public String sizeStr;
1641         public String internalSizeStr;
1642         public String externalSizeStr;
1643         public boolean sizeStale;
1644         public long sizeLoadStart;
1645 
1646         public String normalizedLabel;
1647 
1648         // A location where extra info can be placed to be used by custom filters.
1649         public Object extraInfo;
1650 
AppEntry(Context context, ApplicationInfo info, long id)1651         public AppEntry(Context context, ApplicationInfo info, long id) {
1652             apkFile = new File(info.sourceDir);
1653             this.id = id;
1654             this.info = info;
1655             this.size = SIZE_UNKNOWN;
1656             this.sizeStale = true;
1657             ensureLabel(context);
1658             // Speed up the cache of the icon and label description if they haven't been created.
1659             ThreadUtils.postOnBackgroundThread(() -> {
1660                 if (this.icon == null) {
1661                     this.ensureIconLocked(context);
1662                 }
1663                 if (this.labelDescription == null) {
1664                     this.ensureLabelDescriptionLocked(context);
1665                 }
1666             });
1667         }
1668 
ensureLabel(Context context)1669         public void ensureLabel(Context context) {
1670             if (this.label == null || !this.mounted) {
1671                 if (!this.apkFile.exists()) {
1672                     this.mounted = false;
1673                     this.label = info.packageName;
1674                 } else {
1675                     this.mounted = true;
1676                     CharSequence label = info.loadLabel(context.getPackageManager());
1677                     this.label = label != null ? label.toString() : info.packageName;
1678                 }
1679             }
1680         }
1681 
ensureIconLocked(Context context)1682         boolean ensureIconLocked(Context context) {
1683             if (this.icon == null) {
1684                 if (this.apkFile.exists()) {
1685                     this.icon = LibUtils.getBadgedIcon(context, info);
1686                     return true;
1687                 } else {
1688                     this.mounted = false;
1689                     this.icon = context.getDrawable(context.getResources().getIdentifier(
1690                             "sym_app_on_sd_unavailable_icon", "drawable", "android"));
1691                 }
1692             } else if (!this.mounted) {
1693                 // If the app wasn't mounted but is now mounted, reload
1694                 // its icon.
1695                 if (this.apkFile.exists()) {
1696                     this.mounted = true;
1697                     this.icon = LibUtils.getBadgedIcon(context, info);
1698                     return true;
1699                 }
1700             }
1701             return false;
1702         }
1703 
getVersion(Context context)1704         public String getVersion(Context context) {
1705             try {
1706                 return context.getPackageManager().getPackageInfo(info.packageName, 0).versionName;
1707             } catch (PackageManager.NameNotFoundException e) {
1708                 return "";
1709             }
1710         }
1711 
1712         /**
1713          * Get the label description which distinguishes a personal app from a work app for
1714          * accessibility purpose. If the app is in a work profile, then add a "work" prefix to the
1715          * app label.
1716          *
1717          * @param context The application context
1718          */
ensureLabelDescriptionLocked(Context context)1719         public void ensureLabelDescriptionLocked(Context context) {
1720             final int userId = UserHandle.getUserId(this.info.uid);
1721             if (UserManager.get(context).isManagedProfile(userId)) {
1722                 this.labelDescription = "";
1723             } else {
1724                 this.labelDescription = this.label;
1725             }
1726         }
1727     }
1728 
hasFlag(int flags, int flag)1729     private static boolean hasFlag(int flags, int flag) {
1730         return (flags & flag) != 0;
1731     }
1732 
1733     /**
1734      * Compare by label, then package name, then uid.
1735      */
1736     public static final Comparator<ApplicationsState.AppEntry> ALPHA_COMPARATOR =
1737             new Comparator<ApplicationsState.AppEntry>() {
1738                 private final Collator sCollator = Collator.getInstance();
1739 
1740                 @Override
1741                 public int compare(ApplicationsState.AppEntry object1,
1742                         ApplicationsState.AppEntry object2) {
1743                     int compareResult = sCollator.compare(object1.label, object2.label);
1744                     if (compareResult != 0) {
1745                         return compareResult;
1746                     }
1747                     if (object1.info != null && object2.info != null) {
1748                         compareResult =
1749                                 sCollator.compare(object1.info.packageName,
1750                                         object2.info.packageName);
1751                         if (compareResult != 0) {
1752                             return compareResult;
1753                         }
1754                     }
1755 
1756                     return object1.info.uid - object2.info.uid;
1757                 }
1758             };
1759 
1760     public static final Comparator<ApplicationsState.AppEntry> SIZE_COMPARATOR
1761             = new Comparator<ApplicationsState.AppEntry>() {
1762         @Override
1763         public int compare(ApplicationsState.AppEntry object1, ApplicationsState.AppEntry object2) {
1764             if (object1.size < object2.size) return 1;
1765             if (object1.size > object2.size) return -1;
1766             return ALPHA_COMPARATOR.compare(object1, object2);
1767         }
1768     };
1769 
1770     public static final Comparator<ApplicationsState.AppEntry> INTERNAL_SIZE_COMPARATOR
1771             = new Comparator<ApplicationsState.AppEntry>() {
1772         @Override
1773         public int compare(ApplicationsState.AppEntry object1, ApplicationsState.AppEntry object2) {
1774             if (object1.internalSize < object2.internalSize) return 1;
1775             if (object1.internalSize > object2.internalSize) return -1;
1776             return ALPHA_COMPARATOR.compare(object1, object2);
1777         }
1778     };
1779 
1780     public static final Comparator<ApplicationsState.AppEntry> EXTERNAL_SIZE_COMPARATOR
1781             = new Comparator<ApplicationsState.AppEntry>() {
1782         @Override
1783         public int compare(ApplicationsState.AppEntry object1, ApplicationsState.AppEntry object2) {
1784             if (object1.externalSize < object2.externalSize) return 1;
1785             if (object1.externalSize > object2.externalSize) return -1;
1786             return ALPHA_COMPARATOR.compare(object1, object2);
1787         }
1788     };
1789 
1790     public interface AppFilter {
init()1791         void init();
1792 
init(Context context)1793         default void init(Context context) {
1794             init();
1795         }
1796 
filterApp(ApplicationsState.AppEntry info)1797         boolean filterApp(ApplicationsState.AppEntry info);
1798     }
1799 
1800     public static final ApplicationsState.AppFilter
1801             FILTER_PERSONAL = new ApplicationsState.AppFilter() {
1802         private int mCurrentUser;
1803 
1804         @Override
1805         public void init() {
1806             mCurrentUser = ActivityManager.getCurrentUser();
1807         }
1808 
1809         @Override
1810         public boolean filterApp(
1811                 ApplicationsState.AppEntry entry) {
1812             return UserHandle.getUserId(entry.info.uid) == mCurrentUser;
1813         }
1814     };
1815 
1816     public static final ApplicationsState.AppFilter
1817             FILTER_WITHOUT_DISABLED_UNTIL_USED = new ApplicationsState.AppFilter() {
1818         @Override
1819         public void init() {
1820             // do nothing
1821         }
1822 
1823         @Override
1824         public boolean filterApp(
1825                 ApplicationsState.AppEntry entry) {
1826             return entry.info.enabledSetting
1827                     != PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED;
1828         }
1829     };
1830 
1831 
1832     /**
1833      * Displays a combined list with "downloaded" and "visible in launcher" apps only.
1834      */
1835     public static final ApplicationsState.AppFilter
1836             FILTER_DOWNLOADED_AND_LAUNCHER = new ApplicationsState.AppFilter() {
1837         @Override
1838         public void init() {
1839         }
1840 
1841         @Override
1842         public boolean filterApp(
1843                 ApplicationsState.AppEntry entry) {
1844             if (AppUtils.isInstant(entry.info)) {
1845                 return false;
1846             } else if (hasFlag(entry.info.flags, ApplicationInfo.FLAG_UPDATED_SYSTEM_APP)) {
1847                 return true;
1848             } else if (!hasFlag(entry.info.flags, ApplicationInfo.FLAG_SYSTEM)) {
1849                 return true;
1850             } else if (entry.hasLauncherEntry) {
1851                 return true;
1852             } else {
1853                 return hasFlag(entry.info.flags, ApplicationInfo.FLAG_SYSTEM) && entry.isHomeApp;
1854             }
1855         }
1856     };
1857 
1858     /**
1859      * Displays a combined list with "downloaded" and "visible in launcher" apps only.
1860      */
1861     public static final ApplicationsState.AppFilter
1862             FILTER_DOWNLOADED_AND_LAUNCHER_AND_INSTANT = new ApplicationsState.AppFilter() {
1863 
1864         @Override
1865         public void init() {
1866         }
1867 
1868         @Override
1869         public boolean filterApp(
1870                 ApplicationsState.AppEntry entry) {
1871             return AppUtils.isInstant(entry.info)
1872                     || FILTER_DOWNLOADED_AND_LAUNCHER.filterApp(entry);
1873         }
1874 
1875     };
1876 
1877     public static final ApplicationsState.AppFilter
1878             FILTER_THIRD_PARTY = new ApplicationsState.AppFilter() {
1879         @Override
1880         public void init() {
1881         }
1882 
1883         @Override
1884         public boolean filterApp(
1885                 ApplicationsState.AppEntry entry) {
1886             if (hasFlag(entry.info.flags, ApplicationInfo.FLAG_UPDATED_SYSTEM_APP)) {
1887                 return true;
1888             } else {
1889                 return !hasFlag(entry.info.flags, ApplicationInfo.FLAG_SYSTEM);
1890             }
1891         }
1892     };
1893 
1894     public static final ApplicationsState.AppFilter
1895             FILTER_DISABLED = new ApplicationsState.AppFilter() {
1896         @Override
1897         public void init() {
1898         }
1899 
1900         @Override
1901         public boolean filterApp(
1902                 ApplicationsState.AppEntry entry) {
1903             return !entry.info.enabled && !AppUtils.isInstant(entry.info);
1904         }
1905     };
1906 
1907     public static final ApplicationsState.AppFilter
1908             FILTER_INSTANT = new ApplicationsState.AppFilter() {
1909         @Override
1910         public void init() {
1911         }
1912 
1913         @Override
1914         public boolean filterApp(
1915                 ApplicationsState.AppEntry entry) {
1916             return AppUtils.isInstant(entry.info);
1917         }
1918     };
1919 
1920     public static final ApplicationsState.AppFilter
1921             FILTER_ALL_ENABLED = new ApplicationsState.AppFilter() {
1922         @Override
1923         public void init() {
1924         }
1925 
1926         @Override
1927         public boolean filterApp(
1928                 ApplicationsState.AppEntry entry) {
1929             return entry.info.enabled && !AppUtils.isInstant(entry.info);
1930         }
1931     };
1932 
1933     public static final ApplicationsState.AppFilter
1934             FILTER_EVERYTHING = new ApplicationsState.AppFilter() {
1935         @Override
1936         public void init() {
1937         }
1938 
1939         @Override
1940         public boolean filterApp(
1941                 ApplicationsState.AppEntry entry) {
1942             return true;
1943         }
1944     };
1945 
1946 
1947     public static final ApplicationsState.AppFilter
1948             FILTER_NOT_HIDE = new ApplicationsState.AppFilter() {
1949         private String[] mHidePackageNames;
1950 
1951         @Override
1952         public void init(Context context) {
1953             mHidePackageNames = context.getResources()
1954                     .getStringArray(context.getResources().getIdentifier(
1955                             "config_hideWhenDisabled_packageNames", "array", "android"));
1956         }
1957 
1958         @Override
1959         public void init() {
1960         }
1961 
1962         @Override
1963         public boolean filterApp(
1964                 ApplicationsState.AppEntry entry) {
1965             if (ArrayUtils.contains(mHidePackageNames, entry.info.packageName)) {
1966                 if (!entry.info.enabled) {
1967                     return false;
1968                 } else {
1969                     return entry.info.enabledSetting
1970                             != PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED;
1971                 }
1972             }
1973 
1974             return true;
1975         }
1976     };
1977 
1978     public static final ApplicationsState.AppFilter
1979             FILTER_GAMES = new ApplicationsState.AppFilter() {
1980         @Override
1981         public void init() {
1982         }
1983 
1984         @Override
1985         public boolean filterApp(ApplicationsState.AppEntry info) {
1986             // TODO: Update for the new game category.
1987             boolean isGame;
1988             synchronized (info.info) {
1989                 isGame = hasFlag(info.info.flags, ApplicationInfo.FLAG_IS_GAME)
1990                         || info.info.category == ApplicationInfo.CATEGORY_GAME;
1991             }
1992             return isGame;
1993         }
1994     };
1995 
1996     public static class VolumeFilter implements
1997             ApplicationsState.AppFilter {
1998         private final String mVolumeUuid;
1999 
VolumeFilter(String volumeUuid)2000         public VolumeFilter(String volumeUuid) {
2001             mVolumeUuid = volumeUuid;
2002         }
2003 
2004         @Override
init()2005         public void init() {
2006         }
2007 
2008         @Override
filterApp( ApplicationsState.AppEntry info)2009         public boolean filterApp(
2010                 ApplicationsState.AppEntry info) {
2011             return Objects.equals(info.info.volumeUuid, mVolumeUuid);
2012         }
2013     }
2014 
2015     public static class CompoundFilter implements
2016             ApplicationsState.AppFilter {
2017         private final ApplicationsState.AppFilter mFirstFilter;
2018         private final ApplicationsState.AppFilter mSecondFilter;
2019 
CompoundFilter(ApplicationsState.AppFilter first, ApplicationsState.AppFilter second)2020         public CompoundFilter(ApplicationsState.AppFilter first,
2021                 ApplicationsState.AppFilter second) {
2022             mFirstFilter = first;
2023             mSecondFilter = second;
2024         }
2025 
2026         @Override
init(Context context)2027         public void init(Context context) {
2028             mFirstFilter.init(context);
2029             mSecondFilter.init(context);
2030         }
2031 
2032         @Override
init()2033         public void init() {
2034             mFirstFilter.init();
2035             mSecondFilter.init();
2036         }
2037 
2038         @Override
filterApp( ApplicationsState.AppEntry info)2039         public boolean filterApp(
2040                 ApplicationsState.AppEntry info) {
2041             return mFirstFilter.filterApp(info) && mSecondFilter.filterApp(info);
2042         }
2043     }
2044 
2045     public static final ApplicationsState.AppFilter
2046             FILTER_AUDIO = new ApplicationsState.AppFilter() {
2047         @Override
2048         public void init() {
2049         }
2050 
2051         @Override
2052         public boolean filterApp(
2053                 ApplicationsState.AppEntry entry) {
2054             boolean isMusicApp;
2055             synchronized (entry) {
2056                 isMusicApp = entry.info.category == ApplicationInfo.CATEGORY_AUDIO;
2057             }
2058             return isMusicApp;
2059         }
2060     };
2061 
2062     public static final ApplicationsState.AppFilter
2063             FILTER_MOVIES = new ApplicationsState.AppFilter() {
2064         @Override
2065         public void init() {
2066         }
2067 
2068         @Override
2069         public boolean filterApp(
2070                 ApplicationsState.AppEntry entry) {
2071             boolean isMovieApp;
2072             synchronized (entry) {
2073                 isMovieApp = entry.info.category == ApplicationInfo.CATEGORY_VIDEO;
2074             }
2075             return isMovieApp;
2076         }
2077     };
2078 
2079     public static final ApplicationsState.AppFilter
2080             FILTER_PHOTOS =
2081             new ApplicationsState.AppFilter() {
2082                 @Override
2083                 public void init() {
2084                 }
2085 
2086                 @Override
2087                 public boolean filterApp(
2088                         ApplicationsState.AppEntry entry) {
2089                     boolean isPhotosApp;
2090                     synchronized (entry) {
2091                         isPhotosApp = entry.info.category == ApplicationInfo.CATEGORY_IMAGE;
2092                     }
2093                     return isPhotosApp;
2094                 }
2095             };
2096 
2097     public static final ApplicationsState.AppFilter
2098             FILTER_OTHER_APPS =
2099             new ApplicationsState.AppFilter() {
2100                 @Override
2101                 public void init() {
2102                 }
2103 
2104                 @Override
2105                 public boolean filterApp(
2106                         ApplicationsState.AppEntry entry) {
2107                     boolean isCategorized;
2108                     synchronized (entry) {
2109                         isCategorized =
2110                                 FILTER_AUDIO.filterApp(entry)
2111                                         || FILTER_GAMES.filterApp(entry)
2112                                         || FILTER_MOVIES.filterApp(entry)
2113                                         || FILTER_PHOTOS.filterApp(entry);
2114                     }
2115                     return !isCategorized;
2116                 }
2117             };
2118 }
2119