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