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