2 * Copyright (C) 2010 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.android.settings.applications;
19 import com.android.settings.R;
20 import com.android.settings.users.UserUtils;
22 import android.app.ActivityManager;
23 import android.app.ActivityManagerNative;
24 import android.app.ActivityThread;
25 import android.content.ComponentName;
26 import android.content.Context;
27 import android.content.pm.ApplicationInfo;
28 import android.content.pm.PackageInfo;
29 import android.content.pm.PackageItemInfo;
30 import android.content.pm.PackageManager;
31 import android.content.pm.ServiceInfo;
32 import android.content.pm.UserInfo;
33 import android.content.res.Resources;
34 import android.graphics.drawable.Drawable;
35 import android.os.Handler;
36 import android.os.HandlerThread;
37 import android.os.Looper;
38 import android.os.Message;
39 import android.os.RemoteException;
40 import android.os.UserHandle;
41 import android.os.UserManager;
42 import android.text.format.Formatter;
43 import android.util.Log;
44 import android.util.SparseArray;
46 import java.util.ArrayList;
47 import java.util.Collections;
48 import java.util.Comparator;
49 import java.util.HashMap;
50 import java.util.Iterator;
51 import java.util.List;
54 * Singleton for retrieving and monitoring the state about all running
55 * applications/processes/services.
57 public class RunningState {
58 static final String TAG = "RunningState";
59 static final boolean DEBUG_COMPARE = false;
61 static Object sGlobalLock = new Object();
62 static RunningState sInstance;
64 static final int MSG_RESET_CONTENTS = 1;
65 static final int MSG_UPDATE_CONTENTS = 2;
66 static final int MSG_REFRESH_UI = 3;
67 static final int MSG_UPDATE_TIME = 4;
69 static final long TIME_UPDATE_DELAY = 1000;
70 static final long CONTENTS_UPDATE_DELAY = 2000;
72 static final int MAX_SERVICES = 100;
74 final Context mApplicationContext;
75 final ActivityManager mAm;
76 final PackageManager mPm;
77 final UserManager mUm;
80 OnRefreshUiListener mRefreshUiListener;
82 final InterestingConfigChanges mInterestingConfigChanges = new InterestingConfigChanges();
84 // Processes that are hosting a service we are interested in, organized
85 // by uid and name. Note that this mapping does not change even across
86 // service restarts, and during a restart there will still be a process
88 final SparseArray<HashMap<String, ProcessItem>> mServiceProcessesByName
89 = new SparseArray<HashMap<String, ProcessItem>>();
91 // Processes that are hosting a service we are interested in, organized
92 // by their pid. These disappear and re-appear as services are restarted.
93 final SparseArray<ProcessItem> mServiceProcessesByPid
94 = new SparseArray<ProcessItem>();
96 // Used to sort the interesting processes.
97 final ServiceProcessComparator mServiceProcessComparator
98 = new ServiceProcessComparator();
100 // Additional interesting processes to be shown to the user, even if
101 // there is no service running in them.
102 final ArrayList<ProcessItem> mInterestingProcesses = new ArrayList<ProcessItem>();
104 // All currently running processes, for finding dependencies etc.
105 final SparseArray<ProcessItem> mRunningProcesses
106 = new SparseArray<ProcessItem>();
108 // The processes associated with services, in sorted order.
109 final ArrayList<ProcessItem> mProcessItems = new ArrayList<ProcessItem>();
111 // All processes, used for retrieving memory information.
112 final ArrayList<ProcessItem> mAllProcessItems = new ArrayList<ProcessItem>();
114 // If there are other users on the device, these are the merged items
115 // representing all items that would be put in mMergedItems for that user.
116 final SparseArray<MergedItem> mOtherUserMergedItems = new SparseArray<MergedItem>();
118 // If there are other users on the device, these are the merged items
119 // representing all items that would be put in mUserBackgroundItems for that user.
120 final SparseArray<MergedItem> mOtherUserBackgroundItems = new SparseArray<MergedItem>();
122 // Tracking of information about users.
123 final SparseArray<UserState> mUsers = new SparseArray<UserState>();
125 static class AppProcessInfo {
126 final ActivityManager.RunningAppProcessInfo info;
128 boolean hasForegroundServices;
130 AppProcessInfo(ActivityManager.RunningAppProcessInfo _info) {
135 // Temporary structure used when updating above information.
136 final SparseArray<AppProcessInfo> mTmpAppProcesses = new SparseArray<AppProcessInfo>();
140 final Comparator<RunningState.MergedItem> mBackgroundComparator
141 = new Comparator<RunningState.MergedItem>() {
143 public int compare(MergedItem lhs, MergedItem rhs) {
145 Log.i(TAG, "Comparing " + lhs + " with " + rhs);
146 Log.i(TAG, " Proc " + lhs.mProcess + " with " + rhs.mProcess);
147 Log.i(TAG, " UserId " + lhs.mUserId + " with " + rhs.mUserId);
149 if (lhs.mUserId != rhs.mUserId) {
150 if (lhs.mUserId == mMyUserId) return -1;
151 if (rhs.mUserId == mMyUserId) return 1;
152 return lhs.mUserId < rhs.mUserId ? -1 : 1;
154 if (lhs.mProcess == rhs.mProcess) {
155 if (lhs.mLabel == rhs.mLabel) {
158 return lhs.mLabel != null ? lhs.mLabel.compareTo(rhs.mLabel) : -1;
160 if (lhs.mProcess == null) return -1;
161 if (rhs.mProcess == null) return 1;
162 if (DEBUG_COMPARE) Log.i(TAG, " Label " + lhs.mProcess.mLabel
163 + " with " + rhs.mProcess.mLabel);
164 final ActivityManager.RunningAppProcessInfo lhsInfo
165 = lhs.mProcess.mRunningProcessInfo;
166 final ActivityManager.RunningAppProcessInfo rhsInfo
167 = rhs.mProcess.mRunningProcessInfo;
168 final boolean lhsBg = lhsInfo.importance
169 >= ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND;
170 final boolean rhsBg = rhsInfo.importance
171 >= ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND;
172 if (DEBUG_COMPARE) Log.i(TAG, " Bg " + lhsBg + " with " + rhsBg);
173 if (lhsBg != rhsBg) {
174 return lhsBg ? 1 : -1;
176 final boolean lhsA = (lhsInfo.flags
177 & ActivityManager.RunningAppProcessInfo.FLAG_HAS_ACTIVITIES) != 0;
178 final boolean rhsA = (rhsInfo.flags
179 & ActivityManager.RunningAppProcessInfo.FLAG_HAS_ACTIVITIES) != 0;
180 if (DEBUG_COMPARE) Log.i(TAG, " Act " + lhsA + " with " + rhsA);
182 return lhsA ? -1 : 1;
184 if (DEBUG_COMPARE) Log.i(TAG, " Lru " + lhsInfo.lru + " with " + rhsInfo.lru);
185 if (lhsInfo.lru != rhsInfo.lru) {
186 return lhsInfo.lru < rhsInfo.lru ? -1 : 1;
188 if (lhs.mProcess.mLabel == rhs.mProcess.mLabel) {
191 if (lhs.mProcess.mLabel == null) return 1;
192 if (rhs.mProcess.mLabel == null) return -1;
193 return lhs.mProcess.mLabel.compareTo(rhs.mProcess.mLabel);
197 // ----- following protected by mLock -----
199 // Lock for protecting the state that will be shared between the
200 // background update thread and the UI thread.
201 final Object mLock = new Object();
205 boolean mWatchingBackgroundItems;
207 ArrayList<BaseItem> mItems = new ArrayList<BaseItem>();
208 ArrayList<MergedItem> mMergedItems = new ArrayList<MergedItem>();
209 ArrayList<MergedItem> mBackgroundItems = new ArrayList<MergedItem>();
210 ArrayList<MergedItem> mUserBackgroundItems = new ArrayList<MergedItem>();
212 int mNumBackgroundProcesses;
213 long mBackgroundProcessMemory;
214 int mNumForegroundProcesses;
215 long mForegroundProcessMemory;
216 int mNumServiceProcesses;
217 long mServiceProcessMemory;
219 // ----- BACKGROUND MONITORING THREAD -----
221 final HandlerThread mBackgroundThread;
222 final class BackgroundHandler extends Handler {
223 public BackgroundHandler(Looper looper) {
228 public void handleMessage(Message msg) {
230 case MSG_RESET_CONTENTS:
233 case MSG_UPDATE_CONTENTS:
234 synchronized (mLock) {
239 Message cmd = mHandler.obtainMessage(MSG_REFRESH_UI);
240 cmd.arg1 = update(mApplicationContext, mAm) ? 1 : 0;
241 mHandler.sendMessage(cmd);
242 removeMessages(MSG_UPDATE_CONTENTS);
243 msg = obtainMessage(MSG_UPDATE_CONTENTS);
244 sendMessageDelayed(msg, CONTENTS_UPDATE_DELAY);
250 final BackgroundHandler mBackgroundHandler;
252 final Handler mHandler = new Handler() {
253 int mNextUpdate = OnRefreshUiListener.REFRESH_TIME;
256 public void handleMessage(Message msg) {
259 mNextUpdate = msg.arg1 != 0
260 ? OnRefreshUiListener.REFRESH_STRUCTURE
261 : OnRefreshUiListener.REFRESH_DATA;
263 case MSG_UPDATE_TIME:
264 synchronized (mLock) {
269 removeMessages(MSG_UPDATE_TIME);
270 Message m = obtainMessage(MSG_UPDATE_TIME);
271 sendMessageDelayed(m, TIME_UPDATE_DELAY);
273 if (mRefreshUiListener != null) {
274 //Log.i("foo", "Refresh UI: " + mNextUpdate
275 // + " @ " + SystemClock.uptimeMillis());
276 mRefreshUiListener.onRefreshUi(mNextUpdate);
277 mNextUpdate = OnRefreshUiListener.REFRESH_TIME;
284 // ----- DATA STRUCTURES -----
286 static interface OnRefreshUiListener {
287 public static final int REFRESH_TIME = 0;
288 public static final int REFRESH_DATA = 1;
289 public static final int REFRESH_STRUCTURE = 2;
291 public void onRefreshUi(int what);
294 static class UserState {
300 static class BaseItem {
301 final boolean mIsProcess;
304 PackageItemInfo mPackageInfo;
305 CharSequence mDisplayLabel;
315 boolean mNeedDivider;
318 public BaseItem(boolean isProcess, int userId) {
319 mIsProcess = isProcess;
323 public Drawable loadIcon(Context context, RunningState state) {
324 if (mPackageInfo != null) {
325 return mPackageInfo.loadIcon(state.mPm);
331 static class ServiceItem extends BaseItem {
332 ActivityManager.RunningServiceInfo mRunningService;
333 ServiceInfo mServiceInfo;
334 boolean mShownAsStarted;
336 MergedItem mMergedItem;
338 public ServiceItem(int userId) {
339 super(false, userId);
343 static class ProcessItem extends BaseItem {
344 final HashMap<ComponentName, ServiceItem> mServices
345 = new HashMap<ComponentName, ServiceItem>();
346 final SparseArray<ProcessItem> mDependentProcesses
347 = new SparseArray<ProcessItem>();
350 final String mProcessName;
354 int mLastNumDependentProcesses;
357 ActivityManager.RunningAppProcessInfo mRunningProcessInfo;
359 MergedItem mMergedItem;
361 boolean mInteresting;
363 // Purely for sorting.
368 public ProcessItem(Context context, int uid, String processName) {
369 super(true, UserHandle.getUserId(uid));
370 mDescription = context.getResources().getString(
371 R.string.service_process_name, processName);
373 mProcessName = processName;
376 void ensureLabel(PackageManager pm) {
377 if (mLabel != null) {
382 ApplicationInfo ai = pm.getApplicationInfo(mProcessName,
383 PackageManager.GET_UNINSTALLED_PACKAGES);
384 if (ai.uid == mUid) {
385 mDisplayLabel = ai.loadLabel(pm);
386 mLabel = mDisplayLabel.toString();
390 } catch (PackageManager.NameNotFoundException e) {
393 // If we couldn't get information about the overall
394 // process, try to find something about the uid.
395 String[] pkgs = pm.getPackagesForUid(mUid);
397 // If there is one package with this uid, that is what we want.
398 if (pkgs.length == 1) {
400 ApplicationInfo ai = pm.getApplicationInfo(pkgs[0],
401 PackageManager.GET_UNINSTALLED_PACKAGES);
402 mDisplayLabel = ai.loadLabel(pm);
403 mLabel = mDisplayLabel.toString();
406 } catch (PackageManager.NameNotFoundException e) {
410 // If there are multiple, see if one gives us the official name
412 for (String name : pkgs) {
414 PackageInfo pi = pm.getPackageInfo(name, 0);
415 if (pi.sharedUserLabel != 0) {
416 CharSequence nm = pm.getText(name,
417 pi.sharedUserLabel, pi.applicationInfo);
420 mLabel = nm.toString();
421 mPackageInfo = pi.applicationInfo;
425 } catch (PackageManager.NameNotFoundException e) {
429 // If still don't have anything to display, just use the
431 if (mServices.size() > 0) {
432 ApplicationInfo ai = mServices.values().iterator().next()
433 .mServiceInfo.applicationInfo;
435 mDisplayLabel = mPackageInfo.loadLabel(pm);
436 mLabel = mDisplayLabel.toString();
440 // Finally... whatever, just pick the first package's name.
442 ApplicationInfo ai = pm.getApplicationInfo(pkgs[0],
443 PackageManager.GET_UNINSTALLED_PACKAGES);
444 mDisplayLabel = ai.loadLabel(pm);
445 mLabel = mDisplayLabel.toString();
448 } catch (PackageManager.NameNotFoundException e) {
452 boolean updateService(Context context, ActivityManager.RunningServiceInfo service) {
453 final PackageManager pm = context.getPackageManager();
455 boolean changed = false;
456 ServiceItem si = mServices.get(service.service);
459 si = new ServiceItem(mUserId);
460 si.mRunningService = service;
462 si.mServiceInfo = ActivityThread.getPackageManager().getServiceInfo(
463 service.service, PackageManager.GET_UNINSTALLED_PACKAGES,
464 UserHandle.getUserId(service.uid));
466 if (si.mServiceInfo == null) {
467 Log.d("RunningService", "getServiceInfo returned null for: "
471 } catch (RemoteException e) {
473 si.mDisplayLabel = makeLabel(pm,
474 si.mRunningService.service.getClassName(), si.mServiceInfo);
475 mLabel = mDisplayLabel != null ? mDisplayLabel.toString() : null;
476 si.mPackageInfo = si.mServiceInfo.applicationInfo;
477 mServices.put(service.service, si);
479 si.mCurSeq = mCurSeq;
480 si.mRunningService = service;
481 long activeSince = service.restarting == 0 ? service.activeSince : -1;
482 if (si.mActiveSince != activeSince) {
483 si.mActiveSince = activeSince;
486 if (service.clientPackage != null && service.clientLabel != 0) {
487 if (si.mShownAsStarted) {
488 si.mShownAsStarted = false;
492 Resources clientr = pm.getResourcesForApplication(service.clientPackage);
493 String label = clientr.getString(service.clientLabel);
494 si.mDescription = context.getResources().getString(
495 R.string.service_client_name, label);
496 } catch (PackageManager.NameNotFoundException e) {
497 si.mDescription = null;
500 if (!si.mShownAsStarted) {
501 si.mShownAsStarted = true;
504 si.mDescription = context.getResources().getString(
505 R.string.service_started_by_app);
511 boolean updateSize(Context context, long pss, int curSeq) {
513 if (mCurSeq == curSeq) {
514 String sizeStr = Formatter.formatShortFileSize(
516 if (!sizeStr.equals(mSizeStr)){
518 // We update this on the second tick where we update just
519 // the text in the current items, so no need to say we
527 boolean buildDependencyChain(Context context, PackageManager pm, int curSeq) {
528 final int NP = mDependentProcesses.size();
529 boolean changed = false;
530 for (int i=0; i<NP; i++) {
531 ProcessItem proc = mDependentProcesses.valueAt(i);
532 if (proc.mClient != this) {
536 proc.mCurSeq = curSeq;
537 proc.ensureLabel(pm);
538 changed |= proc.buildDependencyChain(context, pm, curSeq);
541 if (mLastNumDependentProcesses != mDependentProcesses.size()) {
543 mLastNumDependentProcesses = mDependentProcesses.size();
549 void addDependentProcesses(ArrayList<BaseItem> dest,
550 ArrayList<ProcessItem> destProc) {
551 final int NP = mDependentProcesses.size();
552 for (int i=0; i<NP; i++) {
553 ProcessItem proc = mDependentProcesses.valueAt(i);
554 proc.addDependentProcesses(dest, destProc);
563 static class MergedItem extends BaseItem {
564 ProcessItem mProcess;
566 final ArrayList<ProcessItem> mOtherProcesses = new ArrayList<ProcessItem>();
567 final ArrayList<ServiceItem> mServices = new ArrayList<ServiceItem>();
568 final ArrayList<MergedItem> mChildren = new ArrayList<MergedItem>();
570 private int mLastNumProcesses = -1, mLastNumServices = -1;
572 MergedItem(int userId) {
573 super(false, userId);
576 private void setDescription(Context context, int numProcesses, int numServices) {
577 if (mLastNumProcesses != numProcesses || mLastNumServices != numServices) {
578 mLastNumProcesses = numProcesses;
579 mLastNumServices = numServices;
580 int resid = R.string.running_processes_item_description_s_s;
581 if (numProcesses != 1) {
582 resid = numServices != 1
583 ? R.string.running_processes_item_description_p_p
584 : R.string.running_processes_item_description_p_s;
585 } else if (numServices != 1) {
586 resid = R.string.running_processes_item_description_s_p;
588 mDescription = context.getResources().getString(resid, numProcesses,
593 boolean update(Context context, boolean background) {
594 mBackground = background;
597 // This is a merged item that contains a child collection
598 // of items... that is, it is an entire user, containing
599 // everything associated with that user. So set it up as such.
600 // For concrete stuff we need about the process of this item,
601 // we will just use the info from the first child.
602 MergedItem child0 = mChildren.get(0);
603 mPackageInfo = child0.mProcess.mPackageInfo;
604 mLabel = mUser != null ? mUser.mLabel : null;
605 mDisplayLabel = mLabel;
606 int numProcesses = 0;
609 for (int i=0; i<mChildren.size(); i++) {
610 MergedItem child = mChildren.get(i);
611 numProcesses += child.mLastNumProcesses;
612 numServices += child.mLastNumServices;
613 if (child.mActiveSince >= 0 && mActiveSince < child.mActiveSince) {
614 mActiveSince = child.mActiveSince;
618 setDescription(context, numProcesses, numServices);
621 mPackageInfo = mProcess.mPackageInfo;
622 mDisplayLabel = mProcess.mDisplayLabel;
623 mLabel = mProcess.mLabel;
626 setDescription(context, (mProcess.mPid > 0 ? 1 : 0) + mOtherProcesses.size(),
631 for (int i=0; i<mServices.size(); i++) {
632 ServiceItem si = mServices.get(i);
633 if (si.mActiveSince >= 0 && mActiveSince < si.mActiveSince) {
634 mActiveSince = si.mActiveSince;
642 boolean updateSize(Context context) {
645 for (int i=0; i<mChildren.size(); i++) {
646 MergedItem child = mChildren.get(i);
647 child.updateSize(context);
648 mSize += child.mSize;
651 mSize = mProcess.mSize;
652 for (int i=0; i<mOtherProcesses.size(); i++) {
653 mSize += mOtherProcesses.get(i).mSize;
657 String sizeStr = Formatter.formatShortFileSize(
659 if (!sizeStr.equals(mSizeStr)){
661 // We update this on the second tick where we update just
662 // the text in the current items, so no need to say we
669 public Drawable loadIcon(Context context, RunningState state) {
671 return super.loadIcon(context, state);
673 if (mUser.mIcon != null) {
674 return mUser.mIcon.getConstantState().newDrawable();
676 return context.getResources().getDrawable(
677 com.android.internal.R.drawable.ic_menu_cc);
681 class ServiceProcessComparator implements Comparator<ProcessItem> {
682 public int compare(ProcessItem object1, ProcessItem object2) {
683 if (object1.mUserId != object2.mUserId) {
684 if (object1.mUserId == mMyUserId) return -1;
685 if (object2.mUserId == mMyUserId) return 1;
686 return object1.mUserId < object2.mUserId ? -1 : 1;
688 if (object1.mIsStarted != object2.mIsStarted) {
689 // Non-started processes go last.
690 return object1.mIsStarted ? -1 : 1;
692 if (object1.mIsSystem != object2.mIsSystem) {
693 // System processes go below non-system.
694 return object1.mIsSystem ? 1 : -1;
696 if (object1.mActiveSince != object2.mActiveSince) {
697 // Remaining ones are sorted with the longest running
699 return (object1.mActiveSince > object2.mActiveSince) ? -1 : 1;
705 static CharSequence makeLabel(PackageManager pm,
706 String className, PackageItemInfo item) {
707 if (item != null && (item.labelRes != 0
708 || item.nonLocalizedLabel != null)) {
709 CharSequence label = item.loadLabel(pm);
715 String label = className;
716 int tail = label.lastIndexOf('.');
718 label = label.substring(tail+1, label.length());
723 static RunningState getInstance(Context context) {
724 synchronized (sGlobalLock) {
725 if (sInstance == null) {
726 sInstance = new RunningState(context);
732 private RunningState(Context context) {
733 mApplicationContext = context.getApplicationContext();
734 mAm = (ActivityManager)mApplicationContext.getSystemService(Context.ACTIVITY_SERVICE);
735 mPm = mApplicationContext.getPackageManager();
736 mUm = (UserManager)mApplicationContext.getSystemService(Context.USER_SERVICE);
737 mMyUserId = UserHandle.myUserId();
739 mBackgroundThread = new HandlerThread("RunningState:Background");
740 mBackgroundThread.start();
741 mBackgroundHandler = new BackgroundHandler(mBackgroundThread.getLooper());
744 void resume(OnRefreshUiListener listener) {
745 synchronized (mLock) {
747 mRefreshUiListener = listener;
748 if (mInterestingConfigChanges.applyNewConfig(mApplicationContext.getResources())) {
750 mBackgroundHandler.removeMessages(MSG_RESET_CONTENTS);
751 mBackgroundHandler.removeMessages(MSG_UPDATE_CONTENTS);
752 mBackgroundHandler.sendEmptyMessage(MSG_RESET_CONTENTS);
754 if (!mBackgroundHandler.hasMessages(MSG_UPDATE_CONTENTS)) {
755 mBackgroundHandler.sendEmptyMessage(MSG_UPDATE_CONTENTS);
757 mHandler.sendEmptyMessage(MSG_UPDATE_TIME);
762 synchronized (mLock) {
763 mBackgroundHandler.removeMessages(MSG_UPDATE_CONTENTS);
764 mBackgroundHandler.sendEmptyMessage(MSG_UPDATE_CONTENTS);
769 synchronized (mLock) {
775 synchronized (mLock) {
779 } catch (InterruptedException e) {
786 synchronized (mLock) {
788 mRefreshUiListener = null;
789 mHandler.removeMessages(MSG_UPDATE_TIME);
793 private boolean isInterestingProcess(ActivityManager.RunningAppProcessInfo pi) {
794 if ((pi.flags&ActivityManager.RunningAppProcessInfo.FLAG_CANT_SAVE_STATE) != 0) {
797 if ((pi.flags&ActivityManager.RunningAppProcessInfo.FLAG_PERSISTENT) == 0
798 && pi.importance >= ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND
799 && pi.importance < ActivityManager.RunningAppProcessInfo.IMPORTANCE_CANT_SAVE_STATE
800 && pi.importanceReasonCode
801 == ActivityManager.RunningAppProcessInfo.REASON_UNKNOWN) {
807 private void reset() {
808 mServiceProcessesByName.clear();
809 mServiceProcessesByPid.clear();
810 mInterestingProcesses.clear();
811 mRunningProcesses.clear();
812 mProcessItems.clear();
813 mAllProcessItems.clear();
817 private void addOtherUserItem(Context context, ArrayList<MergedItem> newMergedItems,
818 SparseArray<MergedItem> userItems, MergedItem newItem) {
819 MergedItem userItem = userItems.get(newItem.mUserId);
820 boolean first = userItem == null || userItem.mCurSeq != mSequence;
822 if (userItem == null) {
823 userItem = new MergedItem(newItem.mUserId);
824 userItems.put(newItem.mUserId, userItem);
826 userItem.mChildren.clear();
828 userItem.mCurSeq = mSequence;
829 if ((userItem.mUser=mUsers.get(newItem.mUserId)) == null) {
830 userItem.mUser = new UserState();
831 UserInfo info = mUm.getUserInfo(newItem.mUserId);
832 userItem.mUser.mInfo = info;
834 userItem.mUser.mIcon = UserUtils.getUserIcon(mUm, info,
835 context.getResources());
837 String name = info != null ? info.name : null;
839 name = Integer.toString(info.id);
841 userItem.mUser.mLabel = context.getResources().getString(
842 R.string.running_process_item_user_label, name);
844 newMergedItems.add(userItem);
846 userItem.mChildren.add(newItem);
849 private boolean update(Context context, ActivityManager am) {
850 final PackageManager pm = context.getPackageManager();
854 boolean changed = false;
856 // Retrieve list of services, filtering out anything that definitely
857 // won't be shown in the UI.
858 List<ActivityManager.RunningServiceInfo> services
859 = am.getRunningServices(MAX_SERVICES);
860 int NS = services != null ? services.size() : 0;
861 for (int i=0; i<NS; i++) {
862 ActivityManager.RunningServiceInfo si = services.get(i);
863 // We are not interested in services that have not been started
864 // and don't have a known client, because
865 // there is nothing the user can do about them.
866 if (!si.started && si.clientLabel == 0) {
872 // We likewise don't care about services running in a
873 // persistent process like the system or phone.
874 if ((si.flags&ActivityManager.RunningServiceInfo.FLAG_PERSISTENT_PROCESS)
883 // Retrieve list of running processes, organizing them into a sparse
884 // array for easy retrieval.
885 List<ActivityManager.RunningAppProcessInfo> processes
886 = am.getRunningAppProcesses();
887 final int NP = processes != null ? processes.size() : 0;
888 mTmpAppProcesses.clear();
889 for (int i=0; i<NP; i++) {
890 ActivityManager.RunningAppProcessInfo pi = processes.get(i);
891 mTmpAppProcesses.put(pi.pid, new AppProcessInfo(pi));
894 // Initial iteration through running services to collect per-process
896 for (int i=0; i<NS; i++) {
897 ActivityManager.RunningServiceInfo si = services.get(i);
898 if (si.restarting == 0 && si.pid > 0) {
899 AppProcessInfo ainfo = mTmpAppProcesses.get(si.pid);
901 ainfo.hasServices = true;
903 ainfo.hasForegroundServices = true;
909 // Update state we are maintaining about process that are running services.
910 for (int i=0; i<NS; i++) {
911 ActivityManager.RunningServiceInfo si = services.get(i);
913 // If this service's process is in use at a higher importance
914 // due to another process bound to one of its services, then we
915 // won't put it in the top-level list of services. Instead we
916 // want it to be included in the set of processes that the other
918 if (si.restarting == 0 && si.pid > 0) {
919 AppProcessInfo ainfo = mTmpAppProcesses.get(si.pid);
920 if (ainfo != null && !ainfo.hasForegroundServices) {
921 // This process does not have any foreground services.
922 // If its importance is greater than the service importance
923 // then there is something else more significant that is
924 // keeping it around that it should possibly be included as
925 // a part of instead of being shown by itself.
926 if (ainfo.info.importance
927 < ActivityManager.RunningAppProcessInfo.IMPORTANCE_SERVICE) {
928 // Follow process chain to see if there is something
929 // else that could be shown
930 boolean skip = false;
931 ainfo = mTmpAppProcesses.get(ainfo.info.importanceReasonPid);
932 while (ainfo != null) {
933 if (ainfo.hasServices || isInterestingProcess(ainfo.info)) {
937 ainfo = mTmpAppProcesses.get(ainfo.info.importanceReasonPid);
946 HashMap<String, ProcessItem> procs = mServiceProcessesByName.get(si.uid);
948 procs = new HashMap<String, ProcessItem>();
949 mServiceProcessesByName.put(si.uid, procs);
951 ProcessItem proc = procs.get(si.process);
954 proc = new ProcessItem(context, si.uid, si.process);
955 procs.put(si.process, proc);
958 if (proc.mCurSeq != mSequence) {
959 int pid = si.restarting == 0 ? si.pid : 0;
960 if (pid != proc.mPid) {
962 if (proc.mPid != pid) {
963 if (proc.mPid != 0) {
964 mServiceProcessesByPid.remove(proc.mPid);
967 mServiceProcessesByPid.put(pid, proc);
972 proc.mDependentProcesses.clear();
973 proc.mCurSeq = mSequence;
975 changed |= proc.updateService(context, si);
978 // Now update the map of other processes that are running (but
979 // don't have services actively running inside them).
980 for (int i=0; i<NP; i++) {
981 ActivityManager.RunningAppProcessInfo pi = processes.get(i);
982 ProcessItem proc = mServiceProcessesByPid.get(pi.pid);
984 // This process is not one that is a direct container
985 // of a service, so look for it in the secondary
987 proc = mRunningProcesses.get(pi.pid);
990 proc = new ProcessItem(context, pi.uid, pi.processName);
992 mRunningProcesses.put(pi.pid, proc);
994 proc.mDependentProcesses.clear();
997 if (isInterestingProcess(pi)) {
998 if (!mInterestingProcesses.contains(proc)) {
1000 mInterestingProcesses.add(proc);
1002 proc.mCurSeq = mSequence;
1003 proc.mInteresting = true;
1004 proc.ensureLabel(pm);
1006 proc.mInteresting = false;
1009 proc.mRunningSeq = mSequence;
1010 proc.mRunningProcessInfo = pi;
1013 // Build the chains from client processes to the process they are
1014 // dependent on; also remove any old running processes.
1015 int NRP = mRunningProcesses.size();
1016 for (int i = 0; i < NRP;) {
1017 ProcessItem proc = mRunningProcesses.valueAt(i);
1018 if (proc.mRunningSeq == mSequence) {
1019 int clientPid = proc.mRunningProcessInfo.importanceReasonPid;
1020 if (clientPid != 0) {
1021 ProcessItem client = mServiceProcessesByPid.get(clientPid);
1022 if (client == null) {
1023 client = mRunningProcesses.get(clientPid);
1025 if (client != null) {
1026 client.mDependentProcesses.put(proc.mPid, proc);
1029 // In this pass the process doesn't have a client.
1030 // Clear to make sure that, if it later gets the same one,
1031 // we will detect the change.
1032 proc.mClient = null;
1037 mRunningProcesses.remove(mRunningProcesses.keyAt(i));
1042 // Remove any old interesting processes.
1043 int NHP = mInterestingProcesses.size();
1044 for (int i=0; i<NHP; i++) {
1045 ProcessItem proc = mInterestingProcesses.get(i);
1046 if (!proc.mInteresting || mRunningProcesses.get(proc.mPid) == null) {
1048 mInterestingProcesses.remove(i);
1054 // Follow the tree from all primary service processes to all
1055 // processes they are dependent on, marking these processes as
1056 // still being active and determining if anything has changed.
1057 final int NAP = mServiceProcessesByPid.size();
1058 for (int i=0; i<NAP; i++) {
1059 ProcessItem proc = mServiceProcessesByPid.valueAt(i);
1060 if (proc.mCurSeq == mSequence) {
1061 changed |= proc.buildDependencyChain(context, pm, mSequence);
1065 // Look for services and their primary processes that no longer exist...
1066 ArrayList<Integer> uidToDelete = null;
1067 for (int i=0; i<mServiceProcessesByName.size(); i++) {
1068 HashMap<String, ProcessItem> procs = mServiceProcessesByName.valueAt(i);
1069 Iterator<ProcessItem> pit = procs.values().iterator();
1070 while (pit.hasNext()) {
1071 ProcessItem pi = pit.next();
1072 if (pi.mCurSeq == mSequence) {
1075 // Sanity: a non-process can't be dependent on
1077 pi.mDependentProcesses.clear();
1082 if (procs.size() == 0) {
1083 if (uidToDelete == null) {
1084 uidToDelete = new ArrayList<Integer>();
1086 uidToDelete.add(mServiceProcessesByName.keyAt(i));
1089 mServiceProcessesByPid.remove(pi.mPid);
1093 Iterator<ServiceItem> sit = pi.mServices.values().iterator();
1094 while (sit.hasNext()) {
1095 ServiceItem si = sit.next();
1096 if (si.mCurSeq != mSequence) {
1104 if (uidToDelete != null) {
1105 for (int i = 0; i < uidToDelete.size(); i++) {
1106 int uid = uidToDelete.get(i);
1107 mServiceProcessesByName.remove(uid);
1112 // First determine an order for the services.
1113 ArrayList<ProcessItem> sortedProcesses = new ArrayList<ProcessItem>();
1114 for (int i=0; i<mServiceProcessesByName.size(); i++) {
1115 for (ProcessItem pi : mServiceProcessesByName.valueAt(i).values()) {
1116 pi.mIsSystem = false;
1117 pi.mIsStarted = true;
1118 pi.mActiveSince = Long.MAX_VALUE;
1119 for (ServiceItem si : pi.mServices.values()) {
1120 if (si.mServiceInfo != null
1121 && (si.mServiceInfo.applicationInfo.flags
1122 & ApplicationInfo.FLAG_SYSTEM) != 0) {
1123 pi.mIsSystem = true;
1125 if (si.mRunningService != null
1126 && si.mRunningService.clientLabel != 0) {
1127 pi.mIsStarted = false;
1128 if (pi.mActiveSince > si.mRunningService.activeSince) {
1129 pi.mActiveSince = si.mRunningService.activeSince;
1133 sortedProcesses.add(pi);
1137 Collections.sort(sortedProcesses, mServiceProcessComparator);
1139 ArrayList<BaseItem> newItems = new ArrayList<BaseItem>();
1140 ArrayList<MergedItem> newMergedItems = new ArrayList<MergedItem>();
1141 SparseArray<MergedItem> otherUsers = null;
1142 mProcessItems.clear();
1143 for (int i=0; i<sortedProcesses.size(); i++) {
1144 ProcessItem pi = sortedProcesses.get(i);
1145 pi.mNeedDivider = false;
1147 int firstProc = mProcessItems.size();
1148 // First add processes we are dependent on.
1149 pi.addDependentProcesses(newItems, mProcessItems);
1150 // And add the process itself.
1153 mProcessItems.add(pi);
1156 // Now add the services running in it.
1157 MergedItem mergedItem = null;
1158 boolean haveAllMerged = false;
1159 boolean needDivider = false;
1160 for (ServiceItem si : pi.mServices.values()) {
1161 si.mNeedDivider = needDivider;
1164 if (si.mMergedItem != null) {
1165 if (mergedItem != null && mergedItem != si.mMergedItem) {
1166 haveAllMerged = false;
1168 mergedItem = si.mMergedItem;
1170 haveAllMerged = false;
1174 if (!haveAllMerged || mergedItem == null
1175 || mergedItem.mServices.size() != pi.mServices.size()) {
1176 // Whoops, we need to build a new MergedItem!
1177 mergedItem = new MergedItem(pi.mUserId);
1178 for (ServiceItem si : pi.mServices.values()) {
1179 mergedItem.mServices.add(si);
1180 si.mMergedItem = mergedItem;
1182 mergedItem.mProcess = pi;
1183 mergedItem.mOtherProcesses.clear();
1184 for (int mpi=firstProc; mpi<(mProcessItems.size()-1); mpi++) {
1185 mergedItem.mOtherProcesses.add(mProcessItems.get(mpi));
1189 mergedItem.update(context, false);
1190 if (mergedItem.mUserId != mMyUserId) {
1191 addOtherUserItem(context, newMergedItems, mOtherUserMergedItems, mergedItem);
1193 newMergedItems.add(mergedItem);
1197 // Finally, interesting processes need to be shown and will
1199 NHP = mInterestingProcesses.size();
1200 for (int i=0; i<NHP; i++) {
1201 ProcessItem proc = mInterestingProcesses.get(i);
1202 if (proc.mClient == null && proc.mServices.size() <= 0) {
1203 if (proc.mMergedItem == null) {
1204 proc.mMergedItem = new MergedItem(proc.mUserId);
1205 proc.mMergedItem.mProcess = proc;
1207 proc.mMergedItem.update(context, false);
1208 if (proc.mMergedItem.mUserId != mMyUserId) {
1209 addOtherUserItem(context, newMergedItems, mOtherUserMergedItems,
1212 newMergedItems.add(0, proc.mMergedItem);
1214 mProcessItems.add(proc);
1218 // Finally finally, user aggregated merged items need to be
1219 // updated now that they have all of their children.
1220 final int NU = mOtherUserMergedItems.size();
1221 for (int i=0; i<NU; i++) {
1222 MergedItem user = mOtherUserMergedItems.valueAt(i);
1223 if (user.mCurSeq == mSequence) {
1224 user.update(context, false);
1228 synchronized (mLock) {
1230 mMergedItems = newMergedItems;
1234 // Count number of interesting other (non-active) processes, and
1235 // build a list of all processes we will retrieve memory for.
1236 mAllProcessItems.clear();
1237 mAllProcessItems.addAll(mProcessItems);
1238 int numBackgroundProcesses = 0;
1239 int numForegroundProcesses = 0;
1240 int numServiceProcesses = 0;
1241 NRP = mRunningProcesses.size();
1242 for (int i=0; i<NRP; i++) {
1243 ProcessItem proc = mRunningProcesses.valueAt(i);
1244 if (proc.mCurSeq != mSequence) {
1245 // We didn't hit this process as a dependency on one
1246 // of our active ones, so add it up if needed.
1247 if (proc.mRunningProcessInfo.importance >=
1248 ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND) {
1249 numBackgroundProcesses++;
1250 mAllProcessItems.add(proc);
1251 } else if (proc.mRunningProcessInfo.importance <=
1252 ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE) {
1253 numForegroundProcesses++;
1254 mAllProcessItems.add(proc);
1256 Log.i("RunningState", "Unknown non-service process: "
1257 + proc.mProcessName + " #" + proc.mPid);
1260 numServiceProcesses++;
1264 long backgroundProcessMemory = 0;
1265 long foregroundProcessMemory = 0;
1266 long serviceProcessMemory = 0;
1267 ArrayList<MergedItem> newBackgroundItems = null;
1268 ArrayList<MergedItem> newUserBackgroundItems = null;
1269 boolean diffUsers = false;
1271 final int numProc = mAllProcessItems.size();
1272 int[] pids = new int[numProc];
1273 for (int i=0; i<numProc; i++) {
1274 pids[i] = mAllProcessItems.get(i).mPid;
1276 long[] pss = ActivityManagerNative.getDefault()
1277 .getProcessPss(pids);
1279 for (int i=0; i<pids.length; i++) {
1280 ProcessItem proc = mAllProcessItems.get(i);
1281 changed |= proc.updateSize(context, pss[i], mSequence);
1282 if (proc.mCurSeq == mSequence) {
1283 serviceProcessMemory += proc.mSize;
1284 } else if (proc.mRunningProcessInfo.importance >=
1285 ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND) {
1286 backgroundProcessMemory += proc.mSize;
1287 MergedItem mergedItem;
1288 if (newBackgroundItems != null) {
1289 mergedItem = proc.mMergedItem = new MergedItem(proc.mUserId);
1290 proc.mMergedItem.mProcess = proc;
1291 diffUsers |= mergedItem.mUserId != mMyUserId;
1292 newBackgroundItems.add(mergedItem);
1294 if (bgIndex >= mBackgroundItems.size()
1295 || mBackgroundItems.get(bgIndex).mProcess != proc) {
1296 newBackgroundItems = new ArrayList<MergedItem>(numBackgroundProcesses);
1297 for (int bgi=0; bgi<bgIndex; bgi++) {
1298 mergedItem = mBackgroundItems.get(bgi);
1299 diffUsers |= mergedItem.mUserId != mMyUserId;
1300 newBackgroundItems.add(mergedItem);
1302 mergedItem = proc.mMergedItem = new MergedItem(proc.mUserId);
1303 proc.mMergedItem.mProcess = proc;
1304 diffUsers |= mergedItem.mUserId != mMyUserId;
1305 newBackgroundItems.add(mergedItem);
1307 mergedItem = mBackgroundItems.get(bgIndex);
1310 mergedItem.update(context, true);
1311 mergedItem.updateSize(context);
1313 } else if (proc.mRunningProcessInfo.importance <=
1314 ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE) {
1315 foregroundProcessMemory += proc.mSize;
1318 } catch (RemoteException e) {
1321 if (newBackgroundItems == null) {
1322 // One or more at the bottom may no longer exist.
1323 if (mBackgroundItems.size() > numBackgroundProcesses) {
1324 newBackgroundItems = new ArrayList<MergedItem>(numBackgroundProcesses);
1325 for (int bgi=0; bgi<numBackgroundProcesses; bgi++) {
1326 MergedItem mergedItem = mBackgroundItems.get(bgi);
1327 diffUsers |= mergedItem.mUserId != mMyUserId;
1328 newBackgroundItems.add(mergedItem);
1333 if (newBackgroundItems != null) {
1334 // The background items have changed; we need to re-build the
1337 // Easy: there are no other users, we can just use the same array.
1338 newUserBackgroundItems = newBackgroundItems;
1340 // We now need to re-build the per-user list so that background
1341 // items for users are collapsed together.
1342 newUserBackgroundItems = new ArrayList<MergedItem>();
1343 final int NB = newBackgroundItems.size();
1344 for (int i=0; i<NB; i++) {
1345 MergedItem mergedItem = newBackgroundItems.get(i);
1346 if (mergedItem.mUserId != mMyUserId) {
1347 addOtherUserItem(context, newUserBackgroundItems,
1348 mOtherUserBackgroundItems, mergedItem);
1350 newUserBackgroundItems.add(mergedItem);
1353 // And user aggregated merged items need to be
1354 // updated now that they have all of their children.
1355 final int NU = mOtherUserBackgroundItems.size();
1356 for (int i=0; i<NU; i++) {
1357 MergedItem user = mOtherUserBackgroundItems.valueAt(i);
1358 if (user.mCurSeq == mSequence) {
1359 user.update(context, true);
1360 user.updateSize(context);
1366 for (int i=0; i<mMergedItems.size(); i++) {
1367 mMergedItems.get(i).updateSize(context);
1370 synchronized (mLock) {
1371 mNumBackgroundProcesses = numBackgroundProcesses;
1372 mNumForegroundProcesses = numForegroundProcesses;
1373 mNumServiceProcesses = numServiceProcesses;
1374 mBackgroundProcessMemory = backgroundProcessMemory;
1375 mForegroundProcessMemory = foregroundProcessMemory;
1376 mServiceProcessMemory = serviceProcessMemory;
1377 if (newBackgroundItems != null) {
1378 mBackgroundItems = newBackgroundItems;
1379 mUserBackgroundItems = newUserBackgroundItems;
1380 if (mWatchingBackgroundItems) {
1393 ArrayList<BaseItem> getCurrentItems() {
1394 synchronized (mLock) {
1399 void setWatchingBackgroundItems(boolean watching) {
1400 synchronized (mLock) {
1401 mWatchingBackgroundItems = watching;
1405 ArrayList<MergedItem> getCurrentMergedItems() {
1406 synchronized (mLock) {
1407 return mMergedItems;
1411 ArrayList<MergedItem> getCurrentBackgroundItems() {
1412 synchronized (mLock) {
1413 return mUserBackgroundItems;