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