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