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