Allow user to see and stop heavy-weight processes.

Change-Id: If5caed3972ab03a54fbf8c459cdfc136e4bdc020
This commit is contained in:
Dianne Hackborn
2010-06-24 11:29:50 -07:00
parent 12fd447d6f
commit c01b0c83fc
5 changed files with 242 additions and 148 deletions

View File

@@ -1788,6 +1788,9 @@ found in the list of installed applications.</string>
<!-- Running service details, default description for services that are started. -->
<string name="service_stop_description">This service was started by its
application. Stopping it may cause the application to fail.</string>
<!-- Running service details, description for running heavy-weight process. -->
<string name="heavy_weight_stop_description">This application can not safely
be stopped. Doing so may lose some of your current work.</string>
<!-- Running service details, default description for services that are managed. -->
<string name="service_manage_description"><xliff:g id="client_name">%1$s</xliff:g>:
currently in use. Touch Settings to control it.</string>

View File

@@ -130,8 +130,16 @@ public class RunningProcessesView extends FrameLayout
uptimeView.setText(DateUtils.formatElapsedTime(builder,
(SystemClock.uptimeMillis()-mFirstRunTime)/1000));
} else {
boolean isService = false;
if (mItem instanceof RunningState.MergedItem) {
isService = ((RunningState.MergedItem)mItem).mServices.size() > 0;
}
if (isService) {
uptimeView.setText(context.getResources().getText(
R.string.service_restarting));
} else {
uptimeView.setText("");
}
}
}
}

View File

@@ -55,6 +55,8 @@ public class RunningServiceDetails extends Activity {
RunningProcessesView.ActiveItem mSnippetActiveItem;
RunningProcessesView.ViewHolder mSnippetViewHolder;
int mNumServices, mNumProcesses;
TextView mServicesHeader;
TextView mProcessesHeader;
final ArrayList<ActiveDetail> mActiveDetails = new ArrayList<ActiveDetail>();
@@ -79,7 +81,7 @@ public class RunningServiceDetails extends Activity {
} catch (ActivityNotFoundException e) {
Log.w(TAG, e);
}
} else {
} else if (mActiveItem.mItem instanceof RunningState.ServiceItem) {
RunningState.ServiceItem si = (RunningState.ServiceItem)mActiveItem.mItem;
stopService(new Intent().setComponent(si.mRunningService.service));
if (mMergedItem == null || mMergedItem.mServices.size() <= 1) {
@@ -91,6 +93,10 @@ public class RunningServiceDetails extends Activity {
mBackgroundHandler.sendEmptyMessage(MSG_UPDATE_CONTENTS);
}
}
} else {
// Heavy-weight process. We'll do a force-stop on it.
mAm.forceStopPackage(mActiveItem.mItem.mPackageInfo.packageName);
finish();
}
}
}
@@ -163,46 +169,32 @@ public class RunningServiceDetails extends Activity {
return false;
}
void addDetailViews() {
for (int i=mActiveDetails.size()-1; i>=0; i--) {
mAllDetails.removeView(mActiveDetails.get(i).mRootView);
}
mActiveDetails.clear();
if (mServicesHeader != null) {
mAllDetails.removeView(mServicesHeader);
mServicesHeader = null;
}
if (mProcessesHeader != null) {
mAllDetails.removeView(mProcessesHeader);
mProcessesHeader = null;
}
if (mMergedItem != null) {
for (int i=0; i<mMergedItem.mServices.size(); i++) {
if (i == 0) {
void addServiceDetailsView(RunningState.ServiceItem si, RunningState.MergedItem mi) {
if (mNumServices == 0) {
mServicesHeader = (TextView)mInflater.inflate(R.layout.separator_label,
mAllDetails, false);
mServicesHeader.setText(R.string.runningservicedetails_services_title);
mAllDetails.addView(mServicesHeader);
}
RunningState.ServiceItem si = mMergedItem.mServices.get(i);
mNumServices++;
RunningState.BaseItem bi = si != null ? si : mi;
ActiveDetail detail = new ActiveDetail();
View root = mInflater.inflate(R.layout.running_service_details_service,
mAllDetails, false);
mAllDetails.addView(root);
detail.mRootView = root;
detail.mViewHolder = new RunningProcessesView.ViewHolder(root);
detail.mActiveItem = detail.mViewHolder.bind(mState, si, mBuilder);
detail.mActiveItem = detail.mViewHolder.bind(mState, bi, mBuilder);
if (si.mRunningService.clientLabel != 0) {
if (si != null && si.mRunningService.clientLabel != 0) {
detail.mManageIntent = mAm.getRunningServiceControlPanel(
si.mRunningService.service);
}
TextView description = (TextView)root.findViewById(R.id.comp_description);
if (si.mServiceInfo.descriptionRes != 0) {
if (si != null && si.mServiceInfo.descriptionRes != 0) {
description.setText(getPackageManager().getText(
si.mServiceInfo.packageName, si.mServiceInfo.descriptionRes,
si.mServiceInfo.applicationInfo));
@@ -217,7 +209,9 @@ public class RunningServiceDetails extends Activity {
} catch (PackageManager.NameNotFoundException e) {
}
} else {
description.setText(getText(R.string.service_stop_description));
description.setText(getText(si != null
? R.string.service_stop_description
: R.string.heavy_weight_stop_description));
}
}
@@ -230,21 +224,15 @@ public class RunningServiceDetails extends Activity {
mActiveDetails.add(detail);
}
boolean didProcess = false;
for (int i=-1; i<mMergedItem.mOtherProcesses.size(); i++) {
RunningState.ProcessItem pi = i < 0 ? mMergedItem.mProcess
: mMergedItem.mOtherProcesses.get(i);
if (pi.mPid <= 0) {
continue;
}
if (!didProcess) {
void addProcessDetailsView(RunningState.ProcessItem pi, boolean isMain) {
if (mNumProcesses == 0) {
mProcessesHeader = (TextView)mInflater.inflate(R.layout.separator_label,
mAllDetails, false);
mProcessesHeader.setText(R.string.runningservicedetails_processes_title);
mAllDetails.addView(mProcessesHeader);
didProcess = true;
}
mNumProcesses++;
ActiveDetail detail = new ActiveDetail();
View root = mInflater.inflate(R.layout.running_service_details_process,
mAllDetails, false);
@@ -254,7 +242,7 @@ public class RunningServiceDetails extends Activity {
detail.mActiveItem = detail.mViewHolder.bind(mState, pi, mBuilder);
TextView description = (TextView)root.findViewById(R.id.comp_description);
if (i < 0) {
if (isMain) {
description.setText(R.string.main_running_process_description);
} else {
int textid = 0;
@@ -274,7 +262,7 @@ public class RunningServiceDetails extends Activity {
}
if (providers != null) {
for (int j=0; j<providers.size(); j++) {
ProviderInfo prov = providers.get(i);
ProviderInfo prov = providers.get(j);
if (comp.getClassName().equals(prov.name)) {
label = RunningState.makeLabel(getPackageManager(),
prov.name, prov);
@@ -303,6 +291,46 @@ public class RunningServiceDetails extends Activity {
mActiveDetails.add(detail);
}
void addDetailViews() {
for (int i=mActiveDetails.size()-1; i>=0; i--) {
mAllDetails.removeView(mActiveDetails.get(i).mRootView);
}
mActiveDetails.clear();
if (mServicesHeader != null) {
mAllDetails.removeView(mServicesHeader);
mServicesHeader = null;
}
if (mProcessesHeader != null) {
mAllDetails.removeView(mProcessesHeader);
mProcessesHeader = null;
}
mNumServices = mNumProcesses = 0;
if (mMergedItem != null) {
for (int i=0; i<mMergedItem.mServices.size(); i++) {
addServiceDetailsView(mMergedItem.mServices.get(i), mMergedItem);
}
if (mMergedItem.mServices.size() <= 0) {
// This item does not have any services, so it must be
// a heavy-weight process... we will put a fake service
// entry for it, to allow the user to "stop" it.
addServiceDetailsView(null, mMergedItem);
}
for (int i=-1; i<mMergedItem.mOtherProcesses.size(); i++) {
RunningState.ProcessItem pi = i < 0 ? mMergedItem.mProcess
: mMergedItem.mOtherProcesses.get(i);
if (pi.mPid <= 0) {
continue;
}
addProcessDetailsView(pi, i < 0);
}
}
}

View File

@@ -47,18 +47,34 @@ import java.util.List;
*/
public class RunningState {
final SparseArray<HashMap<String, ProcessItem>> mProcesses
// 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>>();
final SparseArray<ProcessItem> mActiveProcesses
// 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();
// Temporary for finding process dependencies.
// Additional heavy-weight processes to be shown to the user, even if
// there is no service running in them.
final ArrayList<ProcessItem> mHeavyProcesses = 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;
@@ -128,6 +144,8 @@ public class RunningState {
int mRunningSeq;
ActivityManager.RunningAppProcessInfo mRunningProcessInfo;
MergedItem mMergedItem;
// Purely for sorting.
boolean mIsSystem;
boolean mIsStarted;
@@ -435,10 +453,10 @@ public class RunningState {
continue;
}
HashMap<String, ProcessItem> procs = mProcesses.get(si.uid);
HashMap<String, ProcessItem> procs = mServiceProcessesByName.get(si.uid);
if (procs == null) {
procs = new HashMap<String, ProcessItem>();
mProcesses.put(si.uid, procs);
mServiceProcessesByName.put(si.uid, procs);
}
ProcessItem proc = procs.get(si.process);
if (proc == null) {
@@ -453,10 +471,10 @@ public class RunningState {
changed = true;
if (proc.mPid != pid) {
if (proc.mPid != 0) {
mActiveProcesses.remove(proc.mPid);
mServiceProcessesByPid.remove(proc.mPid);
}
if (pid != 0) {
mActiveProcesses.put(pid, proc);
mServiceProcessesByPid.put(pid, proc);
}
proc.mPid = pid;
}
@@ -474,19 +492,30 @@ public class RunningState {
final int NP = processes != null ? processes.size() : 0;
for (int i=0; i<NP; i++) {
ActivityManager.RunningAppProcessInfo pi = processes.get(i);
ProcessItem proc = mActiveProcesses.get(pi.pid);
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 ((pi.flags&ActivityManager.RunningAppProcessInfo.FLAG_HEAVY_WEIGHT) != 0) {
if (!mHeavyProcesses.contains(proc)) {
changed = true;
mHeavyProcesses.add(proc);
}
proc.mCurSeq = mSequence;
proc.ensureLabel(pm);
}
proc.mRunningSeq = mSequence;
proc.mRunningProcessInfo = pi;
}
@@ -499,7 +528,7 @@ public class RunningState {
if (proc.mRunningSeq == mSequence) {
int clientPid = proc.mRunningProcessInfo.importanceReasonPid;
if (clientPid != 0) {
ProcessItem client = mActiveProcesses.get(clientPid);
ProcessItem client = mServiceProcessesByPid.get(clientPid);
if (client == null) {
client = mRunningProcesses.get(clientPid);
}
@@ -508,29 +537,42 @@ public class RunningState {
}
} else {
// In this pass the process doesn't have a client.
// Clear to make sure if it later gets the same one
// that we will detect the change.
// 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 heavy processes.
int NHP = mHeavyProcesses.size();
for (int i=0; i<NHP; i++) {
ProcessItem proc = mHeavyProcesses.get(i);
if (mRunningProcesses.get(proc.mPid) == null) {
changed = true;
mHeavyProcesses.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 = mActiveProcesses.size();
final int NAP = mServiceProcessesByPid.size();
for (int i=0; i<NAP; i++) {
ProcessItem proc = mActiveProcesses.valueAt(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<mProcesses.size(); i++) {
HashMap<String, ProcessItem> procs = mProcesses.valueAt(i);
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();
@@ -545,10 +587,10 @@ public class RunningState {
changed = true;
pit.remove();
if (procs.size() == 0) {
mProcesses.remove(mProcesses.keyAt(i));
mServiceProcessesByName.remove(mServiceProcessesByName.keyAt(i));
}
if (pi.mPid != 0) {
mActiveProcesses.remove(pi.mPid);
mServiceProcessesByPid.remove(pi.mPid);
}
continue;
}
@@ -566,8 +608,8 @@ public class RunningState {
if (changed) {
// First determine an order for the services.
ArrayList<ProcessItem> sortedProcesses = new ArrayList<ProcessItem>();
for (int i=0; i<mProcesses.size(); i++) {
for (ProcessItem pi : mProcesses.valueAt(i).values()) {
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;
@@ -643,6 +685,23 @@ public class RunningState {
mergedItem.update(context);
newMergedItems.add(mergedItem);
}
// Finally, heavy-weight processes need to be shown and will
// go at the top.
NHP = mHeavyProcesses.size();
for (int i=0; i<NHP; i++) {
ProcessItem proc = mHeavyProcesses.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);
newMergedItems.add(0, proc.mMergedItem);
mProcessItems.add(proc);
}
}
synchronized (mLock) {
mItems = newItems;
mMergedItems = newMergedItems;

View File

@@ -288,9 +288,9 @@ public class BatteryHistoryChart extends View {
}
void finishPaths(int w, int h, int levelh, int startX, int y, Path curLevelPath,
int lastBatX, boolean lastCharging, boolean lastScreenOn, Path lastPath) {
int lastX, boolean lastCharging, boolean lastScreenOn, Path lastPath) {
if (curLevelPath != null) {
if (lastBatX >= 0) {
if (lastX >= 0 && lastX < w) {
if (lastPath != null) {
lastPath.lineTo(w, y);
}
@@ -301,10 +301,10 @@ public class BatteryHistoryChart extends View {
curLevelPath.close();
}
if (lastCharging) {
if (lastCharging && lastX < w) {
mChargingPath.lineTo(w, h-mChargingOffset);
}
if (lastScreenOn) {
if (lastScreenOn && lastX < w) {
mScreenOnPath.lineTo(w, h-mScreenOnOffset);
}
}
@@ -329,7 +329,7 @@ public class BatteryHistoryChart extends View {
final int levelh = h - mLevelOffset;
BatteryStats.HistoryItem rec = mHistFirst;
int x = 0, y = 0, startX = 0, lastX = -1, lastY = -1, lastBatX = -1;
int x = 0, y = 0, startX = 0, lastX = -1, lastY = -1;
int i = 0;
Path curLevelPath = null;
Path lastLinePath = null;
@@ -342,11 +342,8 @@ public class BatteryHistoryChart extends View {
if (lastX != x) {
// We have moved by at least a pixel.
if (lastY == y) {
// Battery level is still the same; don't plot,
// but remember it.
lastBatX = x;
} else {
if (lastY != y) {
// Don't plot changes within a pixel.
Path path;
byte value = rec.batteryLevel;
if (value <= BATTERY_CRITICAL) path = mBatCriticalPath;
@@ -372,7 +369,6 @@ public class BatteryHistoryChart extends View {
}
lastX = x;
lastY = y;
lastBatX = -1;
final boolean charging =
(rec.states&HistoryItem.STATE_BATTERY_PLUGGED_FLAG) != 0;
@@ -399,9 +395,9 @@ public class BatteryHistoryChart extends View {
}
} else if (curLevelPath != null) {
finishPaths(x+1, h, levelh, startX, lastY, curLevelPath, lastBatX,
finishPaths(x+1, h, levelh, startX, lastY, curLevelPath, lastX,
lastCharging, lastScreenOn, lastLinePath);
lastX = lastY = lastBatX = -1;
lastX = lastY = -1;
curLevelPath = null;
lastLinePath = null;
lastCharging = lastScreenOn = false;
@@ -411,7 +407,7 @@ public class BatteryHistoryChart extends View {
i++;
}
finishPaths(w, h, levelh, startX, lastY, curLevelPath, lastBatX,
finishPaths(w, h, levelh, startX, lastY, curLevelPath, lastX,
lastCharging, lastScreenOn, lastLinePath);
}