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.settings.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         mHideManagedProfiles = mMyUserId != UserHandle.USER_OWNER;
    785         mResumed = false;
    786         mBackgroundThread = new HandlerThread("RunningState:Background");
    787         mBackgroundThread.start();
    788         mBackgroundHandler = new BackgroundHandler(mBackgroundThread.getLooper());
    789         mUmBroadcastReceiver.register(mApplicationContext);
    790     }
    791 
    792     void resume(OnRefreshUiListener listener) {
    793         synchronized (mLock) {
    794             mResumed = true;
    795             mRefreshUiListener = listener;
    796             boolean usersChanged = mUmBroadcastReceiver.checkUsersChangedLocked();
    797             boolean configChanged =
    798                     mInterestingConfigChanges.applyNewConfig(mApplicationContext.getResources());
    799             if (usersChanged || configChanged) {
    800                 mHaveData = false;
    801                 mBackgroundHandler.removeMessages(MSG_RESET_CONTENTS);
    802                 mBackgroundHandler.removeMessages(MSG_UPDATE_CONTENTS);
    803                 mBackgroundHandler.sendEmptyMessage(MSG_RESET_CONTENTS);
    804             }
    805             if (!mBackgroundHandler.hasMessages(MSG_UPDATE_CONTENTS)) {
    806                 mBackgroundHandler.sendEmptyMessage(MSG_UPDATE_CONTENTS);
    807             }
    808             mHandler.sendEmptyMessage(MSG_UPDATE_TIME);
    809         }
    810     }
    811 
    812     void updateNow() {
    813         synchronized (mLock) {
    814             mBackgroundHandler.removeMessages(MSG_UPDATE_CONTENTS);
    815             mBackgroundHandler.sendEmptyMessage(MSG_UPDATE_CONTENTS);
    816         }
    817     }
    818 
    819     boolean hasData() {
    820         synchronized (mLock) {
    821             return mHaveData;
    822         }
    823     }
    824 
    825     void waitForData() {
    826         synchronized (mLock) {
    827             while (!mHaveData) {
    828                 try {
    829                     mLock.wait(0);
    830                 } catch (InterruptedException e) {
    831                 }
    832             }
    833         }
    834     }
    835 
    836     void pause() {
    837         synchronized (mLock) {
    838             mResumed = false;
    839             mRefreshUiListener = null;
    840             mHandler.removeMessages(MSG_UPDATE_TIME);
    841         }
    842     }
    843 
    844     private boolean isInterestingProcess(ActivityManager.RunningAppProcessInfo pi) {
    845         if ((pi.flags&ActivityManager.RunningAppProcessInfo.FLAG_CANT_SAVE_STATE) != 0) {
    846             return true;
    847         }
    848         if ((pi.flags&ActivityManager.RunningAppProcessInfo.FLAG_PERSISTENT) == 0
    849                 && pi.importance >= ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND
    850                 && pi.importance < ActivityManager.RunningAppProcessInfo.IMPORTANCE_CANT_SAVE_STATE
    851                 && pi.importanceReasonCode
    852                         == ActivityManager.RunningAppProcessInfo.REASON_UNKNOWN) {
    853             return true;
    854         }
    855         return false;
    856     }
    857 
    858     private void reset() {
    859         mServiceProcessesByName.clear();
    860         mServiceProcessesByPid.clear();
    861         mInterestingProcesses.clear();
    862         mRunningProcesses.clear();
    863         mProcessItems.clear();
    864         mAllProcessItems.clear();
    865     }
    866 
    867     private void addOtherUserItem(Context context, ArrayList<MergedItem> newMergedItems,
    868             SparseArray<MergedItem> userItems, MergedItem newItem) {
    869         MergedItem userItem = userItems.get(newItem.mUserId);
    870         boolean first = userItem == null || userItem.mCurSeq != mSequence;
    871         if (first) {
    872             UserInfo info = mUm.getUserInfo(newItem.mUserId);
    873             if (info == null) {
    874                 // The user no longer exists, skip
    875                 return;
    876             }
    877             if (mHideManagedProfiles && info.isManagedProfile()) {
    878                 return;
    879             }
    880             if (userItem == null) {
    881                 userItem = new MergedItem(newItem.mUserId);
    882                 userItems.put(newItem.mUserId, userItem);
    883             } else {
    884                 userItem.mChildren.clear();
    885             }
    886             userItem.mCurSeq = mSequence;
    887             userItem.mUser = new UserState();
    888             userItem.mUser.mInfo = info;
    889             userItem.mUser.mIcon = Utils.getUserIcon(context, mUm, info);
    890             userItem.mUser.mLabel = Utils.getUserLabel(context, info);
    891             newMergedItems.add(userItem);
    892         }
    893         userItem.mChildren.add(newItem);
    894     }
    895 
    896     private boolean update(Context context, ActivityManager am) {
    897         final PackageManager pm = context.getPackageManager();
    898 
    899         mSequence++;
    900 
    901         boolean changed = false;
    902 
    903         // Retrieve list of services, filtering out anything that definitely
    904         // won't be shown in the UI.
    905         List<ActivityManager.RunningServiceInfo> services
    906                 = am.getRunningServices(MAX_SERVICES);
    907         int NS = services != null ? services.size() : 0;
    908         for (int i=0; i<NS; i++) {
    909             ActivityManager.RunningServiceInfo si = services.get(i);
    910             // We are not interested in services that have not been started
    911             // and don't have a known client, because
    912             // there is nothing the user can do about them.
    913             if (!si.started && si.clientLabel == 0) {
    914                 services.remove(i);
    915                 i--;
    916                 NS--;
    917                 continue;
    918             }
    919             // We likewise don't care about services running in a
    920             // persistent process like the system or phone.
    921             if ((si.flags&ActivityManager.RunningServiceInfo.FLAG_PERSISTENT_PROCESS)
    922                     != 0) {
    923                 services.remove(i);
    924                 i--;
    925                 NS--;
    926                 continue;
    927             }
    928         }
    929 
    930         // Retrieve list of running processes, organizing them into a sparse
    931         // array for easy retrieval.
    932         List<ActivityManager.RunningAppProcessInfo> processes
    933                 = am.getRunningAppProcesses();
    934         final int NP = processes != null ? processes.size() : 0;
    935         mTmpAppProcesses.clear();
    936         for (int i=0; i<NP; i++) {
    937             ActivityManager.RunningAppProcessInfo pi = processes.get(i);
    938             mTmpAppProcesses.put(pi.pid, new AppProcessInfo(pi));
    939         }
    940 
    941         // Initial iteration through running services to collect per-process
    942         // info about them.
    943         for (int i=0; i<NS; i++) {
    944             ActivityManager.RunningServiceInfo si = services.get(i);
    945             if (si.restarting == 0 && si.pid > 0) {
    946                 AppProcessInfo ainfo = mTmpAppProcesses.get(si.pid);
    947                 if (ainfo != null) {
    948                     ainfo.hasServices = true;
    949                     if (si.foreground) {
    950                         ainfo.hasForegroundServices = true;
    951                     }
    952                 }
    953             }
    954         }
    955 
    956         // Update state we are maintaining about process that are running services.
    957         for (int i=0; i<NS; i++) {
    958             ActivityManager.RunningServiceInfo si = services.get(i);
    959 
    960             // If this service's process is in use at a higher importance
    961             // due to another process bound to one of its services, then we
    962             // won't put it in the top-level list of services.  Instead we
    963             // want it to be included in the set of processes that the other
    964             // process needs.
    965             if (si.restarting == 0 && si.pid > 0) {
    966                 AppProcessInfo ainfo = mTmpAppProcesses.get(si.pid);
    967                 if (ainfo != null && !ainfo.hasForegroundServices) {
    968                     // This process does not have any foreground services.
    969                     // If its importance is greater than the service importance
    970                     // then there is something else more significant that is
    971                     // keeping it around that it should possibly be included as
    972                     // a part of instead of being shown by itself.
    973                     if (ainfo.info.importance
    974                             < ActivityManager.RunningAppProcessInfo.IMPORTANCE_SERVICE) {
    975                         // Follow process chain to see if there is something
    976                         // else that could be shown
    977                         boolean skip = false;
    978                         ainfo = mTmpAppProcesses.get(ainfo.info.importanceReasonPid);
    979                         while (ainfo != null) {
    980                             if (ainfo.hasServices || isInterestingProcess(ainfo.info)) {
    981                                 skip = true;
    982                                 break;
    983                             }
    984                             ainfo = mTmpAppProcesses.get(ainfo.info.importanceReasonPid);
    985                         }
    986                         if (skip) {
    987                             continue;
    988                         }
    989                     }
    990                 }
    991             }
    992 
    993             HashMap<String, ProcessItem> procs = mServiceProcessesByName.get(si.uid);
    994             if (procs == null) {
    995                 procs = new HashMap<String, ProcessItem>();
    996                 mServiceProcessesByName.put(si.uid, procs);
    997             }
    998             ProcessItem proc = procs.get(si.process);
    999             if (proc == null) {
   1000                 changed = true;
   1001                 proc = new ProcessItem(context, si.uid, si.process);
   1002                 procs.put(si.process, proc);
   1003             }
   1004 
   1005             if (proc.mCurSeq != mSequence) {
   1006                 int pid = si.restarting == 0 ? si.pid : 0;
   1007                 if (pid != proc.mPid) {
   1008                     changed = true;
   1009                     if (proc.mPid != pid) {
   1010                         if (proc.mPid != 0) {
   1011                             mServiceProcessesByPid.remove(proc.mPid);
   1012                         }
   1013                         if (pid != 0) {
   1014                             mServiceProcessesByPid.put(pid, proc);
   1015                         }
   1016                         proc.mPid = pid;
   1017                     }
   1018                 }
   1019                 proc.mDependentProcesses.clear();
   1020                 proc.mCurSeq = mSequence;
   1021             }
   1022             changed |= proc.updateService(context, si);
   1023         }
   1024 
   1025         // Now update the map of other processes that are running (but
   1026         // don't have services actively running inside them).
   1027         for (int i=0; i<NP; i++) {
   1028             ActivityManager.RunningAppProcessInfo pi = processes.get(i);
   1029             ProcessItem proc = mServiceProcessesByPid.get(pi.pid);
   1030             if (proc == null) {
   1031                 // This process is not one that is a direct container
   1032                 // of a service, so look for it in the secondary
   1033                 // running list.
   1034                 proc = mRunningProcesses.get(pi.pid);
   1035                 if (proc == null) {
   1036                     changed = true;
   1037                     proc = new ProcessItem(context, pi.uid, pi.processName);
   1038                     proc.mPid = pi.pid;
   1039                     mRunningProcesses.put(pi.pid, proc);
   1040                 }
   1041                 proc.mDependentProcesses.clear();
   1042             }
   1043 
   1044             if (isInterestingProcess(pi)) {
   1045                 if (!mInterestingProcesses.contains(proc)) {
   1046                     changed = true;
   1047                     mInterestingProcesses.add(proc);
   1048                 }
   1049                 proc.mCurSeq = mSequence;
   1050                 proc.mInteresting = true;
   1051                 proc.ensureLabel(pm);
   1052             } else {
   1053                 proc.mInteresting = false;
   1054             }
   1055 
   1056             proc.mRunningSeq = mSequence;
   1057             proc.mRunningProcessInfo = pi;
   1058         }
   1059 
   1060         // Build the chains from client processes to the process they are
   1061         // dependent on; also remove any old running processes.
   1062         int NRP = mRunningProcesses.size();
   1063         for (int i = 0; i < NRP;) {
   1064             ProcessItem proc = mRunningProcesses.valueAt(i);
   1065             if (proc.mRunningSeq == mSequence) {
   1066                 int clientPid = proc.mRunningProcessInfo.importanceReasonPid;
   1067                 if (clientPid != 0) {
   1068                     ProcessItem client = mServiceProcessesByPid.get(clientPid);
   1069                     if (client == null) {
   1070                         client = mRunningProcesses.get(clientPid);
   1071                     }
   1072                     if (client != null) {
   1073                         client.mDependentProcesses.put(proc.mPid, proc);
   1074                     }
   1075                 } else {
   1076                     // In this pass the process doesn't have a client.
   1077                     // Clear to make sure that, if it later gets the same one,
   1078                     // we will detect the change.
   1079                     proc.mClient = null;
   1080                 }
   1081                 i++;
   1082             } else {
   1083                 changed = true;
   1084                 mRunningProcesses.remove(mRunningProcesses.keyAt(i));
   1085                 NRP--;
   1086             }
   1087         }
   1088 
   1089         // Remove any old interesting processes.
   1090         int NHP = mInterestingProcesses.size();
   1091         for (int i=0; i<NHP; i++) {
   1092             ProcessItem proc = mInterestingProcesses.get(i);
   1093             if (!proc.mInteresting || mRunningProcesses.get(proc.mPid) == null) {
   1094                 changed = true;
   1095                 mInterestingProcesses.remove(i);
   1096                 i--;
   1097                 NHP--;
   1098             }
   1099         }
   1100 
   1101         // Follow the tree from all primary service processes to all
   1102         // processes they are dependent on, marking these processes as
   1103         // still being active and determining if anything has changed.
   1104         final int NAP = mServiceProcessesByPid.size();
   1105         for (int i=0; i<NAP; i++) {
   1106             ProcessItem proc = mServiceProcessesByPid.valueAt(i);
   1107             if (proc.mCurSeq == mSequence) {
   1108                 changed |= proc.buildDependencyChain(context, pm, mSequence);
   1109             }
   1110         }
   1111 
   1112         // Look for services and their primary processes that no longer exist...
   1113         ArrayList<Integer> uidToDelete = null;
   1114         for (int i=0; i<mServiceProcessesByName.size(); i++) {
   1115             HashMap<String, ProcessItem> procs = mServiceProcessesByName.valueAt(i);
   1116             Iterator<ProcessItem> pit = procs.values().iterator();
   1117             while (pit.hasNext()) {
   1118                 ProcessItem pi = pit.next();
   1119                 if (pi.mCurSeq == mSequence) {
   1120                     pi.ensureLabel(pm);
   1121                     if (pi.mPid == 0) {
   1122                         // Sanity: a non-process can't be dependent on
   1123                         // anything.
   1124                         pi.mDependentProcesses.clear();
   1125                     }
   1126                 } else {
   1127                     changed = true;
   1128                     pit.remove();
   1129                     if (procs.size() == 0) {
   1130                         if (uidToDelete == null) {
   1131                             uidToDelete = new ArrayList<Integer>();
   1132                         }
   1133                         uidToDelete.add(mServiceProcessesByName.keyAt(i));
   1134                     }
   1135                     if (pi.mPid != 0) {
   1136                         mServiceProcessesByPid.remove(pi.mPid);
   1137                     }
   1138                     continue;
   1139                 }
   1140                 Iterator<ServiceItem> sit = pi.mServices.values().iterator();
   1141                 while (sit.hasNext()) {
   1142                     ServiceItem si = sit.next();
   1143                     if (si.mCurSeq != mSequence) {
   1144                         changed = true;
   1145                         sit.remove();
   1146                     }
   1147                 }
   1148             }
   1149         }
   1150 
   1151         if (uidToDelete != null) {
   1152             for (int i = 0; i < uidToDelete.size(); i++) {
   1153                 int uid = uidToDelete.get(i);
   1154                 mServiceProcessesByName.remove(uid);
   1155             }
   1156         }
   1157 
   1158         if (changed) {
   1159             // First determine an order for the services.
   1160             ArrayList<ProcessItem> sortedProcesses = new ArrayList<ProcessItem>();
   1161             for (int i=0; i<mServiceProcessesByName.size(); i++) {
   1162                 for (ProcessItem pi : mServiceProcessesByName.valueAt(i).values()) {
   1163                     pi.mIsSystem = false;
   1164                     pi.mIsStarted = true;
   1165                     pi.mActiveSince = Long.MAX_VALUE;
   1166                     for (ServiceItem si : pi.mServices.values()) {
   1167                         if (si.mServiceInfo != null
   1168                                 && (si.mServiceInfo.applicationInfo.flags
   1169                                         & ApplicationInfo.FLAG_SYSTEM) != 0) {
   1170                             pi.mIsSystem = true;
   1171                         }
   1172                         if (si.mRunningService != null
   1173                                 && si.mRunningService.clientLabel != 0) {
   1174                             pi.mIsStarted = false;
   1175                             if (pi.mActiveSince > si.mRunningService.activeSince) {
   1176                                 pi.mActiveSince = si.mRunningService.activeSince;
   1177                             }
   1178                         }
   1179                     }
   1180                     sortedProcesses.add(pi);
   1181                 }
   1182             }
   1183 
   1184             Collections.sort(sortedProcesses, mServiceProcessComparator);
   1185 
   1186             ArrayList<BaseItem> newItems = new ArrayList<BaseItem>();
   1187             ArrayList<MergedItem> newMergedItems = new ArrayList<MergedItem>();
   1188             mProcessItems.clear();
   1189             for (int i=0; i<sortedProcesses.size(); i++) {
   1190                 ProcessItem pi = sortedProcesses.get(i);
   1191                 pi.mNeedDivider = false;
   1192 
   1193                 int firstProc = mProcessItems.size();
   1194                 // First add processes we are dependent on.
   1195                 pi.addDependentProcesses(newItems, mProcessItems);
   1196                 // And add the process itself.
   1197                 newItems.add(pi);
   1198                 if (pi.mPid > 0) {
   1199                     mProcessItems.add(pi);
   1200                 }
   1201 
   1202                 // Now add the services running in it.
   1203                 MergedItem mergedItem = null;
   1204                 boolean haveAllMerged = false;
   1205                 boolean needDivider = false;
   1206                 for (ServiceItem si : pi.mServices.values()) {
   1207                     si.mNeedDivider = needDivider;
   1208                     needDivider = true;
   1209                     newItems.add(si);
   1210                     if (si.mMergedItem != null) {
   1211                         if (mergedItem != null && mergedItem != si.mMergedItem) {
   1212                             haveAllMerged = false;
   1213                         }
   1214                         mergedItem = si.mMergedItem;
   1215                     } else {
   1216                         haveAllMerged = false;
   1217                     }
   1218                 }
   1219 
   1220                 if (!haveAllMerged || mergedItem == null
   1221                         || mergedItem.mServices.size() != pi.mServices.size()) {
   1222                     // Whoops, we need to build a new MergedItem!
   1223                     mergedItem = new MergedItem(pi.mUserId);
   1224                     for (ServiceItem si : pi.mServices.values()) {
   1225                         mergedItem.mServices.add(si);
   1226                         si.mMergedItem = mergedItem;
   1227                     }
   1228                     mergedItem.mProcess = pi;
   1229                     mergedItem.mOtherProcesses.clear();
   1230                     for (int mpi=firstProc; mpi<(mProcessItems.size()-1); mpi++) {
   1231                         mergedItem.mOtherProcesses.add(mProcessItems.get(mpi));
   1232                     }
   1233                 }
   1234 
   1235                 mergedItem.update(context, false);
   1236                 if (mergedItem.mUserId != mMyUserId) {
   1237                     addOtherUserItem(context, newMergedItems, mOtherUserMergedItems, mergedItem);
   1238                 } else {
   1239                     newMergedItems.add(mergedItem);
   1240                 }
   1241             }
   1242 
   1243             // Finally, interesting processes need to be shown and will
   1244             // go at the top.
   1245             NHP = mInterestingProcesses.size();
   1246             for (int i=0; i<NHP; i++) {
   1247                 ProcessItem proc = mInterestingProcesses.get(i);
   1248                 if (proc.mClient == null && proc.mServices.size() <= 0) {
   1249                     if (proc.mMergedItem == null) {
   1250                         proc.mMergedItem = new MergedItem(proc.mUserId);
   1251                         proc.mMergedItem.mProcess = proc;
   1252                     }
   1253                     proc.mMergedItem.update(context, false);
   1254                     if (proc.mMergedItem.mUserId != mMyUserId) {
   1255                         addOtherUserItem(context, newMergedItems, mOtherUserMergedItems,
   1256                                 proc.mMergedItem);
   1257                     } else {
   1258                         newMergedItems.add(0, proc.mMergedItem);
   1259                     }
   1260                     mProcessItems.add(proc);
   1261                 }
   1262             }
   1263 
   1264             // Finally finally, user aggregated merged items need to be
   1265             // updated now that they have all of their children.
   1266             final int NU = mOtherUserMergedItems.size();
   1267             for (int i=0; i<NU; i++) {
   1268                 MergedItem user = mOtherUserMergedItems.valueAt(i);
   1269                 if (user.mCurSeq == mSequence) {
   1270                     user.update(context, false);
   1271                 }
   1272             }
   1273 
   1274             synchronized (mLock) {
   1275                 mItems = newItems;
   1276                 mMergedItems = newMergedItems;
   1277             }
   1278         }
   1279 
   1280         // Count number of interesting other (non-active) processes, and
   1281         // build a list of all processes we will retrieve memory for.
   1282         mAllProcessItems.clear();
   1283         mAllProcessItems.addAll(mProcessItems);
   1284         int numBackgroundProcesses = 0;
   1285         int numForegroundProcesses = 0;
   1286         int numServiceProcesses = 0;
   1287         NRP = mRunningProcesses.size();
   1288         for (int i=0; i<NRP; i++) {
   1289             ProcessItem proc = mRunningProcesses.valueAt(i);
   1290             if (proc.mCurSeq != mSequence) {
   1291                 // We didn't hit this process as a dependency on one
   1292                 // of our active ones, so add it up if needed.
   1293                 if (proc.mRunningProcessInfo.importance >=
   1294                         ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND) {
   1295                     numBackgroundProcesses++;
   1296                     mAllProcessItems.add(proc);
   1297                 } else if (proc.mRunningProcessInfo.importance <=
   1298                         ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE) {
   1299                     numForegroundProcesses++;
   1300                     mAllProcessItems.add(proc);
   1301                 } else {
   1302                     Log.i("RunningState", "Unknown non-service process: "
   1303                             + proc.mProcessName + " #" + proc.mPid);
   1304                 }
   1305             } else {
   1306                 numServiceProcesses++;
   1307             }
   1308         }
   1309 
   1310         long backgroundProcessMemory = 0;
   1311         long foregroundProcessMemory = 0;
   1312         long serviceProcessMemory = 0;
   1313         ArrayList<MergedItem> newBackgroundItems = null;
   1314         ArrayList<MergedItem> newUserBackgroundItems = null;
   1315         boolean diffUsers = false;
   1316         try {
   1317             final int numProc = mAllProcessItems.size();
   1318             int[] pids = new int[numProc];
   1319             for (int i=0; i<numProc; i++) {
   1320                 pids[i] = mAllProcessItems.get(i).mPid;
   1321             }
   1322             long[] pss = ActivityManagerNative.getDefault()
   1323                     .getProcessPss(pids);
   1324             int bgIndex = 0;
   1325             for (int i=0; i<pids.length; i++) {
   1326                 ProcessItem proc = mAllProcessItems.get(i);
   1327                 changed |= proc.updateSize(context, pss[i], mSequence);
   1328                 if (proc.mCurSeq == mSequence) {
   1329                     serviceProcessMemory += proc.mSize;
   1330                 } else if (proc.mRunningProcessInfo.importance >=
   1331                         ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND) {
   1332                     backgroundProcessMemory += proc.mSize;
   1333                     MergedItem mergedItem;
   1334                     if (newBackgroundItems != null) {
   1335                         mergedItem = proc.mMergedItem = new MergedItem(proc.mUserId);
   1336                         proc.mMergedItem.mProcess = proc;
   1337                         diffUsers |= mergedItem.mUserId != mMyUserId;
   1338                         newBackgroundItems.add(mergedItem);
   1339                     } else {
   1340                         if (bgIndex >= mBackgroundItems.size()
   1341                                 || mBackgroundItems.get(bgIndex).mProcess != proc) {
   1342                             newBackgroundItems = new ArrayList<MergedItem>(numBackgroundProcesses);
   1343                             for (int bgi=0; bgi<bgIndex; bgi++) {
   1344                                 mergedItem = mBackgroundItems.get(bgi);
   1345                                 diffUsers |= mergedItem.mUserId != mMyUserId;
   1346                                 newBackgroundItems.add(mergedItem);
   1347                             }
   1348                             mergedItem = proc.mMergedItem = new MergedItem(proc.mUserId);
   1349                             proc.mMergedItem.mProcess = proc;
   1350                             diffUsers |= mergedItem.mUserId != mMyUserId;
   1351                             newBackgroundItems.add(mergedItem);
   1352                         } else {
   1353                             mergedItem = mBackgroundItems.get(bgIndex);
   1354                         }
   1355                     }
   1356                     mergedItem.update(context, true);
   1357                     mergedItem.updateSize(context);
   1358                     bgIndex++;
   1359                 } else if (proc.mRunningProcessInfo.importance <=
   1360                         ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE) {
   1361                     foregroundProcessMemory += proc.mSize;
   1362                 }
   1363             }
   1364         } catch (RemoteException e) {
   1365         }
   1366 
   1367         if (newBackgroundItems == null) {
   1368             // One or more at the bottom may no longer exist.
   1369             if (mBackgroundItems.size() > numBackgroundProcesses) {
   1370                 newBackgroundItems = new ArrayList<MergedItem>(numBackgroundProcesses);
   1371                 for (int bgi=0; bgi<numBackgroundProcesses; bgi++) {
   1372                     MergedItem mergedItem = mBackgroundItems.get(bgi);
   1373                     diffUsers |= mergedItem.mUserId != mMyUserId;
   1374                     newBackgroundItems.add(mergedItem);
   1375                 }
   1376             }
   1377         }
   1378 
   1379         if (newBackgroundItems != null) {
   1380             // The background items have changed; we need to re-build the
   1381             // per-user items.
   1382             if (!diffUsers) {
   1383                 // Easy: there are no other users, we can just use the same array.
   1384                 newUserBackgroundItems = newBackgroundItems;
   1385             } else {
   1386                 // We now need to re-build the per-user list so that background
   1387                 // items for users are collapsed together.
   1388                 newUserBackgroundItems = new ArrayList<MergedItem>();
   1389                 final int NB = newBackgroundItems.size();
   1390                 for (int i=0; i<NB; i++) {
   1391                     MergedItem mergedItem = newBackgroundItems.get(i);
   1392                     if (mergedItem.mUserId != mMyUserId) {
   1393                         addOtherUserItem(context, newUserBackgroundItems,
   1394                                 mOtherUserBackgroundItems, mergedItem);
   1395                     } else {
   1396                         newUserBackgroundItems.add(mergedItem);
   1397                     }
   1398                 }
   1399                 // And user aggregated merged items need to be
   1400                 // updated now that they have all of their children.
   1401                 final int NU = mOtherUserBackgroundItems.size();
   1402                 for (int i=0; i<NU; i++) {
   1403                     MergedItem user = mOtherUserBackgroundItems.valueAt(i);
   1404                     if (user.mCurSeq == mSequence) {
   1405                         user.update(context, true);
   1406                         user.updateSize(context);
   1407                     }
   1408                 }
   1409             }
   1410         }
   1411 
   1412         for (int i=0; i<mMergedItems.size(); i++) {
   1413             mMergedItems.get(i).updateSize(context);
   1414         }
   1415 
   1416         synchronized (mLock) {
   1417             mNumBackgroundProcesses = numBackgroundProcesses;
   1418             mNumForegroundProcesses = numForegroundProcesses;
   1419             mNumServiceProcesses = numServiceProcesses;
   1420             mBackgroundProcessMemory = backgroundProcessMemory;
   1421             mForegroundProcessMemory = foregroundProcessMemory;
   1422             mServiceProcessMemory = serviceProcessMemory;
   1423             if (newBackgroundItems != null) {
   1424                 mBackgroundItems = newBackgroundItems;
   1425                 mUserBackgroundItems = newUserBackgroundItems;
   1426                 if (mWatchingBackgroundItems) {
   1427                     changed = true;
   1428                 }
   1429             }
   1430             if (!mHaveData) {
   1431                 mHaveData = true;
   1432                 mLock.notifyAll();
   1433             }
   1434         }
   1435 
   1436         return changed;
   1437     }
   1438 
   1439     void setWatchingBackgroundItems(boolean watching) {
   1440         synchronized (mLock) {
   1441             mWatchingBackgroundItems = watching;
   1442         }
   1443     }
   1444 
   1445     ArrayList<MergedItem> getCurrentMergedItems() {
   1446         synchronized (mLock) {
   1447             return mMergedItems;
   1448         }
   1449     }
   1450 
   1451     ArrayList<MergedItem> getCurrentBackgroundItems() {
   1452         synchronized (mLock) {
   1453             return mUserBackgroundItems;
   1454         }
   1455     }
   1456 }
   1457