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