Home | History | Annotate | Download | only in applications
      1 package com.android.settings.applications;
      2 
      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.ApplicationInfo;
      9 import android.content.pm.IPackageStatsObserver;
     10 import android.content.pm.PackageManager;
     11 import android.content.pm.PackageStats;
     12 import android.content.pm.PackageManager.NameNotFoundException;
     13 import android.graphics.drawable.Drawable;
     14 import android.net.Uri;
     15 import android.os.Handler;
     16 import android.os.HandlerThread;
     17 import android.os.Looper;
     18 import android.os.Message;
     19 import android.os.Process;
     20 import android.os.SystemClock;
     21 import android.text.format.Formatter;
     22 import android.util.Log;
     23 
     24 import java.io.File;
     25 import java.text.Collator;
     26 import java.text.Normalizer;
     27 import java.text.Normalizer.Form;
     28 import java.util.ArrayList;
     29 import java.util.Collections;
     30 import java.util.Comparator;
     31 import java.util.HashMap;
     32 import java.util.List;
     33 import java.util.regex.Pattern;
     34 
     35 /**
     36  * Keeps track of information about all installed applications, lazy-loading
     37  * as needed.
     38  */
     39 public class ApplicationsState {
     40     static final String TAG = "ApplicationsState";
     41     static final boolean DEBUG = false;
     42     static final boolean DEBUG_LOCKING = false;
     43 
     44     public static interface Callbacks {
     45         public void onRunningStateChanged(boolean running);
     46         public void onPackageListChanged();
     47         public void onRebuildComplete(ArrayList<AppEntry> apps);
     48         public void onPackageIconChanged();
     49         public void onPackageSizeChanged(String packageName);
     50         public void onAllSizesComputed();
     51     }
     52 
     53     public static interface AppFilter {
     54         public void init();
     55         public boolean filterApp(ApplicationInfo info);
     56     }
     57 
     58     static final int SIZE_UNKNOWN = -1;
     59     static final int SIZE_INVALID = -2;
     60 
     61     static final Pattern REMOVE_DIACRITICALS_PATTERN
     62             = Pattern.compile("\\p{InCombiningDiacriticalMarks}+");
     63 
     64     public static String normalize(String str) {
     65         String tmp = Normalizer.normalize(str, Form.NFD);
     66         return REMOVE_DIACRITICALS_PATTERN.matcher(tmp)
     67                 .replaceAll("").toLowerCase();
     68     }
     69 
     70     public static class SizeInfo {
     71         long cacheSize;
     72         long codeSize;
     73         long dataSize;
     74         long externalCodeSize;
     75         long externalDataSize;
     76 
     77         // This is the part of externalDataSize that is in the cache
     78         // section of external storage.  Note that we don't just combine
     79         // this with cacheSize because currently the platform can't
     80         // automatically trim this data when needed, so it is something
     81         // the user may need to manage.  The externalDataSize also includes
     82         // this value, since what this is here is really the part of
     83         // externalDataSize that we can just consider to be "cache" files
     84         // for purposes of cleaning them up in the app details UI.
     85         long externalCacheSize;
     86     }
     87 
     88     public static class AppEntry extends SizeInfo {
     89         final File apkFile;
     90         final long id;
     91         String label;
     92         long size;
     93         long internalSize;
     94         long externalSize;
     95 
     96         boolean mounted;
     97 
     98         String getNormalizedLabel() {
     99             if (normalizedLabel != null) {
    100                 return normalizedLabel;
    101             }
    102             normalizedLabel = normalize(label);
    103             return normalizedLabel;
    104         }
    105 
    106         // Need to synchronize on 'this' for the following.
    107         ApplicationInfo info;
    108         Drawable icon;
    109         String sizeStr;
    110         String internalSizeStr;
    111         String externalSizeStr;
    112         boolean sizeStale;
    113         long sizeLoadStart;
    114 
    115         String normalizedLabel;
    116 
    117         AppEntry(Context context, ApplicationInfo info, long id) {
    118             apkFile = new File(info.sourceDir);
    119             this.id = id;
    120             this.info = info;
    121             this.size = SIZE_UNKNOWN;
    122             this.sizeStale = true;
    123             ensureLabel(context);
    124         }
    125 
    126         void ensureLabel(Context context) {
    127             if (this.label == null || !this.mounted) {
    128                 if (!this.apkFile.exists()) {
    129                     this.mounted = false;
    130                     this.label = info.packageName;
    131                 } else {
    132                     this.mounted = true;
    133                     CharSequence label = info.loadLabel(context.getPackageManager());
    134                     this.label = label != null ? label.toString() : info.packageName;
    135                 }
    136             }
    137         }
    138 
    139         boolean ensureIconLocked(Context context, PackageManager pm) {
    140             if (this.icon == null) {
    141                 if (this.apkFile.exists()) {
    142                     this.icon = this.info.loadIcon(pm);
    143                     return true;
    144                 } else {
    145                     this.mounted = false;
    146                     this.icon = context.getResources().getDrawable(
    147                             com.android.internal.R.drawable.sym_app_on_sd_unavailable_icon);
    148                 }
    149             } else if (!this.mounted) {
    150                 // If the app wasn't mounted but is now mounted, reload
    151                 // its icon.
    152                 if (this.apkFile.exists()) {
    153                     this.mounted = true;
    154                     this.icon = this.info.loadIcon(pm);
    155                     return true;
    156                 }
    157             }
    158             return false;
    159         }
    160     }
    161 
    162     public static final Comparator<AppEntry> ALPHA_COMPARATOR = new Comparator<AppEntry>() {
    163         private final Collator sCollator = Collator.getInstance();
    164         @Override
    165         public int compare(AppEntry object1, AppEntry object2) {
    166             if (object1.info.enabled != object2.info.enabled) {
    167                 return object1.info.enabled ? -1 : 1;
    168             }
    169             return sCollator.compare(object1.label, object2.label);
    170         }
    171     };
    172 
    173     public static final Comparator<AppEntry> SIZE_COMPARATOR
    174             = new Comparator<AppEntry>() {
    175         private final Collator sCollator = Collator.getInstance();
    176         @Override
    177         public int compare(AppEntry object1, AppEntry object2) {
    178             if (object1.size < object2.size) return 1;
    179             if (object1.size > object2.size) return -1;
    180             return sCollator.compare(object1.label, object2.label);
    181         }
    182     };
    183 
    184     public static final Comparator<AppEntry> INTERNAL_SIZE_COMPARATOR
    185             = new Comparator<AppEntry>() {
    186         private final Collator sCollator = Collator.getInstance();
    187         @Override
    188         public int compare(AppEntry object1, AppEntry object2) {
    189             if (object1.internalSize < object2.internalSize) return 1;
    190             if (object1.internalSize > object2.internalSize) return -1;
    191             return sCollator.compare(object1.label, object2.label);
    192         }
    193     };
    194 
    195     public static final Comparator<AppEntry> EXTERNAL_SIZE_COMPARATOR
    196             = new Comparator<AppEntry>() {
    197         private final Collator sCollator = Collator.getInstance();
    198         @Override
    199         public int compare(AppEntry object1, AppEntry object2) {
    200             if (object1.externalSize < object2.externalSize) return 1;
    201             if (object1.externalSize > object2.externalSize) return -1;
    202             return sCollator.compare(object1.label, object2.label);
    203         }
    204     };
    205 
    206     public static final AppFilter THIRD_PARTY_FILTER = new AppFilter() {
    207         public void init() {
    208         }
    209 
    210         @Override
    211         public boolean filterApp(ApplicationInfo info) {
    212             if ((info.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) {
    213                 return true;
    214             } else if ((info.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
    215                 return true;
    216             }
    217             return false;
    218         }
    219     };
    220 
    221     public static final AppFilter ON_SD_CARD_FILTER = new AppFilter() {
    222         final CanBeOnSdCardChecker mCanBeOnSdCardChecker
    223                 = new CanBeOnSdCardChecker();
    224 
    225         public void init() {
    226             mCanBeOnSdCardChecker.init();
    227         }
    228 
    229         @Override
    230         public boolean filterApp(ApplicationInfo info) {
    231             return mCanBeOnSdCardChecker.check(info);
    232         }
    233     };
    234 
    235     final Context mContext;
    236     final PackageManager mPm;
    237     PackageIntentReceiver mPackageIntentReceiver;
    238 
    239     boolean mResumed;
    240 
    241     // Information about all applications.  Synchronize on mEntriesMap
    242     // to protect access to these.
    243     final ArrayList<Session> mSessions = new ArrayList<Session>();
    244     final ArrayList<Session> mRebuildingSessions = new ArrayList<Session>();
    245     final InterestingConfigChanges mInterestingConfigChanges = new InterestingConfigChanges();
    246     final HashMap<String, AppEntry> mEntriesMap = new HashMap<String, AppEntry>();
    247     final ArrayList<AppEntry> mAppEntries = new ArrayList<AppEntry>();
    248     List<ApplicationInfo> mApplications = new ArrayList<ApplicationInfo>();
    249     long mCurId = 1;
    250     String mCurComputingSizePkg;
    251     boolean mSessionsChanged;
    252 
    253     // Temporary for dispatching session callbacks.  Only touched by main thread.
    254     final ArrayList<Session> mActiveSessions = new ArrayList<Session>();
    255 
    256     /**
    257      * Receives notifications when applications are added/removed.
    258      */
    259     private class PackageIntentReceiver extends BroadcastReceiver {
    260          void registerReceiver() {
    261              IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
    262              filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
    263              filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
    264              filter.addDataScheme("package");
    265              mContext.registerReceiver(this, filter);
    266              // Register for events related to sdcard installation.
    267              IntentFilter sdFilter = new IntentFilter();
    268              sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
    269              sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
    270              mContext.registerReceiver(this, sdFilter);
    271          }
    272          void unregisterReceiver() {
    273              mContext.unregisterReceiver(this);
    274          }
    275          @Override
    276          public void onReceive(Context context, Intent intent) {
    277              String actionStr = intent.getAction();
    278              if (Intent.ACTION_PACKAGE_ADDED.equals(actionStr)) {
    279                  Uri data = intent.getData();
    280                  String pkgName = data.getEncodedSchemeSpecificPart();
    281                  addPackage(pkgName);
    282              } else if (Intent.ACTION_PACKAGE_REMOVED.equals(actionStr)) {
    283                  Uri data = intent.getData();
    284                  String pkgName = data.getEncodedSchemeSpecificPart();
    285                  removePackage(pkgName);
    286              } else if (Intent.ACTION_PACKAGE_CHANGED.equals(actionStr)) {
    287                  Uri data = intent.getData();
    288                  String pkgName = data.getEncodedSchemeSpecificPart();
    289                  invalidatePackage(pkgName);
    290              } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(actionStr) ||
    291                      Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(actionStr)) {
    292                  // When applications become available or unavailable (perhaps because
    293                  // the SD card was inserted or ejected) we need to refresh the
    294                  // AppInfo with new label, icon and size information as appropriate
    295                  // given the newfound (un)availability of the application.
    296                  // A simple way to do that is to treat the refresh as a package
    297                  // removal followed by a package addition.
    298                  String pkgList[] = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
    299                  if (pkgList == null || pkgList.length == 0) {
    300                      // Ignore
    301                      return;
    302                  }
    303                  boolean avail = Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(actionStr);
    304                  if (avail) {
    305                      for (String pkgName : pkgList) {
    306                          invalidatePackage(pkgName);
    307                      }
    308                  }
    309              }
    310          }
    311     }
    312 
    313     void rebuildActiveSessions() {
    314         synchronized (mEntriesMap) {
    315             if (!mSessionsChanged) {
    316                 return;
    317             }
    318             mActiveSessions.clear();
    319             for (int i=0; i<mSessions.size(); i++) {
    320                 Session s = mSessions.get(i);
    321                 if (s.mResumed) {
    322                     mActiveSessions.add(s);
    323                 }
    324             }
    325         }
    326     }
    327 
    328     class MainHandler extends Handler {
    329         static final int MSG_REBUILD_COMPLETE = 1;
    330         static final int MSG_PACKAGE_LIST_CHANGED = 2;
    331         static final int MSG_PACKAGE_ICON_CHANGED = 3;
    332         static final int MSG_PACKAGE_SIZE_CHANGED = 4;
    333         static final int MSG_ALL_SIZES_COMPUTED = 5;
    334         static final int MSG_RUNNING_STATE_CHANGED = 6;
    335 
    336         @Override
    337         public void handleMessage(Message msg) {
    338             rebuildActiveSessions();
    339             switch (msg.what) {
    340                 case MSG_REBUILD_COMPLETE: {
    341                     Session s = (Session)msg.obj;
    342                     if (mActiveSessions.contains(s)) {
    343                         s.mCallbacks.onRebuildComplete(s.mLastAppList);
    344                     }
    345                 } break;
    346                 case MSG_PACKAGE_LIST_CHANGED: {
    347                     for (int i=0; i<mActiveSessions.size(); i++) {
    348                         mActiveSessions.get(i).mCallbacks.onPackageListChanged();
    349                     }
    350                 } break;
    351                 case MSG_PACKAGE_ICON_CHANGED: {
    352                     for (int i=0; i<mActiveSessions.size(); i++) {
    353                         mActiveSessions.get(i).mCallbacks.onPackageIconChanged();
    354                     }
    355                 } break;
    356                 case MSG_PACKAGE_SIZE_CHANGED: {
    357                     for (int i=0; i<mActiveSessions.size(); i++) {
    358                         mActiveSessions.get(i).mCallbacks.onPackageSizeChanged(
    359                                 (String)msg.obj);
    360                     }
    361                 } break;
    362                 case MSG_ALL_SIZES_COMPUTED: {
    363                     for (int i=0; i<mActiveSessions.size(); i++) {
    364                         mActiveSessions.get(i).mCallbacks.onAllSizesComputed();
    365                     }
    366                 } break;
    367                 case MSG_RUNNING_STATE_CHANGED: {
    368                     for (int i=0; i<mActiveSessions.size(); i++) {
    369                         mActiveSessions.get(i).mCallbacks.onRunningStateChanged(
    370                                 msg.arg1 != 0);
    371                     }
    372                 } break;
    373             }
    374         }
    375     }
    376 
    377     final MainHandler mMainHandler = new MainHandler();
    378 
    379     // --------------------------------------------------------------
    380 
    381     static final Object sLock = new Object();
    382     static ApplicationsState sInstance;
    383 
    384     static ApplicationsState getInstance(Application app) {
    385         synchronized (sLock) {
    386             if (sInstance == null) {
    387                 sInstance = new ApplicationsState(app);
    388             }
    389             return sInstance;
    390         }
    391     }
    392 
    393     private ApplicationsState(Application app) {
    394         mContext = app;
    395         mPm = mContext.getPackageManager();
    396         mThread = new HandlerThread("ApplicationsState.Loader",
    397                 Process.THREAD_PRIORITY_BACKGROUND);
    398         mThread.start();
    399         mBackgroundHandler = new BackgroundHandler(mThread.getLooper());
    400 
    401         /**
    402          * This is a trick to prevent the foreground thread from being delayed.
    403          * The problem is that Dalvik monitors are initially spin locks, to keep
    404          * them lightweight.  This leads to unfair contention -- Even though the
    405          * background thread only holds the lock for a short amount of time, if
    406          * it keeps running and locking again it can prevent the main thread from
    407          * acquiring its lock for a long time...  sometimes even > 5 seconds
    408          * (leading to an ANR).
    409          *
    410          * Dalvik will promote a monitor to a "real" lock if it detects enough
    411          * contention on it.  It doesn't figure this out fast enough for us
    412          * here, though, so this little trick will force it to turn into a real
    413          * lock immediately.
    414          */
    415         synchronized (mEntriesMap) {
    416             try {
    417                 mEntriesMap.wait(1);
    418             } catch (InterruptedException e) {
    419             }
    420         }
    421     }
    422 
    423     public class Session {
    424         final Callbacks mCallbacks;
    425         boolean mResumed;
    426 
    427         // Rebuilding of app list.  Synchronized on mRebuildSync.
    428         final Object mRebuildSync = new Object();
    429         boolean mRebuildRequested;
    430         boolean mRebuildAsync;
    431         AppFilter mRebuildFilter;
    432         Comparator<AppEntry> mRebuildComparator;
    433         ArrayList<AppEntry> mRebuildResult;
    434         ArrayList<AppEntry> mLastAppList;
    435 
    436         Session(Callbacks callbacks) {
    437             mCallbacks = callbacks;
    438         }
    439 
    440         public void resume() {
    441             if (DEBUG_LOCKING) Log.v(TAG, "resume about to acquire lock...");
    442             synchronized (mEntriesMap) {
    443                 if (!mResumed) {
    444                     mResumed = true;
    445                     mSessionsChanged = true;
    446                     doResumeIfNeededLocked();
    447                 }
    448             }
    449             if (DEBUG_LOCKING) Log.v(TAG, "...resume releasing lock");
    450         }
    451 
    452         public void pause() {
    453             if (DEBUG_LOCKING) Log.v(TAG, "pause about to acquire lock...");
    454             synchronized (mEntriesMap) {
    455                 if (mResumed) {
    456                     mResumed = false;
    457                     mSessionsChanged = true;
    458                     mBackgroundHandler.removeMessages(BackgroundHandler.MSG_REBUILD_LIST, this);
    459                     doPauseIfNeededLocked();
    460                 }
    461                 if (DEBUG_LOCKING) Log.v(TAG, "...pause releasing lock");
    462             }
    463         }
    464 
    465         // Creates a new list of app entries with the given filter and comparator.
    466         ArrayList<AppEntry> rebuild(AppFilter filter, Comparator<AppEntry> comparator) {
    467             synchronized (mRebuildSync) {
    468                 synchronized (mEntriesMap) {
    469                     mRebuildingSessions.add(this);
    470                     mRebuildRequested = true;
    471                     mRebuildAsync = false;
    472                     mRebuildFilter = filter;
    473                     mRebuildComparator = comparator;
    474                     mRebuildResult = null;
    475                     if (!mBackgroundHandler.hasMessages(BackgroundHandler.MSG_REBUILD_LIST)) {
    476                         Message msg = mBackgroundHandler.obtainMessage(
    477                                 BackgroundHandler.MSG_REBUILD_LIST);
    478                         mBackgroundHandler.sendMessage(msg);
    479                     }
    480                 }
    481 
    482                 // We will wait for .25s for the list to be built.
    483                 long waitend = SystemClock.uptimeMillis()+250;
    484 
    485                 while (mRebuildResult == null) {
    486                     long now = SystemClock.uptimeMillis();
    487                     if (now >= waitend) {
    488                         break;
    489                     }
    490                     try {
    491                         mRebuildSync.wait(waitend - now);
    492                     } catch (InterruptedException e) {
    493                     }
    494                 }
    495 
    496                 mRebuildAsync = true;
    497 
    498                 return mRebuildResult;
    499             }
    500         }
    501 
    502         void handleRebuildList() {
    503             AppFilter filter;
    504             Comparator<AppEntry> comparator;
    505             synchronized (mRebuildSync) {
    506                 if (!mRebuildRequested) {
    507                     return;
    508                 }
    509 
    510                 filter = mRebuildFilter;
    511                 comparator = mRebuildComparator;
    512                 mRebuildRequested = false;
    513                 mRebuildFilter = null;
    514                 mRebuildComparator = null;
    515             }
    516 
    517             Process.setThreadPriority(Process.THREAD_PRIORITY_FOREGROUND);
    518 
    519             if (filter != null) {
    520                 filter.init();
    521             }
    522 
    523             List<ApplicationInfo> apps;
    524             synchronized (mEntriesMap) {
    525                 apps = new ArrayList<ApplicationInfo>(mApplications);
    526             }
    527 
    528             ArrayList<AppEntry> filteredApps = new ArrayList<AppEntry>();
    529             if (DEBUG) Log.i(TAG, "Rebuilding...");
    530             for (int i=0; i<apps.size(); i++) {
    531                 ApplicationInfo info = apps.get(i);
    532                 if (filter == null || filter.filterApp(info)) {
    533                     synchronized (mEntriesMap) {
    534                         if (DEBUG_LOCKING) Log.v(TAG, "rebuild acquired lock");
    535                         AppEntry entry = getEntryLocked(info);
    536                         entry.ensureLabel(mContext);
    537                         if (DEBUG) Log.i(TAG, "Using " + info.packageName + ": " + entry);
    538                         filteredApps.add(entry);
    539                         if (DEBUG_LOCKING) Log.v(TAG, "rebuild releasing lock");
    540                     }
    541                 }
    542             }
    543 
    544             Collections.sort(filteredApps, comparator);
    545 
    546             synchronized (mRebuildSync) {
    547                 if (!mRebuildRequested) {
    548                     mLastAppList = filteredApps;
    549                     if (!mRebuildAsync) {
    550                         mRebuildResult = filteredApps;
    551                         mRebuildSync.notifyAll();
    552                     } else {
    553                         if (!mMainHandler.hasMessages(MainHandler.MSG_REBUILD_COMPLETE, this)) {
    554                             Message msg = mMainHandler.obtainMessage(
    555                                     MainHandler.MSG_REBUILD_COMPLETE, this);
    556                             mMainHandler.sendMessage(msg);
    557                         }
    558                     }
    559                 }
    560             }
    561 
    562             Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
    563         }
    564 
    565         public void release() {
    566             pause();
    567             synchronized (mEntriesMap) {
    568                 mSessions.remove(this);
    569             }
    570         }
    571     }
    572 
    573     public Session newSession(Callbacks callbacks) {
    574         Session s = new Session(callbacks);
    575         synchronized (mEntriesMap) {
    576             mSessions.add(s);
    577         }
    578         return s;
    579     }
    580 
    581     void doResumeIfNeededLocked() {
    582         if (mResumed) {
    583             return;
    584         }
    585         mResumed = true;
    586         if (mPackageIntentReceiver == null) {
    587             mPackageIntentReceiver = new PackageIntentReceiver();
    588             mPackageIntentReceiver.registerReceiver();
    589         }
    590         mApplications = mPm.getInstalledApplications(
    591                 PackageManager.GET_UNINSTALLED_PACKAGES |
    592                 PackageManager.GET_DISABLED_COMPONENTS);
    593         if (mApplications == null) {
    594             mApplications = new ArrayList<ApplicationInfo>();
    595         }
    596 
    597         if (mInterestingConfigChanges.applyNewConfig(mContext.getResources())) {
    598             // If an interesting part of the configuration has changed, we
    599             // should completely reload the app entries.
    600             mEntriesMap.clear();
    601             mAppEntries.clear();
    602         } else {
    603             for (int i=0; i<mAppEntries.size(); i++) {
    604                 mAppEntries.get(i).sizeStale = true;
    605             }
    606         }
    607 
    608         for (int i=0; i<mApplications.size(); i++) {
    609             final ApplicationInfo info = mApplications.get(i);
    610             // Need to trim out any applications that are disabled by
    611             // something different than the user.
    612             if (!info.enabled && info.enabledSetting
    613                     != PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) {
    614                 mApplications.remove(i);
    615                 i--;
    616                 continue;
    617             }
    618             final AppEntry entry = mEntriesMap.get(info.packageName);
    619             if (entry != null) {
    620                 entry.info = info;
    621             }
    622         }
    623         mCurComputingSizePkg = null;
    624         if (!mBackgroundHandler.hasMessages(BackgroundHandler.MSG_LOAD_ENTRIES)) {
    625             mBackgroundHandler.sendEmptyMessage(BackgroundHandler.MSG_LOAD_ENTRIES);
    626         }
    627     }
    628 
    629     void doPauseIfNeededLocked() {
    630         if (!mResumed) {
    631             return;
    632         }
    633         for (int i=0; i<mSessions.size(); i++) {
    634             if (mSessions.get(i).mResumed) {
    635                 return;
    636             }
    637         }
    638         mResumed = false;
    639         if (mPackageIntentReceiver != null) {
    640             mPackageIntentReceiver.unregisterReceiver();
    641             mPackageIntentReceiver = null;
    642         }
    643     }
    644 
    645     AppEntry getEntry(String packageName) {
    646         if (DEBUG_LOCKING) Log.v(TAG, "getEntry about to acquire lock...");
    647         synchronized (mEntriesMap) {
    648             AppEntry entry = mEntriesMap.get(packageName);
    649             if (entry == null) {
    650                 for (int i=0; i<mApplications.size(); i++) {
    651                     ApplicationInfo info = mApplications.get(i);
    652                     if (packageName.equals(info.packageName)) {
    653                         entry = getEntryLocked(info);
    654                         break;
    655                     }
    656                 }
    657             }
    658             if (DEBUG_LOCKING) Log.v(TAG, "...getEntry releasing lock");
    659             return entry;
    660         }
    661     }
    662 
    663     void ensureIcon(AppEntry entry) {
    664         if (entry.icon != null) {
    665             return;
    666         }
    667         synchronized (entry) {
    668             entry.ensureIconLocked(mContext, mPm);
    669         }
    670     }
    671 
    672     void requestSize(String packageName) {
    673         if (DEBUG_LOCKING) Log.v(TAG, "requestSize about to acquire lock...");
    674         synchronized (mEntriesMap) {
    675             AppEntry entry = mEntriesMap.get(packageName);
    676             if (entry != null) {
    677                 mPm.getPackageSizeInfo(packageName, mBackgroundHandler.mStatsObserver);
    678             }
    679             if (DEBUG_LOCKING) Log.v(TAG, "...requestSize releasing lock");
    680         }
    681     }
    682 
    683     long sumCacheSizes() {
    684         long sum = 0;
    685         if (DEBUG_LOCKING) Log.v(TAG, "sumCacheSizes about to acquire lock...");
    686         synchronized (mEntriesMap) {
    687             if (DEBUG_LOCKING) Log.v(TAG, "-> sumCacheSizes now has lock");
    688             for (int i=mAppEntries.size()-1; i>=0; i--) {
    689                 sum += mAppEntries.get(i).cacheSize;
    690             }
    691             if (DEBUG_LOCKING) Log.v(TAG, "...sumCacheSizes releasing lock");
    692         }
    693         return sum;
    694     }
    695 
    696     int indexOfApplicationInfoLocked(String pkgName) {
    697         for (int i=mApplications.size()-1; i>=0; i--) {
    698             if (mApplications.get(i).packageName.equals(pkgName)) {
    699                 return i;
    700             }
    701         }
    702         return -1;
    703     }
    704 
    705     void addPackage(String pkgName) {
    706         try {
    707             synchronized (mEntriesMap) {
    708                 if (DEBUG_LOCKING) Log.v(TAG, "addPackage acquired lock");
    709                 if (DEBUG) Log.i(TAG, "Adding package " + pkgName);
    710                 if (!mResumed) {
    711                     // If we are not resumed, we will do a full query the
    712                     // next time we resume, so there is no reason to do work
    713                     // here.
    714                     if (DEBUG_LOCKING) Log.v(TAG, "addPackage release lock: not resumed");
    715                     return;
    716                 }
    717                 if (indexOfApplicationInfoLocked(pkgName) >= 0) {
    718                     if (DEBUG) Log.i(TAG, "Package already exists!");
    719                     if (DEBUG_LOCKING) Log.v(TAG, "addPackage release lock: already exists");
    720                     return;
    721                 }
    722                 ApplicationInfo info = mPm.getApplicationInfo(pkgName,
    723                         PackageManager.GET_UNINSTALLED_PACKAGES |
    724                         PackageManager.GET_DISABLED_COMPONENTS);
    725                 mApplications.add(info);
    726                 if (!mBackgroundHandler.hasMessages(BackgroundHandler.MSG_LOAD_ENTRIES)) {
    727                     mBackgroundHandler.sendEmptyMessage(BackgroundHandler.MSG_LOAD_ENTRIES);
    728                 }
    729                 if (!mMainHandler.hasMessages(MainHandler.MSG_PACKAGE_LIST_CHANGED)) {
    730                     mMainHandler.sendEmptyMessage(MainHandler.MSG_PACKAGE_LIST_CHANGED);
    731                 }
    732                 if (DEBUG_LOCKING) Log.v(TAG, "addPackage releasing lock");
    733             }
    734         } catch (NameNotFoundException e) {
    735         }
    736     }
    737 
    738     void removePackage(String pkgName) {
    739         synchronized (mEntriesMap) {
    740             if (DEBUG_LOCKING) Log.v(TAG, "removePackage acquired lock");
    741             int idx = indexOfApplicationInfoLocked(pkgName);
    742             if (DEBUG) Log.i(TAG, "removePackage: " + pkgName + " @ " + idx);
    743             if (idx >= 0) {
    744                 AppEntry entry = mEntriesMap.get(pkgName);
    745                 if (DEBUG) Log.i(TAG, "removePackage: " + entry);
    746                 if (entry != null) {
    747                     mEntriesMap.remove(pkgName);
    748                     mAppEntries.remove(entry);
    749                 }
    750                 mApplications.remove(idx);
    751                 if (!mMainHandler.hasMessages(MainHandler.MSG_PACKAGE_LIST_CHANGED)) {
    752                     mMainHandler.sendEmptyMessage(MainHandler.MSG_PACKAGE_LIST_CHANGED);
    753                 }
    754             }
    755             if (DEBUG_LOCKING) Log.v(TAG, "removePackage releasing lock");
    756         }
    757     }
    758 
    759     void invalidatePackage(String pkgName) {
    760         removePackage(pkgName);
    761         addPackage(pkgName);
    762     }
    763 
    764     AppEntry getEntryLocked(ApplicationInfo info) {
    765         AppEntry entry = mEntriesMap.get(info.packageName);
    766         if (DEBUG) Log.i(TAG, "Looking up entry of pkg " + info.packageName + ": " + entry);
    767         if (entry == null) {
    768             if (DEBUG) Log.i(TAG, "Creating AppEntry for " + info.packageName);
    769             entry = new AppEntry(mContext, info, mCurId++);
    770             mEntriesMap.put(info.packageName, entry);
    771             mAppEntries.add(entry);
    772         } else if (entry.info != info) {
    773             entry.info = info;
    774         }
    775         return entry;
    776     }
    777 
    778     // --------------------------------------------------------------
    779 
    780     private long getTotalInternalSize(PackageStats ps) {
    781         if (ps != null) {
    782             return ps.codeSize + ps.dataSize;
    783         }
    784         return SIZE_INVALID;
    785     }
    786 
    787     private long getTotalExternalSize(PackageStats ps) {
    788         if (ps != null) {
    789             return ps.externalCodeSize + ps.externalDataSize
    790                     + ps.externalMediaSize + ps.externalObbSize;
    791         }
    792         return SIZE_INVALID;
    793     }
    794 
    795     private String getSizeStr(long size) {
    796         if (size >= 0) {
    797             return Formatter.formatFileSize(mContext, size);
    798         }
    799         return null;
    800     }
    801 
    802     final HandlerThread mThread;
    803     final BackgroundHandler mBackgroundHandler;
    804     class BackgroundHandler extends Handler {
    805         static final int MSG_REBUILD_LIST = 1;
    806         static final int MSG_LOAD_ENTRIES = 2;
    807         static final int MSG_LOAD_ICONS = 3;
    808         static final int MSG_LOAD_SIZES = 4;
    809 
    810         boolean mRunning;
    811 
    812         final IPackageStatsObserver.Stub mStatsObserver = new IPackageStatsObserver.Stub() {
    813             public void onGetStatsCompleted(PackageStats stats, boolean succeeded) {
    814                 boolean sizeChanged = false;
    815                 synchronized (mEntriesMap) {
    816                     if (DEBUG_LOCKING) Log.v(TAG, "onGetStatsCompleted acquired lock");
    817                     AppEntry entry = mEntriesMap.get(stats.packageName);
    818                     if (entry != null) {
    819                         synchronized (entry) {
    820                             entry.sizeStale = false;
    821                             entry.sizeLoadStart = 0;
    822                             long externalCodeSize = stats.externalCodeSize
    823                                     + stats.externalObbSize;
    824                             long externalDataSize = stats.externalDataSize
    825                                     + stats.externalMediaSize + stats.externalCacheSize;
    826                             long newSize = externalCodeSize + externalDataSize
    827                                     + getTotalInternalSize(stats);
    828                             if (entry.size != newSize ||
    829                                     entry.cacheSize != stats.cacheSize ||
    830                                     entry.codeSize != stats.codeSize ||
    831                                     entry.dataSize != stats.dataSize ||
    832                                     entry.externalCodeSize != externalCodeSize ||
    833                                     entry.externalDataSize != externalDataSize ||
    834                                     entry.externalCacheSize != stats.externalCacheSize) {
    835                                 entry.size = newSize;
    836                                 entry.cacheSize = stats.cacheSize;
    837                                 entry.codeSize = stats.codeSize;
    838                                 entry.dataSize = stats.dataSize;
    839                                 entry.externalCodeSize = externalCodeSize;
    840                                 entry.externalDataSize = externalDataSize;
    841                                 entry.externalCacheSize = stats.externalCacheSize;
    842                                 entry.sizeStr = getSizeStr(entry.size);
    843                                 entry.internalSize = getTotalInternalSize(stats);
    844                                 entry.internalSizeStr = getSizeStr(entry.internalSize);
    845                                 entry.externalSize = getTotalExternalSize(stats);
    846                                 entry.externalSizeStr = getSizeStr(entry.externalSize);
    847                                 if (DEBUG) Log.i(TAG, "Set size of " + entry.label + " " + entry
    848                                         + ": " + entry.sizeStr);
    849                                 sizeChanged = true;
    850                             }
    851                         }
    852                         if (sizeChanged) {
    853                             Message msg = mMainHandler.obtainMessage(
    854                                     MainHandler.MSG_PACKAGE_SIZE_CHANGED, stats.packageName);
    855                             mMainHandler.sendMessage(msg);
    856                         }
    857                     }
    858                     if (mCurComputingSizePkg == null
    859                             || mCurComputingSizePkg.equals(stats.packageName)) {
    860                         mCurComputingSizePkg = null;
    861                         sendEmptyMessage(MSG_LOAD_SIZES);
    862                     }
    863                     if (DEBUG_LOCKING) Log.v(TAG, "onGetStatsCompleted releasing lock");
    864                 }
    865             }
    866         };
    867 
    868         BackgroundHandler(Looper looper) {
    869             super(looper);
    870         }
    871 
    872         @Override
    873         public void handleMessage(Message msg) {
    874             // Always try rebuilding list first thing, if needed.
    875             ArrayList<Session> rebuildingSessions = null;
    876             synchronized (mEntriesMap) {
    877                 if (mRebuildingSessions.size() > 0) {
    878                     rebuildingSessions = new ArrayList<Session>(mRebuildingSessions);
    879                     mRebuildingSessions.clear();
    880                 }
    881             }
    882             if (rebuildingSessions != null) {
    883                 for (int i=0; i<rebuildingSessions.size(); i++) {
    884                     rebuildingSessions.get(i).handleRebuildList();
    885                 }
    886             }
    887 
    888             switch (msg.what) {
    889                 case MSG_REBUILD_LIST: {
    890                 } break;
    891                 case MSG_LOAD_ENTRIES: {
    892                     int numDone = 0;
    893                     synchronized (mEntriesMap) {
    894                         if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_ENTRIES acquired lock");
    895                         for (int i=0; i<mApplications.size() && numDone<6; i++) {
    896                             if (!mRunning) {
    897                                 mRunning = true;
    898                                 Message m = mMainHandler.obtainMessage(
    899                                         MainHandler.MSG_RUNNING_STATE_CHANGED, 1);
    900                                 mMainHandler.sendMessage(m);
    901                             }
    902                             ApplicationInfo info = mApplications.get(i);
    903                             if (mEntriesMap.get(info.packageName) == null) {
    904                                 numDone++;
    905                                 getEntryLocked(info);
    906                             }
    907                         }
    908                         if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_ENTRIES releasing lock");
    909                     }
    910 
    911                     if (numDone >= 6) {
    912                         sendEmptyMessage(MSG_LOAD_ENTRIES);
    913                     } else {
    914                         sendEmptyMessage(MSG_LOAD_ICONS);
    915                     }
    916                 } break;
    917                 case MSG_LOAD_ICONS: {
    918                     int numDone = 0;
    919                     synchronized (mEntriesMap) {
    920                         if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_ICONS acquired lock");
    921                         for (int i=0; i<mAppEntries.size() && numDone<2; i++) {
    922                             AppEntry entry = mAppEntries.get(i);
    923                             if (entry.icon == null || !entry.mounted) {
    924                                 synchronized (entry) {
    925                                     if (entry.ensureIconLocked(mContext, mPm)) {
    926                                         if (!mRunning) {
    927                                             mRunning = true;
    928                                             Message m = mMainHandler.obtainMessage(
    929                                                     MainHandler.MSG_RUNNING_STATE_CHANGED, 1);
    930                                             mMainHandler.sendMessage(m);
    931                                         }
    932                                         numDone++;
    933                                     }
    934                                 }
    935                             }
    936                         }
    937                         if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_ICONS releasing lock");
    938                     }
    939                     if (numDone > 0) {
    940                         if (!mMainHandler.hasMessages(MainHandler.MSG_PACKAGE_ICON_CHANGED)) {
    941                             mMainHandler.sendEmptyMessage(MainHandler.MSG_PACKAGE_ICON_CHANGED);
    942                         }
    943                     }
    944                     if (numDone >= 2) {
    945                         sendEmptyMessage(MSG_LOAD_ICONS);
    946                     } else {
    947                         sendEmptyMessage(MSG_LOAD_SIZES);
    948                     }
    949                 } break;
    950                 case MSG_LOAD_SIZES: {
    951                     synchronized (mEntriesMap) {
    952                         if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_SIZES acquired lock");
    953                         if (mCurComputingSizePkg != null) {
    954                             if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_SIZES releasing: currently computing");
    955                             return;
    956                         }
    957 
    958                         long now = SystemClock.uptimeMillis();
    959                         for (int i=0; i<mAppEntries.size(); i++) {
    960                             AppEntry entry = mAppEntries.get(i);
    961                             if (entry.size == SIZE_UNKNOWN || entry.sizeStale) {
    962                                 if (entry.sizeLoadStart == 0 ||
    963                                         (entry.sizeLoadStart < (now-20*1000))) {
    964                                     if (!mRunning) {
    965                                         mRunning = true;
    966                                         Message m = mMainHandler.obtainMessage(
    967                                                 MainHandler.MSG_RUNNING_STATE_CHANGED, 1);
    968                                         mMainHandler.sendMessage(m);
    969                                     }
    970                                     entry.sizeLoadStart = now;
    971                                     mCurComputingSizePkg = entry.info.packageName;
    972                                     mPm.getPackageSizeInfo(mCurComputingSizePkg, mStatsObserver);
    973                                 }
    974                                 if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_SIZES releasing: now computing");
    975                                 return;
    976                             }
    977                         }
    978                         if (!mMainHandler.hasMessages(MainHandler.MSG_ALL_SIZES_COMPUTED)) {
    979                             mMainHandler.sendEmptyMessage(MainHandler.MSG_ALL_SIZES_COMPUTED);
    980                             mRunning = false;
    981                             Message m = mMainHandler.obtainMessage(
    982                                     MainHandler.MSG_RUNNING_STATE_CHANGED, 0);
    983                             mMainHandler.sendMessage(m);
    984                         }
    985                         if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_SIZES releasing lock");
    986                     }
    987                 } break;
    988             }
    989         }
    990 
    991     }
    992 }
    993