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.settings.applications;
19 import android.app.ActivityManager;
20 import android.app.AppGlobals;
21 import android.app.Application;
22 import android.content.BroadcastReceiver;
23 import android.content.Context;
24 import android.content.Intent;
25 import android.content.IntentFilter;
26 import android.content.pm.ApplicationInfo;
27 import android.content.pm.IPackageManager;
28 import android.content.pm.IPackageStatsObserver;
29 import android.content.pm.PackageManager;
30 import android.content.pm.PackageStats;
31 import android.content.pm.ParceledListSlice;
32 import android.content.pm.ResolveInfo;
33 import android.graphics.drawable.Drawable;
34 import android.net.Uri;
35 import android.os.Handler;
36 import android.os.HandlerThread;
37 import android.os.Looper;
38 import android.os.Message;
39 import android.os.Process;
40 import android.os.RemoteException;
41 import android.os.SystemClock;
42 import android.os.UserHandle;
43 import android.os.UserManager;
44 import android.text.format.Formatter;
45 import android.util.Log;
46 import android.util.SparseArray;
49 import java.text.Collator;
50 import java.text.Normalizer;
51 import java.text.Normalizer.Form;
52 import java.util.ArrayList;
53 import java.util.Collections;
54 import java.util.Comparator;
55 import java.util.HashMap;
56 import java.util.List;
57 import java.util.Objects;
58 import java.util.regex.Pattern;
61 * Keeps track of information about all installed applications, lazy-loading
64 public class ApplicationsState {
65 static final String TAG = "ApplicationsState";
66 static final boolean DEBUG = false;
67 static final boolean DEBUG_LOCKING = false;
69 public static interface Callbacks {
70 public void onRunningStateChanged(boolean running);
71 public void onPackageListChanged();
72 public void onRebuildComplete(ArrayList<AppEntry> apps);
73 public void onPackageIconChanged();
74 public void onPackageSizeChanged(String packageName);
75 public void onAllSizesComputed();
76 public void onLauncherInfoChanged();
77 public void onLoadEntriesCompleted();
80 public static interface AppFilter {
82 public boolean filterApp(AppEntry info);
85 static final int SIZE_UNKNOWN = -1;
86 static final int SIZE_INVALID = -2;
88 static final Pattern REMOVE_DIACRITICALS_PATTERN
89 = Pattern.compile("\\p{InCombiningDiacriticalMarks}+");
91 public static String normalize(String str) {
92 String tmp = Normalizer.normalize(str, Form.NFD);
93 return REMOVE_DIACRITICALS_PATTERN.matcher(tmp)
94 .replaceAll("").toLowerCase();
97 public static class SizeInfo {
101 long externalCodeSize;
102 long externalDataSize;
104 // This is the part of externalDataSize that is in the cache
105 // section of external storage. Note that we don't just combine
106 // this with cacheSize because currently the platform can't
107 // automatically trim this data when needed, so it is something
108 // the user may need to manage. The externalDataSize also includes
109 // this value, since what this is here is really the part of
110 // externalDataSize that we can just consider to be "cache" files
111 // for purposes of cleaning them up in the app details UI.
112 long externalCacheSize;
115 public static class AppEntry extends SizeInfo {
125 boolean hasLauncherEntry;
127 String getNormalizedLabel() {
128 if (normalizedLabel != null) {
129 return normalizedLabel;
131 normalizedLabel = normalize(label);
132 return normalizedLabel;
135 // Need to synchronize on 'this' for the following.
136 public ApplicationInfo info;
139 String internalSizeStr;
140 String externalSizeStr;
144 String normalizedLabel;
146 // A location where extra info can be placed to be used by custom filters.
149 AppEntry(Context context, ApplicationInfo info, long id) {
150 apkFile = new File(info.sourceDir);
153 this.size = SIZE_UNKNOWN;
154 this.sizeStale = true;
155 ensureLabel(context);
158 void ensureLabel(Context context) {
159 if (this.label == null || !this.mounted) {
160 if (!this.apkFile.exists()) {
161 this.mounted = false;
162 this.label = info.packageName;
165 CharSequence label = info.loadLabel(context.getPackageManager());
166 this.label = label != null ? label.toString() : info.packageName;
171 boolean ensureIconLocked(Context context, PackageManager pm) {
172 if (this.icon == null) {
173 if (this.apkFile.exists()) {
174 this.icon = getBadgedIcon(pm);
177 this.mounted = false;
178 this.icon = context.getDrawable(
179 com.android.internal.R.drawable.sym_app_on_sd_unavailable_icon);
181 } else if (!this.mounted) {
182 // If the app wasn't mounted but is now mounted, reload
184 if (this.apkFile.exists()) {
186 this.icon = getBadgedIcon(pm);
193 private Drawable getBadgedIcon(PackageManager pm) {
194 // Do badging ourself so that it comes from the user of the app not the current user.
195 return pm.getUserBadgedIcon(pm.loadUnbadgedItemIcon(info, info),
196 new UserHandle(UserHandle.getUserId(info.uid)));
200 public static final Comparator<AppEntry> ALPHA_COMPARATOR = new Comparator<AppEntry>() {
201 private final Collator sCollator = Collator.getInstance();
203 public int compare(AppEntry object1, AppEntry object2) {
204 return sCollator.compare(object1.label, object2.label);
208 public static final Comparator<AppEntry> SIZE_COMPARATOR
209 = new Comparator<AppEntry>() {
210 private final Collator sCollator = Collator.getInstance();
212 public int compare(AppEntry object1, AppEntry object2) {
213 if (object1.size < object2.size) return 1;
214 if (object1.size > object2.size) return -1;
215 return sCollator.compare(object1.label, object2.label);
219 public static final Comparator<AppEntry> INTERNAL_SIZE_COMPARATOR
220 = new Comparator<AppEntry>() {
221 private final Collator sCollator = Collator.getInstance();
223 public int compare(AppEntry object1, AppEntry object2) {
224 if (object1.internalSize < object2.internalSize) return 1;
225 if (object1.internalSize > object2.internalSize) return -1;
226 return sCollator.compare(object1.label, object2.label);
230 public static final Comparator<AppEntry> EXTERNAL_SIZE_COMPARATOR
231 = new Comparator<AppEntry>() {
232 private final Collator sCollator = Collator.getInstance();
234 public int compare(AppEntry object1, AppEntry object2) {
235 if (object1.externalSize < object2.externalSize) return 1;
236 if (object1.externalSize > object2.externalSize) return -1;
237 return sCollator.compare(object1.label, object2.label);
241 public static final AppFilter FILTER_PERSONAL = new AppFilter() {
242 private int mCurrentUser;
245 mCurrentUser = ActivityManager.getCurrentUser();
249 public boolean filterApp(AppEntry entry) {
250 return UserHandle.getUserId(entry.info.uid) == mCurrentUser;
254 public static final AppFilter FILTER_WORK = new AppFilter() {
255 private int mCurrentUser;
258 mCurrentUser = ActivityManager.getCurrentUser();
262 public boolean filterApp(AppEntry entry) {
263 return UserHandle.getUserId(entry.info.uid) != mCurrentUser;
267 public static final AppFilter FILTER_DOWNLOADED_AND_LAUNCHER = new AppFilter() {
272 public boolean filterApp(AppEntry entry) {
273 if ((entry.info.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) {
275 } else if ((entry.info.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
277 } else if (entry.hasLauncherEntry) {
284 public static final AppFilter FILTER_THIRD_PARTY = new AppFilter() {
289 public boolean filterApp(AppEntry entry) {
290 if ((entry.info.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) {
292 } else if ((entry.info.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
299 public static final AppFilter FILTER_DISABLED = new AppFilter() {
304 public boolean filterApp(AppEntry entry) {
305 return !entry.info.enabled;
309 public static final AppFilter FILTER_ALL_ENABLED = new AppFilter() {
314 public boolean filterApp(AppEntry entry) {
315 return entry.info.enabled;
319 public static final AppFilter FILTER_EVERYTHING = new AppFilter() {
324 public boolean filterApp(AppEntry entry) {
329 public static final AppFilter FILTER_WITH_DOMAIN_URLS = new AppFilter() {
334 public boolean filterApp(AppEntry entry) {
335 return (entry.info.privateFlags & ApplicationInfo.PRIVATE_FLAG_HAS_DOMAIN_URLS) != 0;
339 public static class VolumeFilter implements AppFilter {
340 private final String mVolumeUuid;
342 public VolumeFilter(String volumeUuid) {
343 mVolumeUuid = volumeUuid;
351 public boolean filterApp(AppEntry info) {
352 return Objects.equals(info.info.volumeUuid, mVolumeUuid);
356 public static class CompoundFilter implements AppFilter {
357 private final AppFilter mFirstFilter;
358 private final AppFilter mSecondFilter;
360 public CompoundFilter(AppFilter first, AppFilter second) {
361 mFirstFilter = first;
362 mSecondFilter = second;
368 mSecondFilter.init();
372 public boolean filterApp(AppEntry info) {
373 return mFirstFilter.filterApp(info) && mSecondFilter.filterApp(info);
377 final Context mContext;
378 final PackageManager mPm;
379 final IPackageManager mIpm;
380 final UserManager mUm;
381 final int mOwnerRetrieveFlags;
382 final int mRetrieveFlags;
383 PackageIntentReceiver mPackageIntentReceiver;
386 boolean mHaveDisabledApps;
388 // Information about all applications. Synchronize on mEntriesMap
389 // to protect access to these.
390 final ArrayList<Session> mSessions = new ArrayList<Session>();
391 final ArrayList<Session> mRebuildingSessions = new ArrayList<Session>();
392 final InterestingConfigChanges mInterestingConfigChanges = new InterestingConfigChanges();
393 // Map: userid => (Map: package name => AppEntry)
394 final SparseArray<HashMap<String, AppEntry>> mEntriesMap =
395 new SparseArray<HashMap<String, AppEntry>>();
396 final ArrayList<AppEntry> mAppEntries = new ArrayList<AppEntry>();
397 List<ApplicationInfo> mApplications = new ArrayList<ApplicationInfo>();
399 String mCurComputingSizePkg;
400 int mCurComputingSizeUserId;
401 boolean mSessionsChanged;
403 // Temporary for dispatching session callbacks. Only touched by main thread.
404 final ArrayList<Session> mActiveSessions = new ArrayList<Session>();
407 * Receives notifications when applications are added/removed.
409 private class PackageIntentReceiver extends BroadcastReceiver {
410 void registerReceiver() {
411 IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
412 filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
413 filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
414 filter.addDataScheme("package");
415 mContext.registerReceiver(this, filter);
416 // Register for events related to sdcard installation.
417 IntentFilter sdFilter = new IntentFilter();
418 sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
419 sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
420 mContext.registerReceiver(this, sdFilter);
421 // Register for events related to user creation/deletion.
422 IntentFilter userFilter = new IntentFilter();
423 userFilter.addAction(Intent.ACTION_USER_ADDED);
424 userFilter.addAction(Intent.ACTION_USER_REMOVED);
425 mContext.registerReceiver(this, userFilter);
427 void unregisterReceiver() {
428 mContext.unregisterReceiver(this);
431 public void onReceive(Context context, Intent intent) {
432 String actionStr = intent.getAction();
433 if (Intent.ACTION_PACKAGE_ADDED.equals(actionStr)) {
434 Uri data = intent.getData();
435 String pkgName = data.getEncodedSchemeSpecificPart();
436 for (int i = 0; i < mEntriesMap.size(); i++) {
437 addPackage(pkgName, mEntriesMap.keyAt(i));
439 } else if (Intent.ACTION_PACKAGE_REMOVED.equals(actionStr)) {
440 Uri data = intent.getData();
441 String pkgName = data.getEncodedSchemeSpecificPart();
442 for (int i = 0; i < mEntriesMap.size(); i++) {
443 removePackage(pkgName, mEntriesMap.keyAt(i));
445 } else if (Intent.ACTION_PACKAGE_CHANGED.equals(actionStr)) {
446 Uri data = intent.getData();
447 String pkgName = data.getEncodedSchemeSpecificPart();
448 for (int i = 0; i < mEntriesMap.size(); i++) {
449 invalidatePackage(pkgName, mEntriesMap.keyAt(i));
451 } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(actionStr) ||
452 Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(actionStr)) {
453 // When applications become available or unavailable (perhaps because
454 // the SD card was inserted or ejected) we need to refresh the
455 // AppInfo with new label, icon and size information as appropriate
456 // given the newfound (un)availability of the application.
457 // A simple way to do that is to treat the refresh as a package
458 // removal followed by a package addition.
459 String pkgList[] = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
460 if (pkgList == null || pkgList.length == 0) {
464 boolean avail = Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(actionStr);
466 for (String pkgName : pkgList) {
467 for (int i = 0; i < mEntriesMap.size(); i++) {
468 invalidatePackage(pkgName, mEntriesMap.keyAt(i));
472 } else if (Intent.ACTION_USER_ADDED.equals(actionStr)) {
473 addUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL));
474 } else if (Intent.ACTION_USER_REMOVED.equals(actionStr)) {
475 removeUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL));
480 void rebuildActiveSessions() {
481 synchronized (mEntriesMap) {
482 if (!mSessionsChanged) {
485 mActiveSessions.clear();
486 for (int i=0; i<mSessions.size(); i++) {
487 Session s = mSessions.get(i);
489 mActiveSessions.add(s);
495 class MainHandler extends Handler {
496 static final int MSG_REBUILD_COMPLETE = 1;
497 static final int MSG_PACKAGE_LIST_CHANGED = 2;
498 static final int MSG_PACKAGE_ICON_CHANGED = 3;
499 static final int MSG_PACKAGE_SIZE_CHANGED = 4;
500 static final int MSG_ALL_SIZES_COMPUTED = 5;
501 static final int MSG_RUNNING_STATE_CHANGED = 6;
502 static final int MSG_LAUNCHER_INFO_CHANGED = 7;
503 static final int MSG_LOAD_ENTRIES_COMPLETE = 8;
506 public void handleMessage(Message msg) {
507 rebuildActiveSessions();
509 case MSG_REBUILD_COMPLETE: {
510 Session s = (Session)msg.obj;
511 if (mActiveSessions.contains(s)) {
512 s.mCallbacks.onRebuildComplete(s.mLastAppList);
515 case MSG_PACKAGE_LIST_CHANGED: {
516 for (int i=0; i<mActiveSessions.size(); i++) {
517 mActiveSessions.get(i).mCallbacks.onPackageListChanged();
520 case MSG_PACKAGE_ICON_CHANGED: {
521 for (int i=0; i<mActiveSessions.size(); i++) {
522 mActiveSessions.get(i).mCallbacks.onPackageIconChanged();
525 case MSG_PACKAGE_SIZE_CHANGED: {
526 for (int i=0; i<mActiveSessions.size(); i++) {
527 mActiveSessions.get(i).mCallbacks.onPackageSizeChanged(
531 case MSG_ALL_SIZES_COMPUTED: {
532 for (int i=0; i<mActiveSessions.size(); i++) {
533 mActiveSessions.get(i).mCallbacks.onAllSizesComputed();
536 case MSG_RUNNING_STATE_CHANGED: {
537 for (int i=0; i<mActiveSessions.size(); i++) {
538 mActiveSessions.get(i).mCallbacks.onRunningStateChanged(
542 case MSG_LAUNCHER_INFO_CHANGED: {
543 for (int i=0; i<mActiveSessions.size(); i++) {
544 mActiveSessions.get(i).mCallbacks.onLauncherInfoChanged();
547 case MSG_LOAD_ENTRIES_COMPLETE: {
548 for (int i=0; i<mActiveSessions.size(); i++) {
549 mActiveSessions.get(i).mCallbacks.onLoadEntriesCompleted();
556 final MainHandler mMainHandler = new MainHandler();
558 // --------------------------------------------------------------
560 static final Object sLock = new Object();
561 static ApplicationsState sInstance;
563 static ApplicationsState getInstance(Application app) {
564 synchronized (sLock) {
565 if (sInstance == null) {
566 sInstance = new ApplicationsState(app);
572 private ApplicationsState(Application app) {
574 mPm = mContext.getPackageManager();
575 mIpm = AppGlobals.getPackageManager();
576 mUm = (UserManager) app.getSystemService(Context.USER_SERVICE);
577 for (UserHandle user : mUm.getUserProfiles()) {
578 mEntriesMap.put(user.getIdentifier(), new HashMap<String, AppEntry>());
580 mThread = new HandlerThread("ApplicationsState.Loader",
581 Process.THREAD_PRIORITY_BACKGROUND);
583 mBackgroundHandler = new BackgroundHandler(mThread.getLooper());
585 // Only the owner can see all apps.
586 mOwnerRetrieveFlags = PackageManager.GET_UNINSTALLED_PACKAGES |
587 PackageManager.GET_DISABLED_COMPONENTS |
588 PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS;
589 mRetrieveFlags = PackageManager.GET_DISABLED_COMPONENTS |
590 PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS;
593 * This is a trick to prevent the foreground thread from being delayed.
594 * The problem is that Dalvik monitors are initially spin locks, to keep
595 * them lightweight. This leads to unfair contention -- Even though the
596 * background thread only holds the lock for a short amount of time, if
597 * it keeps running and locking again it can prevent the main thread from
598 * acquiring its lock for a long time... sometimes even > 5 seconds
599 * (leading to an ANR).
601 * Dalvik will promote a monitor to a "real" lock if it detects enough
602 * contention on it. It doesn't figure this out fast enough for us
603 * here, though, so this little trick will force it to turn into a real
606 synchronized (mEntriesMap) {
609 } catch (InterruptedException e) {
614 Looper getBackgroundLooper() {
615 return mThread.getLooper();
618 public class Session {
619 final Callbacks mCallbacks;
622 // Rebuilding of app list. Synchronized on mRebuildSync.
623 final Object mRebuildSync = new Object();
624 boolean mRebuildRequested;
625 boolean mRebuildAsync;
626 AppFilter mRebuildFilter;
627 Comparator<AppEntry> mRebuildComparator;
628 ArrayList<AppEntry> mRebuildResult;
629 ArrayList<AppEntry> mLastAppList;
631 Session(Callbacks callbacks) {
632 mCallbacks = callbacks;
635 public void resume() {
636 if (DEBUG_LOCKING) Log.v(TAG, "resume about to acquire lock...");
637 synchronized (mEntriesMap) {
640 mSessionsChanged = true;
641 doResumeIfNeededLocked();
644 if (DEBUG_LOCKING) Log.v(TAG, "...resume releasing lock");
647 public void pause() {
648 if (DEBUG_LOCKING) Log.v(TAG, "pause about to acquire lock...");
649 synchronized (mEntriesMap) {
652 mSessionsChanged = true;
653 mBackgroundHandler.removeMessages(BackgroundHandler.MSG_REBUILD_LIST, this);
654 doPauseIfNeededLocked();
656 if (DEBUG_LOCKING) Log.v(TAG, "...pause releasing lock");
660 ArrayList<AppEntry> getAllApps() {
661 synchronized (mEntriesMap) {
662 return new ArrayList<>(mAppEntries);
666 // Creates a new list of app entries with the given filter and comparator.
667 ArrayList<AppEntry> rebuild(AppFilter filter, Comparator<AppEntry> comparator) {
668 synchronized (mRebuildSync) {
669 synchronized (mEntriesMap) {
670 mRebuildingSessions.add(this);
671 mRebuildRequested = true;
672 mRebuildAsync = false;
673 mRebuildFilter = filter;
674 mRebuildComparator = comparator;
675 mRebuildResult = null;
676 if (!mBackgroundHandler.hasMessages(BackgroundHandler.MSG_REBUILD_LIST)) {
677 Message msg = mBackgroundHandler.obtainMessage(
678 BackgroundHandler.MSG_REBUILD_LIST);
679 mBackgroundHandler.sendMessage(msg);
683 // We will wait for .25s for the list to be built.
684 long waitend = SystemClock.uptimeMillis()+250;
686 while (mRebuildResult == null) {
687 long now = SystemClock.uptimeMillis();
688 if (now >= waitend) {
692 mRebuildSync.wait(waitend - now);
693 } catch (InterruptedException e) {
697 mRebuildAsync = true;
699 return mRebuildResult;
703 void handleRebuildList() {
705 Comparator<AppEntry> comparator;
706 synchronized (mRebuildSync) {
707 if (!mRebuildRequested) {
711 filter = mRebuildFilter;
712 comparator = mRebuildComparator;
713 mRebuildRequested = false;
714 mRebuildFilter = null;
715 mRebuildComparator = null;
718 Process.setThreadPriority(Process.THREAD_PRIORITY_FOREGROUND);
720 if (filter != null) {
725 synchronized (mEntriesMap) {
726 apps = new ArrayList<>(mAppEntries);
729 ArrayList<AppEntry> filteredApps = new ArrayList<AppEntry>();
730 if (DEBUG) Log.i(TAG, "Rebuilding...");
731 for (int i=0; i<apps.size(); i++) {
732 AppEntry entry = apps.get(i);
733 if (filter == null || filter.filterApp(entry)) {
734 synchronized (mEntriesMap) {
735 if (DEBUG_LOCKING) Log.v(TAG, "rebuild acquired lock");
736 entry.ensureLabel(mContext);
737 if (DEBUG) Log.i(TAG, "Using " + entry.info.packageName + ": " + entry);
738 filteredApps.add(entry);
739 if (DEBUG_LOCKING) Log.v(TAG, "rebuild releasing lock");
744 Collections.sort(filteredApps, comparator);
746 synchronized (mRebuildSync) {
747 if (!mRebuildRequested) {
748 mLastAppList = filteredApps;
749 if (!mRebuildAsync) {
750 mRebuildResult = filteredApps;
751 mRebuildSync.notifyAll();
753 if (!mMainHandler.hasMessages(MainHandler.MSG_REBUILD_COMPLETE, this)) {
754 Message msg = mMainHandler.obtainMessage(
755 MainHandler.MSG_REBUILD_COMPLETE, this);
756 mMainHandler.sendMessage(msg);
762 Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
765 public void release() {
767 synchronized (mEntriesMap) {
768 mSessions.remove(this);
773 public Session newSession(Callbacks callbacks) {
774 Session s = new Session(callbacks);
775 synchronized (mEntriesMap) {
781 void doResumeIfNeededLocked() {
786 if (mPackageIntentReceiver == null) {
787 mPackageIntentReceiver = new PackageIntentReceiver();
788 mPackageIntentReceiver.registerReceiver();
790 mApplications = new ArrayList<ApplicationInfo>();
791 for (UserHandle user : mUm.getUserProfiles()) {
793 // If this user is new, it needs a map created.
794 if (mEntriesMap.indexOfKey(user.getIdentifier()) < 0) {
795 mEntriesMap.put(user.getIdentifier(), new HashMap<String, AppEntry>());
797 ParceledListSlice<ApplicationInfo> list =
798 mIpm.getInstalledApplications(
799 user.isOwner() ? mOwnerRetrieveFlags : mRetrieveFlags,
800 user.getIdentifier());
801 mApplications.addAll(list.getList());
802 } catch (RemoteException e) {
806 if (mInterestingConfigChanges.applyNewConfig(mContext.getResources())) {
807 // If an interesting part of the configuration has changed, we
808 // should completely reload the app entries.
811 for (int i=0; i<mAppEntries.size(); i++) {
812 mAppEntries.get(i).sizeStale = true;
816 mHaveDisabledApps = false;
817 for (int i=0; i<mApplications.size(); i++) {
818 final ApplicationInfo info = mApplications.get(i);
819 // Need to trim out any applications that are disabled by
820 // something different than the user.
822 if (info.enabledSetting != PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) {
823 mApplications.remove(i);
827 mHaveDisabledApps = true;
829 int userId = UserHandle.getUserId(info.uid);
830 final AppEntry entry = mEntriesMap.get(userId).get(info.packageName);
835 if (mAppEntries.size() > mApplications.size()) {
836 // There are less apps now, some must have been uninstalled.
839 mCurComputingSizePkg = null;
840 if (!mBackgroundHandler.hasMessages(BackgroundHandler.MSG_LOAD_ENTRIES)) {
841 mBackgroundHandler.sendEmptyMessage(BackgroundHandler.MSG_LOAD_ENTRIES);
845 private void clearEntries() {
846 for (int i = 0; i < mEntriesMap.size(); i++) {
847 mEntriesMap.valueAt(i).clear();
852 public boolean haveDisabledApps() {
853 return mHaveDisabledApps;
856 void doPauseIfNeededLocked() {
860 for (int i=0; i<mSessions.size(); i++) {
861 if (mSessions.get(i).mResumed) {
868 void doPauseLocked() {
870 if (mPackageIntentReceiver != null) {
871 mPackageIntentReceiver.unregisterReceiver();
872 mPackageIntentReceiver = null;
876 AppEntry getEntry(String packageName, int userId) {
877 if (DEBUG_LOCKING) Log.v(TAG, "getEntry about to acquire lock...");
878 synchronized (mEntriesMap) {
879 AppEntry entry = mEntriesMap.get(userId).get(packageName);
881 ApplicationInfo info = getAppInfoLocked(packageName, userId);
884 info = mIpm.getApplicationInfo(packageName, 0, userId);
885 } catch (RemoteException e) {
886 Log.w(TAG, "getEntry couldn't reach PackageManager", e);
890 entry = getEntryLocked(info);
892 if (DEBUG_LOCKING) Log.v(TAG, "...getEntry releasing lock");
897 private ApplicationInfo getAppInfoLocked(String pkg, int userId) {
898 for (int i = 0; i < mApplications.size(); i++) {
899 ApplicationInfo info = mApplications.get(i);
900 if (pkg.equals(info.packageName)
901 && userId == UserHandle.getUserId(info.uid)) {
908 void ensureIcon(AppEntry entry) {
909 if (entry.icon != null) {
912 synchronized (entry) {
913 entry.ensureIconLocked(mContext, mPm);
917 void requestSize(String packageName, int userId) {
918 if (DEBUG_LOCKING) Log.v(TAG, "requestSize about to acquire lock...");
919 synchronized (mEntriesMap) {
920 AppEntry entry = mEntriesMap.get(userId).get(packageName);
922 mPm.getPackageSizeInfo(packageName, userId, mBackgroundHandler.mStatsObserver);
924 if (DEBUG_LOCKING) Log.v(TAG, "...requestSize releasing lock");
928 long sumCacheSizes() {
930 if (DEBUG_LOCKING) Log.v(TAG, "sumCacheSizes about to acquire lock...");
931 synchronized (mEntriesMap) {
932 if (DEBUG_LOCKING) Log.v(TAG, "-> sumCacheSizes now has lock");
933 for (int i=mAppEntries.size()-1; i>=0; i--) {
934 sum += mAppEntries.get(i).cacheSize;
936 if (DEBUG_LOCKING) Log.v(TAG, "...sumCacheSizes releasing lock");
941 int indexOfApplicationInfoLocked(String pkgName, int userId) {
942 for (int i=mApplications.size()-1; i>=0; i--) {
943 ApplicationInfo appInfo = mApplications.get(i);
944 if (appInfo.packageName.equals(pkgName)
945 && UserHandle.getUserId(appInfo.uid) == userId) {
952 void addPackage(String pkgName, int userId) {
954 synchronized (mEntriesMap) {
955 if (DEBUG_LOCKING) Log.v(TAG, "addPackage acquired lock");
956 if (DEBUG) Log.i(TAG, "Adding package " + pkgName);
958 // If we are not resumed, we will do a full query the
959 // next time we resume, so there is no reason to do work
961 if (DEBUG_LOCKING) Log.v(TAG, "addPackage release lock: not resumed");
964 if (indexOfApplicationInfoLocked(pkgName, userId) >= 0) {
965 if (DEBUG) Log.i(TAG, "Package already exists!");
966 if (DEBUG_LOCKING) Log.v(TAG, "addPackage release lock: already exists");
969 ApplicationInfo info = mIpm.getApplicationInfo(pkgName,
970 userId == UserHandle.USER_OWNER ? mOwnerRetrieveFlags : mRetrieveFlags,
976 if (info.enabledSetting
977 != PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) {
980 mHaveDisabledApps = true;
982 mApplications.add(info);
983 if (!mBackgroundHandler.hasMessages(BackgroundHandler.MSG_LOAD_ENTRIES)) {
984 mBackgroundHandler.sendEmptyMessage(BackgroundHandler.MSG_LOAD_ENTRIES);
986 if (!mMainHandler.hasMessages(MainHandler.MSG_PACKAGE_LIST_CHANGED)) {
987 mMainHandler.sendEmptyMessage(MainHandler.MSG_PACKAGE_LIST_CHANGED);
989 if (DEBUG_LOCKING) Log.v(TAG, "addPackage releasing lock");
991 } catch (RemoteException e) {
995 void removePackage(String pkgName, int userId) {
996 synchronized (mEntriesMap) {
997 if (DEBUG_LOCKING) Log.v(TAG, "removePackage acquired lock");
998 int idx = indexOfApplicationInfoLocked(pkgName, userId);
999 if (DEBUG) Log.i(TAG, "removePackage: " + pkgName + " @ " + idx);
1001 AppEntry entry = mEntriesMap.get(userId).get(pkgName);
1002 if (DEBUG) Log.i(TAG, "removePackage: " + entry);
1003 if (entry != null) {
1004 mEntriesMap.get(userId).remove(pkgName);
1005 mAppEntries.remove(entry);
1007 ApplicationInfo info = mApplications.get(idx);
1008 mApplications.remove(idx);
1009 if (!info.enabled) {
1010 mHaveDisabledApps = false;
1011 for (int i=0; i<mApplications.size(); i++) {
1012 if (!mApplications.get(i).enabled) {
1013 mHaveDisabledApps = true;
1018 if (!mMainHandler.hasMessages(MainHandler.MSG_PACKAGE_LIST_CHANGED)) {
1019 mMainHandler.sendEmptyMessage(MainHandler.MSG_PACKAGE_LIST_CHANGED);
1022 if (DEBUG_LOCKING) Log.v(TAG, "removePackage releasing lock");
1026 void invalidatePackage(String pkgName, int userId) {
1027 removePackage(pkgName, userId);
1028 addPackage(pkgName, userId);
1031 void addUser(int userId) {
1032 if (mUm.getUserProfiles().contains(new UserHandle(userId))) {
1033 synchronized (mEntriesMap) {
1034 mEntriesMap.put(userId, new HashMap<String, AppEntry>());
1036 // If resumed, Manually pause, then cause a resume to repopulate the app list.
1037 // This is the simplest way to reload the packages so that the new user
1038 // is included. Otherwise the list will be repopulated on next resume.
1040 doResumeIfNeededLocked();
1042 if (!mMainHandler.hasMessages(MainHandler.MSG_PACKAGE_LIST_CHANGED)) {
1043 mMainHandler.sendEmptyMessage(MainHandler.MSG_PACKAGE_LIST_CHANGED);
1049 void removeUser(int userId) {
1050 synchronized (mEntriesMap) {
1051 HashMap<String, AppEntry> userMap = mEntriesMap.get(userId);
1052 if (userMap != null) {
1053 for (AppEntry appEntry : userMap.values()) {
1054 mAppEntries.remove(appEntry);
1055 mApplications.remove(appEntry.info);
1057 mEntriesMap.remove(userId);
1058 if (!mMainHandler.hasMessages(MainHandler.MSG_PACKAGE_LIST_CHANGED)) {
1059 mMainHandler.sendEmptyMessage(MainHandler.MSG_PACKAGE_LIST_CHANGED);
1065 AppEntry getEntryLocked(ApplicationInfo info) {
1066 int userId = UserHandle.getUserId(info.uid);
1067 AppEntry entry = mEntriesMap.get(userId).get(info.packageName);
1068 if (DEBUG) Log.i(TAG, "Looking up entry of pkg " + info.packageName + ": " + entry);
1069 if (entry == null) {
1070 if (DEBUG) Log.i(TAG, "Creating AppEntry for " + info.packageName);
1071 entry = new AppEntry(mContext, info, mCurId++);
1072 mEntriesMap.get(userId).put(info.packageName, entry);
1073 mAppEntries.add(entry);
1074 } else if (entry.info != info) {
1080 // --------------------------------------------------------------
1082 private long getTotalInternalSize(PackageStats ps) {
1084 return ps.codeSize + ps.dataSize;
1086 return SIZE_INVALID;
1089 private long getTotalExternalSize(PackageStats ps) {
1091 // We also include the cache size here because for non-emulated
1092 // we don't automtically clean cache files.
1093 return ps.externalCodeSize + ps.externalDataSize
1094 + ps.externalCacheSize
1095 + ps.externalMediaSize + ps.externalObbSize;
1097 return SIZE_INVALID;
1100 private String getSizeStr(long size) {
1102 return Formatter.formatFileSize(mContext, size);
1107 final HandlerThread mThread;
1108 final BackgroundHandler mBackgroundHandler;
1109 class BackgroundHandler extends Handler {
1110 static final int MSG_REBUILD_LIST = 1;
1111 static final int MSG_LOAD_ENTRIES = 2;
1112 static final int MSG_LOAD_ICONS = 3;
1113 static final int MSG_LOAD_SIZES = 4;
1114 static final int MSG_LOAD_LAUNCHER = 5;
1118 final IPackageStatsObserver.Stub mStatsObserver = new IPackageStatsObserver.Stub() {
1119 public void onGetStatsCompleted(PackageStats stats, boolean succeeded) {
1120 boolean sizeChanged = false;
1121 synchronized (mEntriesMap) {
1122 if (DEBUG_LOCKING) Log.v(TAG, "onGetStatsCompleted acquired lock");
1123 HashMap<String, AppEntry> userMap = mEntriesMap.get(stats.userHandle);
1124 if (userMap == null) {
1125 // The user must have been removed.
1128 AppEntry entry = userMap.get(stats.packageName);
1129 if (entry != null) {
1130 synchronized (entry) {
1131 entry.sizeStale = false;
1132 entry.sizeLoadStart = 0;
1133 long externalCodeSize = stats.externalCodeSize
1134 + stats.externalObbSize;
1135 long externalDataSize = stats.externalDataSize
1136 + stats.externalMediaSize;
1137 long newSize = externalCodeSize + externalDataSize
1138 + getTotalInternalSize(stats);
1139 if (entry.size != newSize ||
1140 entry.cacheSize != stats.cacheSize ||
1141 entry.codeSize != stats.codeSize ||
1142 entry.dataSize != stats.dataSize ||
1143 entry.externalCodeSize != externalCodeSize ||
1144 entry.externalDataSize != externalDataSize ||
1145 entry.externalCacheSize != stats.externalCacheSize) {
1146 entry.size = newSize;
1147 entry.cacheSize = stats.cacheSize;
1148 entry.codeSize = stats.codeSize;
1149 entry.dataSize = stats.dataSize;
1150 entry.externalCodeSize = externalCodeSize;
1151 entry.externalDataSize = externalDataSize;
1152 entry.externalCacheSize = stats.externalCacheSize;
1153 entry.sizeStr = getSizeStr(entry.size);
1154 entry.internalSize = getTotalInternalSize(stats);
1155 entry.internalSizeStr = getSizeStr(entry.internalSize);
1156 entry.externalSize = getTotalExternalSize(stats);
1157 entry.externalSizeStr = getSizeStr(entry.externalSize);
1158 if (DEBUG) Log.i(TAG, "Set size of " + entry.label + " " + entry
1159 + ": " + entry.sizeStr);
1164 Message msg = mMainHandler.obtainMessage(
1165 MainHandler.MSG_PACKAGE_SIZE_CHANGED, stats.packageName);
1166 mMainHandler.sendMessage(msg);
1169 if (mCurComputingSizePkg != null
1170 && (mCurComputingSizePkg.equals(stats.packageName)
1171 && mCurComputingSizeUserId == stats.userHandle)) {
1172 mCurComputingSizePkg = null;
1173 sendEmptyMessage(MSG_LOAD_SIZES);
1175 if (DEBUG_LOCKING) Log.v(TAG, "onGetStatsCompleted releasing lock");
1180 BackgroundHandler(Looper looper) {
1185 public void handleMessage(Message msg) {
1186 // Always try rebuilding list first thing, if needed.
1187 ArrayList<Session> rebuildingSessions = null;
1188 synchronized (mEntriesMap) {
1189 if (mRebuildingSessions.size() > 0) {
1190 rebuildingSessions = new ArrayList<Session>(mRebuildingSessions);
1191 mRebuildingSessions.clear();
1194 if (rebuildingSessions != null) {
1195 for (int i=0; i<rebuildingSessions.size(); i++) {
1196 rebuildingSessions.get(i).handleRebuildList();
1201 case MSG_REBUILD_LIST: {
1203 case MSG_LOAD_ENTRIES: {
1205 synchronized (mEntriesMap) {
1206 if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_ENTRIES acquired lock");
1207 for (int i = 0; i < mApplications.size() && numDone < 6; i++) {
1210 Message m = mMainHandler.obtainMessage(
1211 MainHandler.MSG_RUNNING_STATE_CHANGED, 1);
1212 mMainHandler.sendMessage(m);
1214 ApplicationInfo info = mApplications.get(i);
1215 int userId = UserHandle.getUserId(info.uid);
1216 if (mEntriesMap.get(userId).get(info.packageName) == null) {
1218 getEntryLocked(info);
1221 if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_ENTRIES releasing lock");
1225 sendEmptyMessage(MSG_LOAD_ENTRIES);
1227 if (!mMainHandler.hasMessages(MainHandler.MSG_LOAD_ENTRIES_COMPLETE)) {
1228 mMainHandler.sendEmptyMessage(MainHandler.MSG_LOAD_ENTRIES_COMPLETE);
1230 sendEmptyMessage(MSG_LOAD_LAUNCHER);
1233 case MSG_LOAD_LAUNCHER: {
1234 Intent launchIntent = new Intent(Intent.ACTION_MAIN, null)
1235 .addCategory(Intent.CATEGORY_LAUNCHER);
1237 for (int i = 0; i < mEntriesMap.size(); i++) {
1238 int userId = mEntriesMap.keyAt(i);
1239 List<ResolveInfo> intents = mPm.queryIntentActivitiesAsUser(launchIntent,
1240 PackageManager.GET_DISABLED_COMPONENTS, userId);
1241 synchronized (mEntriesMap) {
1242 if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_LAUNCHER acquired lock");
1243 HashMap<String, AppEntry> userEntries = mEntriesMap.valueAt(i);
1244 final int N = intents.size();
1245 for (int j = 0; j < N; j++) {
1246 String packageName = intents.get(j).activityInfo.packageName;
1247 AppEntry entry = userEntries.get(packageName);
1248 if (entry != null) {
1249 entry.hasLauncherEntry = true;
1251 Log.w(TAG, "Cannot find pkg: " + packageName
1252 + " on user " + userId);
1255 if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_LAUNCHER releasing lock");
1259 if (!mMainHandler.hasMessages(MainHandler.MSG_LAUNCHER_INFO_CHANGED)) {
1260 mMainHandler.sendEmptyMessage(MainHandler.MSG_LAUNCHER_INFO_CHANGED);
1262 sendEmptyMessage(MSG_LOAD_ICONS);
1264 case MSG_LOAD_ICONS: {
1266 synchronized (mEntriesMap) {
1267 if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_ICONS acquired lock");
1268 for (int i=0; i<mAppEntries.size() && numDone<2; i++) {
1269 AppEntry entry = mAppEntries.get(i);
1270 if (entry.icon == null || !entry.mounted) {
1271 synchronized (entry) {
1272 if (entry.ensureIconLocked(mContext, mPm)) {
1275 Message m = mMainHandler.obtainMessage(
1276 MainHandler.MSG_RUNNING_STATE_CHANGED, 1);
1277 mMainHandler.sendMessage(m);
1284 if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_ICONS releasing lock");
1287 if (!mMainHandler.hasMessages(MainHandler.MSG_PACKAGE_ICON_CHANGED)) {
1288 mMainHandler.sendEmptyMessage(MainHandler.MSG_PACKAGE_ICON_CHANGED);
1292 sendEmptyMessage(MSG_LOAD_ICONS);
1294 sendEmptyMessage(MSG_LOAD_SIZES);
1297 case MSG_LOAD_SIZES: {
1298 synchronized (mEntriesMap) {
1299 if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_SIZES acquired lock");
1300 if (mCurComputingSizePkg != null) {
1301 if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_SIZES releasing: currently computing");
1305 long now = SystemClock.uptimeMillis();
1306 for (int i=0; i<mAppEntries.size(); i++) {
1307 AppEntry entry = mAppEntries.get(i);
1308 if (entry.size == SIZE_UNKNOWN || entry.sizeStale) {
1309 if (entry.sizeLoadStart == 0 ||
1310 (entry.sizeLoadStart < (now-20*1000))) {
1313 Message m = mMainHandler.obtainMessage(
1314 MainHandler.MSG_RUNNING_STATE_CHANGED, 1);
1315 mMainHandler.sendMessage(m);
1317 entry.sizeLoadStart = now;
1318 mCurComputingSizePkg = entry.info.packageName;
1319 mCurComputingSizeUserId = UserHandle.getUserId(entry.info.uid);
1320 mPm.getPackageSizeInfo(mCurComputingSizePkg,
1321 mCurComputingSizeUserId, mStatsObserver);
1323 if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_SIZES releasing: now computing");
1327 if (!mMainHandler.hasMessages(MainHandler.MSG_ALL_SIZES_COMPUTED)) {
1328 mMainHandler.sendEmptyMessage(MainHandler.MSG_ALL_SIZES_COMPUTED);
1330 Message m = mMainHandler.obtainMessage(
1331 MainHandler.MSG_RUNNING_STATE_CHANGED, 0);
1332 mMainHandler.sendMessage(m);
1334 if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_SIZES releasing lock");