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.PackageStats;
33 import android.content.pm.ParceledListSlice;
34 import android.content.pm.ResolveInfo;
35 import android.content.pm.UserInfo;
36 import android.graphics.drawable.Drawable;
37 import android.net.Uri;
38 import android.os.Handler;
39 import android.os.HandlerThread;
40 import android.os.Looper;
41 import android.os.Message;
42 import android.os.Process;
43 import android.os.RemoteException;
44 import android.os.SystemClock;
45 import android.os.UserHandle;
46 import android.os.UserManager;
47 import android.text.format.Formatter;
48 import android.util.Log;
49 import android.util.SparseArray;
51 import com.android.internal.util.ArrayUtils;
52 import com.android.settingslib.R;
55 import java.text.Collator;
56 import java.text.Normalizer;
57 import java.text.Normalizer.Form;
58 import java.util.ArrayList;
59 import java.util.Collections;
60 import java.util.Comparator;
61 import java.util.HashMap;
62 import java.util.List;
63 import java.util.Objects;
64 import java.util.regex.Pattern;
67 * Keeps track of information about all installed applications, lazy-loading
70 public class ApplicationsState {
71 static final String TAG = "ApplicationsState";
72 static final boolean DEBUG = false;
73 static final boolean DEBUG_LOCKING = false;
75 public static final int SIZE_UNKNOWN = -1;
76 public static final int SIZE_INVALID = -2;
78 static final Pattern REMOVE_DIACRITICALS_PATTERN
79 = Pattern.compile("\\p{InCombiningDiacriticalMarks}+");
81 static final Object sLock = new Object();
82 static ApplicationsState sInstance;
84 public static ApplicationsState getInstance(Application app) {
85 synchronized (sLock) {
86 if (sInstance == null) {
87 sInstance = new ApplicationsState(app);
93 final Context mContext;
94 final PackageManager mPm;
95 final IPackageManager mIpm;
96 final UserManager mUm;
97 final StorageStatsManager mStats;
98 final int mAdminRetrieveFlags;
99 final int mRetrieveFlags;
100 PackageIntentReceiver mPackageIntentReceiver;
103 boolean mHaveDisabledApps;
104 boolean mHaveInstantApps;
106 // Information about all applications. Synchronize on mEntriesMap
107 // to protect access to these.
108 final ArrayList<Session> mSessions = new ArrayList<Session>();
109 final ArrayList<Session> mRebuildingSessions = new ArrayList<Session>();
110 final InterestingConfigChanges mInterestingConfigChanges = new InterestingConfigChanges();
111 // Map: userid => (Map: package name => AppEntry)
112 final SparseArray<HashMap<String, AppEntry>> mEntriesMap =
113 new SparseArray<HashMap<String, AppEntry>>();
114 final ArrayList<AppEntry> mAppEntries = new ArrayList<AppEntry>();
115 List<ApplicationInfo> mApplications = new ArrayList<ApplicationInfo>();
117 String mCurComputingSizeUuid;
118 String mCurComputingSizePkg;
119 int mCurComputingSizeUserId;
120 boolean mSessionsChanged;
122 // Temporary for dispatching session callbacks. Only touched by main thread.
123 final ArrayList<Session> mActiveSessions = new ArrayList<Session>();
125 final HandlerThread mThread;
126 final BackgroundHandler mBackgroundHandler;
127 final MainHandler mMainHandler = new MainHandler(Looper.getMainLooper());
129 private ApplicationsState(Application app) {
131 mPm = mContext.getPackageManager();
132 mIpm = AppGlobals.getPackageManager();
133 mUm = mContext.getSystemService(UserManager.class);
134 mStats = mContext.getSystemService(StorageStatsManager.class);
135 for (int userId : mUm.getProfileIdsWithDisabled(UserHandle.myUserId())) {
136 mEntriesMap.put(userId, new HashMap<String, AppEntry>());
138 mThread = new HandlerThread("ApplicationsState.Loader",
139 Process.THREAD_PRIORITY_BACKGROUND);
141 mBackgroundHandler = new BackgroundHandler(mThread.getLooper());
143 // Only the owner can see all apps.
144 mAdminRetrieveFlags = PackageManager.MATCH_ANY_USER |
145 PackageManager.MATCH_DISABLED_COMPONENTS |
146 PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS;
147 mRetrieveFlags = PackageManager.MATCH_DISABLED_COMPONENTS |
148 PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS;
151 * This is a trick to prevent the foreground thread from being delayed.
152 * The problem is that Dalvik monitors are initially spin locks, to keep
153 * them lightweight. This leads to unfair contention -- Even though the
154 * background thread only holds the lock for a short amount of time, if
155 * it keeps running and locking again it can prevent the main thread from
156 * acquiring its lock for a long time... sometimes even > 5 seconds
157 * (leading to an ANR).
159 * Dalvik will promote a monitor to a "real" lock if it detects enough
160 * contention on it. It doesn't figure this out fast enough for us
161 * here, though, so this little trick will force it to turn into a real
164 synchronized (mEntriesMap) {
167 } catch (InterruptedException e) {
172 public Looper getBackgroundLooper() {
173 return mThread.getLooper();
176 public Session newSession(Callbacks callbacks) {
177 Session s = new Session(callbacks);
178 synchronized (mEntriesMap) {
184 void doResumeIfNeededLocked() {
189 if (mPackageIntentReceiver == null) {
190 mPackageIntentReceiver = new PackageIntentReceiver();
191 mPackageIntentReceiver.registerReceiver();
193 mApplications = new ArrayList<ApplicationInfo>();
194 for (UserInfo user : mUm.getProfiles(UserHandle.myUserId())) {
196 // If this user is new, it needs a map created.
197 if (mEntriesMap.indexOfKey(user.id) < 0) {
198 mEntriesMap.put(user.id, new HashMap<String, AppEntry>());
200 @SuppressWarnings("unchecked")
201 ParceledListSlice<ApplicationInfo> list =
202 mIpm.getInstalledApplications(
203 user.isAdmin() ? mAdminRetrieveFlags : mRetrieveFlags,
205 mApplications.addAll(list.getList());
206 } catch (RemoteException e) {
210 if (mInterestingConfigChanges.applyNewConfig(mContext.getResources())) {
211 // If an interesting part of the configuration has changed, we
212 // should completely reload the app entries.
215 for (int i=0; i<mAppEntries.size(); i++) {
216 mAppEntries.get(i).sizeStale = true;
220 mHaveDisabledApps = false;
221 mHaveInstantApps = false;
222 for (int i=0; i<mApplications.size(); i++) {
223 final ApplicationInfo info = mApplications.get(i);
224 // Need to trim out any applications that are disabled by
225 // something different than the user.
227 if (info.enabledSetting != PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) {
228 mApplications.remove(i);
232 mHaveDisabledApps = true;
234 if (!mHaveInstantApps && AppUtils.isInstant(info)) {
235 mHaveInstantApps = true;
238 int userId = UserHandle.getUserId(info.uid);
239 final AppEntry entry = mEntriesMap.get(userId).get(info.packageName);
244 if (mAppEntries.size() > mApplications.size()) {
245 // There are less apps now, some must have been uninstalled.
248 mCurComputingSizePkg = null;
249 if (!mBackgroundHandler.hasMessages(BackgroundHandler.MSG_LOAD_ENTRIES)) {
250 mBackgroundHandler.sendEmptyMessage(BackgroundHandler.MSG_LOAD_ENTRIES);
254 private void clearEntries() {
255 for (int i = 0; i < mEntriesMap.size(); i++) {
256 mEntriesMap.valueAt(i).clear();
261 public boolean haveDisabledApps() {
262 return mHaveDisabledApps;
264 public boolean haveInstantApps() {
265 return mHaveInstantApps;
268 void doPauseIfNeededLocked() {
272 for (int i=0; i<mSessions.size(); i++) {
273 if (mSessions.get(i).mResumed) {
280 void doPauseLocked() {
282 if (mPackageIntentReceiver != null) {
283 mPackageIntentReceiver.unregisterReceiver();
284 mPackageIntentReceiver = null;
288 public AppEntry getEntry(String packageName, int userId) {
289 if (DEBUG_LOCKING) Log.v(TAG, "getEntry about to acquire lock...");
290 synchronized (mEntriesMap) {
291 AppEntry entry = mEntriesMap.get(userId).get(packageName);
293 ApplicationInfo info = getAppInfoLocked(packageName, userId);
296 info = mIpm.getApplicationInfo(packageName, 0, userId);
297 } catch (RemoteException e) {
298 Log.w(TAG, "getEntry couldn't reach PackageManager", e);
303 entry = getEntryLocked(info);
306 if (DEBUG_LOCKING) Log.v(TAG, "...getEntry releasing lock");
311 private ApplicationInfo getAppInfoLocked(String pkg, int userId) {
312 for (int i = 0; i < mApplications.size(); i++) {
313 ApplicationInfo info = mApplications.get(i);
314 if (pkg.equals(info.packageName)
315 && userId == UserHandle.getUserId(info.uid)) {
322 public void ensureIcon(AppEntry entry) {
323 if (entry.icon != null) {
326 synchronized (entry) {
327 entry.ensureIconLocked(mContext, mPm);
331 public void requestSize(String packageName, int userId) {
332 if (DEBUG_LOCKING) Log.v(TAG, "requestSize about to acquire lock...");
333 synchronized (mEntriesMap) {
334 AppEntry entry = mEntriesMap.get(userId).get(packageName);
335 if (entry != null && (entry.info.flags & ApplicationInfo.FLAG_INSTALLED) != 0) {
336 mBackgroundHandler.post(() -> {
337 final StorageStats stats = mStats.queryStatsForPackage(entry.info.volumeUuid,
338 packageName, UserHandle.of(userId));
339 final PackageStats legacyStats = new PackageStats(packageName, userId);
340 legacyStats.codeSize = stats.getCodeBytes();
341 legacyStats.dataSize = stats.getDataBytes();
342 legacyStats.cacheSize = stats.getCacheBytes();
344 mBackgroundHandler.mStatsObserver.onGetStatsCompleted(legacyStats, true);
345 } catch (RemoteException ignored) {
349 if (DEBUG_LOCKING) Log.v(TAG, "...requestSize releasing lock");
353 long sumCacheSizes() {
355 if (DEBUG_LOCKING) Log.v(TAG, "sumCacheSizes about to acquire lock...");
356 synchronized (mEntriesMap) {
357 if (DEBUG_LOCKING) Log.v(TAG, "-> sumCacheSizes now has lock");
358 for (int i=mAppEntries.size()-1; i>=0; i--) {
359 sum += mAppEntries.get(i).cacheSize;
361 if (DEBUG_LOCKING) Log.v(TAG, "...sumCacheSizes releasing lock");
366 int indexOfApplicationInfoLocked(String pkgName, int userId) {
367 for (int i=mApplications.size()-1; i>=0; i--) {
368 ApplicationInfo appInfo = mApplications.get(i);
369 if (appInfo.packageName.equals(pkgName)
370 && UserHandle.getUserId(appInfo.uid) == userId) {
377 void addPackage(String pkgName, int userId) {
379 synchronized (mEntriesMap) {
380 if (DEBUG_LOCKING) Log.v(TAG, "addPackage acquired lock");
381 if (DEBUG) Log.i(TAG, "Adding package " + pkgName);
383 // If we are not resumed, we will do a full query the
384 // next time we resume, so there is no reason to do work
386 if (DEBUG_LOCKING) Log.v(TAG, "addPackage release lock: not resumed");
389 if (indexOfApplicationInfoLocked(pkgName, userId) >= 0) {
390 if (DEBUG) Log.i(TAG, "Package already exists!");
391 if (DEBUG_LOCKING) Log.v(TAG, "addPackage release lock: already exists");
394 ApplicationInfo info = mIpm.getApplicationInfo(pkgName,
395 mUm.isUserAdmin(userId) ? mAdminRetrieveFlags : mRetrieveFlags,
401 if (info.enabledSetting
402 != PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) {
405 mHaveDisabledApps = true;
407 if (AppUtils.isInstant(info)) {
408 mHaveInstantApps = true;
410 mApplications.add(info);
411 if (!mBackgroundHandler.hasMessages(BackgroundHandler.MSG_LOAD_ENTRIES)) {
412 mBackgroundHandler.sendEmptyMessage(BackgroundHandler.MSG_LOAD_ENTRIES);
414 if (!mMainHandler.hasMessages(MainHandler.MSG_PACKAGE_LIST_CHANGED)) {
415 mMainHandler.sendEmptyMessage(MainHandler.MSG_PACKAGE_LIST_CHANGED);
417 if (DEBUG_LOCKING) Log.v(TAG, "addPackage releasing lock");
419 } catch (RemoteException e) {
423 public void removePackage(String pkgName, int userId) {
424 synchronized (mEntriesMap) {
425 if (DEBUG_LOCKING) Log.v(TAG, "removePackage acquired lock");
426 int idx = indexOfApplicationInfoLocked(pkgName, userId);
427 if (DEBUG) Log.i(TAG, "removePackage: " + pkgName + " @ " + idx);
429 AppEntry entry = mEntriesMap.get(userId).get(pkgName);
430 if (DEBUG) Log.i(TAG, "removePackage: " + entry);
432 mEntriesMap.get(userId).remove(pkgName);
433 mAppEntries.remove(entry);
435 ApplicationInfo info = mApplications.get(idx);
436 mApplications.remove(idx);
438 mHaveDisabledApps = false;
439 for (ApplicationInfo otherInfo : mApplications) {
440 if (!otherInfo.enabled) {
441 mHaveDisabledApps = true;
446 if (AppUtils.isInstant(info)) {
447 mHaveInstantApps = false;
448 for (ApplicationInfo otherInfo : mApplications) {
449 if (AppUtils.isInstant(otherInfo)) {
450 mHaveInstantApps = true;
455 if (!mMainHandler.hasMessages(MainHandler.MSG_PACKAGE_LIST_CHANGED)) {
456 mMainHandler.sendEmptyMessage(MainHandler.MSG_PACKAGE_LIST_CHANGED);
459 if (DEBUG_LOCKING) Log.v(TAG, "removePackage releasing lock");
463 public void invalidatePackage(String pkgName, int userId) {
464 removePackage(pkgName, userId);
465 addPackage(pkgName, userId);
468 private void addUser(int userId) {
469 final int profileIds[] = mUm.getProfileIdsWithDisabled(UserHandle.myUserId());
470 if (ArrayUtils.contains(profileIds, userId)) {
471 synchronized (mEntriesMap) {
472 mEntriesMap.put(userId, new HashMap<String, AppEntry>());
474 // If resumed, Manually pause, then cause a resume to repopulate the app list.
475 // This is the simplest way to reload the packages so that the new user
476 // is included. Otherwise the list will be repopulated on next resume.
478 doResumeIfNeededLocked();
480 if (!mMainHandler.hasMessages(MainHandler.MSG_PACKAGE_LIST_CHANGED)) {
481 mMainHandler.sendEmptyMessage(MainHandler.MSG_PACKAGE_LIST_CHANGED);
487 private void removeUser(int userId) {
488 synchronized (mEntriesMap) {
489 HashMap<String, AppEntry> userMap = mEntriesMap.get(userId);
490 if (userMap != null) {
491 for (AppEntry appEntry : userMap.values()) {
492 mAppEntries.remove(appEntry);
493 mApplications.remove(appEntry.info);
495 mEntriesMap.remove(userId);
496 if (!mMainHandler.hasMessages(MainHandler.MSG_PACKAGE_LIST_CHANGED)) {
497 mMainHandler.sendEmptyMessage(MainHandler.MSG_PACKAGE_LIST_CHANGED);
503 private AppEntry getEntryLocked(ApplicationInfo info) {
504 int userId = UserHandle.getUserId(info.uid);
505 AppEntry entry = mEntriesMap.get(userId).get(info.packageName);
506 if (DEBUG) Log.i(TAG, "Looking up entry of pkg " + info.packageName + ": " + entry);
508 if (DEBUG) Log.i(TAG, "Creating AppEntry for " + info.packageName);
509 entry = new AppEntry(mContext, info, mCurId++);
510 mEntriesMap.get(userId).put(info.packageName, entry);
511 mAppEntries.add(entry);
512 } else if (entry.info != info) {
518 // --------------------------------------------------------------
520 private long getTotalInternalSize(PackageStats ps) {
522 return ps.codeSize + ps.dataSize;
527 private long getTotalExternalSize(PackageStats ps) {
529 // We also include the cache size here because for non-emulated
530 // we don't automtically clean cache files.
531 return ps.externalCodeSize + ps.externalDataSize
532 + ps.externalCacheSize
533 + ps.externalMediaSize + ps.externalObbSize;
538 private String getSizeStr(long size) {
540 return Formatter.formatFileSize(mContext, size);
545 void rebuildActiveSessions() {
546 synchronized (mEntriesMap) {
547 if (!mSessionsChanged) {
550 mActiveSessions.clear();
551 for (int i=0; i<mSessions.size(); i++) {
552 Session s = mSessions.get(i);
554 mActiveSessions.add(s);
560 public static String normalize(String str) {
561 String tmp = Normalizer.normalize(str, Form.NFD);
562 return REMOVE_DIACRITICALS_PATTERN.matcher(tmp)
563 .replaceAll("").toLowerCase();
566 public class Session {
567 final Callbacks mCallbacks;
570 // Rebuilding of app list. Synchronized on mRebuildSync.
571 final Object mRebuildSync = new Object();
572 boolean mRebuildRequested;
573 boolean mRebuildAsync;
574 AppFilter mRebuildFilter;
575 Comparator<AppEntry> mRebuildComparator;
576 ArrayList<AppEntry> mRebuildResult;
577 ArrayList<AppEntry> mLastAppList;
578 boolean mRebuildForeground;
580 Session(Callbacks callbacks) {
581 mCallbacks = callbacks;
584 public void resume() {
585 if (DEBUG_LOCKING) Log.v(TAG, "resume about to acquire lock...");
586 synchronized (mEntriesMap) {
589 mSessionsChanged = true;
590 doResumeIfNeededLocked();
593 if (DEBUG_LOCKING) Log.v(TAG, "...resume releasing lock");
596 public void pause() {
597 if (DEBUG_LOCKING) Log.v(TAG, "pause about to acquire lock...");
598 synchronized (mEntriesMap) {
601 mSessionsChanged = true;
602 mBackgroundHandler.removeMessages(BackgroundHandler.MSG_REBUILD_LIST, this);
603 doPauseIfNeededLocked();
605 if (DEBUG_LOCKING) Log.v(TAG, "...pause releasing lock");
609 public ArrayList<AppEntry> getAllApps() {
610 synchronized (mEntriesMap) {
611 return new ArrayList<>(mAppEntries);
615 // Creates a new list of app entries with the given filter and comparator.
616 public ArrayList<AppEntry> rebuild(AppFilter filter, Comparator<AppEntry> comparator) {
617 return rebuild(filter, comparator, true);
620 public ArrayList<AppEntry> rebuild(AppFilter filter, Comparator<AppEntry> comparator,
621 boolean foreground) {
622 synchronized (mRebuildSync) {
623 synchronized (mRebuildingSessions) {
624 mRebuildingSessions.add(this);
625 mRebuildRequested = true;
626 mRebuildAsync = true;
627 mRebuildFilter = filter;
628 mRebuildComparator = comparator;
629 mRebuildForeground = foreground;
630 mRebuildResult = null;
631 if (!mBackgroundHandler.hasMessages(BackgroundHandler.MSG_REBUILD_LIST)) {
632 Message msg = mBackgroundHandler.obtainMessage(
633 BackgroundHandler.MSG_REBUILD_LIST);
634 mBackgroundHandler.sendMessage(msg);
642 void handleRebuildList() {
644 Comparator<AppEntry> comparator;
645 synchronized (mRebuildSync) {
646 if (!mRebuildRequested) {
650 filter = mRebuildFilter;
651 comparator = mRebuildComparator;
652 mRebuildRequested = false;
653 mRebuildFilter = null;
654 mRebuildComparator = null;
655 if (mRebuildForeground) {
656 Process.setThreadPriority(Process.THREAD_PRIORITY_FOREGROUND);
657 mRebuildForeground = false;
661 if (filter != null) {
662 filter.init(mContext);
666 synchronized (mEntriesMap) {
667 apps = new ArrayList<>(mAppEntries);
670 ArrayList<AppEntry> filteredApps = new ArrayList<AppEntry>();
671 if (DEBUG) Log.i(TAG, "Rebuilding...");
672 for (int i=0; i<apps.size(); i++) {
673 AppEntry entry = apps.get(i);
674 if (entry != null && (filter == null || filter.filterApp(entry))) {
675 synchronized (mEntriesMap) {
676 if (DEBUG_LOCKING) Log.v(TAG, "rebuild acquired lock");
677 if (comparator != null) {
678 // Only need the label if we are going to be sorting.
679 entry.ensureLabel(mContext);
681 if (DEBUG) Log.i(TAG, "Using " + entry.info.packageName + ": " + entry);
682 filteredApps.add(entry);
683 if (DEBUG_LOCKING) Log.v(TAG, "rebuild releasing lock");
688 if (comparator != null) {
689 synchronized (mEntriesMap) {
690 // Locking to ensure that the background handler does not mutate
691 // the size of AppEntries used for ordering while sorting.
692 Collections.sort(filteredApps, comparator);
696 synchronized (mRebuildSync) {
697 if (!mRebuildRequested) {
698 mLastAppList = filteredApps;
699 if (!mRebuildAsync) {
700 mRebuildResult = filteredApps;
701 mRebuildSync.notifyAll();
703 if (!mMainHandler.hasMessages(MainHandler.MSG_REBUILD_COMPLETE, this)) {
704 Message msg = mMainHandler.obtainMessage(
705 MainHandler.MSG_REBUILD_COMPLETE, this);
706 mMainHandler.sendMessage(msg);
712 Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
715 public void release() {
717 synchronized (mEntriesMap) {
718 mSessions.remove(this);
723 class MainHandler extends Handler {
724 static final int MSG_REBUILD_COMPLETE = 1;
725 static final int MSG_PACKAGE_LIST_CHANGED = 2;
726 static final int MSG_PACKAGE_ICON_CHANGED = 3;
727 static final int MSG_PACKAGE_SIZE_CHANGED = 4;
728 static final int MSG_ALL_SIZES_COMPUTED = 5;
729 static final int MSG_RUNNING_STATE_CHANGED = 6;
730 static final int MSG_LAUNCHER_INFO_CHANGED = 7;
731 static final int MSG_LOAD_ENTRIES_COMPLETE = 8;
733 public MainHandler(Looper looper) {
738 public void handleMessage(Message msg) {
739 rebuildActiveSessions();
741 case MSG_REBUILD_COMPLETE: {
742 Session s = (Session)msg.obj;
743 if (mActiveSessions.contains(s)) {
744 s.mCallbacks.onRebuildComplete(s.mLastAppList);
747 case MSG_PACKAGE_LIST_CHANGED: {
748 for (int i=0; i<mActiveSessions.size(); i++) {
749 mActiveSessions.get(i).mCallbacks.onPackageListChanged();
752 case MSG_PACKAGE_ICON_CHANGED: {
753 for (int i=0; i<mActiveSessions.size(); i++) {
754 mActiveSessions.get(i).mCallbacks.onPackageIconChanged();
757 case MSG_PACKAGE_SIZE_CHANGED: {
758 for (int i=0; i<mActiveSessions.size(); i++) {
759 mActiveSessions.get(i).mCallbacks.onPackageSizeChanged(
763 case MSG_ALL_SIZES_COMPUTED: {
764 for (int i=0; i<mActiveSessions.size(); i++) {
765 mActiveSessions.get(i).mCallbacks.onAllSizesComputed();
768 case MSG_RUNNING_STATE_CHANGED: {
769 for (int i=0; i<mActiveSessions.size(); i++) {
770 mActiveSessions.get(i).mCallbacks.onRunningStateChanged(
774 case MSG_LAUNCHER_INFO_CHANGED: {
775 for (int i=0; i<mActiveSessions.size(); i++) {
776 mActiveSessions.get(i).mCallbacks.onLauncherInfoChanged();
779 case MSG_LOAD_ENTRIES_COMPLETE: {
780 for (int i=0; i<mActiveSessions.size(); i++) {
781 mActiveSessions.get(i).mCallbacks.onLoadEntriesCompleted();
788 private class BackgroundHandler extends Handler {
789 static final int MSG_REBUILD_LIST = 1;
790 static final int MSG_LOAD_ENTRIES = 2;
791 static final int MSG_LOAD_ICONS = 3;
792 static final int MSG_LOAD_SIZES = 4;
793 static final int MSG_LOAD_LAUNCHER = 5;
794 static final int MSG_LOAD_HOME_APP = 6;
798 BackgroundHandler(Looper looper) {
803 public void handleMessage(Message msg) {
804 // Always try rebuilding list first thing, if needed.
805 ArrayList<Session> rebuildingSessions = null;
806 synchronized (mRebuildingSessions) {
807 if (mRebuildingSessions.size() > 0) {
808 rebuildingSessions = new ArrayList<Session>(mRebuildingSessions);
809 mRebuildingSessions.clear();
812 if (rebuildingSessions != null) {
813 for (int i=0; i<rebuildingSessions.size(); i++) {
814 rebuildingSessions.get(i).handleRebuildList();
819 case MSG_REBUILD_LIST: {
821 case MSG_LOAD_ENTRIES: {
823 synchronized (mEntriesMap) {
824 if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_ENTRIES acquired lock");
825 for (int i = 0; i < mApplications.size() && numDone < 6; i++) {
828 Message m = mMainHandler.obtainMessage(
829 MainHandler.MSG_RUNNING_STATE_CHANGED, 1);
830 mMainHandler.sendMessage(m);
832 ApplicationInfo info = mApplications.get(i);
833 int userId = UserHandle.getUserId(info.uid);
834 if (mEntriesMap.get(userId).get(info.packageName) == null) {
836 getEntryLocked(info);
838 if (userId != 0 && mEntriesMap.indexOfKey(0) >= 0) {
839 // If this app is for a profile and we are on the owner, remove
840 // the owner entry if it isn't installed. This will prevent
841 // duplicates of work only apps showing up as 'not installed
843 // Note: This depends on us traversing the users in order, which
844 // happens because of the way we generate the list in
845 // doResumeIfNeededLocked.
846 AppEntry entry = mEntriesMap.get(0).get(info.packageName);
848 (entry.info.flags & ApplicationInfo.FLAG_INSTALLED) == 0) {
849 mEntriesMap.get(0).remove(info.packageName);
850 mAppEntries.remove(entry);
854 if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_ENTRIES releasing lock");
858 sendEmptyMessage(MSG_LOAD_ENTRIES);
860 if (!mMainHandler.hasMessages(MainHandler.MSG_LOAD_ENTRIES_COMPLETE)) {
861 mMainHandler.sendEmptyMessage(MainHandler.MSG_LOAD_ENTRIES_COMPLETE);
863 sendEmptyMessage(MSG_LOAD_HOME_APP);
866 case MSG_LOAD_HOME_APP: {
867 final List<ResolveInfo> homeActivities = new ArrayList<>();
868 mPm.getHomeActivities(homeActivities);
869 synchronized (mEntriesMap) {
870 final int entryCount = mEntriesMap.size();
871 for (int i = 0; i < entryCount; i++) {
872 if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_HOME_APP acquired lock");
873 final HashMap<String, AppEntry> userEntries = mEntriesMap.valueAt(i);
874 for (ResolveInfo activity : homeActivities) {
875 String packageName = activity.activityInfo.packageName;
876 AppEntry entry = userEntries.get(packageName);
878 entry.isHomeApp = true;
881 if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_HOME_APP releasing lock");
884 sendEmptyMessage(MSG_LOAD_LAUNCHER);
887 case MSG_LOAD_LAUNCHER: {
888 Intent launchIntent = new Intent(Intent.ACTION_MAIN, null)
889 .addCategory(Intent.CATEGORY_LAUNCHER);
890 for (int i = 0; i < mEntriesMap.size(); i++) {
891 int userId = mEntriesMap.keyAt(i);
892 // If we do not specify MATCH_DIRECT_BOOT_AWARE or
893 // MATCH_DIRECT_BOOT_UNAWARE, system will derive and update the flags
894 // according to the user's lock state. When the user is locked, components
895 // with ComponentInfo#directBootAware == false will be filtered. We should
896 // explicitly include both direct boot aware and unaware components here.
897 List<ResolveInfo> intents = mPm.queryIntentActivitiesAsUser(
899 PackageManager.MATCH_DISABLED_COMPONENTS
900 | PackageManager.MATCH_DIRECT_BOOT_AWARE
901 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
904 synchronized (mEntriesMap) {
905 if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_LAUNCHER acquired lock");
906 HashMap<String, AppEntry> userEntries = mEntriesMap.valueAt(i);
907 final int N = intents.size();
908 for (int j = 0; j < N; j++) {
909 String packageName = intents.get(j).activityInfo.packageName;
910 AppEntry entry = userEntries.get(packageName);
912 entry.hasLauncherEntry = true;
914 Log.w(TAG, "Cannot find pkg: " + packageName
915 + " on user " + userId);
918 if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_LAUNCHER releasing lock");
922 if (!mMainHandler.hasMessages(MainHandler.MSG_LAUNCHER_INFO_CHANGED)) {
923 mMainHandler.sendEmptyMessage(MainHandler.MSG_LAUNCHER_INFO_CHANGED);
925 sendEmptyMessage(MSG_LOAD_ICONS);
927 case MSG_LOAD_ICONS: {
929 synchronized (mEntriesMap) {
930 if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_ICONS acquired lock");
931 for (int i=0; i<mAppEntries.size() && numDone<2; i++) {
932 AppEntry entry = mAppEntries.get(i);
933 if (entry.icon == null || !entry.mounted) {
934 synchronized (entry) {
935 if (entry.ensureIconLocked(mContext, mPm)) {
938 Message m = mMainHandler.obtainMessage(
939 MainHandler.MSG_RUNNING_STATE_CHANGED, 1);
940 mMainHandler.sendMessage(m);
947 if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_ICONS releasing lock");
950 if (!mMainHandler.hasMessages(MainHandler.MSG_PACKAGE_ICON_CHANGED)) {
951 mMainHandler.sendEmptyMessage(MainHandler.MSG_PACKAGE_ICON_CHANGED);
955 sendEmptyMessage(MSG_LOAD_ICONS);
957 sendEmptyMessage(MSG_LOAD_SIZES);
960 case MSG_LOAD_SIZES: {
961 synchronized (mEntriesMap) {
962 if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_SIZES acquired lock");
963 if (mCurComputingSizePkg != null) {
964 if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_SIZES releasing: currently computing");
968 long now = SystemClock.uptimeMillis();
969 for (int i=0; i<mAppEntries.size(); i++) {
970 AppEntry entry = mAppEntries.get(i);
971 if ((entry.info.flags & ApplicationInfo.FLAG_INSTALLED) != 0
972 && (entry.size == SIZE_UNKNOWN || entry.sizeStale)) {
973 if (entry.sizeLoadStart == 0 ||
974 (entry.sizeLoadStart < (now-20*1000))) {
977 Message m = mMainHandler.obtainMessage(
978 MainHandler.MSG_RUNNING_STATE_CHANGED, 1);
979 mMainHandler.sendMessage(m);
981 entry.sizeLoadStart = now;
982 mCurComputingSizeUuid = entry.info.volumeUuid;
983 mCurComputingSizePkg = entry.info.packageName;
984 mCurComputingSizeUserId = UserHandle.getUserId(entry.info.uid);
986 mBackgroundHandler.post(() -> {
988 final StorageStats stats = mStats.queryStatsForPackage(
989 mCurComputingSizeUuid, mCurComputingSizePkg,
990 UserHandle.of(mCurComputingSizeUserId));
991 final PackageStats legacyStats = new PackageStats(
992 mCurComputingSizePkg, mCurComputingSizeUserId);
993 legacyStats.codeSize = stats.getCodeBytes();
994 legacyStats.dataSize = stats.getDataBytes();
995 legacyStats.cacheSize = stats.getCacheBytes();
997 mStatsObserver.onGetStatsCompleted(legacyStats, true);
998 } catch (RemoteException ignored) {
1000 } catch (IllegalStateException e) {
1001 Log.e(TAG,"An exception occurred while fetching app size", e);
1003 mStatsObserver.onGetStatsCompleted(null, false);
1004 } catch (RemoteException ignored) {
1010 if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_SIZES releasing: now computing");
1014 if (!mMainHandler.hasMessages(MainHandler.MSG_ALL_SIZES_COMPUTED)) {
1015 mMainHandler.sendEmptyMessage(MainHandler.MSG_ALL_SIZES_COMPUTED);
1017 Message m = mMainHandler.obtainMessage(
1018 MainHandler.MSG_RUNNING_STATE_CHANGED, 0);
1019 mMainHandler.sendMessage(m);
1021 if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_SIZES releasing lock");
1027 final IPackageStatsObserver.Stub mStatsObserver = new IPackageStatsObserver.Stub() {
1028 public void onGetStatsCompleted(PackageStats stats, boolean succeeded) {
1029 boolean sizeChanged = false;
1030 synchronized (mEntriesMap) {
1031 if (DEBUG_LOCKING) Log.v(TAG, "onGetStatsCompleted acquired lock");
1032 HashMap<String, AppEntry> userMap = mEntriesMap.get(stats.userHandle);
1033 if (userMap == null) {
1034 // The user must have been removed.
1037 AppEntry entry = userMap.get(stats.packageName);
1038 if (entry != null) {
1039 synchronized (entry) {
1040 entry.sizeStale = false;
1041 entry.sizeLoadStart = 0;
1042 long externalCodeSize = stats.externalCodeSize
1043 + stats.externalObbSize;
1044 long externalDataSize = stats.externalDataSize
1045 + stats.externalMediaSize;
1046 long newSize = externalCodeSize + externalDataSize
1047 + getTotalInternalSize(stats);
1048 if (entry.size != newSize ||
1049 entry.cacheSize != stats.cacheSize ||
1050 entry.codeSize != stats.codeSize ||
1051 entry.dataSize != stats.dataSize ||
1052 entry.externalCodeSize != externalCodeSize ||
1053 entry.externalDataSize != externalDataSize ||
1054 entry.externalCacheSize != stats.externalCacheSize) {
1055 entry.size = newSize;
1056 entry.cacheSize = stats.cacheSize;
1057 entry.codeSize = stats.codeSize;
1058 entry.dataSize = stats.dataSize;
1059 entry.externalCodeSize = externalCodeSize;
1060 entry.externalDataSize = externalDataSize;
1061 entry.externalCacheSize = stats.externalCacheSize;
1062 entry.sizeStr = getSizeStr(entry.size);
1063 entry.internalSize = getTotalInternalSize(stats);
1064 entry.internalSizeStr = getSizeStr(entry.internalSize);
1065 entry.externalSize = getTotalExternalSize(stats);
1066 entry.externalSizeStr = getSizeStr(entry.externalSize);
1067 if (DEBUG) Log.i(TAG, "Set size of " + entry.label + " " + entry
1068 + ": " + entry.sizeStr);
1073 Message msg = mMainHandler.obtainMessage(
1074 MainHandler.MSG_PACKAGE_SIZE_CHANGED, stats.packageName);
1075 mMainHandler.sendMessage(msg);
1078 if (mCurComputingSizePkg != null
1079 && (mCurComputingSizePkg.equals(stats.packageName)
1080 && mCurComputingSizeUserId == stats.userHandle)) {
1081 mCurComputingSizePkg = null;
1082 sendEmptyMessage(MSG_LOAD_SIZES);
1084 if (DEBUG_LOCKING) Log.v(TAG, "onGetStatsCompleted releasing lock");
1091 * Receives notifications when applications are added/removed.
1093 private class PackageIntentReceiver extends BroadcastReceiver {
1094 void registerReceiver() {
1095 IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
1096 filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
1097 filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
1098 filter.addDataScheme("package");
1099 mContext.registerReceiver(this, filter);
1100 // Register for events related to sdcard installation.
1101 IntentFilter sdFilter = new IntentFilter();
1102 sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
1103 sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
1104 mContext.registerReceiver(this, sdFilter);
1105 // Register for events related to user creation/deletion.
1106 IntentFilter userFilter = new IntentFilter();
1107 userFilter.addAction(Intent.ACTION_USER_ADDED);
1108 userFilter.addAction(Intent.ACTION_USER_REMOVED);
1109 mContext.registerReceiver(this, userFilter);
1111 void unregisterReceiver() {
1112 mContext.unregisterReceiver(this);
1115 public void onReceive(Context context, Intent intent) {
1116 String actionStr = intent.getAction();
1117 if (Intent.ACTION_PACKAGE_ADDED.equals(actionStr)) {
1118 Uri data = intent.getData();
1119 String pkgName = data.getEncodedSchemeSpecificPart();
1120 for (int i = 0; i < mEntriesMap.size(); i++) {
1121 addPackage(pkgName, mEntriesMap.keyAt(i));
1123 } else if (Intent.ACTION_PACKAGE_REMOVED.equals(actionStr)) {
1124 Uri data = intent.getData();
1125 String pkgName = data.getEncodedSchemeSpecificPart();
1126 for (int i = 0; i < mEntriesMap.size(); i++) {
1127 removePackage(pkgName, mEntriesMap.keyAt(i));
1129 } else if (Intent.ACTION_PACKAGE_CHANGED.equals(actionStr)) {
1130 Uri data = intent.getData();
1131 String pkgName = data.getEncodedSchemeSpecificPart();
1132 for (int i = 0; i < mEntriesMap.size(); i++) {
1133 invalidatePackage(pkgName, mEntriesMap.keyAt(i));
1135 } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(actionStr) ||
1136 Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(actionStr)) {
1137 // When applications become available or unavailable (perhaps because
1138 // the SD card was inserted or ejected) we need to refresh the
1139 // AppInfo with new label, icon and size information as appropriate
1140 // given the newfound (un)availability of the application.
1141 // A simple way to do that is to treat the refresh as a package
1142 // removal followed by a package addition.
1143 String pkgList[] = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
1144 if (pkgList == null || pkgList.length == 0) {
1148 boolean avail = Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(actionStr);
1150 for (String pkgName : pkgList) {
1151 for (int i = 0; i < mEntriesMap.size(); i++) {
1152 invalidatePackage(pkgName, mEntriesMap.keyAt(i));
1156 } else if (Intent.ACTION_USER_ADDED.equals(actionStr)) {
1157 addUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL));
1158 } else if (Intent.ACTION_USER_REMOVED.equals(actionStr)) {
1159 removeUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL));
1164 public interface Callbacks {
1165 void onRunningStateChanged(boolean running);
1166 void onPackageListChanged();
1167 void onRebuildComplete(ArrayList<AppEntry> apps);
1168 void onPackageIconChanged();
1169 void onPackageSizeChanged(String packageName);
1170 void onAllSizesComputed();
1171 void onLauncherInfoChanged();
1172 void onLoadEntriesCompleted();
1175 public static class SizeInfo {
1176 public long cacheSize;
1177 public long codeSize;
1178 public long dataSize;
1179 public long externalCodeSize;
1180 public long externalDataSize;
1182 // This is the part of externalDataSize that is in the cache
1183 // section of external storage. Note that we don't just combine
1184 // this with cacheSize because currently the platform can't
1185 // automatically trim this data when needed, so it is something
1186 // the user may need to manage. The externalDataSize also includes
1187 // this value, since what this is here is really the part of
1188 // externalDataSize that we can just consider to be "cache" files
1189 // for purposes of cleaning them up in the app details UI.
1190 public long externalCacheSize;
1193 public static class AppEntry extends SizeInfo {
1194 public final File apkFile;
1195 public final long id;
1196 public String label;
1198 public long internalSize;
1199 public long externalSize;
1201 public boolean mounted;
1204 * Setting this to {@code true} prevents the entry to be filtered by
1205 * {@link #FILTER_DOWNLOADED_AND_LAUNCHER}.
1207 public boolean hasLauncherEntry;
1210 * Whether or not it's a Home app.
1212 public boolean isHomeApp;
1214 public String getNormalizedLabel() {
1215 if (normalizedLabel != null) {
1216 return normalizedLabel;
1218 normalizedLabel = normalize(label);
1219 return normalizedLabel;
1222 // Need to synchronize on 'this' for the following.
1223 public ApplicationInfo info;
1224 public Drawable icon;
1225 public String sizeStr;
1226 public String internalSizeStr;
1227 public String externalSizeStr;
1228 public boolean sizeStale;
1229 public long sizeLoadStart;
1231 public String normalizedLabel;
1233 // A location where extra info can be placed to be used by custom filters.
1234 public Object extraInfo;
1236 AppEntry(Context context, ApplicationInfo info, long id) {
1237 apkFile = new File(info.sourceDir);
1240 this.size = SIZE_UNKNOWN;
1241 this.sizeStale = true;
1242 ensureLabel(context);
1245 public void ensureLabel(Context context) {
1246 if (this.label == null || !this.mounted) {
1247 if (!this.apkFile.exists()) {
1248 this.mounted = false;
1249 this.label = info.packageName;
1251 this.mounted = true;
1252 CharSequence label = info.loadLabel(context.getPackageManager());
1253 this.label = label != null ? label.toString() : info.packageName;
1258 boolean ensureIconLocked(Context context, PackageManager pm) {
1259 if (this.icon == null) {
1260 if (this.apkFile.exists()) {
1261 this.icon = getBadgedIcon(pm);
1264 this.mounted = false;
1265 this.icon = context.getDrawable(
1266 com.android.internal.R.drawable.sym_app_on_sd_unavailable_icon);
1268 } else if (!this.mounted) {
1269 // If the app wasn't mounted but is now mounted, reload
1271 if (this.apkFile.exists()) {
1272 this.mounted = true;
1273 this.icon = getBadgedIcon(pm);
1280 private Drawable getBadgedIcon(PackageManager pm) {
1281 // Do badging ourself so that it comes from the user of the app not the current user.
1282 return pm.getUserBadgedIcon(pm.loadUnbadgedItemIcon(info, info),
1283 new UserHandle(UserHandle.getUserId(info.uid)));
1286 public String getVersion(Context context) {
1288 return context.getPackageManager().getPackageInfo(info.packageName, 0).versionName;
1289 } catch (PackageManager.NameNotFoundException e) {
1296 * Compare by label, then package name, then uid.
1298 public static final Comparator<AppEntry> ALPHA_COMPARATOR = new Comparator<AppEntry>() {
1299 private final Collator sCollator = Collator.getInstance();
1301 public int compare(AppEntry object1, AppEntry object2) {
1302 int compareResult = sCollator.compare(object1.label, object2.label);
1303 if (compareResult != 0) {
1304 return compareResult;
1306 if (object1.info != null && object2.info != null) {
1308 sCollator.compare(object1.info.packageName, object2.info.packageName);
1309 if (compareResult != 0) {
1310 return compareResult;
1313 return object1.info.uid - object2.info.uid;
1317 public static final Comparator<AppEntry> SIZE_COMPARATOR
1318 = new Comparator<AppEntry>() {
1320 public int compare(AppEntry object1, AppEntry object2) {
1321 if (object1.size < object2.size) return 1;
1322 if (object1.size > object2.size) return -1;
1323 return ALPHA_COMPARATOR.compare(object1, object2);
1327 public static final Comparator<AppEntry> INTERNAL_SIZE_COMPARATOR
1328 = new Comparator<AppEntry>() {
1330 public int compare(AppEntry object1, AppEntry object2) {
1331 if (object1.internalSize < object2.internalSize) return 1;
1332 if (object1.internalSize > object2.internalSize) return -1;
1333 return ALPHA_COMPARATOR.compare(object1, object2);
1337 public static final Comparator<AppEntry> EXTERNAL_SIZE_COMPARATOR
1338 = new Comparator<AppEntry>() {
1340 public int compare(AppEntry object1, AppEntry object2) {
1341 if (object1.externalSize < object2.externalSize) return 1;
1342 if (object1.externalSize > object2.externalSize) return -1;
1343 return ALPHA_COMPARATOR.compare(object1, object2);
1347 public interface AppFilter {
1349 default void init(Context context) {
1352 boolean filterApp(AppEntry info);
1355 public static final AppFilter FILTER_PERSONAL = new AppFilter() {
1356 private int mCurrentUser;
1359 public void init() {
1360 mCurrentUser = ActivityManager.getCurrentUser();
1364 public boolean filterApp(AppEntry entry) {
1365 return UserHandle.getUserId(entry.info.uid) == mCurrentUser;
1369 public static final AppFilter FILTER_WITHOUT_DISABLED_UNTIL_USED = new AppFilter() {
1371 public void init() {
1376 public boolean filterApp(AppEntry entry) {
1377 return entry.info.enabledSetting
1378 != PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED;
1382 public static final AppFilter FILTER_WORK = new AppFilter() {
1383 private int mCurrentUser;
1386 public void init() {
1387 mCurrentUser = ActivityManager.getCurrentUser();
1391 public boolean filterApp(AppEntry entry) {
1392 return UserHandle.getUserId(entry.info.uid) != mCurrentUser;
1397 * Displays a combined list with "downloaded" and "visible in launcher" apps only.
1399 public static final AppFilter FILTER_DOWNLOADED_AND_LAUNCHER = new AppFilter() {
1401 public void init() {
1405 public boolean filterApp(AppEntry entry) {
1406 if (AppUtils.isInstant(entry.info)) {
1408 } else if ((entry.info.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) {
1410 } else if ((entry.info.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
1412 } else if (entry.hasLauncherEntry) {
1414 } else if ((entry.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0 && entry.isHomeApp) {
1422 * Displays a combined list with "downloaded" and "visible in launcher" apps only.
1424 public static final AppFilter FILTER_DOWNLOADED_AND_LAUNCHER_AND_INSTANT = new AppFilter() {
1427 public void init() {
1431 public boolean filterApp(AppEntry entry) {
1432 return AppUtils.isInstant(entry.info)
1433 || FILTER_DOWNLOADED_AND_LAUNCHER.filterApp(entry);
1438 public static final AppFilter FILTER_THIRD_PARTY = new AppFilter() {
1440 public void init() {
1444 public boolean filterApp(AppEntry entry) {
1445 if ((entry.info.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) {
1447 } else if ((entry.info.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
1454 public static final AppFilter FILTER_DISABLED = new AppFilter() {
1456 public void init() {
1460 public boolean filterApp(AppEntry entry) {
1461 return !entry.info.enabled && !AppUtils.isInstant(entry.info);
1465 public static final AppFilter FILTER_INSTANT = new AppFilter() {
1467 public void init() {
1471 public boolean filterApp(AppEntry entry) {
1472 return AppUtils.isInstant(entry.info);
1476 public static final AppFilter FILTER_ALL_ENABLED = new AppFilter() {
1478 public void init() {
1482 public boolean filterApp(AppEntry entry) {
1483 return entry.info.enabled && !AppUtils.isInstant(entry.info);
1487 public static final AppFilter FILTER_EVERYTHING = new AppFilter() {
1489 public void init() {
1493 public boolean filterApp(AppEntry entry) {
1498 public static final AppFilter FILTER_WITH_DOMAIN_URLS = new AppFilter() {
1500 public void init() {
1504 public boolean filterApp(AppEntry entry) {
1505 return (entry.info.privateFlags & ApplicationInfo.PRIVATE_FLAG_HAS_DOMAIN_URLS) != 0;
1509 public static final AppFilter FILTER_NOT_HIDE = new AppFilter() {
1510 private String[] mHidePackageNames;
1513 public void init(Context context) {
1514 mHidePackageNames = context.getResources()
1515 .getStringArray(R.array.config_hideWhenDisabled_packageNames);
1519 public void init() {
1523 public boolean filterApp(AppEntry entry) {
1524 if (ArrayUtils.contains(mHidePackageNames, entry.info.packageName)) {
1525 if (!entry.info.enabled) {
1527 } else if (entry.info.enabledSetting ==
1528 PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED) {
1537 public static final AppFilter FILTER_GAMES = new AppFilter() {
1539 public void init() {
1543 public boolean filterApp(ApplicationsState.AppEntry info) {
1544 // TODO: Update for the new game category.
1546 synchronized (info.info) {
1547 isGame = ((info.info.flags & ApplicationInfo.FLAG_IS_GAME) != 0)
1548 || info.info.category == ApplicationInfo.CATEGORY_GAME;
1554 public static class VolumeFilter implements AppFilter {
1555 private final String mVolumeUuid;
1557 public VolumeFilter(String volumeUuid) {
1558 mVolumeUuid = volumeUuid;
1562 public void init() {
1566 public boolean filterApp(AppEntry info) {
1567 return Objects.equals(info.info.volumeUuid, mVolumeUuid);
1571 public static class CompoundFilter implements AppFilter {
1572 private final AppFilter mFirstFilter;
1573 private final AppFilter mSecondFilter;
1575 public CompoundFilter(AppFilter first, AppFilter second) {
1576 mFirstFilter = first;
1577 mSecondFilter = second;
1581 public void init(Context context) {
1582 mFirstFilter.init(context);
1583 mSecondFilter.init(context);
1587 public void init() {
1588 mFirstFilter.init();
1589 mSecondFilter.init();
1593 public boolean filterApp(AppEntry info) {
1594 return mFirstFilter.filterApp(info) && mSecondFilter.filterApp(info);
1598 public static final AppFilter FILTER_AUDIO = new AppFilter() {
1600 public void init() {
1604 public boolean filterApp(AppEntry entry) {
1606 synchronized(entry) {
1607 isMusicApp = entry.info.category == ApplicationInfo.CATEGORY_AUDIO;
1613 public static final AppFilter FILTER_MOVIES = new AppFilter() {
1615 public void init() {
1619 public boolean filterApp(AppEntry entry) {
1621 synchronized(entry) {
1622 isMovieApp = entry.info.category == ApplicationInfo.CATEGORY_VIDEO;
1628 public static final AppFilter FILTER_OTHER_APPS =
1631 public void init() {}
1634 public boolean filterApp(AppEntry entry) {
1635 boolean isCategorized;
1636 synchronized (entry) {
1638 FILTER_AUDIO.filterApp(entry)
1639 || FILTER_GAMES.filterApp(entry)
1640 || FILTER_MOVIES.filterApp(entry);
1642 return !isCategorized;