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