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