OSDN Git Service

Merge "Clear only keystore credential entires" into mnc-dev
[android-x86/packages-apps-Settings.git] / src / com / android / settings / applications / ApplicationsState.java
1 /*
2  * Copyright (C) 2015 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 package com.android.settings.applications;
18
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;
47
48 import java.io.File;
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;
59
60 /**
61  * Keeps track of information about all installed applications, lazy-loading
62  * as needed.
63  */
64 public class ApplicationsState {
65     static final String TAG = "ApplicationsState";
66     static final boolean DEBUG = false;
67     static final boolean DEBUG_LOCKING = false;
68
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();
78     }
79
80     public static interface AppFilter {
81         public void init();
82         public boolean filterApp(AppEntry info);
83     }
84
85     static final int SIZE_UNKNOWN = -1;
86     static final int SIZE_INVALID = -2;
87
88     static final Pattern REMOVE_DIACRITICALS_PATTERN
89             = Pattern.compile("\\p{InCombiningDiacriticalMarks}+");
90
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();
95     }
96
97     public static class SizeInfo {
98         long cacheSize;
99         long codeSize;
100         long dataSize;
101         long externalCodeSize;
102         long externalDataSize;
103
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;
113     }
114
115     public static class AppEntry extends SizeInfo {
116         final File apkFile;
117         final long id;
118         String label;
119         long size;
120         long internalSize;
121         long externalSize;
122
123         boolean mounted;
124
125         boolean hasLauncherEntry;
126
127         String getNormalizedLabel() {
128             if (normalizedLabel != null) {
129                 return normalizedLabel;
130             }
131             normalizedLabel = normalize(label);
132             return normalizedLabel;
133         }
134
135         // Need to synchronize on 'this' for the following.
136         public ApplicationInfo info;
137         Drawable icon;
138         String sizeStr;
139         String internalSizeStr;
140         String externalSizeStr;
141         boolean sizeStale;
142         long sizeLoadStart;
143
144         String normalizedLabel;
145
146         // A location where extra info can be placed to be used by custom filters.
147         Object extraInfo;
148
149         AppEntry(Context context, ApplicationInfo info, long id) {
150             apkFile = new File(info.sourceDir);
151             this.id = id;
152             this.info = info;
153             this.size = SIZE_UNKNOWN;
154             this.sizeStale = true;
155             ensureLabel(context);
156         }
157
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;
163                 } else {
164                     this.mounted = true;
165                     CharSequence label = info.loadLabel(context.getPackageManager());
166                     this.label = label != null ? label.toString() : info.packageName;
167                 }
168             }
169         }
170
171         boolean ensureIconLocked(Context context, PackageManager pm) {
172             if (this.icon == null) {
173                 if (this.apkFile.exists()) {
174                     this.icon = getBadgedIcon(pm);
175                     return true;
176                 } else {
177                     this.mounted = false;
178                     this.icon = context.getDrawable(
179                             com.android.internal.R.drawable.sym_app_on_sd_unavailable_icon);
180                 }
181             } else if (!this.mounted) {
182                 // If the app wasn't mounted but is now mounted, reload
183                 // its icon.
184                 if (this.apkFile.exists()) {
185                     this.mounted = true;
186                     this.icon = getBadgedIcon(pm);
187                     return true;
188                 }
189             }
190             return false;
191         }
192
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)));
197         }
198     }
199
200     public static final Comparator<AppEntry> ALPHA_COMPARATOR = new Comparator<AppEntry>() {
201         private final Collator sCollator = Collator.getInstance();
202         @Override
203         public int compare(AppEntry object1, AppEntry object2) {
204             return sCollator.compare(object1.label, object2.label);
205         }
206     };
207
208     public static final Comparator<AppEntry> SIZE_COMPARATOR
209             = new Comparator<AppEntry>() {
210         private final Collator sCollator = Collator.getInstance();
211         @Override
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);
216         }
217     };
218
219     public static final Comparator<AppEntry> INTERNAL_SIZE_COMPARATOR
220             = new Comparator<AppEntry>() {
221         private final Collator sCollator = Collator.getInstance();
222         @Override
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);
227         }
228     };
229
230     public static final Comparator<AppEntry> EXTERNAL_SIZE_COMPARATOR
231             = new Comparator<AppEntry>() {
232         private final Collator sCollator = Collator.getInstance();
233         @Override
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);
238         }
239     };
240
241     public static final AppFilter FILTER_PERSONAL = new AppFilter() {
242         private int mCurrentUser;
243
244         public void init() {
245             mCurrentUser = ActivityManager.getCurrentUser();
246         }
247
248         @Override
249         public boolean filterApp(AppEntry entry) {
250             return UserHandle.getUserId(entry.info.uid) == mCurrentUser;
251         }
252     };
253
254     public static final AppFilter FILTER_WORK = new AppFilter() {
255         private int mCurrentUser;
256
257         public void init() {
258             mCurrentUser = ActivityManager.getCurrentUser();
259         }
260
261         @Override
262         public boolean filterApp(AppEntry entry) {
263             return UserHandle.getUserId(entry.info.uid) != mCurrentUser;
264         }
265     };
266
267     public static final AppFilter FILTER_DOWNLOADED_AND_LAUNCHER = new AppFilter() {
268         public void init() {
269         }
270
271         @Override
272         public boolean filterApp(AppEntry entry) {
273             if ((entry.info.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) {
274                 return true;
275             } else if ((entry.info.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
276                 return true;
277             } else if (entry.hasLauncherEntry) {
278                 return true;
279             }
280             return false;
281         }
282     };
283
284     public static final AppFilter FILTER_THIRD_PARTY = new AppFilter() {
285         public void init() {
286         }
287
288         @Override
289         public boolean filterApp(AppEntry entry) {
290             if ((entry.info.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) {
291                 return true;
292             } else if ((entry.info.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
293                 return true;
294             }
295             return false;
296         }
297     };
298
299     public static final AppFilter FILTER_DISABLED = new AppFilter() {
300         public void init() {
301         }
302
303         @Override
304         public boolean filterApp(AppEntry entry) {
305             return !entry.info.enabled;
306         }
307     };
308
309     public static final AppFilter FILTER_ALL_ENABLED = new AppFilter() {
310         public void init() {
311         }
312
313         @Override
314         public boolean filterApp(AppEntry entry) {
315             return entry.info.enabled;
316         }
317     };
318
319     public static final AppFilter FILTER_EVERYTHING = new AppFilter() {
320         public void init() {
321         }
322
323         @Override
324         public boolean filterApp(AppEntry entry) {
325             return true;
326         }
327     };
328
329     public static final AppFilter FILTER_WITH_DOMAIN_URLS = new AppFilter() {
330         public void init() {
331         }
332
333         @Override
334         public boolean filterApp(AppEntry entry) {
335             return (entry.info.privateFlags & ApplicationInfo.PRIVATE_FLAG_HAS_DOMAIN_URLS) != 0;
336         }
337     };
338
339     public static class VolumeFilter implements AppFilter {
340         private final String mVolumeUuid;
341
342         public VolumeFilter(String volumeUuid) {
343             mVolumeUuid = volumeUuid;
344         }
345
346         @Override
347         public void init() {
348         }
349
350         @Override
351         public boolean filterApp(AppEntry info) {
352             return Objects.equals(info.info.volumeUuid, mVolumeUuid);
353         }
354     }
355
356     public static class CompoundFilter implements AppFilter {
357         private final AppFilter mFirstFilter;
358         private final AppFilter mSecondFilter;
359
360         public CompoundFilter(AppFilter first, AppFilter second) {
361             mFirstFilter = first;
362             mSecondFilter = second;
363         }
364
365         @Override
366         public void init() {
367             mFirstFilter.init();
368             mSecondFilter.init();
369         }
370
371         @Override
372         public boolean filterApp(AppEntry info) {
373             return mFirstFilter.filterApp(info) && mSecondFilter.filterApp(info);
374         }
375     }
376
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;
384
385     boolean mResumed;
386     boolean mHaveDisabledApps;
387
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>();
398     long mCurId = 1;
399     String mCurComputingSizePkg;
400     int mCurComputingSizeUserId;
401     boolean mSessionsChanged;
402
403     // Temporary for dispatching session callbacks.  Only touched by main thread.
404     final ArrayList<Session> mActiveSessions = new ArrayList<Session>();
405
406     /**
407      * Receives notifications when applications are added/removed.
408      */
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);
426          }
427          void unregisterReceiver() {
428              mContext.unregisterReceiver(this);
429          }
430          @Override
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));
438                  }
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));
444                  }
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));
450                  }
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) {
461                      // Ignore
462                      return;
463                  }
464                  boolean avail = Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(actionStr);
465                  if (avail) {
466                      for (String pkgName : pkgList) {
467                          for (int i = 0; i < mEntriesMap.size(); i++) {
468                              invalidatePackage(pkgName, mEntriesMap.keyAt(i));
469                          }
470                      }
471                  }
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));
476              }
477          }
478     }
479
480     void rebuildActiveSessions() {
481         synchronized (mEntriesMap) {
482             if (!mSessionsChanged) {
483                 return;
484             }
485             mActiveSessions.clear();
486             for (int i=0; i<mSessions.size(); i++) {
487                 Session s = mSessions.get(i);
488                 if (s.mResumed) {
489                     mActiveSessions.add(s);
490                 }
491             }
492         }
493     }
494
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;
504
505         @Override
506         public void handleMessage(Message msg) {
507             rebuildActiveSessions();
508             switch (msg.what) {
509                 case MSG_REBUILD_COMPLETE: {
510                     Session s = (Session)msg.obj;
511                     if (mActiveSessions.contains(s)) {
512                         s.mCallbacks.onRebuildComplete(s.mLastAppList);
513                     }
514                 } break;
515                 case MSG_PACKAGE_LIST_CHANGED: {
516                     for (int i=0; i<mActiveSessions.size(); i++) {
517                         mActiveSessions.get(i).mCallbacks.onPackageListChanged();
518                     }
519                 } break;
520                 case MSG_PACKAGE_ICON_CHANGED: {
521                     for (int i=0; i<mActiveSessions.size(); i++) {
522                         mActiveSessions.get(i).mCallbacks.onPackageIconChanged();
523                     }
524                 } break;
525                 case MSG_PACKAGE_SIZE_CHANGED: {
526                     for (int i=0; i<mActiveSessions.size(); i++) {
527                         mActiveSessions.get(i).mCallbacks.onPackageSizeChanged(
528                                 (String)msg.obj);
529                     }
530                 } break;
531                 case MSG_ALL_SIZES_COMPUTED: {
532                     for (int i=0; i<mActiveSessions.size(); i++) {
533                         mActiveSessions.get(i).mCallbacks.onAllSizesComputed();
534                     }
535                 } break;
536                 case MSG_RUNNING_STATE_CHANGED: {
537                     for (int i=0; i<mActiveSessions.size(); i++) {
538                         mActiveSessions.get(i).mCallbacks.onRunningStateChanged(
539                                 msg.arg1 != 0);
540                     }
541                 } break;
542                 case MSG_LAUNCHER_INFO_CHANGED: {
543                     for (int i=0; i<mActiveSessions.size(); i++) {
544                         mActiveSessions.get(i).mCallbacks.onLauncherInfoChanged();
545                     }
546                 } break;
547                 case MSG_LOAD_ENTRIES_COMPLETE: {
548                     for (int i=0; i<mActiveSessions.size(); i++) {
549                         mActiveSessions.get(i).mCallbacks.onLoadEntriesCompleted();
550                     }
551                 } break;
552             }
553         }
554     }
555
556     final MainHandler mMainHandler = new MainHandler();
557
558     // --------------------------------------------------------------
559
560     static final Object sLock = new Object();
561     static ApplicationsState sInstance;
562
563     static ApplicationsState getInstance(Application app) {
564         synchronized (sLock) {
565             if (sInstance == null) {
566                 sInstance = new ApplicationsState(app);
567             }
568             return sInstance;
569         }
570     }
571
572     private ApplicationsState(Application app) {
573         mContext = 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>());
579         }
580         mThread = new HandlerThread("ApplicationsState.Loader",
581                 Process.THREAD_PRIORITY_BACKGROUND);
582         mThread.start();
583         mBackgroundHandler = new BackgroundHandler(mThread.getLooper());
584
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;
591
592         /**
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).
600          *
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
604          * lock immediately.
605          */
606         synchronized (mEntriesMap) {
607             try {
608                 mEntriesMap.wait(1);
609             } catch (InterruptedException e) {
610             }
611         }
612     }
613
614     Looper getBackgroundLooper() {
615         return mThread.getLooper();
616     }
617
618     public class Session {
619         final Callbacks mCallbacks;
620         boolean mResumed;
621
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;
630
631         Session(Callbacks callbacks) {
632             mCallbacks = callbacks;
633         }
634
635         public void resume() {
636             if (DEBUG_LOCKING) Log.v(TAG, "resume about to acquire lock...");
637             synchronized (mEntriesMap) {
638                 if (!mResumed) {
639                     mResumed = true;
640                     mSessionsChanged = true;
641                     doResumeIfNeededLocked();
642                 }
643             }
644             if (DEBUG_LOCKING) Log.v(TAG, "...resume releasing lock");
645         }
646
647         public void pause() {
648             if (DEBUG_LOCKING) Log.v(TAG, "pause about to acquire lock...");
649             synchronized (mEntriesMap) {
650                 if (mResumed) {
651                     mResumed = false;
652                     mSessionsChanged = true;
653                     mBackgroundHandler.removeMessages(BackgroundHandler.MSG_REBUILD_LIST, this);
654                     doPauseIfNeededLocked();
655                 }
656                 if (DEBUG_LOCKING) Log.v(TAG, "...pause releasing lock");
657             }
658         }
659
660         ArrayList<AppEntry> getAllApps() {
661             synchronized (mEntriesMap) {
662                 return new ArrayList<>(mAppEntries);
663             }
664         }
665
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);
680                     }
681                 }
682
683                 // We will wait for .25s for the list to be built.
684                 long waitend = SystemClock.uptimeMillis()+250;
685
686                 while (mRebuildResult == null) {
687                     long now = SystemClock.uptimeMillis();
688                     if (now >= waitend) {
689                         break;
690                     }
691                     try {
692                         mRebuildSync.wait(waitend - now);
693                     } catch (InterruptedException e) {
694                     }
695                 }
696
697                 mRebuildAsync = true;
698
699                 return mRebuildResult;
700             }
701         }
702
703         void handleRebuildList() {
704             AppFilter filter;
705             Comparator<AppEntry> comparator;
706             synchronized (mRebuildSync) {
707                 if (!mRebuildRequested) {
708                     return;
709                 }
710
711                 filter = mRebuildFilter;
712                 comparator = mRebuildComparator;
713                 mRebuildRequested = false;
714                 mRebuildFilter = null;
715                 mRebuildComparator = null;
716             }
717
718             Process.setThreadPriority(Process.THREAD_PRIORITY_FOREGROUND);
719
720             if (filter != null) {
721                 filter.init();
722             }
723
724             List<AppEntry> apps;
725             synchronized (mEntriesMap) {
726                 apps = new ArrayList<>(mAppEntries);
727             }
728
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");
740                     }
741                 }
742             }
743
744             Collections.sort(filteredApps, comparator);
745
746             synchronized (mRebuildSync) {
747                 if (!mRebuildRequested) {
748                     mLastAppList = filteredApps;
749                     if (!mRebuildAsync) {
750                         mRebuildResult = filteredApps;
751                         mRebuildSync.notifyAll();
752                     } else {
753                         if (!mMainHandler.hasMessages(MainHandler.MSG_REBUILD_COMPLETE, this)) {
754                             Message msg = mMainHandler.obtainMessage(
755                                     MainHandler.MSG_REBUILD_COMPLETE, this);
756                             mMainHandler.sendMessage(msg);
757                         }
758                     }
759                 }
760             }
761
762             Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
763         }
764
765         public void release() {
766             pause();
767             synchronized (mEntriesMap) {
768                 mSessions.remove(this);
769             }
770         }
771     }
772
773     public Session newSession(Callbacks callbacks) {
774         Session s = new Session(callbacks);
775         synchronized (mEntriesMap) {
776             mSessions.add(s);
777         }
778         return s;
779     }
780
781     void doResumeIfNeededLocked() {
782         if (mResumed) {
783             return;
784         }
785         mResumed = true;
786         if (mPackageIntentReceiver == null) {
787             mPackageIntentReceiver = new PackageIntentReceiver();
788             mPackageIntentReceiver.registerReceiver();
789         }
790         mApplications = new ArrayList<ApplicationInfo>();
791         for (UserHandle user : mUm.getUserProfiles()) {
792             try {
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>());
796                 }
797                 ParceledListSlice<ApplicationInfo> list =
798                         mIpm.getInstalledApplications(
799                                 user.isOwner() ? mOwnerRetrieveFlags : mRetrieveFlags,
800                                 user.getIdentifier());
801                 mApplications.addAll(list.getList());
802             } catch (RemoteException e) {
803             }
804         }
805
806         if (mInterestingConfigChanges.applyNewConfig(mContext.getResources())) {
807             // If an interesting part of the configuration has changed, we
808             // should completely reload the app entries.
809             clearEntries();
810         } else {
811             for (int i=0; i<mAppEntries.size(); i++) {
812                 mAppEntries.get(i).sizeStale = true;
813             }
814         }
815
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.
821             if (!info.enabled) {
822                 if (info.enabledSetting != PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) {
823                     mApplications.remove(i);
824                     i--;
825                     continue;
826                 }
827                 mHaveDisabledApps = true;
828             }
829             int userId = UserHandle.getUserId(info.uid);
830             final AppEntry entry = mEntriesMap.get(userId).get(info.packageName);
831             if (entry != null) {
832                 entry.info = info;
833             }
834         }
835         if (mAppEntries.size() > mApplications.size()) {
836             // There are less apps now, some must have been uninstalled.
837             clearEntries();
838         }
839         mCurComputingSizePkg = null;
840         if (!mBackgroundHandler.hasMessages(BackgroundHandler.MSG_LOAD_ENTRIES)) {
841             mBackgroundHandler.sendEmptyMessage(BackgroundHandler.MSG_LOAD_ENTRIES);
842         }
843     }
844
845     private void clearEntries() {
846         for (int i = 0; i < mEntriesMap.size(); i++) {
847             mEntriesMap.valueAt(i).clear();
848         }
849         mAppEntries.clear();
850     }
851
852     public boolean haveDisabledApps() {
853         return mHaveDisabledApps;
854     }
855
856     void doPauseIfNeededLocked() {
857         if (!mResumed) {
858             return;
859         }
860         for (int i=0; i<mSessions.size(); i++) {
861             if (mSessions.get(i).mResumed) {
862                 return;
863             }
864         }
865         doPauseLocked();
866     }
867
868     void doPauseLocked() {
869         mResumed = false;
870         if (mPackageIntentReceiver != null) {
871             mPackageIntentReceiver.unregisterReceiver();
872             mPackageIntentReceiver = null;
873         }
874     }
875
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);
880             if (entry == null) {
881                 ApplicationInfo info = getAppInfoLocked(packageName, userId);
882                 if (info == null) {
883                     try {
884                         info = mIpm.getApplicationInfo(packageName, 0, userId);
885                     } catch (RemoteException e) {
886                         Log.w(TAG, "getEntry couldn't reach PackageManager", e);
887                         return null;
888                     }
889                 }
890                 entry = getEntryLocked(info);
891             }
892             if (DEBUG_LOCKING) Log.v(TAG, "...getEntry releasing lock");
893             return entry;
894         }
895     }
896
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)) {
902                 return info;
903             }
904         }
905         return null;
906     }
907
908     void ensureIcon(AppEntry entry) {
909         if (entry.icon != null) {
910             return;
911         }
912         synchronized (entry) {
913             entry.ensureIconLocked(mContext, mPm);
914         }
915     }
916
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);
921             if (entry != null) {
922                 mPm.getPackageSizeInfo(packageName, userId, mBackgroundHandler.mStatsObserver);
923             }
924             if (DEBUG_LOCKING) Log.v(TAG, "...requestSize releasing lock");
925         }
926     }
927
928     long sumCacheSizes() {
929         long sum = 0;
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;
935             }
936             if (DEBUG_LOCKING) Log.v(TAG, "...sumCacheSizes releasing lock");
937         }
938         return sum;
939     }
940
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) {
946                 return i;
947             }
948         }
949         return -1;
950     }
951
952     void addPackage(String pkgName, int userId) {
953         try {
954             synchronized (mEntriesMap) {
955                 if (DEBUG_LOCKING) Log.v(TAG, "addPackage acquired lock");
956                 if (DEBUG) Log.i(TAG, "Adding package " + pkgName);
957                 if (!mResumed) {
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
960                     // here.
961                     if (DEBUG_LOCKING) Log.v(TAG, "addPackage release lock: not resumed");
962                     return;
963                 }
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");
967                     return;
968                 }
969                 ApplicationInfo info = mIpm.getApplicationInfo(pkgName,
970                         userId == UserHandle.USER_OWNER ? mOwnerRetrieveFlags : mRetrieveFlags,
971                                 userId);
972                 if (info == null) {
973                     return;
974                 }
975                 if (!info.enabled) {
976                     if (info.enabledSetting
977                             != PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) {
978                         return;
979                     }
980                     mHaveDisabledApps = true;
981                 }
982                 mApplications.add(info);
983                 if (!mBackgroundHandler.hasMessages(BackgroundHandler.MSG_LOAD_ENTRIES)) {
984                     mBackgroundHandler.sendEmptyMessage(BackgroundHandler.MSG_LOAD_ENTRIES);
985                 }
986                 if (!mMainHandler.hasMessages(MainHandler.MSG_PACKAGE_LIST_CHANGED)) {
987                     mMainHandler.sendEmptyMessage(MainHandler.MSG_PACKAGE_LIST_CHANGED);
988                 }
989                 if (DEBUG_LOCKING) Log.v(TAG, "addPackage releasing lock");
990             }
991         } catch (RemoteException e) {
992         }
993     }
994
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);
1000             if (idx >= 0) {
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);
1006                 }
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;
1014                             break;
1015                         }
1016                     }
1017                 }
1018                 if (!mMainHandler.hasMessages(MainHandler.MSG_PACKAGE_LIST_CHANGED)) {
1019                     mMainHandler.sendEmptyMessage(MainHandler.MSG_PACKAGE_LIST_CHANGED);
1020                 }
1021             }
1022             if (DEBUG_LOCKING) Log.v(TAG, "removePackage releasing lock");
1023         }
1024     }
1025
1026     void invalidatePackage(String pkgName, int userId) {
1027         removePackage(pkgName, userId);
1028         addPackage(pkgName, userId);
1029     }
1030
1031     void addUser(int userId) {
1032         if (mUm.getUserProfiles().contains(new UserHandle(userId))) {
1033             synchronized (mEntriesMap) {
1034                 mEntriesMap.put(userId, new HashMap<String, AppEntry>());
1035                 if (mResumed) {
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.
1039                     doPauseLocked();
1040                     doResumeIfNeededLocked();
1041                 }
1042                 if (!mMainHandler.hasMessages(MainHandler.MSG_PACKAGE_LIST_CHANGED)) {
1043                     mMainHandler.sendEmptyMessage(MainHandler.MSG_PACKAGE_LIST_CHANGED);
1044                 }
1045             }
1046         }
1047     }
1048
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);
1056                 }
1057                 mEntriesMap.remove(userId);
1058                 if (!mMainHandler.hasMessages(MainHandler.MSG_PACKAGE_LIST_CHANGED)) {
1059                     mMainHandler.sendEmptyMessage(MainHandler.MSG_PACKAGE_LIST_CHANGED);
1060                 }
1061             }
1062         }
1063     }
1064
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) {
1075             entry.info = info;
1076         }
1077         return entry;
1078     }
1079
1080     // --------------------------------------------------------------
1081
1082     private long getTotalInternalSize(PackageStats ps) {
1083         if (ps != null) {
1084             return ps.codeSize + ps.dataSize;
1085         }
1086         return SIZE_INVALID;
1087     }
1088
1089     private long getTotalExternalSize(PackageStats ps) {
1090         if (ps != null) {
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;
1096         }
1097         return SIZE_INVALID;
1098     }
1099
1100     private String getSizeStr(long size) {
1101         if (size >= 0) {
1102             return Formatter.formatFileSize(mContext, size);
1103         }
1104         return null;
1105     }
1106
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;
1115
1116         boolean mRunning;
1117
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.
1126                         return;
1127                     }
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);
1160                                 sizeChanged = true;
1161                             }
1162                         }
1163                         if (sizeChanged) {
1164                             Message msg = mMainHandler.obtainMessage(
1165                                     MainHandler.MSG_PACKAGE_SIZE_CHANGED, stats.packageName);
1166                             mMainHandler.sendMessage(msg);
1167                         }
1168                     }
1169                     if (mCurComputingSizePkg != null
1170                             && (mCurComputingSizePkg.equals(stats.packageName)
1171                                     && mCurComputingSizeUserId == stats.userHandle)) {
1172                         mCurComputingSizePkg = null;
1173                         sendEmptyMessage(MSG_LOAD_SIZES);
1174                     }
1175                     if (DEBUG_LOCKING) Log.v(TAG, "onGetStatsCompleted releasing lock");
1176                 }
1177             }
1178         };
1179
1180         BackgroundHandler(Looper looper) {
1181             super(looper);
1182         }
1183
1184         @Override
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();
1192                 }
1193             }
1194             if (rebuildingSessions != null) {
1195                 for (int i=0; i<rebuildingSessions.size(); i++) {
1196                     rebuildingSessions.get(i).handleRebuildList();
1197                 }
1198             }
1199
1200             switch (msg.what) {
1201                 case MSG_REBUILD_LIST: {
1202                 } break;
1203                 case MSG_LOAD_ENTRIES: {
1204                     int numDone = 0;
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++) {
1208                             if (!mRunning) {
1209                                 mRunning = true;
1210                                 Message m = mMainHandler.obtainMessage(
1211                                         MainHandler.MSG_RUNNING_STATE_CHANGED, 1);
1212                                 mMainHandler.sendMessage(m);
1213                             }
1214                             ApplicationInfo info = mApplications.get(i);
1215                             int userId = UserHandle.getUserId(info.uid);
1216                             if (mEntriesMap.get(userId).get(info.packageName) == null) {
1217                                 numDone++;
1218                                 getEntryLocked(info);
1219                             }
1220                         }
1221                         if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_ENTRIES releasing lock");
1222                     }
1223
1224                     if (numDone >= 6) {
1225                         sendEmptyMessage(MSG_LOAD_ENTRIES);
1226                     } else {
1227                         if (!mMainHandler.hasMessages(MainHandler.MSG_LOAD_ENTRIES_COMPLETE)) {
1228                             mMainHandler.sendEmptyMessage(MainHandler.MSG_LOAD_ENTRIES_COMPLETE);
1229                         }
1230                         sendEmptyMessage(MSG_LOAD_LAUNCHER);
1231                     }
1232                 } break;
1233                 case MSG_LOAD_LAUNCHER: {
1234                     Intent launchIntent = new Intent(Intent.ACTION_MAIN, null)
1235                             .addCategory(Intent.CATEGORY_LAUNCHER);
1236
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;
1250                                 } else {
1251                                     Log.w(TAG, "Cannot find pkg: " + packageName
1252                                             + " on user " + userId);
1253                                 }
1254                             }
1255                             if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_LAUNCHER releasing lock");
1256                         }
1257                     }
1258
1259                     if (!mMainHandler.hasMessages(MainHandler.MSG_LAUNCHER_INFO_CHANGED)) {
1260                         mMainHandler.sendEmptyMessage(MainHandler.MSG_LAUNCHER_INFO_CHANGED);
1261                     }
1262                     sendEmptyMessage(MSG_LOAD_ICONS);
1263                 } break;
1264                 case MSG_LOAD_ICONS: {
1265                     int numDone = 0;
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)) {
1273                                         if (!mRunning) {
1274                                             mRunning = true;
1275                                             Message m = mMainHandler.obtainMessage(
1276                                                     MainHandler.MSG_RUNNING_STATE_CHANGED, 1);
1277                                             mMainHandler.sendMessage(m);
1278                                         }
1279                                         numDone++;
1280                                     }
1281                                 }
1282                             }
1283                         }
1284                         if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_ICONS releasing lock");
1285                     }
1286                     if (numDone > 0) {
1287                         if (!mMainHandler.hasMessages(MainHandler.MSG_PACKAGE_ICON_CHANGED)) {
1288                             mMainHandler.sendEmptyMessage(MainHandler.MSG_PACKAGE_ICON_CHANGED);
1289                         }
1290                     }
1291                     if (numDone >= 2) {
1292                         sendEmptyMessage(MSG_LOAD_ICONS);
1293                     } else {
1294                         sendEmptyMessage(MSG_LOAD_SIZES);
1295                     }
1296                 } break;
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");
1302                             return;
1303                         }
1304
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))) {
1311                                     if (!mRunning) {
1312                                         mRunning = true;
1313                                         Message m = mMainHandler.obtainMessage(
1314                                                 MainHandler.MSG_RUNNING_STATE_CHANGED, 1);
1315                                         mMainHandler.sendMessage(m);
1316                                     }
1317                                     entry.sizeLoadStart = now;
1318                                     mCurComputingSizePkg = entry.info.packageName;
1319                                     mCurComputingSizeUserId = UserHandle.getUserId(entry.info.uid);
1320                                     mPm.getPackageSizeInfo(mCurComputingSizePkg,
1321                                             mCurComputingSizeUserId, mStatsObserver);
1322                                 }
1323                                 if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_SIZES releasing: now computing");
1324                                 return;
1325                             }
1326                         }
1327                         if (!mMainHandler.hasMessages(MainHandler.MSG_ALL_SIZES_COMPUTED)) {
1328                             mMainHandler.sendEmptyMessage(MainHandler.MSG_ALL_SIZES_COMPUTED);
1329                             mRunning = false;
1330                             Message m = mMainHandler.obtainMessage(
1331                                     MainHandler.MSG_RUNNING_STATE_CHANGED, 0);
1332                             mMainHandler.sendMessage(m);
1333                         }
1334                         if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_SIZES releasing lock");
1335                     }
1336                 } break;
1337             }
1338         }
1339
1340     }
1341 }