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