2 * Copyright (C) 2015 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.android.settingslib.applications;
19 import android.app.ActivityManager;
20 import android.app.AppGlobals;
21 import android.app.Application;
22 import android.app.usage.StorageStats;
23 import android.app.usage.StorageStatsManager;
24 import android.content.BroadcastReceiver;
25 import android.content.Context;
26 import android.content.Intent;
27 import android.content.IntentFilter;
28 import android.content.pm.ApplicationInfo;
29 import android.content.pm.IPackageManager;
30 import android.content.pm.IPackageStatsObserver;
31 import android.content.pm.PackageManager;
32 import android.content.pm.PackageManager.NameNotFoundException;
33 import android.content.pm.PackageStats;
34 import android.content.pm.ParceledListSlice;
35 import android.content.pm.ResolveInfo;
36 import android.content.pm.UserInfo;
37 import android.graphics.drawable.Drawable;
38 import android.net.Uri;
39 import android.os.Handler;
40 import android.os.HandlerThread;
41 import android.os.Looper;
42 import android.os.Message;
43 import android.os.Process;
44 import android.os.RemoteException;
45 import android.os.SystemClock;
46 import android.os.UserHandle;
47 import android.os.UserManager;
48 import android.text.format.Formatter;
49 import android.util.IconDrawableFactory;
50 import android.util.Log;
51 import android.util.SparseArray;
53 import com.android.internal.R;
54 import com.android.internal.util.ArrayUtils;
57 import java.io.IOException;
58 import java.text.Collator;
59 import java.text.Normalizer;
60 import java.text.Normalizer.Form;
61 import java.util.ArrayList;
62 import java.util.Collections;
63 import java.util.Comparator;
64 import java.util.HashMap;
65 import java.util.List;
66 import java.util.Objects;
67 import java.util.UUID;
68 import java.util.regex.Pattern;
71 * Keeps track of information about all installed applications, lazy-loading
74 public class ApplicationsState {
75 static final String TAG = "ApplicationsState";
76 static final boolean DEBUG = false;
77 static final boolean DEBUG_LOCKING = false;
79 public static final int SIZE_UNKNOWN = -1;
80 public static final int SIZE_INVALID = -2;
82 static final Pattern REMOVE_DIACRITICALS_PATTERN
83 = Pattern.compile("\\p{InCombiningDiacriticalMarks}+");
85 static final Object sLock = new Object();
86 static ApplicationsState sInstance;
88 public static ApplicationsState getInstance(Application app) {
89 synchronized (sLock) {
90 if (sInstance == null) {
91 sInstance = new ApplicationsState(app);
97 final Context mContext;
98 final PackageManager mPm;
99 final IconDrawableFactory mDrawableFactory;
100 final IPackageManager mIpm;
101 final UserManager mUm;
102 final StorageStatsManager mStats;
103 final int mAdminRetrieveFlags;
104 final int mRetrieveFlags;
105 PackageIntentReceiver mPackageIntentReceiver;
108 boolean mHaveDisabledApps;
109 boolean mHaveInstantApps;
111 // Information about all applications. Synchronize on mEntriesMap
112 // to protect access to these.
113 final ArrayList<Session> mSessions = new ArrayList<Session>();
114 final ArrayList<Session> mRebuildingSessions = new ArrayList<Session>();
115 final InterestingConfigChanges mInterestingConfigChanges = new InterestingConfigChanges();
116 // Map: userid => (Map: package name => AppEntry)
117 final SparseArray<HashMap<String, AppEntry>> mEntriesMap =
118 new SparseArray<HashMap<String, AppEntry>>();
119 final ArrayList<AppEntry> mAppEntries = new ArrayList<AppEntry>();
120 List<ApplicationInfo> mApplications = new ArrayList<ApplicationInfo>();
122 UUID mCurComputingSizeUuid;
123 String mCurComputingSizePkg;
124 int mCurComputingSizeUserId;
125 boolean mSessionsChanged;
127 // Temporary for dispatching session callbacks. Only touched by main thread.
128 final ArrayList<Session> mActiveSessions = new ArrayList<Session>();
130 final HandlerThread mThread;
131 final BackgroundHandler mBackgroundHandler;
132 final MainHandler mMainHandler = new MainHandler(Looper.getMainLooper());
134 private ApplicationsState(Application app) {
136 mPm = mContext.getPackageManager();
137 mDrawableFactory = IconDrawableFactory.newInstance(mContext);
138 mIpm = AppGlobals.getPackageManager();
139 mUm = mContext.getSystemService(UserManager.class);
140 mStats = mContext.getSystemService(StorageStatsManager.class);
141 for (int userId : mUm.getProfileIdsWithDisabled(UserHandle.myUserId())) {
142 mEntriesMap.put(userId, new HashMap<String, AppEntry>());
144 mThread = new HandlerThread("ApplicationsState.Loader",
145 Process.THREAD_PRIORITY_BACKGROUND);
147 mBackgroundHandler = new BackgroundHandler(mThread.getLooper());
149 // Only the owner can see all apps.
150 mAdminRetrieveFlags = PackageManager.MATCH_ANY_USER |
151 PackageManager.MATCH_DISABLED_COMPONENTS |
152 PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS;
153 mRetrieveFlags = PackageManager.MATCH_DISABLED_COMPONENTS |
154 PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS;
157 * This is a trick to prevent the foreground thread from being delayed.
158 * The problem is that Dalvik monitors are initially spin locks, to keep
159 * them lightweight. This leads to unfair contention -- Even though the
160 * background thread only holds the lock for a short amount of time, if
161 * it keeps running and locking again it can prevent the main thread from
162 * acquiring its lock for a long time... sometimes even > 5 seconds
163 * (leading to an ANR).
165 * Dalvik will promote a monitor to a "real" lock if it detects enough
166 * contention on it. It doesn't figure this out fast enough for us
167 * here, though, so this little trick will force it to turn into a real
170 synchronized (mEntriesMap) {
173 } catch (InterruptedException e) {
178 public Looper getBackgroundLooper() {
179 return mThread.getLooper();
182 public Session newSession(Callbacks callbacks) {
183 Session s = new Session(callbacks);
184 synchronized (mEntriesMap) {
190 void doResumeIfNeededLocked() {
195 if (mPackageIntentReceiver == null) {
196 mPackageIntentReceiver = new PackageIntentReceiver();
197 mPackageIntentReceiver.registerReceiver();
199 mApplications = new ArrayList<ApplicationInfo>();
200 for (UserInfo user : mUm.getProfiles(UserHandle.myUserId())) {
202 // If this user is new, it needs a map created.
203 if (mEntriesMap.indexOfKey(user.id) < 0) {
204 mEntriesMap.put(user.id, new HashMap<String, AppEntry>());
206 @SuppressWarnings("unchecked")
207 ParceledListSlice<ApplicationInfo> list =
208 mIpm.getInstalledApplications(
209 user.isAdmin() ? mAdminRetrieveFlags : mRetrieveFlags,
211 mApplications.addAll(list.getList());
212 } catch (RemoteException e) {
216 if (mInterestingConfigChanges.applyNewConfig(mContext.getResources())) {
217 // If an interesting part of the configuration has changed, we
218 // should completely reload the app entries.
221 for (int i=0; i<mAppEntries.size(); i++) {
222 mAppEntries.get(i).sizeStale = true;
226 mHaveDisabledApps = false;
227 mHaveInstantApps = false;
228 for (int i=0; i<mApplications.size(); i++) {
229 final ApplicationInfo info = mApplications.get(i);
230 // Need to trim out any applications that are disabled by
231 // something different than the user.
233 if (info.enabledSetting != PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) {
234 mApplications.remove(i);
238 mHaveDisabledApps = true;
240 if (!mHaveInstantApps && AppUtils.isInstant(info)) {
241 mHaveInstantApps = true;
244 int userId = UserHandle.getUserId(info.uid);
245 final AppEntry entry = mEntriesMap.get(userId).get(info.packageName);
250 if (mAppEntries.size() > mApplications.size()) {
251 // There are less apps now, some must have been uninstalled.
254 mCurComputingSizePkg = null;
255 if (!mBackgroundHandler.hasMessages(BackgroundHandler.MSG_LOAD_ENTRIES)) {
256 mBackgroundHandler.sendEmptyMessage(BackgroundHandler.MSG_LOAD_ENTRIES);
260 private void clearEntries() {
261 for (int i = 0; i < mEntriesMap.size(); i++) {
262 mEntriesMap.valueAt(i).clear();
267 public boolean haveDisabledApps() {
268 return mHaveDisabledApps;
270 public boolean haveInstantApps() {
271 return mHaveInstantApps;
274 void doPauseIfNeededLocked() {
278 for (int i=0; i<mSessions.size(); i++) {
279 if (mSessions.get(i).mResumed) {
286 void doPauseLocked() {
288 if (mPackageIntentReceiver != null) {
289 mPackageIntentReceiver.unregisterReceiver();
290 mPackageIntentReceiver = null;
294 public AppEntry getEntry(String packageName, int userId) {
295 if (DEBUG_LOCKING) Log.v(TAG, "getEntry about to acquire lock...");
296 synchronized (mEntriesMap) {
297 AppEntry entry = mEntriesMap.get(userId).get(packageName);
299 ApplicationInfo info = getAppInfoLocked(packageName, userId);
302 info = mIpm.getApplicationInfo(packageName, 0, userId);
303 } catch (RemoteException e) {
304 Log.w(TAG, "getEntry couldn't reach PackageManager", e);
309 entry = getEntryLocked(info);
312 if (DEBUG_LOCKING) Log.v(TAG, "...getEntry releasing lock");
317 private ApplicationInfo getAppInfoLocked(String pkg, int userId) {
318 for (int i = 0; i < mApplications.size(); i++) {
319 ApplicationInfo info = mApplications.get(i);
320 if (pkg.equals(info.packageName)
321 && userId == UserHandle.getUserId(info.uid)) {
328 public void ensureIcon(AppEntry entry) {
329 if (entry.icon != null) {
332 synchronized (entry) {
333 entry.ensureIconLocked(mContext, mDrawableFactory);
337 public void requestSize(String packageName, int userId) {
338 if (DEBUG_LOCKING) Log.v(TAG, "requestSize about to acquire lock...");
339 synchronized (mEntriesMap) {
340 AppEntry entry = mEntriesMap.get(userId).get(packageName);
341 if (entry != null && (entry.info.flags & ApplicationInfo.FLAG_INSTALLED) != 0) {
342 mBackgroundHandler.post(() -> {
344 final StorageStats stats = mStats.queryStatsForPackage(
345 entry.info.storageUuid, packageName, UserHandle.of(userId));
346 final PackageStats legacy = new PackageStats(packageName, userId);
347 legacy.codeSize = stats.getCodeBytes();
348 legacy.dataSize = stats.getDataBytes();
349 legacy.cacheSize = stats.getCacheBytes();
351 mBackgroundHandler.mStatsObserver.onGetStatsCompleted(legacy, true);
352 } catch (RemoteException ignored) {
354 } catch (NameNotFoundException | IOException e) {
355 Log.w(TAG, "Failed to query stats: " + e);
357 mBackgroundHandler.mStatsObserver.onGetStatsCompleted(null, false);
358 } catch (RemoteException ignored) {
363 if (DEBUG_LOCKING) Log.v(TAG, "...requestSize releasing lock");
367 long sumCacheSizes() {
369 if (DEBUG_LOCKING) Log.v(TAG, "sumCacheSizes about to acquire lock...");
370 synchronized (mEntriesMap) {
371 if (DEBUG_LOCKING) Log.v(TAG, "-> sumCacheSizes now has lock");
372 for (int i=mAppEntries.size()-1; i>=0; i--) {
373 sum += mAppEntries.get(i).cacheSize;
375 if (DEBUG_LOCKING) Log.v(TAG, "...sumCacheSizes releasing lock");
380 int indexOfApplicationInfoLocked(String pkgName, int userId) {
381 for (int i=mApplications.size()-1; i>=0; i--) {
382 ApplicationInfo appInfo = mApplications.get(i);
383 if (appInfo.packageName.equals(pkgName)
384 && UserHandle.getUserId(appInfo.uid) == userId) {
391 void addPackage(String pkgName, int userId) {
393 synchronized (mEntriesMap) {
394 if (DEBUG_LOCKING) Log.v(TAG, "addPackage acquired lock");
395 if (DEBUG) Log.i(TAG, "Adding package " + pkgName);
397 // If we are not resumed, we will do a full query the
398 // next time we resume, so there is no reason to do work
400 if (DEBUG_LOCKING) Log.v(TAG, "addPackage release lock: not resumed");
403 if (indexOfApplicationInfoLocked(pkgName, userId) >= 0) {
404 if (DEBUG) Log.i(TAG, "Package already exists!");
405 if (DEBUG_LOCKING) Log.v(TAG, "addPackage release lock: already exists");
408 ApplicationInfo info = mIpm.getApplicationInfo(pkgName,
409 mUm.isUserAdmin(userId) ? mAdminRetrieveFlags : mRetrieveFlags,
415 if (info.enabledSetting
416 != PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) {
419 mHaveDisabledApps = true;
421 if (AppUtils.isInstant(info)) {
422 mHaveInstantApps = true;
424 mApplications.add(info);
425 if (!mBackgroundHandler.hasMessages(BackgroundHandler.MSG_LOAD_ENTRIES)) {
426 mBackgroundHandler.sendEmptyMessage(BackgroundHandler.MSG_LOAD_ENTRIES);
428 if (!mMainHandler.hasMessages(MainHandler.MSG_PACKAGE_LIST_CHANGED)) {
429 mMainHandler.sendEmptyMessage(MainHandler.MSG_PACKAGE_LIST_CHANGED);
431 if (DEBUG_LOCKING) Log.v(TAG, "addPackage releasing lock");
433 } catch (RemoteException e) {
437 public void removePackage(String pkgName, int userId) {
438 synchronized (mEntriesMap) {
439 if (DEBUG_LOCKING) Log.v(TAG, "removePackage acquired lock");
440 int idx = indexOfApplicationInfoLocked(pkgName, userId);
441 if (DEBUG) Log.i(TAG, "removePackage: " + pkgName + " @ " + idx);
443 AppEntry entry = mEntriesMap.get(userId).get(pkgName);
444 if (DEBUG) Log.i(TAG, "removePackage: " + entry);
446 mEntriesMap.get(userId).remove(pkgName);
447 mAppEntries.remove(entry);
449 ApplicationInfo info = mApplications.get(idx);
450 mApplications.remove(idx);
452 mHaveDisabledApps = false;
453 for (ApplicationInfo otherInfo : mApplications) {
454 if (!otherInfo.enabled) {
455 mHaveDisabledApps = true;
460 if (AppUtils.isInstant(info)) {
461 mHaveInstantApps = false;
462 for (ApplicationInfo otherInfo : mApplications) {
463 if (AppUtils.isInstant(otherInfo)) {
464 mHaveInstantApps = true;
469 if (!mMainHandler.hasMessages(MainHandler.MSG_PACKAGE_LIST_CHANGED)) {
470 mMainHandler.sendEmptyMessage(MainHandler.MSG_PACKAGE_LIST_CHANGED);
473 if (DEBUG_LOCKING) Log.v(TAG, "removePackage releasing lock");
477 public void invalidatePackage(String pkgName, int userId) {
478 removePackage(pkgName, userId);
479 addPackage(pkgName, userId);
482 private void addUser(int userId) {
483 final int profileIds[] = mUm.getProfileIdsWithDisabled(UserHandle.myUserId());
484 if (ArrayUtils.contains(profileIds, userId)) {
485 synchronized (mEntriesMap) {
486 mEntriesMap.put(userId, new HashMap<String, AppEntry>());
488 // If resumed, Manually pause, then cause a resume to repopulate the app list.
489 // This is the simplest way to reload the packages so that the new user
490 // is included. Otherwise the list will be repopulated on next resume.
492 doResumeIfNeededLocked();
494 if (!mMainHandler.hasMessages(MainHandler.MSG_PACKAGE_LIST_CHANGED)) {
495 mMainHandler.sendEmptyMessage(MainHandler.MSG_PACKAGE_LIST_CHANGED);
501 private void removeUser(int userId) {
502 synchronized (mEntriesMap) {
503 HashMap<String, AppEntry> userMap = mEntriesMap.get(userId);
504 if (userMap != null) {
505 for (AppEntry appEntry : userMap.values()) {
506 mAppEntries.remove(appEntry);
507 mApplications.remove(appEntry.info);
509 mEntriesMap.remove(userId);
510 if (!mMainHandler.hasMessages(MainHandler.MSG_PACKAGE_LIST_CHANGED)) {
511 mMainHandler.sendEmptyMessage(MainHandler.MSG_PACKAGE_LIST_CHANGED);
517 private AppEntry getEntryLocked(ApplicationInfo info) {
518 int userId = UserHandle.getUserId(info.uid);
519 AppEntry entry = mEntriesMap.get(userId).get(info.packageName);
520 if (DEBUG) Log.i(TAG, "Looking up entry of pkg " + info.packageName + ": " + entry);
522 if (DEBUG) Log.i(TAG, "Creating AppEntry for " + info.packageName);
523 entry = new AppEntry(mContext, info, mCurId++);
524 mEntriesMap.get(userId).put(info.packageName, entry);
525 mAppEntries.add(entry);
526 } else if (entry.info != info) {
532 // --------------------------------------------------------------
534 private long getTotalInternalSize(PackageStats ps) {
536 return ps.codeSize + ps.dataSize;
541 private long getTotalExternalSize(PackageStats ps) {
543 // We also include the cache size here because for non-emulated
544 // we don't automtically clean cache files.
545 return ps.externalCodeSize + ps.externalDataSize
546 + ps.externalCacheSize
547 + ps.externalMediaSize + ps.externalObbSize;
552 private String getSizeStr(long size) {
554 return Formatter.formatFileSize(mContext, size);
559 void rebuildActiveSessions() {
560 synchronized (mEntriesMap) {
561 if (!mSessionsChanged) {
564 mActiveSessions.clear();
565 for (int i=0; i<mSessions.size(); i++) {
566 Session s = mSessions.get(i);
568 mActiveSessions.add(s);
574 public static String normalize(String str) {
575 String tmp = Normalizer.normalize(str, Form.NFD);
576 return REMOVE_DIACRITICALS_PATTERN.matcher(tmp)
577 .replaceAll("").toLowerCase();
580 public class Session {
581 final Callbacks mCallbacks;
584 // Rebuilding of app list. Synchronized on mRebuildSync.
585 final Object mRebuildSync = new Object();
586 boolean mRebuildRequested;
587 boolean mRebuildAsync;
588 AppFilter mRebuildFilter;
589 Comparator<AppEntry> mRebuildComparator;
590 ArrayList<AppEntry> mRebuildResult;
591 ArrayList<AppEntry> mLastAppList;
592 boolean mRebuildForeground;
594 Session(Callbacks callbacks) {
595 mCallbacks = callbacks;
598 public void resume() {
599 if (DEBUG_LOCKING) Log.v(TAG, "resume about to acquire lock...");
600 synchronized (mEntriesMap) {
603 mSessionsChanged = true;
604 doResumeIfNeededLocked();
607 if (DEBUG_LOCKING) Log.v(TAG, "...resume releasing lock");
610 public void pause() {
611 if (DEBUG_LOCKING) Log.v(TAG, "pause about to acquire lock...");
612 synchronized (mEntriesMap) {
615 mSessionsChanged = true;
616 mBackgroundHandler.removeMessages(BackgroundHandler.MSG_REBUILD_LIST, this);
617 doPauseIfNeededLocked();
619 if (DEBUG_LOCKING) Log.v(TAG, "...pause releasing lock");
623 public ArrayList<AppEntry> getAllApps() {
624 synchronized (mEntriesMap) {
625 return new ArrayList<>(mAppEntries);
629 // Creates a new list of app entries with the given filter and comparator.
630 public ArrayList<AppEntry> rebuild(AppFilter filter, Comparator<AppEntry> comparator) {
631 return rebuild(filter, comparator, true);
634 public ArrayList<AppEntry> rebuild(AppFilter filter, Comparator<AppEntry> comparator,
635 boolean foreground) {
636 synchronized (mRebuildSync) {
637 synchronized (mRebuildingSessions) {
638 mRebuildingSessions.add(this);
639 mRebuildRequested = true;
640 mRebuildAsync = true;
641 mRebuildFilter = filter;
642 mRebuildComparator = comparator;
643 mRebuildForeground = foreground;
644 mRebuildResult = null;
645 if (!mBackgroundHandler.hasMessages(BackgroundHandler.MSG_REBUILD_LIST)) {
646 Message msg = mBackgroundHandler.obtainMessage(
647 BackgroundHandler.MSG_REBUILD_LIST);
648 mBackgroundHandler.sendMessage(msg);
656 void handleRebuildList() {
658 Comparator<AppEntry> comparator;
659 synchronized (mRebuildSync) {
660 if (!mRebuildRequested) {
664 filter = mRebuildFilter;
665 comparator = mRebuildComparator;
666 mRebuildRequested = false;
667 mRebuildFilter = null;
668 mRebuildComparator = null;
669 if (mRebuildForeground) {
670 Process.setThreadPriority(Process.THREAD_PRIORITY_FOREGROUND);
671 mRebuildForeground = false;
675 if (filter != null) {
676 filter.init(mContext);
680 synchronized (mEntriesMap) {
681 apps = new ArrayList<>(mAppEntries);
684 ArrayList<AppEntry> filteredApps = new ArrayList<AppEntry>();
685 if (DEBUG) Log.i(TAG, "Rebuilding...");
686 for (int i=0; i<apps.size(); i++) {
687 AppEntry entry = apps.get(i);
688 if (entry != null && (filter == null || filter.filterApp(entry))) {
689 synchronized (mEntriesMap) {
690 if (DEBUG_LOCKING) Log.v(TAG, "rebuild acquired lock");
691 if (comparator != null) {
692 // Only need the label if we are going to be sorting.
693 entry.ensureLabel(mContext);
695 if (DEBUG) Log.i(TAG, "Using " + entry.info.packageName + ": " + entry);
696 filteredApps.add(entry);
697 if (DEBUG_LOCKING) Log.v(TAG, "rebuild releasing lock");
702 if (comparator != null) {
703 synchronized (mEntriesMap) {
704 // Locking to ensure that the background handler does not mutate
705 // the size of AppEntries used for ordering while sorting.
706 Collections.sort(filteredApps, comparator);
710 synchronized (mRebuildSync) {
711 if (!mRebuildRequested) {
712 mLastAppList = filteredApps;
713 if (!mRebuildAsync) {
714 mRebuildResult = filteredApps;
715 mRebuildSync.notifyAll();
717 if (!mMainHandler.hasMessages(MainHandler.MSG_REBUILD_COMPLETE, this)) {
718 Message msg = mMainHandler.obtainMessage(
719 MainHandler.MSG_REBUILD_COMPLETE, this);
720 mMainHandler.sendMessage(msg);
726 Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
729 public void release() {
731 synchronized (mEntriesMap) {
732 mSessions.remove(this);
737 class MainHandler extends Handler {
738 static final int MSG_REBUILD_COMPLETE = 1;
739 static final int MSG_PACKAGE_LIST_CHANGED = 2;
740 static final int MSG_PACKAGE_ICON_CHANGED = 3;
741 static final int MSG_PACKAGE_SIZE_CHANGED = 4;
742 static final int MSG_ALL_SIZES_COMPUTED = 5;
743 static final int MSG_RUNNING_STATE_CHANGED = 6;
744 static final int MSG_LAUNCHER_INFO_CHANGED = 7;
745 static final int MSG_LOAD_ENTRIES_COMPLETE = 8;
747 public MainHandler(Looper looper) {
752 public void handleMessage(Message msg) {
753 rebuildActiveSessions();
755 case MSG_REBUILD_COMPLETE: {
756 Session s = (Session)msg.obj;
757 if (mActiveSessions.contains(s)) {
758 s.mCallbacks.onRebuildComplete(s.mLastAppList);
761 case MSG_PACKAGE_LIST_CHANGED: {
762 for (int i=0; i<mActiveSessions.size(); i++) {
763 mActiveSessions.get(i).mCallbacks.onPackageListChanged();
766 case MSG_PACKAGE_ICON_CHANGED: {
767 for (int i=0; i<mActiveSessions.size(); i++) {
768 mActiveSessions.get(i).mCallbacks.onPackageIconChanged();
771 case MSG_PACKAGE_SIZE_CHANGED: {
772 for (int i=0; i<mActiveSessions.size(); i++) {
773 mActiveSessions.get(i).mCallbacks.onPackageSizeChanged(
777 case MSG_ALL_SIZES_COMPUTED: {
778 for (int i=0; i<mActiveSessions.size(); i++) {
779 mActiveSessions.get(i).mCallbacks.onAllSizesComputed();
782 case MSG_RUNNING_STATE_CHANGED: {
783 for (int i=0; i<mActiveSessions.size(); i++) {
784 mActiveSessions.get(i).mCallbacks.onRunningStateChanged(
788 case MSG_LAUNCHER_INFO_CHANGED: {
789 for (int i=0; i<mActiveSessions.size(); i++) {
790 mActiveSessions.get(i).mCallbacks.onLauncherInfoChanged();
793 case MSG_LOAD_ENTRIES_COMPLETE: {
794 for (int i=0; i<mActiveSessions.size(); i++) {
795 mActiveSessions.get(i).mCallbacks.onLoadEntriesCompleted();
802 private class BackgroundHandler extends Handler {
803 static final int MSG_REBUILD_LIST = 1;
804 static final int MSG_LOAD_ENTRIES = 2;
805 static final int MSG_LOAD_ICONS = 3;
806 static final int MSG_LOAD_SIZES = 4;
807 static final int MSG_LOAD_LAUNCHER = 5;
808 static final int MSG_LOAD_HOME_APP = 6;
812 BackgroundHandler(Looper looper) {
817 public void handleMessage(Message msg) {
818 // Always try rebuilding list first thing, if needed.
819 ArrayList<Session> rebuildingSessions = null;
820 synchronized (mRebuildingSessions) {
821 if (mRebuildingSessions.size() > 0) {
822 rebuildingSessions = new ArrayList<Session>(mRebuildingSessions);
823 mRebuildingSessions.clear();
826 if (rebuildingSessions != null) {
827 for (int i=0; i<rebuildingSessions.size(); i++) {
828 rebuildingSessions.get(i).handleRebuildList();
833 case MSG_REBUILD_LIST: {
835 case MSG_LOAD_ENTRIES: {
837 synchronized (mEntriesMap) {
838 if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_ENTRIES acquired lock");
839 for (int i = 0; i < mApplications.size() && numDone < 6; i++) {
842 Message m = mMainHandler.obtainMessage(
843 MainHandler.MSG_RUNNING_STATE_CHANGED, 1);
844 mMainHandler.sendMessage(m);
846 ApplicationInfo info = mApplications.get(i);
847 int userId = UserHandle.getUserId(info.uid);
848 if (mEntriesMap.get(userId).get(info.packageName) == null) {
850 getEntryLocked(info);
852 if (userId != 0 && mEntriesMap.indexOfKey(0) >= 0) {
853 // If this app is for a profile and we are on the owner, remove
854 // the owner entry if it isn't installed. This will prevent
855 // duplicates of work only apps showing up as 'not installed
857 // Note: This depends on us traversing the users in order, which
858 // happens because of the way we generate the list in
859 // doResumeIfNeededLocked.
860 AppEntry entry = mEntriesMap.get(0).get(info.packageName);
862 (entry.info.flags & ApplicationInfo.FLAG_INSTALLED) == 0) {
863 mEntriesMap.get(0).remove(info.packageName);
864 mAppEntries.remove(entry);
868 if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_ENTRIES releasing lock");
872 sendEmptyMessage(MSG_LOAD_ENTRIES);
874 if (!mMainHandler.hasMessages(MainHandler.MSG_LOAD_ENTRIES_COMPLETE)) {
875 mMainHandler.sendEmptyMessage(MainHandler.MSG_LOAD_ENTRIES_COMPLETE);
877 sendEmptyMessage(MSG_LOAD_HOME_APP);
880 case MSG_LOAD_HOME_APP: {
881 final List<ResolveInfo> homeActivities = new ArrayList<>();
882 mPm.getHomeActivities(homeActivities);
883 synchronized (mEntriesMap) {
884 final int entryCount = mEntriesMap.size();
885 for (int i = 0; i < entryCount; i++) {
886 if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_HOME_APP acquired lock");
887 final HashMap<String, AppEntry> userEntries = mEntriesMap.valueAt(i);
888 for (ResolveInfo activity : homeActivities) {
889 String packageName = activity.activityInfo.packageName;
890 AppEntry entry = userEntries.get(packageName);
892 entry.isHomeApp = true;
895 if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_HOME_APP releasing lock");
898 sendEmptyMessage(MSG_LOAD_LAUNCHER);
901 case MSG_LOAD_LAUNCHER: {
902 Intent launchIntent = new Intent(Intent.ACTION_MAIN, null)
903 .addCategory(Intent.CATEGORY_LAUNCHER);
904 for (int i = 0; i < mEntriesMap.size(); i++) {
905 int userId = mEntriesMap.keyAt(i);
906 // If we do not specify MATCH_DIRECT_BOOT_AWARE or
907 // MATCH_DIRECT_BOOT_UNAWARE, system will derive and update the flags
908 // according to the user's lock state. When the user is locked, components
909 // with ComponentInfo#directBootAware == false will be filtered. We should
910 // explicitly include both direct boot aware and unaware components here.
911 List<ResolveInfo> intents = mPm.queryIntentActivitiesAsUser(
913 PackageManager.MATCH_DISABLED_COMPONENTS
914 | PackageManager.MATCH_DIRECT_BOOT_AWARE
915 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
918 synchronized (mEntriesMap) {
919 if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_LAUNCHER acquired lock");
920 HashMap<String, AppEntry> userEntries = mEntriesMap.valueAt(i);
921 final int N = intents.size();
922 for (int j = 0; j < N; j++) {
923 String packageName = intents.get(j).activityInfo.packageName;
924 AppEntry entry = userEntries.get(packageName);
926 entry.hasLauncherEntry = true;
928 Log.w(TAG, "Cannot find pkg: " + packageName
929 + " on user " + userId);
932 if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_LAUNCHER releasing lock");
936 if (!mMainHandler.hasMessages(MainHandler.MSG_LAUNCHER_INFO_CHANGED)) {
937 mMainHandler.sendEmptyMessage(MainHandler.MSG_LAUNCHER_INFO_CHANGED);
939 sendEmptyMessage(MSG_LOAD_ICONS);
941 case MSG_LOAD_ICONS: {
943 synchronized (mEntriesMap) {
944 if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_ICONS acquired lock");
945 for (int i=0; i<mAppEntries.size() && numDone<2; i++) {
946 AppEntry entry = mAppEntries.get(i);
947 if (entry.icon == null || !entry.mounted) {
948 synchronized (entry) {
949 if (entry.ensureIconLocked(mContext, mDrawableFactory)) {
952 Message m = mMainHandler.obtainMessage(
953 MainHandler.MSG_RUNNING_STATE_CHANGED, 1);
954 mMainHandler.sendMessage(m);
961 if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_ICONS releasing lock");
964 if (!mMainHandler.hasMessages(MainHandler.MSG_PACKAGE_ICON_CHANGED)) {
965 mMainHandler.sendEmptyMessage(MainHandler.MSG_PACKAGE_ICON_CHANGED);
969 sendEmptyMessage(MSG_LOAD_ICONS);
971 sendEmptyMessage(MSG_LOAD_SIZES);
974 case MSG_LOAD_SIZES: {
975 synchronized (mEntriesMap) {
976 if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_SIZES acquired lock");
977 if (mCurComputingSizePkg != null) {
978 if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_SIZES releasing: currently computing");
982 long now = SystemClock.uptimeMillis();
983 for (int i=0; i<mAppEntries.size(); i++) {
984 AppEntry entry = mAppEntries.get(i);
985 if ((entry.info.flags & ApplicationInfo.FLAG_INSTALLED) != 0
986 && (entry.size == SIZE_UNKNOWN || entry.sizeStale)) {
987 if (entry.sizeLoadStart == 0 ||
988 (entry.sizeLoadStart < (now-20*1000))) {
991 Message m = mMainHandler.obtainMessage(
992 MainHandler.MSG_RUNNING_STATE_CHANGED, 1);
993 mMainHandler.sendMessage(m);
995 entry.sizeLoadStart = now;
996 mCurComputingSizeUuid = entry.info.storageUuid;
997 mCurComputingSizePkg = entry.info.packageName;
998 mCurComputingSizeUserId = UserHandle.getUserId(entry.info.uid);
1000 mBackgroundHandler.post(() -> {
1002 final StorageStats stats = mStats.queryStatsForPackage(
1003 mCurComputingSizeUuid, mCurComputingSizePkg,
1004 UserHandle.of(mCurComputingSizeUserId));
1005 final PackageStats legacy = new PackageStats(
1006 mCurComputingSizePkg, mCurComputingSizeUserId);
1007 legacy.codeSize = stats.getCodeBytes();
1008 legacy.dataSize = stats.getDataBytes();
1009 legacy.cacheSize = stats.getCacheBytes();
1011 mStatsObserver.onGetStatsCompleted(legacy, true);
1012 } catch (RemoteException ignored) {
1014 } catch (NameNotFoundException | IOException e) {
1015 Log.w(TAG, "Failed to query stats: " + e);
1017 mStatsObserver.onGetStatsCompleted(null, false);
1018 } catch (RemoteException ignored) {
1024 if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_SIZES releasing: now computing");
1028 if (!mMainHandler.hasMessages(MainHandler.MSG_ALL_SIZES_COMPUTED)) {
1029 mMainHandler.sendEmptyMessage(MainHandler.MSG_ALL_SIZES_COMPUTED);
1031 Message m = mMainHandler.obtainMessage(
1032 MainHandler.MSG_RUNNING_STATE_CHANGED, 0);
1033 mMainHandler.sendMessage(m);
1035 if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_SIZES releasing lock");
1041 final IPackageStatsObserver.Stub mStatsObserver = new IPackageStatsObserver.Stub() {
1042 public void onGetStatsCompleted(PackageStats stats, boolean succeeded) {
1044 // There is no meaningful information in stats if the call failed.
1048 boolean sizeChanged = false;
1049 synchronized (mEntriesMap) {
1050 if (DEBUG_LOCKING) Log.v(TAG, "onGetStatsCompleted acquired lock");
1051 HashMap<String, AppEntry> userMap = mEntriesMap.get(stats.userHandle);
1052 if (userMap == null) {
1053 // The user must have been removed.
1056 AppEntry entry = userMap.get(stats.packageName);
1057 if (entry != null) {
1058 synchronized (entry) {
1059 entry.sizeStale = false;
1060 entry.sizeLoadStart = 0;
1061 long externalCodeSize = stats.externalCodeSize
1062 + stats.externalObbSize;
1063 long externalDataSize = stats.externalDataSize
1064 + stats.externalMediaSize;
1065 long newSize = externalCodeSize + externalDataSize
1066 + getTotalInternalSize(stats);
1067 if (entry.size != newSize ||
1068 entry.cacheSize != stats.cacheSize ||
1069 entry.codeSize != stats.codeSize ||
1070 entry.dataSize != stats.dataSize ||
1071 entry.externalCodeSize != externalCodeSize ||
1072 entry.externalDataSize != externalDataSize ||
1073 entry.externalCacheSize != stats.externalCacheSize) {
1074 entry.size = newSize;
1075 entry.cacheSize = stats.cacheSize;
1076 entry.codeSize = stats.codeSize;
1077 entry.dataSize = stats.dataSize;
1078 entry.externalCodeSize = externalCodeSize;
1079 entry.externalDataSize = externalDataSize;
1080 entry.externalCacheSize = stats.externalCacheSize;
1081 entry.sizeStr = getSizeStr(entry.size);
1082 entry.internalSize = getTotalInternalSize(stats);
1083 entry.internalSizeStr = getSizeStr(entry.internalSize);
1084 entry.externalSize = getTotalExternalSize(stats);
1085 entry.externalSizeStr = getSizeStr(entry.externalSize);
1086 if (DEBUG) Log.i(TAG, "Set size of " + entry.label + " " + entry
1087 + ": " + entry.sizeStr);
1092 Message msg = mMainHandler.obtainMessage(
1093 MainHandler.MSG_PACKAGE_SIZE_CHANGED, stats.packageName);
1094 mMainHandler.sendMessage(msg);
1097 if (mCurComputingSizePkg != null
1098 && (mCurComputingSizePkg.equals(stats.packageName)
1099 && mCurComputingSizeUserId == stats.userHandle)) {
1100 mCurComputingSizePkg = null;
1101 sendEmptyMessage(MSG_LOAD_SIZES);
1103 if (DEBUG_LOCKING) Log.v(TAG, "onGetStatsCompleted releasing lock");
1110 * Receives notifications when applications are added/removed.
1112 private class PackageIntentReceiver extends BroadcastReceiver {
1113 void registerReceiver() {
1114 IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
1115 filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
1116 filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
1117 filter.addDataScheme("package");
1118 mContext.registerReceiver(this, filter);
1119 // Register for events related to sdcard installation.
1120 IntentFilter sdFilter = new IntentFilter();
1121 sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
1122 sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
1123 mContext.registerReceiver(this, sdFilter);
1124 // Register for events related to user creation/deletion.
1125 IntentFilter userFilter = new IntentFilter();
1126 userFilter.addAction(Intent.ACTION_USER_ADDED);
1127 userFilter.addAction(Intent.ACTION_USER_REMOVED);
1128 mContext.registerReceiver(this, userFilter);
1130 void unregisterReceiver() {
1131 mContext.unregisterReceiver(this);
1134 public void onReceive(Context context, Intent intent) {
1135 String actionStr = intent.getAction();
1136 if (Intent.ACTION_PACKAGE_ADDED.equals(actionStr)) {
1137 Uri data = intent.getData();
1138 String pkgName = data.getEncodedSchemeSpecificPart();
1139 for (int i = 0; i < mEntriesMap.size(); i++) {
1140 addPackage(pkgName, mEntriesMap.keyAt(i));
1142 } else if (Intent.ACTION_PACKAGE_REMOVED.equals(actionStr)) {
1143 Uri data = intent.getData();
1144 String pkgName = data.getEncodedSchemeSpecificPart();
1145 for (int i = 0; i < mEntriesMap.size(); i++) {
1146 removePackage(pkgName, mEntriesMap.keyAt(i));
1148 } else if (Intent.ACTION_PACKAGE_CHANGED.equals(actionStr)) {
1149 Uri data = intent.getData();
1150 String pkgName = data.getEncodedSchemeSpecificPart();
1151 for (int i = 0; i < mEntriesMap.size(); i++) {
1152 invalidatePackage(pkgName, mEntriesMap.keyAt(i));
1154 } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(actionStr) ||
1155 Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(actionStr)) {
1156 // When applications become available or unavailable (perhaps because
1157 // the SD card was inserted or ejected) we need to refresh the
1158 // AppInfo with new label, icon and size information as appropriate
1159 // given the newfound (un)availability of the application.
1160 // A simple way to do that is to treat the refresh as a package
1161 // removal followed by a package addition.
1162 String pkgList[] = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
1163 if (pkgList == null || pkgList.length == 0) {
1167 boolean avail = Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(actionStr);
1169 for (String pkgName : pkgList) {
1170 for (int i = 0; i < mEntriesMap.size(); i++) {
1171 invalidatePackage(pkgName, mEntriesMap.keyAt(i));
1175 } else if (Intent.ACTION_USER_ADDED.equals(actionStr)) {
1176 addUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL));
1177 } else if (Intent.ACTION_USER_REMOVED.equals(actionStr)) {
1178 removeUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL));
1183 public interface Callbacks {
1184 void onRunningStateChanged(boolean running);
1185 void onPackageListChanged();
1186 void onRebuildComplete(ArrayList<AppEntry> apps);
1187 void onPackageIconChanged();
1188 void onPackageSizeChanged(String packageName);
1189 void onAllSizesComputed();
1190 void onLauncherInfoChanged();
1191 void onLoadEntriesCompleted();
1194 public static class SizeInfo {
1195 public long cacheSize;
1196 public long codeSize;
1197 public long dataSize;
1198 public long externalCodeSize;
1199 public long externalDataSize;
1201 // This is the part of externalDataSize that is in the cache
1202 // section of external storage. Note that we don't just combine
1203 // this with cacheSize because currently the platform can't
1204 // automatically trim this data when needed, so it is something
1205 // the user may need to manage. The externalDataSize also includes
1206 // this value, since what this is here is really the part of
1207 // externalDataSize that we can just consider to be "cache" files
1208 // for purposes of cleaning them up in the app details UI.
1209 public long externalCacheSize;
1212 public static class AppEntry extends SizeInfo {
1213 public final File apkFile;
1214 public final long id;
1215 public String label;
1217 public long internalSize;
1218 public long externalSize;
1220 public boolean mounted;
1223 * Setting this to {@code true} prevents the entry to be filtered by
1224 * {@link #FILTER_DOWNLOADED_AND_LAUNCHER}.
1226 public boolean hasLauncherEntry;
1229 * Whether or not it's a Home app.
1231 public boolean isHomeApp;
1233 public String getNormalizedLabel() {
1234 if (normalizedLabel != null) {
1235 return normalizedLabel;
1237 normalizedLabel = normalize(label);
1238 return normalizedLabel;
1241 // Need to synchronize on 'this' for the following.
1242 public ApplicationInfo info;
1243 public Drawable icon;
1244 public String sizeStr;
1245 public String internalSizeStr;
1246 public String externalSizeStr;
1247 public boolean sizeStale;
1248 public long sizeLoadStart;
1250 public String normalizedLabel;
1252 // A location where extra info can be placed to be used by custom filters.
1253 public Object extraInfo;
1255 AppEntry(Context context, ApplicationInfo info, long id) {
1256 apkFile = new File(info.sourceDir);
1259 this.size = SIZE_UNKNOWN;
1260 this.sizeStale = true;
1261 ensureLabel(context);
1264 public void ensureLabel(Context context) {
1265 if (this.label == null || !this.mounted) {
1266 if (!this.apkFile.exists()) {
1267 this.mounted = false;
1268 this.label = info.packageName;
1270 this.mounted = true;
1271 CharSequence label = info.loadLabel(context.getPackageManager());
1272 this.label = label != null ? label.toString() : info.packageName;
1277 boolean ensureIconLocked(Context context, IconDrawableFactory drawableFactory) {
1278 if (this.icon == null) {
1279 if (this.apkFile.exists()) {
1280 this.icon = drawableFactory.getBadgedIcon(info);
1283 this.mounted = false;
1284 this.icon = context.getDrawable(R.drawable.sym_app_on_sd_unavailable_icon);
1286 } else if (!this.mounted) {
1287 // If the app wasn't mounted but is now mounted, reload
1289 if (this.apkFile.exists()) {
1290 this.mounted = true;
1291 this.icon = drawableFactory.getBadgedIcon(info);
1298 public String getVersion(Context context) {
1300 return context.getPackageManager().getPackageInfo(info.packageName, 0).versionName;
1301 } catch (PackageManager.NameNotFoundException e) {
1308 * Compare by label, then package name, then uid.
1310 public static final Comparator<AppEntry> ALPHA_COMPARATOR = new Comparator<AppEntry>() {
1311 private final Collator sCollator = Collator.getInstance();
1313 public int compare(AppEntry object1, AppEntry object2) {
1314 int compareResult = sCollator.compare(object1.label, object2.label);
1315 if (compareResult != 0) {
1316 return compareResult;
1318 if (object1.info != null && object2.info != null) {
1320 sCollator.compare(object1.info.packageName, object2.info.packageName);
1321 if (compareResult != 0) {
1322 return compareResult;
1325 return object1.info.uid - object2.info.uid;
1329 public static final Comparator<AppEntry> SIZE_COMPARATOR
1330 = new Comparator<AppEntry>() {
1332 public int compare(AppEntry object1, AppEntry object2) {
1333 if (object1.size < object2.size) return 1;
1334 if (object1.size > object2.size) return -1;
1335 return ALPHA_COMPARATOR.compare(object1, object2);
1339 public static final Comparator<AppEntry> INTERNAL_SIZE_COMPARATOR
1340 = new Comparator<AppEntry>() {
1342 public int compare(AppEntry object1, AppEntry object2) {
1343 if (object1.internalSize < object2.internalSize) return 1;
1344 if (object1.internalSize > object2.internalSize) return -1;
1345 return ALPHA_COMPARATOR.compare(object1, object2);
1349 public static final Comparator<AppEntry> EXTERNAL_SIZE_COMPARATOR
1350 = new Comparator<AppEntry>() {
1352 public int compare(AppEntry object1, AppEntry object2) {
1353 if (object1.externalSize < object2.externalSize) return 1;
1354 if (object1.externalSize > object2.externalSize) return -1;
1355 return ALPHA_COMPARATOR.compare(object1, object2);
1359 public interface AppFilter {
1361 default void init(Context context) {
1364 boolean filterApp(AppEntry info);
1367 public static final AppFilter FILTER_PERSONAL = new AppFilter() {
1368 private int mCurrentUser;
1371 public void init() {
1372 mCurrentUser = ActivityManager.getCurrentUser();
1376 public boolean filterApp(AppEntry entry) {
1377 return UserHandle.getUserId(entry.info.uid) == mCurrentUser;
1381 public static final AppFilter FILTER_WITHOUT_DISABLED_UNTIL_USED = new AppFilter() {
1383 public void init() {
1388 public boolean filterApp(AppEntry entry) {
1389 return entry.info.enabledSetting
1390 != PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED;
1394 public static final AppFilter FILTER_WORK = new AppFilter() {
1395 private int mCurrentUser;
1398 public void init() {
1399 mCurrentUser = ActivityManager.getCurrentUser();
1403 public boolean filterApp(AppEntry entry) {
1404 return UserHandle.getUserId(entry.info.uid) != mCurrentUser;
1409 * Displays a combined list with "downloaded" and "visible in launcher" apps only.
1411 public static final AppFilter FILTER_DOWNLOADED_AND_LAUNCHER = new AppFilter() {
1413 public void init() {
1417 public boolean filterApp(AppEntry entry) {
1418 if (AppUtils.isInstant(entry.info)) {
1420 } else if ((entry.info.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) {
1422 } else if ((entry.info.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
1424 } else if (entry.hasLauncherEntry) {
1426 } else if ((entry.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0 && entry.isHomeApp) {
1434 * Displays a combined list with "downloaded" and "visible in launcher" apps only.
1436 public static final AppFilter FILTER_DOWNLOADED_AND_LAUNCHER_AND_INSTANT = new AppFilter() {
1439 public void init() {
1443 public boolean filterApp(AppEntry entry) {
1444 return AppUtils.isInstant(entry.info)
1445 || FILTER_DOWNLOADED_AND_LAUNCHER.filterApp(entry);
1450 public static final AppFilter FILTER_THIRD_PARTY = new AppFilter() {
1452 public void init() {
1456 public boolean filterApp(AppEntry entry) {
1457 if ((entry.info.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) {
1459 } else if ((entry.info.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
1466 public static final AppFilter FILTER_DISABLED = new AppFilter() {
1468 public void init() {
1472 public boolean filterApp(AppEntry entry) {
1473 return !entry.info.enabled && !AppUtils.isInstant(entry.info);
1477 public static final AppFilter FILTER_INSTANT = new AppFilter() {
1479 public void init() {
1483 public boolean filterApp(AppEntry entry) {
1484 return AppUtils.isInstant(entry.info);
1488 public static final AppFilter FILTER_ALL_ENABLED = new AppFilter() {
1490 public void init() {
1494 public boolean filterApp(AppEntry entry) {
1495 return entry.info.enabled && !AppUtils.isInstant(entry.info);
1499 public static final AppFilter FILTER_EVERYTHING = new AppFilter() {
1501 public void init() {
1505 public boolean filterApp(AppEntry entry) {
1510 public static final AppFilter FILTER_WITH_DOMAIN_URLS = new AppFilter() {
1512 public void init() {
1516 public boolean filterApp(AppEntry entry) {
1517 return !AppUtils.isInstant(entry.info)
1518 && (entry.info.privateFlags & ApplicationInfo.PRIVATE_FLAG_HAS_DOMAIN_URLS) != 0;
1522 public static final AppFilter FILTER_NOT_HIDE = new AppFilter() {
1523 private String[] mHidePackageNames;
1526 public void init(Context context) {
1527 mHidePackageNames = context.getResources()
1528 .getStringArray(R.array.config_hideWhenDisabled_packageNames);
1532 public void init() {
1536 public boolean filterApp(AppEntry entry) {
1537 if (ArrayUtils.contains(mHidePackageNames, entry.info.packageName)) {
1538 if (!entry.info.enabled) {
1540 } else if (entry.info.enabledSetting ==
1541 PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED) {
1550 public static final AppFilter FILTER_GAMES = new AppFilter() {
1552 public void init() {
1556 public boolean filterApp(ApplicationsState.AppEntry info) {
1557 // TODO: Update for the new game category.
1559 synchronized (info.info) {
1560 isGame = ((info.info.flags & ApplicationInfo.FLAG_IS_GAME) != 0)
1561 || info.info.category == ApplicationInfo.CATEGORY_GAME;
1567 public static class VolumeFilter implements AppFilter {
1568 private final String mVolumeUuid;
1570 public VolumeFilter(String volumeUuid) {
1571 mVolumeUuid = volumeUuid;
1575 public void init() {
1579 public boolean filterApp(AppEntry info) {
1580 return Objects.equals(info.info.volumeUuid, mVolumeUuid);
1584 public static class CompoundFilter implements AppFilter {
1585 private final AppFilter mFirstFilter;
1586 private final AppFilter mSecondFilter;
1588 public CompoundFilter(AppFilter first, AppFilter second) {
1589 mFirstFilter = first;
1590 mSecondFilter = second;
1594 public void init(Context context) {
1595 mFirstFilter.init(context);
1596 mSecondFilter.init(context);
1600 public void init() {
1601 mFirstFilter.init();
1602 mSecondFilter.init();
1606 public boolean filterApp(AppEntry info) {
1607 return mFirstFilter.filterApp(info) && mSecondFilter.filterApp(info);
1611 public static final AppFilter FILTER_AUDIO = new AppFilter() {
1613 public void init() {
1617 public boolean filterApp(AppEntry entry) {
1619 synchronized(entry) {
1620 isMusicApp = entry.info.category == ApplicationInfo.CATEGORY_AUDIO;
1626 public static final AppFilter FILTER_MOVIES = new AppFilter() {
1628 public void init() {
1632 public boolean filterApp(AppEntry entry) {
1634 synchronized(entry) {
1635 isMovieApp = entry.info.category == ApplicationInfo.CATEGORY_VIDEO;
1641 public static final AppFilter FILTER_OTHER_APPS =
1644 public void init() {}
1647 public boolean filterApp(AppEntry entry) {
1648 boolean isCategorized;
1649 synchronized (entry) {
1651 FILTER_AUDIO.filterApp(entry)
1652 || FILTER_GAMES.filterApp(entry)
1653 || FILTER_MOVIES.filterApp(entry);
1655 return !isCategorized;