blob: dbe4a64c07138b0dddc031b7ac89163c0af06cf9 [file] [log] [blame]
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.applications;
import com.android.settings.R;
import android.app.ActivityManager;
import android.app.ActivityManagerNative;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageItemInfo;
import android.content.pm.PackageManager;
import android.content.pm.ServiceInfo;
import android.content.res.Resources;
import android.os.Debug;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
import android.os.SystemClock;
import android.text.format.Formatter;
import android.util.Log;
import android.util.SparseArray;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
/**
* Singleton for retrieving and monitoring the state about all running
* applications/processes/services.
*/
public class RunningState {
static Object sGlobalLock = new Object();
static RunningState sInstance;
static final int MSG_UPDATE_CONTENTS = 1;
static final int MSG_REFRESH_UI = 2;
static final int MSG_UPDATE_TIME = 3;
static final long TIME_UPDATE_DELAY = 1000;
static final long CONTENTS_UPDATE_DELAY = 2000;
static final int MAX_SERVICES = 100;
final Context mApplicationContext;
final ActivityManager mAm;
final PackageManager mPm;
OnRefreshUiListener mRefreshUiListener;
// Processes that are hosting a service we are interested in, organized
// by uid and name. Note that this mapping does not change even across
// service restarts, and during a restart there will still be a process
// entry.
final SparseArray<HashMap<String, ProcessItem>> mServiceProcessesByName
= new SparseArray<HashMap<String, ProcessItem>>();
// Processes that are hosting a service we are interested in, organized
// by their pid. These disappear and re-appear as services are restarted.
final SparseArray<ProcessItem> mServiceProcessesByPid
= new SparseArray<ProcessItem>();
// Used to sort the interesting processes.
final ServiceProcessComparator mServiceProcessComparator
= new ServiceProcessComparator();
// Additional interesting processes to be shown to the user, even if
// there is no service running in them.
final ArrayList<ProcessItem> mInterestingProcesses = new ArrayList<ProcessItem>();
// All currently running processes, for finding dependencies etc.
final SparseArray<ProcessItem> mRunningProcesses
= new SparseArray<ProcessItem>();
// The processes associated with services, in sorted order.
final ArrayList<ProcessItem> mProcessItems = new ArrayList<ProcessItem>();
// All processes, used for retrieving memory information.
final ArrayList<ProcessItem> mAllProcessItems = new ArrayList<ProcessItem>();
int mSequence = 0;
// ----- following protected by mLock -----
// Lock for protecting the state that will be shared between the
// background update thread and the UI thread.
final Object mLock = new Object();
boolean mResumed;
boolean mHaveData;
boolean mWatchingBackgroundItems;
ArrayList<BaseItem> mItems = new ArrayList<BaseItem>();
ArrayList<MergedItem> mMergedItems = new ArrayList<MergedItem>();
ArrayList<MergedItem> mBackgroundItems = new ArrayList<MergedItem>();
int mNumBackgroundProcesses;
long mBackgroundProcessMemory;
int mNumForegroundProcesses;
long mForegroundProcessMemory;
int mNumServiceProcesses;
long mServiceProcessMemory;
// ----- BACKGROUND MONITORING THREAD -----
final HandlerThread mBackgroundThread;
final class BackgroundHandler extends Handler {
public BackgroundHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_UPDATE_CONTENTS:
synchronized (mLock) {
if (!mResumed) {
return;
}
}
Message cmd = mHandler.obtainMessage(MSG_REFRESH_UI);
cmd.arg1 = update(mApplicationContext, mAm) ? 1 : 0;
mHandler.sendMessage(cmd);
removeMessages(MSG_UPDATE_CONTENTS);
msg = obtainMessage(MSG_UPDATE_CONTENTS);
sendMessageDelayed(msg, CONTENTS_UPDATE_DELAY);
break;
}
}
};
final BackgroundHandler mBackgroundHandler;
final Handler mHandler = new Handler() {
int mNextUpdate = OnRefreshUiListener.REFRESH_TIME;
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_REFRESH_UI:
mNextUpdate = msg.arg1 != 0
? OnRefreshUiListener.REFRESH_STRUCTURE
: OnRefreshUiListener.REFRESH_DATA;
break;
case MSG_UPDATE_TIME:
synchronized (mLock) {
if (!mResumed) {
return;
}
}
removeMessages(MSG_UPDATE_TIME);
Message m = obtainMessage(MSG_UPDATE_TIME);
sendMessageDelayed(m, TIME_UPDATE_DELAY);
if (mRefreshUiListener != null) {
//Log.i("foo", "Refresh UI: " + mNextUpdate
// + " @ " + SystemClock.uptimeMillis());
mRefreshUiListener.onRefreshUi(mNextUpdate);
mNextUpdate = OnRefreshUiListener.REFRESH_TIME;
}
break;
}
}
};
// ----- DATA STRUCTURES -----
static interface OnRefreshUiListener {
public static final int REFRESH_TIME = 0;
public static final int REFRESH_DATA = 1;
public static final int REFRESH_STRUCTURE = 2;
public void onRefreshUi(int what);
}
static class BaseItem {
final boolean mIsProcess;
PackageItemInfo mPackageInfo;
CharSequence mDisplayLabel;
String mLabel;
String mDescription;
int mCurSeq;
long mActiveSince;
long mSize;
String mSizeStr;
String mCurSizeStr;
boolean mNeedDivider;
boolean mBackground;
public BaseItem(boolean isProcess) {
mIsProcess = isProcess;
}
}
static class ServiceItem extends BaseItem {
ActivityManager.RunningServiceInfo mRunningService;
ServiceInfo mServiceInfo;
boolean mShownAsStarted;
MergedItem mMergedItem;
public ServiceItem() {
super(false);
}
}
static class ProcessItem extends BaseItem {
final HashMap<ComponentName, ServiceItem> mServices
= new HashMap<ComponentName, ServiceItem>();
final SparseArray<ProcessItem> mDependentProcesses
= new SparseArray<ProcessItem>();
final int mUid;
final String mProcessName;
int mPid;
ProcessItem mClient;
int mLastNumDependentProcesses;
int mRunningSeq;
ActivityManager.RunningAppProcessInfo mRunningProcessInfo;
MergedItem mMergedItem;
// Purely for sorting.
boolean mIsSystem;
boolean mIsStarted;
long mActiveSince;
public ProcessItem(Context context, int uid, String processName) {
super(true);
mDescription = context.getResources().getString(
R.string.service_process_name, processName);
mUid = uid;
mProcessName = processName;
}
void ensureLabel(PackageManager pm) {
if (mLabel != null) {
return;
}
try {
ApplicationInfo ai = pm.getApplicationInfo(mProcessName, 0);
if (ai.uid == mUid) {
mDisplayLabel = ai.loadLabel(pm);
mLabel = mDisplayLabel.toString();
mPackageInfo = ai;
return;
}
} catch (PackageManager.NameNotFoundException e) {
}
// If we couldn't get information about the overall
// process, try to find something about the uid.
String[] pkgs = pm.getPackagesForUid(mUid);
// If there is one package with this uid, that is what we want.
if (pkgs.length == 1) {
try {
ApplicationInfo ai = pm.getApplicationInfo(pkgs[0], 0);
mDisplayLabel = ai.loadLabel(pm);
mLabel = mDisplayLabel.toString();
mPackageInfo = ai;
return;
} catch (PackageManager.NameNotFoundException e) {
}
}
// If there are multiple, see if one gives us the official name
// for this uid.
for (String name : pkgs) {
try {
PackageInfo pi = pm.getPackageInfo(name, 0);
if (pi.sharedUserLabel != 0) {
CharSequence nm = pm.getText(name,
pi.sharedUserLabel, pi.applicationInfo);
if (nm != null) {
mDisplayLabel = nm;
mLabel = nm.toString();
mPackageInfo = pi.applicationInfo;
return;
}
}
} catch (PackageManager.NameNotFoundException e) {
}
}
// If still don't have anything to display, just use the
// service info.
if (mServices.size() > 0) {
mPackageInfo = mServices.values().iterator().next()
.mServiceInfo.applicationInfo;
mDisplayLabel = mPackageInfo.loadLabel(pm);
mLabel = mDisplayLabel.toString();
return;
}
// Finally... whatever, just pick the first package's name.
try {
ApplicationInfo ai = pm.getApplicationInfo(pkgs[0], 0);
mDisplayLabel = ai.loadLabel(pm);
mLabel = mDisplayLabel.toString();
mPackageInfo = ai;
return;
} catch (PackageManager.NameNotFoundException e) {
}
}
boolean updateService(Context context,
ActivityManager.RunningServiceInfo service) {
final PackageManager pm = context.getPackageManager();
boolean changed = false;
ServiceItem si = mServices.get(service.service);
if (si == null) {
changed = true;
si = new ServiceItem();
si.mRunningService = service;
try {
si.mServiceInfo = pm.getServiceInfo(service.service, 0);
} catch (PackageManager.NameNotFoundException e) {
}
si.mDisplayLabel = makeLabel(pm,
si.mRunningService.service.getClassName(), si.mServiceInfo);
mLabel = mDisplayLabel != null ? mDisplayLabel.toString() : null;
si.mPackageInfo = si.mServiceInfo.applicationInfo;
mServices.put(service.service, si);
}
si.mCurSeq = mCurSeq;
si.mRunningService = service;
long activeSince = service.restarting == 0 ? service.activeSince : -1;
if (si.mActiveSince != activeSince) {
si.mActiveSince = activeSince;
changed = true;
}
if (service.clientPackage != null && service.clientLabel != 0) {
if (si.mShownAsStarted) {
si.mShownAsStarted = false;
changed = true;
}
try {
Resources clientr = pm.getResourcesForApplication(service.clientPackage);
String label = clientr.getString(service.clientLabel);
si.mDescription = context.getResources().getString(
R.string.service_client_name, label);
} catch (PackageManager.NameNotFoundException e) {
si.mDescription = null;
}
} else {
if (!si.mShownAsStarted) {
si.mShownAsStarted = true;
changed = true;
}
si.mDescription = context.getResources().getString(
R.string.service_started_by_app);
}
return changed;
}
boolean updateSize(Context context, Debug.MemoryInfo mem, int curSeq) {
mSize = ((long)mem.getTotalPss()) * 1024;
if (mCurSeq == curSeq) {
String sizeStr = Formatter.formatShortFileSize(
context, mSize);
if (!sizeStr.equals(mSizeStr)){
mSizeStr = sizeStr;
// We update this on the second tick where we update just
// the text in the current items, so no need to say we
// changed here.
return false;
}
}
return false;
}
boolean buildDependencyChain(Context context, PackageManager pm, int curSeq) {
final int NP = mDependentProcesses.size();
boolean changed = false;
for (int i=0; i<NP; i++) {
ProcessItem proc = mDependentProcesses.valueAt(i);
if (proc.mClient != this) {
changed = true;
proc.mClient = this;
}
proc.mCurSeq = curSeq;
proc.ensureLabel(pm);
changed |= proc.buildDependencyChain(context, pm, curSeq);
}
if (mLastNumDependentProcesses != mDependentProcesses.size()) {
changed = true;
mLastNumDependentProcesses = mDependentProcesses.size();
}
return changed;
}
void addDependentProcesses(ArrayList<BaseItem> dest,
ArrayList<ProcessItem> destProc) {
final int NP = mDependentProcesses.size();
for (int i=0; i<NP; i++) {
ProcessItem proc = mDependentProcesses.valueAt(i);
proc.addDependentProcesses(dest, destProc);
dest.add(proc);
if (proc.mPid > 0) {
destProc.add(proc);
}
}
}
}
static class MergedItem extends BaseItem {
ProcessItem mProcess;
final ArrayList<ProcessItem> mOtherProcesses = new ArrayList<ProcessItem>();
final ArrayList<ServiceItem> mServices = new ArrayList<ServiceItem>();
private int mLastNumProcesses = -1, mLastNumServices = -1;
MergedItem() {
super(false);
}
boolean update(Context context, boolean background) {
mPackageInfo = mProcess.mPackageInfo;
mDisplayLabel = mProcess.mDisplayLabel;
mLabel = mProcess.mLabel;
mBackground = background;
if (!mBackground) {
int numProcesses = (mProcess.mPid > 0 ? 1 : 0) + mOtherProcesses.size();
int numServices = mServices.size();
if (mLastNumProcesses != numProcesses || mLastNumServices != numServices) {
mLastNumProcesses = numProcesses;
mLastNumServices = numServices;
int resid = R.string.running_processes_item_description_s_s;
if (numProcesses != 1) {
resid = numServices != 1
? R.string.running_processes_item_description_p_p
: R.string.running_processes_item_description_p_s;
} else if (numServices != 1) {
resid = R.string.running_processes_item_description_s_p;
}
mDescription = context.getResources().getString(resid, numProcesses,
numServices);
}
}
mActiveSince = -1;
for (int i=0; i<mServices.size(); i++) {
ServiceItem si = mServices.get(i);
if (si.mActiveSince >= 0 && mActiveSince < si.mActiveSince) {
mActiveSince = si.mActiveSince;
}
}
return false;
}
boolean updateSize(Context context) {
mSize = mProcess.mSize;
for (int i=0; i<mOtherProcesses.size(); i++) {
mSize += mOtherProcesses.get(i).mSize;
}
String sizeStr = Formatter.formatShortFileSize(
context, mSize);
if (!sizeStr.equals(mSizeStr)){
mSizeStr = sizeStr;
// We update this on the second tick where we update just
// the text in the current items, so no need to say we
// changed here.
return false;
}
return false;
}
}
static class ServiceProcessComparator implements Comparator<ProcessItem> {
public int compare(ProcessItem object1, ProcessItem object2) {
if (object1.mIsStarted != object2.mIsStarted) {
// Non-started processes go last.
return object1.mIsStarted ? -1 : 1;
}
if (object1.mIsSystem != object2.mIsSystem) {
// System processes go below non-system.
return object1.mIsSystem ? 1 : -1;
}
if (object1.mActiveSince != object2.mActiveSince) {
// Remaining ones are sorted with the longest running
// services last.
return (object1.mActiveSince > object2.mActiveSince) ? -1 : 1;
}
return 0;
}
}
static CharSequence makeLabel(PackageManager pm,
String className, PackageItemInfo item) {
if (item != null && (item.labelRes != 0
|| item.nonLocalizedLabel != null)) {
CharSequence label = item.loadLabel(pm);
if (label != null) {
return label;
}
}
String label = className;
int tail = label.lastIndexOf('.');
if (tail >= 0) {
label = label.substring(tail+1, label.length());
}
return label;
}
static RunningState getInstance(Context context) {
synchronized (sGlobalLock) {
if (sInstance == null) {
sInstance = new RunningState(context);
}
return sInstance;
}
}
private RunningState(Context context) {
mApplicationContext = context.getApplicationContext();
mAm = (ActivityManager)mApplicationContext.getSystemService(Context.ACTIVITY_SERVICE);
mPm = mApplicationContext.getPackageManager();
mResumed = false;
mBackgroundThread = new HandlerThread("RunningState:Background");
mBackgroundThread.start();
mBackgroundHandler = new BackgroundHandler(mBackgroundThread.getLooper());
}
void resume(OnRefreshUiListener listener) {
synchronized (mLock) {
mResumed = true;
mRefreshUiListener = listener;
if (!mBackgroundHandler.hasMessages(MSG_UPDATE_CONTENTS)) {
mBackgroundHandler.sendEmptyMessage(MSG_UPDATE_CONTENTS);
}
mHandler.sendEmptyMessage(MSG_UPDATE_TIME);
}
}
void updateNow() {
synchronized (mLock) {
mBackgroundHandler.removeMessages(MSG_UPDATE_CONTENTS);
mBackgroundHandler.sendEmptyMessage(MSG_UPDATE_CONTENTS);
}
}
boolean hasData() {
synchronized (mLock) {
return mHaveData;
}
}
void waitForData() {
synchronized (mLock) {
while (!mHaveData) {
try {
mLock.wait(0);
} catch (InterruptedException e) {
}
}
}
}
void pause() {
synchronized (mLock) {
mResumed = false;
mRefreshUiListener = null;
mHandler.removeMessages(MSG_UPDATE_TIME);
}
}
private boolean isInterestingProcess(ActivityManager.RunningAppProcessInfo pi) {
if ((pi.flags&ActivityManager.RunningAppProcessInfo.FLAG_CANT_SAVE_STATE) != 0) {
return true;
}
if ((pi.flags&ActivityManager.RunningAppProcessInfo.FLAG_PERSISTENT) == 0
&& pi.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND
&& pi.importanceReasonCode
== ActivityManager.RunningAppProcessInfo.REASON_UNKNOWN) {
return true;
}
return false;
}
private boolean update(Context context, ActivityManager am) {
final PackageManager pm = context.getPackageManager();
mSequence++;
boolean changed = false;
List<ActivityManager.RunningServiceInfo> services
= am.getRunningServices(MAX_SERVICES);
final int NS = services != null ? services.size() : 0;
for (int i=0; i<NS; i++) {
ActivityManager.RunningServiceInfo si = services.get(i);
// We are not interested in services that have not been started
// and don't have a known client, because
// there is nothing the user can do about them.
if (!si.started && si.clientLabel == 0) {
continue;
}
// We likewise don't care about services running in a
// persistent process like the system or phone.
if ((si.flags&ActivityManager.RunningServiceInfo.FLAG_PERSISTENT_PROCESS)
!= 0) {
continue;
}
HashMap<String, ProcessItem> procs = mServiceProcessesByName.get(si.uid);
if (procs == null) {
procs = new HashMap<String, ProcessItem>();
mServiceProcessesByName.put(si.uid, procs);
}
ProcessItem proc = procs.get(si.process);
if (proc == null) {
changed = true;
proc = new ProcessItem(context, si.uid, si.process);
procs.put(si.process, proc);
}
if (proc.mCurSeq != mSequence) {
int pid = si.restarting == 0 ? si.pid : 0;
if (pid != proc.mPid) {
changed = true;
if (proc.mPid != pid) {
if (proc.mPid != 0) {
mServiceProcessesByPid.remove(proc.mPid);
}
if (pid != 0) {
mServiceProcessesByPid.put(pid, proc);
}
proc.mPid = pid;
}
}
proc.mDependentProcesses.clear();
proc.mCurSeq = mSequence;
}
changed |= proc.updateService(context, si);
}
// Now update the map of other processes that are running (but
// don't have services actively running inside them).
List<ActivityManager.RunningAppProcessInfo> processes
= am.getRunningAppProcesses();
final int NP = processes != null ? processes.size() : 0;
for (int i=0; i<NP; i++) {
ActivityManager.RunningAppProcessInfo pi = processes.get(i);
ProcessItem proc = mServiceProcessesByPid.get(pi.pid);
if (proc == null) {
// This process is not one that is a direct container
// of a service, so look for it in the secondary
// running list.
proc = mRunningProcesses.get(pi.pid);
if (proc == null) {
changed = true;
proc = new ProcessItem(context, pi.uid, pi.processName);
proc.mPid = pi.pid;
mRunningProcesses.put(pi.pid, proc);
}
proc.mDependentProcesses.clear();
}
if (isInterestingProcess(pi)) {
if (!mInterestingProcesses.contains(proc)) {
changed = true;
mInterestingProcesses.add(proc);
}
proc.mCurSeq = mSequence;
proc.ensureLabel(pm);
}
proc.mRunningSeq = mSequence;
proc.mRunningProcessInfo = pi;
}
// Build the chains from client processes to the process they are
// dependent on; also remove any old running processes.
int NRP = mRunningProcesses.size();
for (int i=0; i<NRP; i++) {
ProcessItem proc = mRunningProcesses.valueAt(i);
if (proc.mRunningSeq == mSequence) {
int clientPid = proc.mRunningProcessInfo.importanceReasonPid;
if (clientPid != 0) {
ProcessItem client = mServiceProcessesByPid.get(clientPid);
if (client == null) {
client = mRunningProcesses.get(clientPid);
}
if (client != null) {
client.mDependentProcesses.put(proc.mPid, proc);
}
} else {
// In this pass the process doesn't have a client.
// Clear to make sure that, if it later gets the same one,
// we will detect the change.
proc.mClient = null;
}
} else {
changed = true;
mRunningProcesses.remove(mRunningProcesses.keyAt(i));
}
}
// Remove any old interesting processes.
int NHP = mInterestingProcesses.size();
for (int i=0; i<NHP; i++) {
ProcessItem proc = mInterestingProcesses.get(i);
if (mRunningProcesses.get(proc.mPid) == null) {
changed = true;
mInterestingProcesses.remove(i);
i--;
NHP--;
}
}
// Follow the tree from all primary service processes to all
// processes they are dependent on, marking these processes as
// still being active and determining if anything has changed.
final int NAP = mServiceProcessesByPid.size();
for (int i=0; i<NAP; i++) {
ProcessItem proc = mServiceProcessesByPid.valueAt(i);
if (proc.mCurSeq == mSequence) {
changed |= proc.buildDependencyChain(context, pm, mSequence);
}
}
// Look for services and their primary processes that no longer exist...
for (int i=0; i<mServiceProcessesByName.size(); i++) {
HashMap<String, ProcessItem> procs = mServiceProcessesByName.valueAt(i);
Iterator<ProcessItem> pit = procs.values().iterator();
while (pit.hasNext()) {
ProcessItem pi = pit.next();
if (pi.mCurSeq == mSequence) {
pi.ensureLabel(pm);
if (pi.mPid == 0) {
// Sanity: a non-process can't be dependent on
// anything.
pi.mDependentProcesses.clear();
}
} else {
changed = true;
pit.remove();
if (procs.size() == 0) {
mServiceProcessesByName.remove(mServiceProcessesByName.keyAt(i));
}
if (pi.mPid != 0) {
mServiceProcessesByPid.remove(pi.mPid);
}
continue;
}
Iterator<ServiceItem> sit = pi.mServices.values().iterator();
while (sit.hasNext()) {
ServiceItem si = sit.next();
if (si.mCurSeq != mSequence) {
changed = true;
sit.remove();
}
}
}
}
if (changed) {
// First determine an order for the services.
ArrayList<ProcessItem> sortedProcesses = new ArrayList<ProcessItem>();
for (int i=0; i<mServiceProcessesByName.size(); i++) {
for (ProcessItem pi : mServiceProcessesByName.valueAt(i).values()) {
pi.mIsSystem = false;
pi.mIsStarted = true;
pi.mActiveSince = Long.MAX_VALUE;
for (ServiceItem si : pi.mServices.values()) {
if (si.mServiceInfo != null
&& (si.mServiceInfo.applicationInfo.flags
& ApplicationInfo.FLAG_SYSTEM) != 0) {
pi.mIsSystem = true;
}
if (si.mRunningService != null
&& si.mRunningService.clientLabel != 0) {
pi.mIsStarted = false;
if (pi.mActiveSince > si.mRunningService.activeSince) {
pi.mActiveSince = si.mRunningService.activeSince;
}
}
}
sortedProcesses.add(pi);
}
}
Collections.sort(sortedProcesses, mServiceProcessComparator);
ArrayList<BaseItem> newItems = new ArrayList<BaseItem>();
ArrayList<MergedItem> newMergedItems = new ArrayList<MergedItem>();
mProcessItems.clear();
for (int i=0; i<sortedProcesses.size(); i++) {
ProcessItem pi = sortedProcesses.get(i);
pi.mNeedDivider = false;
int firstProc = mProcessItems.size();
// First add processes we are dependent on.
pi.addDependentProcesses(newItems, mProcessItems);
// And add the process itself.
newItems.add(pi);
if (pi.mPid > 0) {
mProcessItems.add(pi);
}
// Now add the services running in it.
MergedItem mergedItem = null;
boolean haveAllMerged = false;
boolean needDivider = false;
for (ServiceItem si : pi.mServices.values()) {
si.mNeedDivider = needDivider;
needDivider = true;
newItems.add(si);
if (si.mMergedItem != null) {
if (mergedItem != null && mergedItem != si.mMergedItem) {
haveAllMerged = false;
}
mergedItem = si.mMergedItem;
} else {
haveAllMerged = false;
}
}
if (!haveAllMerged || mergedItem == null
|| mergedItem.mServices.size() != pi.mServices.size()) {
// Whoops, we need to build a new MergedItem!
mergedItem = new MergedItem();
for (ServiceItem si : pi.mServices.values()) {
mergedItem.mServices.add(si);
si.mMergedItem = mergedItem;
}
mergedItem.mProcess = pi;
mergedItem.mOtherProcesses.clear();
for (int mpi=firstProc; mpi<(mProcessItems.size()-1); mpi++) {
mergedItem.mOtherProcesses.add(mProcessItems.get(mpi));
}
}
mergedItem.update(context, false);
newMergedItems.add(mergedItem);
}
// Finally, interesting processes need to be shown and will
// go at the top.
NHP = mInterestingProcesses.size();
for (int i=0; i<NHP; i++) {
ProcessItem proc = mInterestingProcesses.get(i);
if (proc.mClient == null && proc.mServices.size() <= 0) {
if (proc.mMergedItem == null) {
proc.mMergedItem = new MergedItem();
proc.mMergedItem.mProcess = proc;
}
proc.mMergedItem.update(context, false);
newMergedItems.add(0, proc.mMergedItem);
mProcessItems.add(proc);
}
}
synchronized (mLock) {
mItems = newItems;
mMergedItems = newMergedItems;
}
}
// Count number of interesting other (non-active) processes, and
// build a list of all processes we will retrieve memory for.
mAllProcessItems.clear();
mAllProcessItems.addAll(mProcessItems);
int numBackgroundProcesses = 0;
int numForegroundProcesses = 0;
int numServiceProcesses = 0;
NRP = mRunningProcesses.size();
for (int i=0; i<NRP; i++) {
ProcessItem proc = mRunningProcesses.valueAt(i);
if (proc.mCurSeq != mSequence) {
// We didn't hit this process as a dependency on one
// of our active ones, so add it up if needed.
if (proc.mRunningProcessInfo.importance >=
ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND) {
numBackgroundProcesses++;
mAllProcessItems.add(proc);
} else if (proc.mRunningProcessInfo.importance <=
ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE) {
numForegroundProcesses++;
mAllProcessItems.add(proc);
} else {
Log.i("RunningState", "Unknown non-service process: "
+ proc.mProcessName + " #" + proc.mPid);
}
} else {
numServiceProcesses++;
}
}
long backgroundProcessMemory = 0;
long foregroundProcessMemory = 0;
long serviceProcessMemory = 0;
ArrayList<MergedItem> newBackgroundItems = null;
try {
final int numProc = mAllProcessItems.size();
int[] pids = new int[numProc];
for (int i=0; i<numProc; i++) {
pids[i] = mAllProcessItems.get(i).mPid;
}
Debug.MemoryInfo[] mem = ActivityManagerNative.getDefault()
.getProcessMemoryInfo(pids);
int bgIndex = 0;
for (int i=0; i<pids.length; i++) {
ProcessItem proc = mAllProcessItems.get(i);
changed |= proc.updateSize(context, mem[i], mSequence);
if (proc.mCurSeq == mSequence) {
serviceProcessMemory += proc.mSize;
} else if (proc.mRunningProcessInfo.importance >=
ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND) {
backgroundProcessMemory += proc.mSize;
MergedItem mergedItem;
if (newBackgroundItems != null) {
mergedItem = proc.mMergedItem = new MergedItem();
proc.mMergedItem.mProcess = proc;
newBackgroundItems.add(mergedItem);
} else {
if (bgIndex >= mBackgroundItems.size()
|| mBackgroundItems.get(bgIndex).mProcess != proc) {
newBackgroundItems = new ArrayList<MergedItem>(numBackgroundProcesses);
for (int bgi=0; bgi<bgIndex; bgi++) {
newBackgroundItems.add(mBackgroundItems.get(bgi));
}
mergedItem = proc.mMergedItem = new MergedItem();
proc.mMergedItem.mProcess = proc;
newBackgroundItems.add(mergedItem);
} else {
mergedItem = mBackgroundItems.get(bgIndex);
}
}
mergedItem.update(context, true);
mergedItem.updateSize(context);
bgIndex++;
} else if (proc.mRunningProcessInfo.importance <=
ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE) {
foregroundProcessMemory += proc.mSize;
}
}
} catch (RemoteException e) {
}
if (newBackgroundItems == null) {
// One or more at the bottom may no longer exit.
if (mBackgroundItems.size() > numBackgroundProcesses) {
newBackgroundItems = new ArrayList<MergedItem>(numBackgroundProcesses);
for (int bgi=0; bgi<numBackgroundProcesses; bgi++) {
newBackgroundItems.add(mBackgroundItems.get(bgi));
}
}
}
for (int i=0; i<mMergedItems.size(); i++) {
mMergedItems.get(i).updateSize(context);
}
synchronized (mLock) {
mNumBackgroundProcesses = numBackgroundProcesses;
mNumForegroundProcesses = numForegroundProcesses;
mNumServiceProcesses = numServiceProcesses;
mBackgroundProcessMemory = backgroundProcessMemory;
mForegroundProcessMemory = foregroundProcessMemory;
mServiceProcessMemory = serviceProcessMemory;
if (newBackgroundItems != null) {
mBackgroundItems = newBackgroundItems;
if (mWatchingBackgroundItems) {
changed = true;
}
}
if (!mHaveData) {
mHaveData = true;
mLock.notifyAll();
}
}
return changed;
}
ArrayList<BaseItem> getCurrentItems() {
synchronized (mLock) {
return mItems;
}
}
void setWatchingBackgroundItems(boolean watching) {
synchronized (mLock) {
mWatchingBackgroundItems = watching;
}
}
ArrayList<MergedItem> getCurrentMergedItems() {
synchronized (mLock) {
return mMergedItems;
}
}
ArrayList<MergedItem> getCurrentBackgroundItems() {
synchronized (mLock) {
return mBackgroundItems;
}
}
}