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