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