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