Home | History | Annotate | Download | only in applications
      1 /*
      2  * Copyright (C) 2010 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.settings.applications;
     18 
     19 import com.android.settings.R;
     20 import com.android.settings.users.UserUtils;
     21 
     22 import android.app.ActivityManager;
     23 import android.app.ActivityManagerNative;
     24 import android.app.ActivityThread;
     25 import android.content.ComponentName;
     26 import android.content.Context;
     27 import android.content.pm.ApplicationInfo;
     28 import android.content.pm.PackageInfo;
     29 import android.content.pm.PackageItemInfo;
     30 import android.content.pm.PackageManager;
     31 import android.content.pm.ServiceInfo;
     32 import android.content.pm.UserInfo;
     33 import android.content.res.Resources;
     34 import android.graphics.drawable.Drawable;
     35 import android.os.Handler;
     36 import android.os.HandlerThread;
     37 import android.os.Looper;
     38 import android.os.Message;
     39 import android.os.RemoteException;
     40 import android.os.UserHandle;
     41 import android.os.UserManager;
     42 import android.text.format.Formatter;
     43 import android.util.Log;
     44 import android.util.SparseArray;
     45 
     46 import java.util.ArrayList;
     47 import java.util.Collections;
     48 import java.util.Comparator;
     49 import java.util.HashMap;
     50 import java.util.Iterator;
     51 import java.util.List;
     52 
     53 /**
     54  * Singleton for retrieving and monitoring the state about all running
     55  * applications/processes/services.
     56  */
     57 public class RunningState {
     58     static final String TAG = "RunningState";
     59     static final boolean DEBUG_COMPARE = false;
     60 
     61     static Object sGlobalLock = new Object();
     62     static RunningState sInstance;
     63 
     64     static final int MSG_RESET_CONTENTS = 1;
     65     static final int MSG_UPDATE_CONTENTS = 2;
     66     static final int MSG_REFRESH_UI = 3;
     67     static final int MSG_UPDATE_TIME = 4;
     68 
     69     static final long TIME_UPDATE_DELAY = 1000;
     70     static final long CONTENTS_UPDATE_DELAY = 2000;
     71 
     72     static final int MAX_SERVICES = 100;
     73 
     74     final Context mApplicationContext;
     75     final ActivityManager mAm;
     76     final PackageManager mPm;
     77     final UserManager mUm;
     78     final int mMyUserId;
     79 
     80     OnRefreshUiListener mRefreshUiListener;
     81 
     82     final InterestingConfigChanges mInterestingConfigChanges = new InterestingConfigChanges();
     83 
     84     // Processes that are hosting a service we are interested in, organized
     85     // by uid and name.  Note that this mapping does not change even across
     86     // service restarts, and during a restart there will still be a process
     87     // entry.
     88     final SparseArray<HashMap<String, ProcessItem>> mServiceProcessesByName
     89             = new SparseArray<HashMap<String, ProcessItem>>();
     90 
     91     // Processes that are hosting a service we are interested in, organized
     92     // by their pid.  These disappear and re-appear as services are restarted.
     93     final SparseArray<ProcessItem> mServiceProcessesByPid
     94             = new SparseArray<ProcessItem>();
     95 
     96     // Used to sort the interesting processes.
     97     final ServiceProcessComparator mServiceProcessComparator
     98             = new ServiceProcessComparator();
     99 
    100     // Additional interesting processes to be shown to the user, even if
    101     // there is no service running in them.
    102     final ArrayList<ProcessItem> mInterestingProcesses = new ArrayList<ProcessItem>();
    103 
    104     // All currently running processes, for finding dependencies etc.
    105     final SparseArray<ProcessItem> mRunningProcesses
    106             = new SparseArray<ProcessItem>();
    107 
    108     // The processes associated with services, in sorted order.
    109     final ArrayList<ProcessItem> mProcessItems = new ArrayList<ProcessItem>();
    110 
    111     // All processes, used for retrieving memory information.
    112     final ArrayList<ProcessItem> mAllProcessItems = new ArrayList<ProcessItem>();
    113 
    114     // If there are other users on the device, these are the merged items
    115     // representing all items that would be put in mMergedItems for that user.
    116     final SparseArray<MergedItem> mOtherUserMergedItems = new SparseArray<MergedItem>();
    117 
    118     // If there are other users on the device, these are the merged items
    119     // representing all items that would be put in mUserBackgroundItems for that user.
    120     final SparseArray<MergedItem> mOtherUserBackgroundItems = new SparseArray<MergedItem>();
    121 
    122     // Tracking of information about users.
    123     final SparseArray<UserState> mUsers = new SparseArray<UserState>();
    124 
    125     static class AppProcessInfo {
    126         final ActivityManager.RunningAppProcessInfo info;
    127         boolean hasServices;
    128         boolean hasForegroundServices;
    129 
    130         AppProcessInfo(ActivityManager.RunningAppProcessInfo _info) {
    131             info = _info;
    132         }
    133     }
    134 
    135     // Temporary structure used when updating above information.
    136     final SparseArray<AppProcessInfo> mTmpAppProcesses = new SparseArray<AppProcessInfo>();
    137 
    138     int mSequence = 0;
    139 
    140     final Comparator<RunningState.MergedItem> mBackgroundComparator
    141         = new Comparator<RunningState.MergedItem>() {
    142             @Override
    143             public int compare(MergedItem lhs, MergedItem rhs) {
    144                 if (DEBUG_COMPARE) {
    145                     Log.i(TAG, "Comparing " + lhs + " with " + rhs);
    146                     Log.i(TAG, "     Proc " + lhs.mProcess + " with " + rhs.mProcess);
    147                     Log.i(TAG, "   UserId " + lhs.mUserId + " with " + rhs.mUserId);
    148                 }
    149                 if (lhs.mUserId != rhs.mUserId) {
    150                     if (lhs.mUserId == mMyUserId) return -1;
    151                     if (rhs.mUserId == mMyUserId) return 1;
    152                     return lhs.mUserId < rhs.mUserId ? -1 : 1;
    153                 }
    154                 if (lhs.mProcess == rhs.mProcess) {
    155                     if (lhs.mLabel == rhs.mLabel) {
    156                         return 0;
    157                     }
    158                     return lhs.mLabel != null ? lhs.mLabel.compareTo(rhs.mLabel) : -1;
    159                 }
    160                 if (lhs.mProcess == null) return -1;
    161                 if (rhs.mProcess == null) return 1;
    162                 if (DEBUG_COMPARE) Log.i(TAG, "    Label " + lhs.mProcess.mLabel
    163                         + " with " + rhs.mProcess.mLabel);
    164                 final ActivityManager.RunningAppProcessInfo lhsInfo
    165                         = lhs.mProcess.mRunningProcessInfo;
    166                 final ActivityManager.RunningAppProcessInfo rhsInfo
    167                         = rhs.mProcess.mRunningProcessInfo;
    168                 final boolean lhsBg = lhsInfo.importance
    169                         >= ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND;
    170                 final boolean rhsBg = rhsInfo.importance
    171                         >= ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND;
    172                         if (DEBUG_COMPARE) Log.i(TAG, "       Bg " + lhsBg + " with " + rhsBg);
    173                 if (lhsBg != rhsBg) {
    174                     return lhsBg ? 1 : -1;
    175                 }
    176                 final boolean lhsA = (lhsInfo.flags
    177                         & ActivityManager.RunningAppProcessInfo.FLAG_HAS_ACTIVITIES) != 0;
    178                 final boolean rhsA = (rhsInfo.flags
    179                         & ActivityManager.RunningAppProcessInfo.FLAG_HAS_ACTIVITIES) != 0;
    180                 if (DEBUG_COMPARE) Log.i(TAG, "      Act " + lhsA + " with " + rhsA);
    181                 if (lhsA != rhsA) {
    182                     return lhsA ? -1 : 1;
    183                 }
    184                 if (DEBUG_COMPARE) Log.i(TAG, "      Lru " + lhsInfo.lru + " with " + rhsInfo.lru);
    185                 if (lhsInfo.lru != rhsInfo.lru) {
    186                     return lhsInfo.lru < rhsInfo.lru ? -1 : 1;
    187                 }
    188                 if (lhs.mProcess.mLabel == rhs.mProcess.mLabel) {
    189                     return 0;
    190                 }
    191                 if (lhs.mProcess.mLabel == null) return 1;
    192                 if (rhs.mProcess.mLabel == null) return -1;
    193                 return lhs.mProcess.mLabel.compareTo(rhs.mProcess.mLabel);
    194             }
    195     };
    196 
    197     // ----- following protected by mLock -----
    198 
    199     // Lock for protecting the state that will be shared between the
    200     // background update thread and the UI thread.
    201     final Object mLock = new Object();
    202 
    203     boolean mResumed;
    204     boolean mHaveData;
    205     boolean mWatchingBackgroundItems;
    206 
    207     ArrayList<BaseItem> mItems = new ArrayList<BaseItem>();
    208     ArrayList<MergedItem> mMergedItems = new ArrayList<MergedItem>();
    209     ArrayList<MergedItem> mBackgroundItems = new ArrayList<MergedItem>();
    210     ArrayList<MergedItem> mUserBackgroundItems = new ArrayList<MergedItem>();
    211 
    212     int mNumBackgroundProcesses;
    213     long mBackgroundProcessMemory;
    214     int mNumForegroundProcesses;
    215     long mForegroundProcessMemory;
    216     int mNumServiceProcesses;
    217     long mServiceProcessMemory;
    218 
    219     // ----- BACKGROUND MONITORING THREAD -----
    220 
    221     final HandlerThread mBackgroundThread;
    222     final class BackgroundHandler extends Handler {
    223         public BackgroundHandler(Looper looper) {
    224             super(looper);
    225         }
    226 
    227         @Override
    228         public void handleMessage(Message msg) {
    229             switch (msg.what) {
    230                 case MSG_RESET_CONTENTS:
    231                     reset();
    232                     break;
    233                 case MSG_UPDATE_CONTENTS:
    234                     synchronized (mLock) {
    235                         if (!mResumed) {
    236                             return;
    237                         }
    238                     }
    239                     Message cmd = mHandler.obtainMessage(MSG_REFRESH_UI);
    240                     cmd.arg1 = update(mApplicationContext, mAm) ? 1 : 0;
    241                     mHandler.sendMessage(cmd);
    242                     removeMessages(MSG_UPDATE_CONTENTS);
    243                     msg = obtainMessage(MSG_UPDATE_CONTENTS);
    244                     sendMessageDelayed(msg, CONTENTS_UPDATE_DELAY);
    245                     break;
    246             }
    247         }
    248     };
    249 
    250     final BackgroundHandler mBackgroundHandler;
    251 
    252     final Handler mHandler = new Handler() {
    253         int mNextUpdate = OnRefreshUiListener.REFRESH_TIME;
    254 
    255         @Override
    256         public void handleMessage(Message msg) {
    257             switch (msg.what) {
    258                 case MSG_REFRESH_UI:
    259                     mNextUpdate = msg.arg1 != 0
    260                             ? OnRefreshUiListener.REFRESH_STRUCTURE
    261                             : OnRefreshUiListener.REFRESH_DATA;
    262                     break;
    263                 case MSG_UPDATE_TIME:
    264                     synchronized (mLock) {
    265                         if (!mResumed) {
    266                             return;
    267                         }
    268                     }
    269                     removeMessages(MSG_UPDATE_TIME);
    270                     Message m = obtainMessage(MSG_UPDATE_TIME);
    271                     sendMessageDelayed(m, TIME_UPDATE_DELAY);
    272 
    273                     if (mRefreshUiListener != null) {
    274                         //Log.i("foo", "Refresh UI: " + mNextUpdate
    275                         //        + " @ " + SystemClock.uptimeMillis());
    276                         mRefreshUiListener.onRefreshUi(mNextUpdate);
    277                         mNextUpdate = OnRefreshUiListener.REFRESH_TIME;
    278                     }
    279                     break;
    280             }
    281         }
    282     };
    283 
    284     // ----- DATA STRUCTURES -----
    285 
    286     static interface OnRefreshUiListener {
    287         public static final int REFRESH_TIME = 0;
    288         public static final int REFRESH_DATA = 1;
    289         public static final int REFRESH_STRUCTURE = 2;
    290 
    291         public void onRefreshUi(int what);
    292     }
    293 
    294     static class UserState {
    295         UserInfo mInfo;
    296         String mLabel;
    297         Drawable mIcon;
    298     }
    299 
    300     static class BaseItem {
    301         final boolean mIsProcess;
    302         final int mUserId;
    303 
    304         PackageItemInfo mPackageInfo;
    305         CharSequence mDisplayLabel;
    306         String mLabel;
    307         String mDescription;
    308 
    309         int mCurSeq;
    310 
    311         long mActiveSince;
    312         long mSize;
    313         String mSizeStr;
    314         String mCurSizeStr;
    315         boolean mNeedDivider;
    316         boolean mBackground;
    317 
    318         public BaseItem(boolean isProcess, int userId) {
    319             mIsProcess = isProcess;
    320             mUserId = userId;
    321         }
    322 
    323         public Drawable loadIcon(Context context, RunningState state) {
    324             if (mPackageInfo != null) {
    325                 return mPackageInfo.loadIcon(state.mPm);
    326             }
    327             return null;
    328         }
    329     }
    330 
    331     static class ServiceItem extends BaseItem {
    332         ActivityManager.RunningServiceInfo mRunningService;
    333         ServiceInfo mServiceInfo;
    334         boolean mShownAsStarted;
    335 
    336         MergedItem mMergedItem;
    337 
    338         public ServiceItem(int userId) {
    339             super(false, userId);
    340         }
    341     }
    342 
    343     static class ProcessItem extends BaseItem {
    344         final HashMap<ComponentName, ServiceItem> mServices
    345                 = new HashMap<ComponentName, ServiceItem>();
    346         final SparseArray<ProcessItem> mDependentProcesses
    347                 = new SparseArray<ProcessItem>();
    348 
    349         final int mUid;
    350         final String mProcessName;
    351         int mPid;
    352 
    353         ProcessItem mClient;
    354         int mLastNumDependentProcesses;
    355 
    356         int mRunningSeq;
    357         ActivityManager.RunningAppProcessInfo mRunningProcessInfo;
    358 
    359         MergedItem mMergedItem;
    360 
    361         boolean mInteresting;
    362 
    363         // Purely for sorting.
    364         boolean mIsSystem;
    365         boolean mIsStarted;
    366         long mActiveSince;
    367 
    368         public ProcessItem(Context context, int uid, String processName) {
    369             super(true, UserHandle.getUserId(uid));
    370             mDescription = context.getResources().getString(
    371                     R.string.service_process_name, processName);
    372             mUid = uid;
    373             mProcessName = processName;
    374         }
    375 
    376         void ensureLabel(PackageManager pm) {
    377             if (mLabel != null) {
    378                 return;
    379             }
    380 
    381             try {
    382                 ApplicationInfo ai = pm.getApplicationInfo(mProcessName,
    383                         PackageManager.GET_UNINSTALLED_PACKAGES);
    384                 if (ai.uid == mUid) {
    385                     mDisplayLabel = ai.loadLabel(pm);
    386                     mLabel = mDisplayLabel.toString();
    387                     mPackageInfo = ai;
    388                     return;
    389                 }
    390             } catch (PackageManager.NameNotFoundException e) {
    391             }
    392 
    393             // If we couldn't get information about the overall
    394             // process, try to find something about the uid.
    395             String[] pkgs = pm.getPackagesForUid(mUid);
    396 
    397             // If there is one package with this uid, that is what we want.
    398             if (pkgs.length == 1) {
    399                 try {
    400                     ApplicationInfo ai = pm.getApplicationInfo(pkgs[0],
    401                             PackageManager.GET_UNINSTALLED_PACKAGES);
    402                     mDisplayLabel = ai.loadLabel(pm);
    403                     mLabel = mDisplayLabel.toString();
    404                     mPackageInfo = ai;
    405                     return;
    406                 } catch (PackageManager.NameNotFoundException e) {
    407                 }
    408             }
    409 
    410             // If there are multiple, see if one gives us the official name
    411             // for this uid.
    412             for (String name : pkgs) {
    413                 try {
    414                     PackageInfo pi = pm.getPackageInfo(name, 0);
    415                     if (pi.sharedUserLabel != 0) {
    416                         CharSequence nm = pm.getText(name,
    417                                 pi.sharedUserLabel, pi.applicationInfo);
    418                         if (nm != null) {
    419                             mDisplayLabel = nm;
    420                             mLabel = nm.toString();
    421                             mPackageInfo = pi.applicationInfo;
    422                             return;
    423                         }
    424                     }
    425                 } catch (PackageManager.NameNotFoundException e) {
    426                 }
    427             }
    428 
    429             // If still don't have anything to display, just use the
    430             // service info.
    431             if (mServices.size() > 0) {
    432                 ApplicationInfo ai = mServices.values().iterator().next()
    433                         .mServiceInfo.applicationInfo;
    434                 mPackageInfo = ai;
    435                 mDisplayLabel = mPackageInfo.loadLabel(pm);
    436                 mLabel = mDisplayLabel.toString();
    437                 return;
    438             }
    439 
    440             // Finally... whatever, just pick the first package's name.
    441             try {
    442                 ApplicationInfo ai = pm.getApplicationInfo(pkgs[0],
    443                         PackageManager.GET_UNINSTALLED_PACKAGES);
    444                 mDisplayLabel = ai.loadLabel(pm);
    445                 mLabel = mDisplayLabel.toString();
    446                 mPackageInfo = ai;
    447                 return;
    448             } catch (PackageManager.NameNotFoundException e) {
    449             }
    450         }
    451 
    452         boolean updateService(Context context, ActivityManager.RunningServiceInfo service) {
    453             final PackageManager pm = context.getPackageManager();
    454 
    455             boolean changed = false;
    456             ServiceItem si = mServices.get(service.service);
    457             if (si == null) {
    458                 changed = true;
    459                 si = new ServiceItem(mUserId);
    460                 si.mRunningService = service;
    461                 try {
    462                     si.mServiceInfo = ActivityThread.getPackageManager().getServiceInfo(
    463                             service.service, PackageManager.GET_UNINSTALLED_PACKAGES,
    464                             UserHandle.getUserId(service.uid));
    465 
    466                     if (si.mServiceInfo == null) {
    467                         Log.d("RunningService", "getServiceInfo returned null for: "
    468                                 + service.service);
    469                         return false;
    470                     }
    471                 } catch (RemoteException e) {
    472                 }
    473                 si.mDisplayLabel = makeLabel(pm,
    474                         si.mRunningService.service.getClassName(), si.mServiceInfo);
    475                 mLabel = mDisplayLabel != null ? mDisplayLabel.toString() : null;
    476                 si.mPackageInfo = si.mServiceInfo.applicationInfo;
    477                 mServices.put(service.service, si);
    478             }
    479             si.mCurSeq = mCurSeq;
    480             si.mRunningService = service;
    481             long activeSince = service.restarting == 0 ? service.activeSince : -1;
    482             if (si.mActiveSince != activeSince) {
    483                 si.mActiveSince = activeSince;
    484                 changed = true;
    485             }
    486             if (service.clientPackage != null && service.clientLabel != 0) {
    487                 if (si.mShownAsStarted) {
    488                     si.mShownAsStarted = false;
    489                     changed = true;
    490                 }
    491                 try {
    492                     Resources clientr = pm.getResourcesForApplication(service.clientPackage);
    493                     String label = clientr.getString(service.clientLabel);
    494                     si.mDescription = context.getResources().getString(
    495                             R.string.service_client_name, label);
    496                 } catch (PackageManager.NameNotFoundException e) {
    497                     si.mDescription = null;
    498                 }
    499             } else {
    500                 if (!si.mShownAsStarted) {
    501                     si.mShownAsStarted = true;
    502                     changed = true;
    503                 }
    504                 si.mDescription = context.getResources().getString(
    505                         R.string.service_started_by_app);
    506             }
    507 
    508             return changed;
    509         }
    510 
    511         boolean updateSize(Context context, long pss, int curSeq) {
    512             mSize = pss * 1024;
    513             if (mCurSeq == curSeq) {
    514                 String sizeStr = Formatter.formatShortFileSize(
    515                         context, mSize);
    516                 if (!sizeStr.equals(mSizeStr)){
    517                     mSizeStr = sizeStr;
    518                     // We update this on the second tick where we update just
    519                     // the text in the current items, so no need to say we
    520                     // changed here.
    521                     return false;
    522                 }
    523             }
    524             return false;
    525         }
    526 
    527         boolean buildDependencyChain(Context context, PackageManager pm, int curSeq) {
    528             final int NP = mDependentProcesses.size();
    529             boolean changed = false;
    530             for (int i=0; i<NP; i++) {
    531                 ProcessItem proc = mDependentProcesses.valueAt(i);
    532                 if (proc.mClient != this) {
    533                     changed = true;
    534                     proc.mClient = this;
    535                 }
    536                 proc.mCurSeq = curSeq;
    537                 proc.ensureLabel(pm);
    538                 changed |= proc.buildDependencyChain(context, pm, curSeq);
    539             }
    540 
    541             if (mLastNumDependentProcesses != mDependentProcesses.size()) {
    542                 changed = true;
    543                 mLastNumDependentProcesses = mDependentProcesses.size();
    544             }
    545 
    546             return changed;
    547         }
    548 
    549         void addDependentProcesses(ArrayList<BaseItem> dest,
    550                 ArrayList<ProcessItem> destProc) {
    551             final int NP = mDependentProcesses.size();
    552             for (int i=0; i<NP; i++) {
    553                 ProcessItem proc = mDependentProcesses.valueAt(i);
    554                 proc.addDependentProcesses(dest, destProc);
    555                 dest.add(proc);
    556                 if (proc.mPid > 0) {
    557                     destProc.add(proc);
    558                 }
    559             }
    560         }
    561     }
    562 
    563     static class MergedItem extends BaseItem {
    564         ProcessItem mProcess;
    565         UserState mUser;
    566         final ArrayList<ProcessItem> mOtherProcesses = new ArrayList<ProcessItem>();
    567         final ArrayList<ServiceItem> mServices = new ArrayList<ServiceItem>();
    568         final ArrayList<MergedItem> mChildren = new ArrayList<MergedItem>();
    569 
    570         private int mLastNumProcesses = -1, mLastNumServices = -1;
    571 
    572         MergedItem(int userId) {
    573             super(false, userId);
    574         }
    575 
    576         private void setDescription(Context context, int numProcesses, int numServices) {
    577             if (mLastNumProcesses != numProcesses || mLastNumServices != numServices) {
    578                 mLastNumProcesses = numProcesses;
    579                 mLastNumServices = numServices;
    580                 int resid = R.string.running_processes_item_description_s_s;
    581                 if (numProcesses != 1) {
    582                     resid = numServices != 1
    583                             ? R.string.running_processes_item_description_p_p
    584                             : R.string.running_processes_item_description_p_s;
    585                 } else if (numServices != 1) {
    586                     resid = R.string.running_processes_item_description_s_p;
    587                 }
    588                 mDescription = context.getResources().getString(resid, numProcesses,
    589                         numServices);
    590             }
    591         }
    592 
    593         boolean update(Context context, boolean background) {
    594             mBackground = background;
    595 
    596             if (mUser != null) {
    597                 // This is a merged item that contains a child collection
    598                 // of items...  that is, it is an entire user, containing
    599                 // everything associated with that user.  So set it up as such.
    600                 // For concrete stuff we need about the process of this item,
    601                 // we will just use the info from the first child.
    602                 MergedItem child0 = mChildren.get(0);
    603                 mPackageInfo = child0.mProcess.mPackageInfo;
    604                 mLabel = mUser != null ? mUser.mLabel : null;
    605                 mDisplayLabel = mLabel;
    606                 int numProcesses = 0;
    607                 int numServices = 0;
    608                 mActiveSince = -1;
    609                 for (int i=0; i<mChildren.size(); i++) {
    610                     MergedItem child = mChildren.get(i);
    611                     numProcesses += child.mLastNumProcesses;
    612                     numServices += child.mLastNumServices;
    613                     if (child.mActiveSince >= 0 && mActiveSince < child.mActiveSince) {
    614                         mActiveSince = child.mActiveSince;
    615                     }
    616                 }
    617                 if (!mBackground) {
    618                     setDescription(context, numProcesses, numServices);
    619                 }
    620             } else {
    621                 mPackageInfo = mProcess.mPackageInfo;
    622                 mDisplayLabel = mProcess.mDisplayLabel;
    623                 mLabel = mProcess.mLabel;
    624 
    625                 if (!mBackground) {
    626                     setDescription(context, (mProcess.mPid > 0 ? 1 : 0) + mOtherProcesses.size(),
    627                             mServices.size());
    628                 }
    629 
    630                 mActiveSince = -1;
    631                 for (int i=0; i<mServices.size(); i++) {
    632                     ServiceItem si = mServices.get(i);
    633                     if (si.mActiveSince >= 0 && mActiveSince < si.mActiveSince) {
    634                         mActiveSince = si.mActiveSince;
    635                     }
    636                 }
    637             }
    638 
    639             return false;
    640         }
    641 
    642         boolean updateSize(Context context) {
    643             if (mUser != null) {
    644                 mSize = 0;
    645                 for (int i=0; i<mChildren.size(); i++) {
    646                     MergedItem child = mChildren.get(i);
    647                     child.updateSize(context);
    648                     mSize += child.mSize;
    649                 }
    650             } else {
    651                 mSize = mProcess.mSize;
    652                 for (int i=0; i<mOtherProcesses.size(); i++) {
    653                     mSize += mOtherProcesses.get(i).mSize;
    654                 }
    655             }
    656 
    657             String sizeStr = Formatter.formatShortFileSize(
    658                     context, mSize);
    659             if (!sizeStr.equals(mSizeStr)){
    660                 mSizeStr = sizeStr;
    661                 // We update this on the second tick where we update just
    662                 // the text in the current items, so no need to say we
    663                 // changed here.
    664                 return false;
    665             }
    666             return false;
    667         }
    668 
    669         public Drawable loadIcon(Context context, RunningState state) {
    670             if (mUser == null) {
    671                 return super.loadIcon(context, state);
    672             }
    673             if (mUser.mIcon != null) {
    674                 return mUser.mIcon.getConstantState().newDrawable();
    675             }
    676             return context.getResources().getDrawable(
    677                     com.android.internal.R.drawable.ic_menu_cc);
    678         }
    679     }
    680 
    681     class ServiceProcessComparator implements Comparator<ProcessItem> {
    682         public int compare(ProcessItem object1, ProcessItem object2) {
    683             if (object1.mUserId != object2.mUserId) {
    684                 if (object1.mUserId == mMyUserId) return -1;
    685                 if (object2.mUserId == mMyUserId) return 1;
    686                 return object1.mUserId < object2.mUserId ? -1 : 1;
    687             }
    688             if (object1.mIsStarted != object2.mIsStarted) {
    689                 // Non-started processes go last.
    690                 return object1.mIsStarted ? -1 : 1;
    691             }
    692             if (object1.mIsSystem != object2.mIsSystem) {
    693                 // System processes go below non-system.
    694                 return object1.mIsSystem ? 1 : -1;
    695             }
    696             if (object1.mActiveSince != object2.mActiveSince) {
    697                 // Remaining ones are sorted with the longest running
    698                 // services last.
    699                 return (object1.mActiveSince > object2.mActiveSince) ? -1 : 1;
    700             }
    701             return 0;
    702         }
    703     }
    704 
    705     static CharSequence makeLabel(PackageManager pm,
    706             String className, PackageItemInfo item) {
    707         if (item != null && (item.labelRes != 0
    708                 || item.nonLocalizedLabel != null)) {
    709             CharSequence label = item.loadLabel(pm);
    710             if (label != null) {
    711                 return label;
    712             }
    713         }
    714 
    715         String label = className;
    716         int tail = label.lastIndexOf('.');
    717         if (tail >= 0) {
    718             label = label.substring(tail+1, label.length());
    719         }
    720         return label;
    721     }
    722 
    723     static RunningState getInstance(Context context) {
    724         synchronized (sGlobalLock) {
    725             if (sInstance == null) {
    726                 sInstance = new RunningState(context);
    727             }
    728             return sInstance;
    729         }
    730     }
    731 
    732     private RunningState(Context context) {
    733         mApplicationContext = context.getApplicationContext();
    734         mAm = (ActivityManager)mApplicationContext.getSystemService(Context.ACTIVITY_SERVICE);
    735         mPm = mApplicationContext.getPackageManager();
    736         mUm = (UserManager)mApplicationContext.getSystemService(Context.USER_SERVICE);
    737         mMyUserId = UserHandle.myUserId();
    738         mResumed = false;
    739         mBackgroundThread = new HandlerThread("RunningState:Background");
    740         mBackgroundThread.start();
    741         mBackgroundHandler = new BackgroundHandler(mBackgroundThread.getLooper());
    742     }
    743 
    744     void resume(OnRefreshUiListener listener) {
    745         synchronized (mLock) {
    746             mResumed = true;
    747             mRefreshUiListener = listener;
    748             if (mInterestingConfigChanges.applyNewConfig(mApplicationContext.getResources())) {
    749                 mHaveData = false;
    750                 mBackgroundHandler.removeMessages(MSG_RESET_CONTENTS);
    751                 mBackgroundHandler.removeMessages(MSG_UPDATE_CONTENTS);
    752                 mBackgroundHandler.sendEmptyMessage(MSG_RESET_CONTENTS);
    753             }
    754             if (!mBackgroundHandler.hasMessages(MSG_UPDATE_CONTENTS)) {
    755                 mBackgroundHandler.sendEmptyMessage(MSG_UPDATE_CONTENTS);
    756             }
    757             mHandler.sendEmptyMessage(MSG_UPDATE_TIME);
    758         }
    759     }
    760 
    761     void updateNow() {
    762         synchronized (mLock) {
    763             mBackgroundHandler.removeMessages(MSG_UPDATE_CONTENTS);
    764             mBackgroundHandler.sendEmptyMessage(MSG_UPDATE_CONTENTS);
    765         }
    766     }
    767 
    768     boolean hasData() {
    769         synchronized (mLock) {
    770             return mHaveData;
    771         }
    772     }
    773 
    774     void waitForData() {
    775         synchronized (mLock) {
    776             while (!mHaveData) {
    777                 try {
    778                     mLock.wait(0);
    779                 } catch (InterruptedException e) {
    780                 }
    781             }
    782         }
    783     }
    784 
    785     void pause() {
    786         synchronized (mLock) {
    787             mResumed = false;
    788             mRefreshUiListener = null;
    789             mHandler.removeMessages(MSG_UPDATE_TIME);
    790         }
    791     }
    792 
    793     private boolean isInterestingProcess(ActivityManager.RunningAppProcessInfo pi) {
    794         if ((pi.flags&ActivityManager.RunningAppProcessInfo.FLAG_CANT_SAVE_STATE) != 0) {
    795             return true;
    796         }
    797         if ((pi.flags&ActivityManager.RunningAppProcessInfo.FLAG_PERSISTENT) == 0
    798                 && pi.importance >= ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND
    799                 && pi.importance < ActivityManager.RunningAppProcessInfo.IMPORTANCE_CANT_SAVE_STATE
    800                 && pi.importanceReasonCode
    801                         == ActivityManager.RunningAppProcessInfo.REASON_UNKNOWN) {
    802             return true;
    803         }
    804         return false;
    805     }
    806 
    807     private void reset() {
    808         mServiceProcessesByName.clear();
    809         mServiceProcessesByPid.clear();
    810         mInterestingProcesses.clear();
    811         mRunningProcesses.clear();
    812         mProcessItems.clear();
    813         mAllProcessItems.clear();
    814         mUsers.clear();
    815     }
    816 
    817     private void addOtherUserItem(Context context, ArrayList<MergedItem> newMergedItems,
    818             SparseArray<MergedItem> userItems, MergedItem newItem) {
    819         MergedItem userItem = userItems.get(newItem.mUserId);
    820         boolean first = userItem == null || userItem.mCurSeq != mSequence;
    821         if (first) {
    822             if (userItem == null) {
    823                 userItem = new MergedItem(newItem.mUserId);
    824                 userItems.put(newItem.mUserId, userItem);
    825             } else {
    826                 userItem.mChildren.clear();
    827             }
    828             userItem.mCurSeq = mSequence;
    829             if ((userItem.mUser=mUsers.get(newItem.mUserId)) == null) {
    830                 userItem.mUser = new UserState();
    831                 UserInfo info = mUm.getUserInfo(newItem.mUserId);
    832                 userItem.mUser.mInfo = info;
    833                 if (info != null) {
    834                     userItem.mUser.mIcon = UserUtils.getUserIcon(mUm, info,
    835                             context.getResources());
    836                 }
    837                 String name = info != null ? info.name : null;
    838                 if (name == null) {
    839                     name = Integer.toString(info.id);
    840                 }
    841                 userItem.mUser.mLabel = context.getResources().getString(
    842                         R.string.running_process_item_user_label, name);
    843             }
    844             newMergedItems.add(userItem);
    845         }
    846         userItem.mChildren.add(newItem);
    847     }
    848 
    849     private boolean update(Context context, ActivityManager am) {
    850         final PackageManager pm = context.getPackageManager();
    851 
    852         mSequence++;
    853 
    854         boolean changed = false;
    855 
    856         // Retrieve list of services, filtering out anything that definitely
    857         // won't be shown in the UI.
    858         List<ActivityManager.RunningServiceInfo> services
    859                 = am.getRunningServices(MAX_SERVICES);
    860         int NS = services != null ? services.size() : 0;
    861         for (int i=0; i<NS; i++) {
    862             ActivityManager.RunningServiceInfo si = services.get(i);
    863             // We are not interested in services that have not been started
    864             // and don't have a known client, because
    865             // there is nothing the user can do about them.
    866             if (!si.started && si.clientLabel == 0) {
    867                 services.remove(i);
    868                 i--;
    869                 NS--;
    870                 continue;
    871             }
    872             // We likewise don't care about services running in a
    873             // persistent process like the system or phone.
    874             if ((si.flags&ActivityManager.RunningServiceInfo.FLAG_PERSISTENT_PROCESS)
    875                     != 0) {
    876                 services.remove(i);
    877                 i--;
    878                 NS--;
    879                 continue;
    880             }
    881         }
    882 
    883         // Retrieve list of running processes, organizing them into a sparse
    884         // array for easy retrieval.
    885         List<ActivityManager.RunningAppProcessInfo> processes
    886                 = am.getRunningAppProcesses();
    887         final int NP = processes != null ? processes.size() : 0;
    888         mTmpAppProcesses.clear();
    889         for (int i=0; i<NP; i++) {
    890             ActivityManager.RunningAppProcessInfo pi = processes.get(i);
    891             mTmpAppProcesses.put(pi.pid, new AppProcessInfo(pi));
    892         }
    893 
    894         // Initial iteration through running services to collect per-process
    895         // info about them.
    896         for (int i=0; i<NS; i++) {
    897             ActivityManager.RunningServiceInfo si = services.get(i);
    898             if (si.restarting == 0 && si.pid > 0) {
    899                 AppProcessInfo ainfo = mTmpAppProcesses.get(si.pid);
    900                 if (ainfo != null) {
    901                     ainfo.hasServices = true;
    902                     if (si.foreground) {
    903                         ainfo.hasForegroundServices = true;
    904                     }
    905                 }
    906             }
    907         }
    908 
    909         // Update state we are maintaining about process that are running services.
    910         for (int i=0; i<NS; i++) {
    911             ActivityManager.RunningServiceInfo si = services.get(i);
    912 
    913             // If this service's process is in use at a higher importance
    914             // due to another process bound to one of its services, then we
    915             // won't put it in the top-level list of services.  Instead we
    916             // want it to be included in the set of processes that the other
    917             // process needs.
    918             if (si.restarting == 0 && si.pid > 0) {
    919                 AppProcessInfo ainfo = mTmpAppProcesses.get(si.pid);
    920                 if (ainfo != null && !ainfo.hasForegroundServices) {
    921                     // This process does not have any foreground services.
    922                     // If its importance is greater than the service importance
    923                     // then there is something else more significant that is
    924                     // keeping it around that it should possibly be included as
    925                     // a part of instead of being shown by itself.
    926                     if (ainfo.info.importance
    927                             < ActivityManager.RunningAppProcessInfo.IMPORTANCE_SERVICE) {
    928                         // Follow process chain to see if there is something
    929                         // else that could be shown
    930                         boolean skip = false;
    931                         ainfo = mTmpAppProcesses.get(ainfo.info.importanceReasonPid);
    932                         while (ainfo != null) {
    933                             if (ainfo.hasServices || isInterestingProcess(ainfo.info)) {
    934                                 skip = true;
    935                                 break;
    936                             }
    937                             ainfo = mTmpAppProcesses.get(ainfo.info.importanceReasonPid);
    938                         }
    939                         if (skip) {
    940                             continue;
    941                         }
    942                     }
    943                 }
    944             }
    945 
    946             HashMap<String, ProcessItem> procs = mServiceProcessesByName.get(si.uid);
    947             if (procs == null) {
    948                 procs = new HashMap<String, ProcessItem>();
    949                 mServiceProcessesByName.put(si.uid, procs);
    950             }
    951             ProcessItem proc = procs.get(si.process);
    952             if (proc == null) {
    953                 changed = true;
    954                 proc = new ProcessItem(context, si.uid, si.process);
    955                 procs.put(si.process, proc);
    956             }
    957 
    958             if (proc.mCurSeq != mSequence) {
    959                 int pid = si.restarting == 0 ? si.pid : 0;
    960                 if (pid != proc.mPid) {
    961                     changed = true;
    962                     if (proc.mPid != pid) {
    963                         if (proc.mPid != 0) {
    964                             mServiceProcessesByPid.remove(proc.mPid);
    965                         }
    966                         if (pid != 0) {
    967                             mServiceProcessesByPid.put(pid, proc);
    968                         }
    969                         proc.mPid = pid;
    970                     }
    971                 }
    972                 proc.mDependentProcesses.clear();
    973                 proc.mCurSeq = mSequence;
    974             }
    975             changed |= proc.updateService(context, si);
    976         }
    977 
    978         // Now update the map of other processes that are running (but
    979         // don't have services actively running inside them).
    980         for (int i=0; i<NP; i++) {
    981             ActivityManager.RunningAppProcessInfo pi = processes.get(i);
    982             ProcessItem proc = mServiceProcessesByPid.get(pi.pid);
    983             if (proc == null) {
    984                 // This process is not one that is a direct container
    985                 // of a service, so look for it in the secondary
    986                 // running list.
    987                 proc = mRunningProcesses.get(pi.pid);
    988                 if (proc == null) {
    989                     changed = true;
    990                     proc = new ProcessItem(context, pi.uid, pi.processName);
    991                     proc.mPid = pi.pid;
    992                     mRunningProcesses.put(pi.pid, proc);
    993                 }
    994                 proc.mDependentProcesses.clear();
    995             }
    996 
    997             if (isInterestingProcess(pi)) {
    998                 if (!mInterestingProcesses.contains(proc)) {
    999                     changed = true;
   1000                     mInterestingProcesses.add(proc);
   1001                 }
   1002                 proc.mCurSeq = mSequence;
   1003                 proc.mInteresting = true;
   1004                 proc.ensureLabel(pm);
   1005             } else {
   1006                 proc.mInteresting = false;
   1007             }
   1008 
   1009             proc.mRunningSeq = mSequence;
   1010             proc.mRunningProcessInfo = pi;
   1011         }
   1012 
   1013         // Build the chains from client processes to the process they are
   1014         // dependent on; also remove any old running processes.
   1015         int NRP = mRunningProcesses.size();
   1016         for (int i = 0; i < NRP;) {
   1017             ProcessItem proc = mRunningProcesses.valueAt(i);
   1018             if (proc.mRunningSeq == mSequence) {
   1019                 int clientPid = proc.mRunningProcessInfo.importanceReasonPid;
   1020                 if (clientPid != 0) {
   1021                     ProcessItem client = mServiceProcessesByPid.get(clientPid);
   1022                     if (client == null) {
   1023                         client = mRunningProcesses.get(clientPid);
   1024                     }
   1025                     if (client != null) {
   1026                         client.mDependentProcesses.put(proc.mPid, proc);
   1027                     }
   1028                 } else {
   1029                     // In this pass the process doesn't have a client.
   1030                     // Clear to make sure that, if it later gets the same one,
   1031                     // we will detect the change.
   1032                     proc.mClient = null;
   1033                 }
   1034                 i++;
   1035             } else {
   1036                 changed = true;
   1037                 mRunningProcesses.remove(mRunningProcesses.keyAt(i));
   1038                 NRP--;
   1039             }
   1040         }
   1041 
   1042         // Remove any old interesting processes.
   1043         int NHP = mInterestingProcesses.size();
   1044         for (int i=0; i<NHP; i++) {
   1045             ProcessItem proc = mInterestingProcesses.get(i);
   1046             if (!proc.mInteresting || mRunningProcesses.get(proc.mPid) == null) {
   1047                 changed = true;
   1048                 mInterestingProcesses.remove(i);
   1049                 i--;
   1050                 NHP--;
   1051             }
   1052         }
   1053 
   1054         // Follow the tree from all primary service processes to all
   1055         // processes they are dependent on, marking these processes as
   1056         // still being active and determining if anything has changed.
   1057         final int NAP = mServiceProcessesByPid.size();
   1058         for (int i=0; i<NAP; i++) {
   1059             ProcessItem proc = mServiceProcessesByPid.valueAt(i);
   1060             if (proc.mCurSeq == mSequence) {
   1061                 changed |= proc.buildDependencyChain(context, pm, mSequence);
   1062             }
   1063         }
   1064 
   1065         // Look for services and their primary processes that no longer exist...
   1066         ArrayList<Integer> uidToDelete = null;
   1067         for (int i=0; i<mServiceProcessesByName.size(); i++) {
   1068             HashMap<String, ProcessItem> procs = mServiceProcessesByName.valueAt(i);
   1069             Iterator<ProcessItem> pit = procs.values().iterator();
   1070             while (pit.hasNext()) {
   1071                 ProcessItem pi = pit.next();
   1072                 if (pi.mCurSeq == mSequence) {
   1073                     pi.ensureLabel(pm);
   1074                     if (pi.mPid == 0) {
   1075                         // Sanity: a non-process can't be dependent on
   1076                         // anything.
   1077                         pi.mDependentProcesses.clear();
   1078                     }
   1079                 } else {
   1080                     changed = true;
   1081                     pit.remove();
   1082                     if (procs.size() == 0) {
   1083                         if (uidToDelete == null) {
   1084                             uidToDelete = new ArrayList<Integer>();
   1085                         }
   1086                         uidToDelete.add(mServiceProcessesByName.keyAt(i));
   1087                     }
   1088                     if (pi.mPid != 0) {
   1089                         mServiceProcessesByPid.remove(pi.mPid);
   1090                     }
   1091                     continue;
   1092                 }
   1093                 Iterator<ServiceItem> sit = pi.mServices.values().iterator();
   1094                 while (sit.hasNext()) {
   1095                     ServiceItem si = sit.next();
   1096                     if (si.mCurSeq != mSequence) {
   1097                         changed = true;
   1098                         sit.remove();
   1099                     }
   1100                 }
   1101             }
   1102         }
   1103 
   1104         if (uidToDelete != null) {
   1105             for (int i = 0; i < uidToDelete.size(); i++) {
   1106                 int uid = uidToDelete.get(i);
   1107                 mServiceProcessesByName.remove(uid);
   1108             }
   1109         }
   1110 
   1111         if (changed) {
   1112             // First determine an order for the services.
   1113             ArrayList<ProcessItem> sortedProcesses = new ArrayList<ProcessItem>();
   1114             for (int i=0; i<mServiceProcessesByName.size(); i++) {
   1115                 for (ProcessItem pi : mServiceProcessesByName.valueAt(i).values()) {
   1116                     pi.mIsSystem = false;
   1117                     pi.mIsStarted = true;
   1118                     pi.mActiveSince = Long.MAX_VALUE;
   1119                     for (ServiceItem si : pi.mServices.values()) {
   1120                         if (si.mServiceInfo != null
   1121                                 && (si.mServiceInfo.applicationInfo.flags
   1122                                         & ApplicationInfo.FLAG_SYSTEM) != 0) {
   1123                             pi.mIsSystem = true;
   1124                         }
   1125                         if (si.mRunningService != null
   1126                                 && si.mRunningService.clientLabel != 0) {
   1127                             pi.mIsStarted = false;
   1128                             if (pi.mActiveSince > si.mRunningService.activeSince) {
   1129                                 pi.mActiveSince = si.mRunningService.activeSince;
   1130                             }
   1131                         }
   1132                     }
   1133                     sortedProcesses.add(pi);
   1134                 }
   1135             }
   1136 
   1137             Collections.sort(sortedProcesses, mServiceProcessComparator);
   1138 
   1139             ArrayList<BaseItem> newItems = new ArrayList<BaseItem>();
   1140             ArrayList<MergedItem> newMergedItems = new ArrayList<MergedItem>();
   1141             SparseArray<MergedItem> otherUsers = null;
   1142             mProcessItems.clear();
   1143             for (int i=0; i<sortedProcesses.size(); i++) {
   1144                 ProcessItem pi = sortedProcesses.get(i);
   1145                 pi.mNeedDivider = false;
   1146 
   1147                 int firstProc = mProcessItems.size();
   1148                 // First add processes we are dependent on.
   1149                 pi.addDependentProcesses(newItems, mProcessItems);
   1150                 // And add the process itself.
   1151                 newItems.add(pi);
   1152                 if (pi.mPid > 0) {
   1153                     mProcessItems.add(pi);
   1154                 }
   1155 
   1156                 // Now add the services running in it.
   1157                 MergedItem mergedItem = null;
   1158                 boolean haveAllMerged = false;
   1159                 boolean needDivider = false;
   1160                 for (ServiceItem si : pi.mServices.values()) {
   1161                     si.mNeedDivider = needDivider;
   1162                     needDivider = true;
   1163                     newItems.add(si);
   1164                     if (si.mMergedItem != null) {
   1165                         if (mergedItem != null && mergedItem != si.mMergedItem) {
   1166                             haveAllMerged = false;
   1167                         }
   1168                         mergedItem = si.mMergedItem;
   1169                     } else {
   1170                         haveAllMerged = false;
   1171                     }
   1172                 }
   1173 
   1174                 if (!haveAllMerged || mergedItem == null
   1175                         || mergedItem.mServices.size() != pi.mServices.size()) {
   1176                     // Whoops, we need to build a new MergedItem!
   1177                     mergedItem = new MergedItem(pi.mUserId);
   1178                     for (ServiceItem si : pi.mServices.values()) {
   1179                         mergedItem.mServices.add(si);
   1180                         si.mMergedItem = mergedItem;
   1181                     }
   1182                     mergedItem.mProcess = pi;
   1183                     mergedItem.mOtherProcesses.clear();
   1184                     for (int mpi=firstProc; mpi<(mProcessItems.size()-1); mpi++) {
   1185                         mergedItem.mOtherProcesses.add(mProcessItems.get(mpi));
   1186                     }
   1187                 }
   1188 
   1189                 mergedItem.update(context, false);
   1190                 if (mergedItem.mUserId != mMyUserId) {
   1191                     addOtherUserItem(context, newMergedItems, mOtherUserMergedItems, mergedItem);
   1192                 } else {
   1193                     newMergedItems.add(mergedItem);
   1194                 }
   1195             }
   1196 
   1197             // Finally, interesting processes need to be shown and will
   1198             // go at the top.
   1199             NHP = mInterestingProcesses.size();
   1200             for (int i=0; i<NHP; i++) {
   1201                 ProcessItem proc = mInterestingProcesses.get(i);
   1202                 if (proc.mClient == null && proc.mServices.size() <= 0) {
   1203                     if (proc.mMergedItem == null) {
   1204                         proc.mMergedItem = new MergedItem(proc.mUserId);
   1205                         proc.mMergedItem.mProcess = proc;
   1206                     }
   1207                     proc.mMergedItem.update(context, false);
   1208                     if (proc.mMergedItem.mUserId != mMyUserId) {
   1209                         addOtherUserItem(context, newMergedItems, mOtherUserMergedItems,
   1210                                 proc.mMergedItem);
   1211                     } else {
   1212                         newMergedItems.add(0, proc.mMergedItem);
   1213                     }
   1214                     mProcessItems.add(proc);
   1215                 }
   1216             }
   1217 
   1218             // Finally finally, user aggregated merged items need to be
   1219             // updated now that they have all of their children.
   1220             final int NU = mOtherUserMergedItems.size();
   1221             for (int i=0; i<NU; i++) {
   1222                 MergedItem user = mOtherUserMergedItems.valueAt(i);
   1223                 if (user.mCurSeq == mSequence) {
   1224                     user.update(context, false);
   1225                 }
   1226             }
   1227 
   1228             synchronized (mLock) {
   1229                 mItems = newItems;
   1230                 mMergedItems = newMergedItems;
   1231             }
   1232         }
   1233 
   1234         // Count number of interesting other (non-active) processes, and
   1235         // build a list of all processes we will retrieve memory for.
   1236         mAllProcessItems.clear();
   1237         mAllProcessItems.addAll(mProcessItems);
   1238         int numBackgroundProcesses = 0;
   1239         int numForegroundProcesses = 0;
   1240         int numServiceProcesses = 0;
   1241         NRP = mRunningProcesses.size();
   1242         for (int i=0; i<NRP; i++) {
   1243             ProcessItem proc = mRunningProcesses.valueAt(i);
   1244             if (proc.mCurSeq != mSequence) {
   1245                 // We didn't hit this process as a dependency on one
   1246                 // of our active ones, so add it up if needed.
   1247                 if (proc.mRunningProcessInfo.importance >=
   1248                         ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND) {
   1249                     numBackgroundProcesses++;
   1250                     mAllProcessItems.add(proc);
   1251                 } else if (proc.mRunningProcessInfo.importance <=
   1252                         ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE) {
   1253                     numForegroundProcesses++;
   1254                     mAllProcessItems.add(proc);
   1255                 } else {
   1256                     Log.i("RunningState", "Unknown non-service process: "
   1257                             + proc.mProcessName + " #" + proc.mPid);
   1258                 }
   1259             } else {
   1260                 numServiceProcesses++;
   1261             }
   1262         }
   1263 
   1264         long backgroundProcessMemory = 0;
   1265         long foregroundProcessMemory = 0;
   1266         long serviceProcessMemory = 0;
   1267         ArrayList<MergedItem> newBackgroundItems = null;
   1268         ArrayList<MergedItem> newUserBackgroundItems = null;
   1269         boolean diffUsers = false;
   1270         try {
   1271             final int numProc = mAllProcessItems.size();
   1272             int[] pids = new int[numProc];
   1273             for (int i=0; i<numProc; i++) {
   1274                 pids[i] = mAllProcessItems.get(i).mPid;
   1275             }
   1276             long[] pss = ActivityManagerNative.getDefault()
   1277                     .getProcessPss(pids);
   1278             int bgIndex = 0;
   1279             for (int i=0; i<pids.length; i++) {
   1280                 ProcessItem proc = mAllProcessItems.get(i);
   1281                 changed |= proc.updateSize(context, pss[i], mSequence);
   1282                 if (proc.mCurSeq == mSequence) {
   1283                     serviceProcessMemory += proc.mSize;
   1284                 } else if (proc.mRunningProcessInfo.importance >=
   1285                         ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND) {
   1286                     backgroundProcessMemory += proc.mSize;
   1287                     MergedItem mergedItem;
   1288                     if (newBackgroundItems != null) {
   1289                         mergedItem = proc.mMergedItem = new MergedItem(proc.mUserId);
   1290                         proc.mMergedItem.mProcess = proc;
   1291                         diffUsers |= mergedItem.mUserId != mMyUserId;
   1292                         newBackgroundItems.add(mergedItem);
   1293                     } else {
   1294                         if (bgIndex >= mBackgroundItems.size()
   1295                                 || mBackgroundItems.get(bgIndex).mProcess != proc) {
   1296                             newBackgroundItems = new ArrayList<MergedItem>(numBackgroundProcesses);
   1297                             for (int bgi=0; bgi<bgIndex; bgi++) {
   1298                                 mergedItem = mBackgroundItems.get(bgi);
   1299                                 diffUsers |= mergedItem.mUserId != mMyUserId;
   1300                                 newBackgroundItems.add(mergedItem);
   1301                             }
   1302                             mergedItem = proc.mMergedItem = new MergedItem(proc.mUserId);
   1303                             proc.mMergedItem.mProcess = proc;
   1304                             diffUsers |= mergedItem.mUserId != mMyUserId;
   1305                             newBackgroundItems.add(mergedItem);
   1306                         } else {
   1307                             mergedItem = mBackgroundItems.get(bgIndex);
   1308                         }
   1309                     }
   1310                     mergedItem.update(context, true);
   1311                     mergedItem.updateSize(context);
   1312                     bgIndex++;
   1313                 } else if (proc.mRunningProcessInfo.importance <=
   1314                         ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE) {
   1315                     foregroundProcessMemory += proc.mSize;
   1316                 }
   1317             }
   1318         } catch (RemoteException e) {
   1319         }
   1320 
   1321         if (newBackgroundItems == null) {
   1322             // One or more at the bottom may no longer exist.
   1323             if (mBackgroundItems.size() > numBackgroundProcesses) {
   1324                 newBackgroundItems = new ArrayList<MergedItem>(numBackgroundProcesses);
   1325                 for (int bgi=0; bgi<numBackgroundProcesses; bgi++) {
   1326                     MergedItem mergedItem = mBackgroundItems.get(bgi);
   1327                     diffUsers |= mergedItem.mUserId != mMyUserId;
   1328                     newBackgroundItems.add(mergedItem);
   1329                 }
   1330             }
   1331         }
   1332 
   1333         if (newBackgroundItems != null) {
   1334             // The background items have changed; we need to re-build the
   1335             // per-user items.
   1336             if (!diffUsers) {
   1337                 // Easy: there are no other users, we can just use the same array.
   1338                 newUserBackgroundItems = newBackgroundItems;
   1339             } else {
   1340                 // We now need to re-build the per-user list so that background
   1341                 // items for users are collapsed together.
   1342                 newUserBackgroundItems = new ArrayList<MergedItem>();
   1343                 final int NB = newBackgroundItems.size();
   1344                 for (int i=0; i<NB; i++) {
   1345                     MergedItem mergedItem = newBackgroundItems.get(i);
   1346                     if (mergedItem.mUserId != mMyUserId) {
   1347                         addOtherUserItem(context, newUserBackgroundItems,
   1348                                 mOtherUserBackgroundItems, mergedItem);
   1349                     } else {
   1350                         newUserBackgroundItems.add(mergedItem);
   1351                     }
   1352                 }
   1353                 // And user aggregated merged items need to be
   1354                 // updated now that they have all of their children.
   1355                 final int NU = mOtherUserBackgroundItems.size();
   1356                 for (int i=0; i<NU; i++) {
   1357                     MergedItem user = mOtherUserBackgroundItems.valueAt(i);
   1358                     if (user.mCurSeq == mSequence) {
   1359                         user.update(context, true);
   1360                         user.updateSize(context);
   1361                     }
   1362                 }
   1363             }
   1364         }
   1365 
   1366         for (int i=0; i<mMergedItems.size(); i++) {
   1367             mMergedItems.get(i).updateSize(context);
   1368         }
   1369 
   1370         synchronized (mLock) {
   1371             mNumBackgroundProcesses = numBackgroundProcesses;
   1372             mNumForegroundProcesses = numForegroundProcesses;
   1373             mNumServiceProcesses = numServiceProcesses;
   1374             mBackgroundProcessMemory = backgroundProcessMemory;
   1375             mForegroundProcessMemory = foregroundProcessMemory;
   1376             mServiceProcessMemory = serviceProcessMemory;
   1377             if (newBackgroundItems != null) {
   1378                 mBackgroundItems = newBackgroundItems;
   1379                 mUserBackgroundItems = newUserBackgroundItems;
   1380                 if (mWatchingBackgroundItems) {
   1381                     changed = true;
   1382                 }
   1383             }
   1384             if (!mHaveData) {
   1385                 mHaveData = true;
   1386                 mLock.notifyAll();
   1387             }
   1388         }
   1389 
   1390         return changed;
   1391     }
   1392 
   1393     ArrayList<BaseItem> getCurrentItems() {
   1394         synchronized (mLock) {
   1395             return mItems;
   1396         }
   1397     }
   1398 
   1399     void setWatchingBackgroundItems(boolean watching) {
   1400         synchronized (mLock) {
   1401             mWatchingBackgroundItems = watching;
   1402         }
   1403     }
   1404 
   1405     ArrayList<MergedItem> getCurrentMergedItems() {
   1406         synchronized (mLock) {
   1407             return mMergedItems;
   1408         }
   1409     }
   1410 
   1411     ArrayList<MergedItem> getCurrentBackgroundItems() {
   1412         synchronized (mLock) {
   1413             return mUserBackgroundItems;
   1414         }
   1415     }
   1416 }
   1417