1 package com.android.settings.applications;
3 import android.app.Application;
4 import android.content.BroadcastReceiver;
5 import android.content.Context;
6 import android.content.Intent;
7 import android.content.IntentFilter;
8 import android.content.pm.ActivityInfo;
9 import android.content.pm.ApplicationInfo;
10 import android.content.pm.IPackageStatsObserver;
11 import android.content.pm.PackageManager;
12 import android.content.pm.PackageStats;
13 import android.content.pm.PackageManager.NameNotFoundException;
14 import android.content.res.Configuration;
15 import android.content.res.Resources;
16 import android.graphics.drawable.Drawable;
17 import android.net.Uri;
18 import android.os.Handler;
19 import android.os.HandlerThread;
20 import android.os.Looper;
21 import android.os.Message;
22 import android.os.Process;
23 import android.os.SystemClock;
24 import android.text.format.Formatter;
25 import android.util.Log;
28 import java.text.Collator;
29 import java.text.Normalizer;
30 import java.text.Normalizer.Form;
31 import java.util.ArrayList;
32 import java.util.Collections;
33 import java.util.Comparator;
34 import java.util.HashMap;
35 import java.util.List;
36 import java.util.regex.Pattern;
39 * Keeps track of information about all installed applications, lazy-loading
42 public class ApplicationsState {
43 static final String TAG = "ApplicationsState";
44 static final boolean DEBUG = false;
45 static final boolean DEBUG_LOCKING = false;
47 public static interface Callbacks {
48 public void onRunningStateChanged(boolean running);
49 public void onPackageListChanged();
50 public void onRebuildComplete(ArrayList<AppEntry> apps);
51 public void onPackageIconChanged();
52 public void onPackageSizeChanged(String packageName);
53 public void onAllSizesComputed();
56 public static interface AppFilter {
58 public boolean filterApp(ApplicationInfo info);
61 static final int SIZE_UNKNOWN = -1;
62 static final int SIZE_INVALID = -2;
64 static final Pattern REMOVE_DIACRITICALS_PATTERN
65 = Pattern.compile("\\p{InCombiningDiacriticalMarks}+");
67 public static String normalize(String str) {
68 String tmp = Normalizer.normalize(str, Form.NFD);
69 return REMOVE_DIACRITICALS_PATTERN.matcher(tmp)
70 .replaceAll("").toLowerCase();
73 public static class SizeInfo {
80 public static class AppEntry extends SizeInfo {
88 String getNormalizedLabel() {
89 if (normalizedLabel != null) {
90 return normalizedLabel;
92 normalizedLabel = normalize(label);
93 return normalizedLabel;
96 // Need to synchronize on 'this' for the following.
103 String normalizedLabel;
105 AppEntry(Context context, ApplicationInfo info, long id) {
106 apkFile = new File(info.sourceDir);
109 this.size = SIZE_UNKNOWN;
110 this.sizeStale = true;
111 ensureLabel(context);
114 void ensureLabel(Context context) {
115 if (this.label == null || !this.mounted) {
116 if (!this.apkFile.exists()) {
117 this.mounted = false;
118 this.label = info.packageName;
121 CharSequence label = info.loadLabel(context.getPackageManager());
122 this.label = label != null ? label.toString() : info.packageName;
127 boolean ensureIconLocked(Context context, PackageManager pm) {
128 if (this.icon == null) {
129 if (this.apkFile.exists()) {
130 this.icon = this.info.loadIcon(pm);
133 this.mounted = false;
134 this.icon = context.getResources().getDrawable(
135 com.android.internal.R.drawable.sym_app_on_sd_unavailable_icon);
137 } else if (!this.mounted) {
138 // If the app wasn't mounted but is now mounted, reload
140 if (this.apkFile.exists()) {
142 this.icon = this.info.loadIcon(pm);
150 public static final Comparator<AppEntry> ALPHA_COMPARATOR = new Comparator<AppEntry>() {
151 private final Collator sCollator = Collator.getInstance();
153 public int compare(AppEntry object1, AppEntry object2) {
154 return sCollator.compare(object1.label, object2.label);
158 public static final Comparator<AppEntry> SIZE_COMPARATOR = new Comparator<AppEntry>() {
159 private final Collator sCollator = Collator.getInstance();
161 public int compare(AppEntry object1, AppEntry object2) {
162 if (object1.size < object2.size) return 1;
163 if (object1.size > object2.size) return -1;
164 return sCollator.compare(object1.label, object2.label);
168 public static final AppFilter THIRD_PARTY_FILTER = new AppFilter() {
173 public boolean filterApp(ApplicationInfo info) {
174 if ((info.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) {
176 } else if ((info.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
183 public static final AppFilter ON_SD_CARD_FILTER = new AppFilter() {
184 final CanBeOnSdCardChecker mCanBeOnSdCardChecker
185 = new CanBeOnSdCardChecker();
188 mCanBeOnSdCardChecker.init();
192 public boolean filterApp(ApplicationInfo info) {
193 return mCanBeOnSdCardChecker.check(info);
197 final Context mContext;
198 final PackageManager mPm;
199 PackageIntentReceiver mPackageIntentReceiver;
202 Callbacks mCurCallbacks;
204 // Information about all applications. Synchronize on mAppEntries
205 // to protect access to these.
206 final InterestingConfigChanges mInterestingConfigChanges = new InterestingConfigChanges();
207 final HashMap<String, AppEntry> mEntriesMap = new HashMap<String, AppEntry>();
208 final ArrayList<AppEntry> mAppEntries = new ArrayList<AppEntry>();
209 List<ApplicationInfo> mApplications = new ArrayList<ApplicationInfo>();
211 String mCurComputingSizePkg;
213 // Rebuilding of app list. Synchronized on mRebuildSync.
214 final Object mRebuildSync = new Object();
215 boolean mRebuildRequested;
216 boolean mRebuildAsync;
217 AppFilter mRebuildFilter;
218 Comparator<AppEntry> mRebuildComparator;
219 ArrayList<AppEntry> mRebuildResult;
222 * Receives notifications when applications are added/removed.
224 private class PackageIntentReceiver extends BroadcastReceiver {
225 void registerReceiver() {
226 IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
227 filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
228 filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
229 filter.addDataScheme("package");
230 mContext.registerReceiver(this, filter);
231 // Register for events related to sdcard installation.
232 IntentFilter sdFilter = new IntentFilter();
233 sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
234 sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
235 mContext.registerReceiver(this, sdFilter);
238 public void onReceive(Context context, Intent intent) {
239 String actionStr = intent.getAction();
240 if (Intent.ACTION_PACKAGE_ADDED.equals(actionStr)) {
241 Uri data = intent.getData();
242 String pkgName = data.getEncodedSchemeSpecificPart();
244 } else if (Intent.ACTION_PACKAGE_REMOVED.equals(actionStr)) {
245 Uri data = intent.getData();
246 String pkgName = data.getEncodedSchemeSpecificPart();
247 removePackage(pkgName);
248 } else if (Intent.ACTION_PACKAGE_CHANGED.equals(actionStr)) {
249 Uri data = intent.getData();
250 String pkgName = data.getEncodedSchemeSpecificPart();
251 removePackage(pkgName);
253 } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(actionStr) ||
254 Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(actionStr)) {
255 // When applications become available or unavailable (perhaps because
256 // the SD card was inserted or ejected) we need to refresh the
257 // AppInfo with new label, icon and size information as appropriate
258 // given the newfound (un)availability of the application.
259 // A simple way to do that is to treat the refresh as a package
260 // removal followed by a package addition.
261 String pkgList[] = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
262 if (pkgList == null || pkgList.length == 0) {
266 boolean avail = Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(actionStr);
268 for (String pkgName : pkgList) {
269 removePackage(pkgName);
277 class MainHandler extends Handler {
278 static final int MSG_REBUILD_COMPLETE = 1;
279 static final int MSG_PACKAGE_LIST_CHANGED = 2;
280 static final int MSG_PACKAGE_ICON_CHANGED = 3;
281 static final int MSG_PACKAGE_SIZE_CHANGED = 4;
282 static final int MSG_ALL_SIZES_COMPUTED = 5;
283 static final int MSG_RUNNING_STATE_CHANGED = 6;
286 public void handleMessage(Message msg) {
288 case MSG_REBUILD_COMPLETE: {
289 if (mCurCallbacks != null) {
290 mCurCallbacks.onRebuildComplete((ArrayList<AppEntry>)msg.obj);
293 case MSG_PACKAGE_LIST_CHANGED: {
294 if (mCurCallbacks != null) {
295 mCurCallbacks.onPackageListChanged();
298 case MSG_PACKAGE_ICON_CHANGED: {
299 if (mCurCallbacks != null) {
300 mCurCallbacks.onPackageIconChanged();
303 case MSG_PACKAGE_SIZE_CHANGED: {
304 if (mCurCallbacks != null) {
305 mCurCallbacks.onPackageSizeChanged((String)msg.obj);
308 case MSG_ALL_SIZES_COMPUTED: {
309 if (mCurCallbacks != null) {
310 mCurCallbacks.onAllSizesComputed();
313 case MSG_RUNNING_STATE_CHANGED: {
314 if (mCurCallbacks != null) {
315 mCurCallbacks.onRunningStateChanged(msg.arg1 != 0);
322 final MainHandler mMainHandler = new MainHandler();
324 // --------------------------------------------------------------
326 static final Object sLock = new Object();
327 static ApplicationsState sInstance;
329 static ApplicationsState getInstance(Application app) {
330 synchronized (sLock) {
331 if (sInstance == null) {
332 sInstance = new ApplicationsState(app);
338 private ApplicationsState(Application app) {
340 mPm = mContext.getPackageManager();
341 mThread = new HandlerThread("ApplicationsState.Loader",
342 Process.THREAD_PRIORITY_BACKGROUND);
344 mBackgroundHandler = new BackgroundHandler(mThread.getLooper());
347 * This is a trick to prevent the foreground thread from being delayed.
348 * The problem is that Dalvik monitors are initially spin locks, to keep
349 * them lightweight. This leads to unfair contention -- Even though the
350 * background thread only holds the lock for a short amount of time, if
351 * it keeps running and locking again it can prevent the main thread from
352 * acquiring its lock for a long time... sometimes even > 5 seconds
353 * (leading to an ANR).
355 * Dalvik will promote a monitor to a "real" lock if it detects enough
356 * contention on it. It doesn't figure this out fast enough for us
357 * here, though, so this little trick will force it to turn into a real
360 synchronized (mEntriesMap) {
363 } catch (InterruptedException e) {
368 void resume(Callbacks callbacks) {
369 if (DEBUG_LOCKING) Log.v(TAG, "resume about to acquire lock...");
370 synchronized (mEntriesMap) {
371 mCurCallbacks = callbacks;
373 if (mPackageIntentReceiver == null) {
374 mPackageIntentReceiver = new PackageIntentReceiver();
375 mPackageIntentReceiver.registerReceiver();
377 mApplications = mPm.getInstalledApplications(
378 PackageManager.GET_UNINSTALLED_PACKAGES |
379 PackageManager.GET_DISABLED_COMPONENTS);
380 if (mApplications == null) {
381 mApplications = new ArrayList<ApplicationInfo>();
384 if (mInterestingConfigChanges.applyNewConfig(mContext.getResources())) {
385 // If an interesting part of the configuration has changed, we
386 // should completely reload the app entries.
390 for (int i=0; i<mAppEntries.size(); i++) {
391 mAppEntries.get(i).sizeStale = true;
395 for (int i=0; i<mApplications.size(); i++) {
396 final ApplicationInfo info = mApplications.get(i);
397 final AppEntry entry = mEntriesMap.get(info.packageName);
402 mCurComputingSizePkg = null;
403 if (!mBackgroundHandler.hasMessages(BackgroundHandler.MSG_LOAD_ENTRIES)) {
404 mBackgroundHandler.sendEmptyMessage(BackgroundHandler.MSG_LOAD_ENTRIES);
406 if (DEBUG_LOCKING) Log.v(TAG, "...resume releasing lock");
411 if (DEBUG_LOCKING) Log.v(TAG, "pause about to acquire lock...");
412 synchronized (mEntriesMap) {
413 mCurCallbacks = null;
415 if (DEBUG_LOCKING) Log.v(TAG, "...pause releasing lock");
419 // Creates a new list of app entries with the given filter and comparator.
420 ArrayList<AppEntry> rebuild(AppFilter filter, Comparator<AppEntry> comparator) {
421 synchronized (mRebuildSync) {
422 mRebuildRequested = true;
423 mRebuildAsync = false;
424 mRebuildFilter = filter;
425 mRebuildComparator = comparator;
426 mRebuildResult = null;
427 if (!mBackgroundHandler.hasMessages(BackgroundHandler.MSG_REBUILD_LIST)) {
428 mBackgroundHandler.sendEmptyMessage(BackgroundHandler.MSG_REBUILD_LIST);
431 // We will wait for .25s for the list to be built.
432 long waitend = SystemClock.uptimeMillis()+250;
434 while (mRebuildResult == null) {
435 long now = SystemClock.uptimeMillis();
436 if (now >= waitend) {
440 mRebuildSync.wait(waitend - now);
441 } catch (InterruptedException e) {
445 mRebuildAsync = true;
447 return mRebuildResult;
451 void handleRebuildList() {
453 Comparator<AppEntry> comparator;
454 synchronized (mRebuildSync) {
455 if (!mRebuildRequested) {
459 filter = mRebuildFilter;
460 comparator = mRebuildComparator;
461 mRebuildRequested = false;
462 mRebuildFilter = null;
463 mRebuildComparator = null;
466 Process.setThreadPriority(Process.THREAD_PRIORITY_FOREGROUND);
468 if (filter != null) {
472 List<ApplicationInfo> apps;
473 synchronized (mEntriesMap) {
474 apps = new ArrayList<ApplicationInfo>(mApplications);
477 ArrayList<AppEntry> filteredApps = new ArrayList<AppEntry>();
478 if (DEBUG) Log.i(TAG, "Rebuilding...");
479 for (int i=0; i<apps.size(); i++) {
480 ApplicationInfo info = apps.get(i);
481 if (filter == null || filter.filterApp(info)) {
482 synchronized (mEntriesMap) {
483 if (DEBUG_LOCKING) Log.v(TAG, "rebuild acquired lock");
484 AppEntry entry = getEntryLocked(info);
485 entry.ensureLabel(mContext);
486 if (DEBUG) Log.i(TAG, "Using " + info.packageName + ": " + entry);
487 filteredApps.add(entry);
488 if (DEBUG_LOCKING) Log.v(TAG, "rebuild releasing lock");
493 Collections.sort(filteredApps, comparator);
495 synchronized (mRebuildSync) {
496 if (!mRebuildRequested) {
497 if (!mRebuildAsync) {
498 mRebuildResult = filteredApps;
499 mRebuildSync.notifyAll();
501 if (!mMainHandler.hasMessages(MainHandler.MSG_REBUILD_COMPLETE)) {
502 Message msg = mMainHandler.obtainMessage(
503 MainHandler.MSG_REBUILD_COMPLETE, filteredApps);
504 mMainHandler.sendMessage(msg);
510 Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
513 AppEntry getEntry(String packageName) {
514 if (DEBUG_LOCKING) Log.v(TAG, "getEntry about to acquire lock...");
515 synchronized (mEntriesMap) {
516 AppEntry entry = mEntriesMap.get(packageName);
518 for (int i=0; i<mApplications.size(); i++) {
519 ApplicationInfo info = mApplications.get(i);
520 if (packageName.equals(info.packageName)) {
521 entry = getEntryLocked(info);
526 if (DEBUG_LOCKING) Log.v(TAG, "...getEntry releasing lock");
531 void ensureIcon(AppEntry entry) {
532 if (entry.icon != null) {
535 synchronized (entry) {
536 entry.ensureIconLocked(mContext, mPm);
540 void requestSize(String packageName) {
541 if (DEBUG_LOCKING) Log.v(TAG, "requestSize about to acquire lock...");
542 synchronized (mEntriesMap) {
543 AppEntry entry = mEntriesMap.get(packageName);
545 mPm.getPackageSizeInfo(packageName, mBackgroundHandler.mStatsObserver);
547 if (DEBUG_LOCKING) Log.v(TAG, "...requestSize releasing lock");
551 long sumCacheSizes() {
553 if (DEBUG_LOCKING) Log.v(TAG, "sumCacheSizes about to acquire lock...");
554 synchronized (mEntriesMap) {
555 if (DEBUG_LOCKING) Log.v(TAG, "-> sumCacheSizes now has lock");
556 for (int i=mAppEntries.size()-1; i>=0; i--) {
557 sum += mAppEntries.get(i).cacheSize;
559 if (DEBUG_LOCKING) Log.v(TAG, "...sumCacheSizes releasing lock");
564 int indexOfApplicationInfoLocked(String pkgName) {
565 for (int i=mApplications.size()-1; i>=0; i--) {
566 if (mApplications.get(i).packageName.equals(pkgName)) {
573 void addPackage(String pkgName) {
575 synchronized (mEntriesMap) {
576 if (DEBUG_LOCKING) Log.v(TAG, "addPackage acquired lock");
577 if (DEBUG) Log.i(TAG, "Adding package " + pkgName);
579 // If we are not resumed, we will do a full query the
580 // next time we resume, so there is no reason to do work
582 if (DEBUG_LOCKING) Log.v(TAG, "addPackage release lock: not resumed");
585 if (indexOfApplicationInfoLocked(pkgName) >= 0) {
586 if (DEBUG) Log.i(TAG, "Package already exists!");
587 if (DEBUG_LOCKING) Log.v(TAG, "addPackage release lock: already exists");
590 ApplicationInfo info = mPm.getApplicationInfo(pkgName,
591 PackageManager.GET_UNINSTALLED_PACKAGES |
592 PackageManager.GET_DISABLED_COMPONENTS);
593 mApplications.add(info);
594 if (!mBackgroundHandler.hasMessages(BackgroundHandler.MSG_LOAD_ENTRIES)) {
595 mBackgroundHandler.sendEmptyMessage(BackgroundHandler.MSG_LOAD_ENTRIES);
597 if (!mMainHandler.hasMessages(MainHandler.MSG_PACKAGE_LIST_CHANGED)) {
598 mMainHandler.sendEmptyMessage(MainHandler.MSG_PACKAGE_LIST_CHANGED);
600 if (DEBUG_LOCKING) Log.v(TAG, "addPackage releasing lock");
602 } catch (NameNotFoundException e) {
606 void removePackage(String pkgName) {
607 synchronized (mEntriesMap) {
608 if (DEBUG_LOCKING) Log.v(TAG, "removePackage acquired lock");
609 int idx = indexOfApplicationInfoLocked(pkgName);
610 if (DEBUG) Log.i(TAG, "removePackage: " + pkgName + " @ " + idx);
612 AppEntry entry = mEntriesMap.get(pkgName);
613 if (DEBUG) Log.i(TAG, "removePackage: " + entry);
615 mEntriesMap.remove(pkgName);
616 mAppEntries.remove(entry);
618 mApplications.remove(idx);
619 if (!mMainHandler.hasMessages(MainHandler.MSG_PACKAGE_LIST_CHANGED)) {
620 mMainHandler.sendEmptyMessage(MainHandler.MSG_PACKAGE_LIST_CHANGED);
623 if (DEBUG_LOCKING) Log.v(TAG, "removePackage releasing lock");
627 AppEntry getEntryLocked(ApplicationInfo info) {
628 AppEntry entry = mEntriesMap.get(info.packageName);
629 if (DEBUG) Log.i(TAG, "Looking up entry of pkg " + info.packageName + ": " + entry);
631 if (DEBUG) Log.i(TAG, "Creating AppEntry for " + info.packageName);
632 entry = new AppEntry(mContext, info, mCurId++);
633 mEntriesMap.put(info.packageName, entry);
634 mAppEntries.add(entry);
635 } else if (entry.info != info) {
641 // --------------------------------------------------------------
643 private long getTotalInternalSize(PackageStats ps) {
645 return ps.codeSize + ps.dataSize;
650 private long getTotalExternalSize(PackageStats ps) {
652 return ps.externalDataSize + ps.externalMediaSize + ps.externalCacheSize
653 + ps.externalObbSize;
658 private String getSizeStr(long size) {
660 return Formatter.formatFileSize(mContext, size);
665 final HandlerThread mThread;
666 final BackgroundHandler mBackgroundHandler;
667 class BackgroundHandler extends Handler {
668 static final int MSG_REBUILD_LIST = 1;
669 static final int MSG_LOAD_ENTRIES = 2;
670 static final int MSG_LOAD_ICONS = 3;
671 static final int MSG_LOAD_SIZES = 4;
675 final IPackageStatsObserver.Stub mStatsObserver = new IPackageStatsObserver.Stub() {
676 public void onGetStatsCompleted(PackageStats stats, boolean succeeded) {
677 boolean sizeChanged = false;
678 synchronized (mEntriesMap) {
679 if (DEBUG_LOCKING) Log.v(TAG, "onGetStatsCompleted acquired lock");
680 AppEntry entry = mEntriesMap.get(stats.packageName);
682 synchronized (entry) {
683 entry.sizeStale = false;
684 entry.sizeLoadStart = 0;
685 long externalSize = getTotalExternalSize(stats);
686 long newSize = externalSize + getTotalInternalSize(stats);
687 if (entry.size != newSize ||
688 entry.cacheSize != stats.cacheSize ||
689 entry.codeSize != stats.codeSize ||
690 entry.dataSize != stats.dataSize ||
691 entry.externalSize != externalSize) {
692 entry.size = newSize;
693 entry.cacheSize = stats.cacheSize;
694 entry.codeSize = stats.codeSize;
695 entry.dataSize = stats.dataSize;
696 entry.externalSize = externalSize;
697 entry.sizeStr = getSizeStr(entry.size);
698 if (DEBUG) Log.i(TAG, "Set size of " + entry.label + " " + entry
699 + ": " + entry.sizeStr);
704 Message msg = mMainHandler.obtainMessage(
705 MainHandler.MSG_PACKAGE_SIZE_CHANGED, stats.packageName);
706 mMainHandler.sendMessage(msg);
709 if (mCurComputingSizePkg == null
710 || mCurComputingSizePkg.equals(stats.packageName)) {
711 mCurComputingSizePkg = null;
712 sendEmptyMessage(MSG_LOAD_SIZES);
714 if (DEBUG_LOCKING) Log.v(TAG, "onGetStatsCompleted releasing lock");
719 BackgroundHandler(Looper looper) {
724 public void handleMessage(Message msg) {
725 // Always try rebuilding list first thing, if needed.
729 case MSG_REBUILD_LIST: {
731 case MSG_LOAD_ENTRIES: {
733 synchronized (mEntriesMap) {
734 if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_ENTRIES acquired lock");
735 for (int i=0; i<mApplications.size() && numDone<6; i++) {
738 Message m = mMainHandler.obtainMessage(
739 MainHandler.MSG_RUNNING_STATE_CHANGED, 1);
740 mMainHandler.sendMessage(m);
742 ApplicationInfo info = mApplications.get(i);
743 if (mEntriesMap.get(info.packageName) == null) {
745 getEntryLocked(info);
748 if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_ENTRIES releasing lock");
752 sendEmptyMessage(MSG_LOAD_ENTRIES);
754 sendEmptyMessage(MSG_LOAD_ICONS);
757 case MSG_LOAD_ICONS: {
759 synchronized (mEntriesMap) {
760 if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_ICONS acquired lock");
761 for (int i=0; i<mAppEntries.size() && numDone<2; i++) {
762 AppEntry entry = mAppEntries.get(i);
763 if (entry.icon == null || !entry.mounted) {
764 synchronized (entry) {
765 if (entry.ensureIconLocked(mContext, mPm)) {
768 Message m = mMainHandler.obtainMessage(
769 MainHandler.MSG_RUNNING_STATE_CHANGED, 1);
770 mMainHandler.sendMessage(m);
777 if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_ICONS releasing lock");
780 if (!mMainHandler.hasMessages(MainHandler.MSG_PACKAGE_ICON_CHANGED)) {
781 mMainHandler.sendEmptyMessage(MainHandler.MSG_PACKAGE_ICON_CHANGED);
785 sendEmptyMessage(MSG_LOAD_ICONS);
787 sendEmptyMessage(MSG_LOAD_SIZES);
790 case MSG_LOAD_SIZES: {
791 synchronized (mEntriesMap) {
792 if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_SIZES acquired lock");
793 if (mCurComputingSizePkg != null) {
794 if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_SIZES releasing: currently computing");
798 long now = SystemClock.uptimeMillis();
799 for (int i=0; i<mAppEntries.size(); i++) {
800 AppEntry entry = mAppEntries.get(i);
801 if (entry.size == SIZE_UNKNOWN || entry.sizeStale) {
802 if (entry.sizeLoadStart == 0 ||
803 (entry.sizeLoadStart < (now-20*1000))) {
806 Message m = mMainHandler.obtainMessage(
807 MainHandler.MSG_RUNNING_STATE_CHANGED, 1);
808 mMainHandler.sendMessage(m);
810 entry.sizeLoadStart = now;
811 mCurComputingSizePkg = entry.info.packageName;
812 mPm.getPackageSizeInfo(mCurComputingSizePkg, mStatsObserver);
814 if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_SIZES releasing: now computing");
818 if (!mMainHandler.hasMessages(MainHandler.MSG_ALL_SIZES_COMPUTED)) {
819 mMainHandler.sendEmptyMessage(MainHandler.MSG_ALL_SIZES_COMPUTED);
821 Message m = mMainHandler.obtainMessage(
822 MainHandler.MSG_RUNNING_STATE_CHANGED, 0);
823 mMainHandler.sendMessage(m);
825 if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_SIZES releasing lock");