diff options
author | Dianne Hackborn <hackbod@google.com> | 2010-06-24 11:29:50 -0700 |
---|---|---|
committer | Dianne Hackborn <hackbod@google.com> | 2010-06-24 12:38:33 -0700 |
commit | c01b0c83fca571229621d16b757a46dc0fae7dfe (patch) | |
tree | 519e7293ca2e3e566a8b979c29148820e79aad20 /src | |
parent | 12fd447d6f46b0c06072b0e9735589bf14cc53f6 (diff) | |
download | packages_apps_Settings-c01b0c83fca571229621d16b757a46dc0fae7dfe.zip packages_apps_Settings-c01b0c83fca571229621d16b757a46dc0fae7dfe.tar.gz packages_apps_Settings-c01b0c83fca571229621d16b757a46dc0fae7dfe.tar.bz2 |
Allow user to see and stop heavy-weight processes.
Change-Id: If5caed3972ab03a54fbf8c459cdfc136e4bdc020
Diffstat (limited to 'src')
4 files changed, 239 insertions, 148 deletions
diff --git a/src/com/android/settings/applications/RunningProcessesView.java b/src/com/android/settings/applications/RunningProcessesView.java index 9fbae6d..d63cc88 100644 --- a/src/com/android/settings/applications/RunningProcessesView.java +++ b/src/com/android/settings/applications/RunningProcessesView.java @@ -130,8 +130,16 @@ public class RunningProcessesView extends FrameLayout uptimeView.setText(DateUtils.formatElapsedTime(builder, (SystemClock.uptimeMillis()-mFirstRunTime)/1000)); } else { - uptimeView.setText(context.getResources().getText( - R.string.service_restarting)); + 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(""); + } } } } diff --git a/src/com/android/settings/applications/RunningServiceDetails.java b/src/com/android/settings/applications/RunningServiceDetails.java index f33625e..f469608 100644 --- a/src/com/android/settings/applications/RunningServiceDetails.java +++ b/src/com/android/settings/applications/RunningServiceDetails.java @@ -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,6 +169,129 @@ public class RunningServiceDetails extends Activity { return false; } + 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); + } + 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, bi, mBuilder); + + 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 != null && si.mServiceInfo.descriptionRes != 0) { + description.setText(getPackageManager().getText( + si.mServiceInfo.packageName, si.mServiceInfo.descriptionRes, + si.mServiceInfo.applicationInfo)); + } else { + if (detail.mManageIntent != null) { + try { + Resources clientr = getPackageManager().getResourcesForApplication( + si.mRunningService.clientPackage); + String label = clientr.getString(si.mRunningService.clientLabel); + description.setText(getString(R.string.service_manage_description, + label)); + } catch (PackageManager.NameNotFoundException e) { + } + } else { + description.setText(getText(si != null + ? R.string.service_stop_description + : R.string.heavy_weight_stop_description)); + } + } + + View button = root.findViewById(R.id.right_button); + button.setOnClickListener(detail); + ((TextView)button).setText(getText(detail.mManageIntent != null + ? R.string.service_manage : R.string.service_stop)); + root.findViewById(R.id.left_button).setVisibility(View.INVISIBLE); + + mActiveDetails.add(detail); + } + + 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); + } + mNumProcesses++; + + ActiveDetail detail = new ActiveDetail(); + View root = mInflater.inflate(R.layout.running_service_details_process, + mAllDetails, false); + mAllDetails.addView(root); + detail.mRootView = root; + detail.mViewHolder = new RunningProcessesView.ViewHolder(root); + detail.mActiveItem = detail.mViewHolder.bind(mState, pi, mBuilder); + + TextView description = (TextView)root.findViewById(R.id.comp_description); + if (isMain) { + description.setText(R.string.main_running_process_description); + } else { + int textid = 0; + CharSequence label = null; + ActivityManager.RunningAppProcessInfo rpi = pi.mRunningProcessInfo; + final ComponentName comp = rpi.importanceReasonComponent; + //Log.i(TAG, "Secondary proc: code=" + rpi.importanceReasonCode + // + " pid=" + rpi.importanceReasonPid + " comp=" + comp); + switch (rpi.importanceReasonCode) { + case ActivityManager.RunningAppProcessInfo.REASON_PROVIDER_IN_USE: + textid = R.string.process_provider_in_use_description; + List<ProviderInfo> providers = null; + if (comp != null) { + providers = getPackageManager() + .queryContentProviders(comp.getPackageName(), + rpi.uid, 0); + } + if (providers != null) { + for (int j=0; j<providers.size(); j++) { + ProviderInfo prov = providers.get(j); + if (comp.getClassName().equals(prov.name)) { + label = RunningState.makeLabel(getPackageManager(), + prov.name, prov); + break; + } + } + } + break; + case ActivityManager.RunningAppProcessInfo.REASON_SERVICE_IN_USE: + textid = R.string.process_service_in_use_description; + if (rpi.importanceReasonComponent != null) { + try { + ServiceInfo serv = getPackageManager().getServiceInfo( + rpi.importanceReasonComponent, 0); + label = RunningState.makeLabel(getPackageManager(), + serv.name, serv); + } catch (NameNotFoundException e) { + } + } + break; + } + if (textid != 0 && label != null) { + description.setText(getString(textid, label)); + } + } + + mActiveDetails.add(detail); + } + void addDetailViews() { for (int i=mActiveDetails.size()-1; i>=0; i--) { mAllDetails.removeView(mActiveDetails.get(i).mRootView); @@ -179,58 +308,20 @@ public class RunningServiceDetails extends Activity { mProcessesHeader = null; } + mNumServices = mNumProcesses = 0; + if (mMergedItem != null) { for (int i=0; i<mMergedItem.mServices.size(); i++) { - if (i == 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); - 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); - - if (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) { - description.setText(getPackageManager().getText( - si.mServiceInfo.packageName, si.mServiceInfo.descriptionRes, - si.mServiceInfo.applicationInfo)); - } else { - if (detail.mManageIntent != null) { - try { - Resources clientr = getPackageManager().getResourcesForApplication( - si.mRunningService.clientPackage); - String label = clientr.getString(si.mRunningService.clientLabel); - description.setText(getString(R.string.service_manage_description, - label)); - } catch (PackageManager.NameNotFoundException e) { - } - } else { - description.setText(getText(R.string.service_stop_description)); - } - } - - View button = root.findViewById(R.id.right_button); - button.setOnClickListener(detail); - ((TextView)button).setText(getText(detail.mManageIntent != null - ? R.string.service_manage : R.string.service_stop)); - root.findViewById(R.id.left_button).setVisibility(View.INVISIBLE); - - mActiveDetails.add(detail); + 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); } - boolean didProcess = false; for (int i=-1; i<mMergedItem.mOtherProcesses.size(); i++) { RunningState.ProcessItem pi = i < 0 ? mMergedItem.mProcess : mMergedItem.mOtherProcesses.get(i); @@ -238,70 +329,7 @@ public class RunningServiceDetails extends Activity { continue; } - if (!didProcess) { - mProcessesHeader = (TextView)mInflater.inflate(R.layout.separator_label, - mAllDetails, false); - mProcessesHeader.setText(R.string.runningservicedetails_processes_title); - mAllDetails.addView(mProcessesHeader); - didProcess = true; - } - ActiveDetail detail = new ActiveDetail(); - View root = mInflater.inflate(R.layout.running_service_details_process, - mAllDetails, false); - mAllDetails.addView(root); - detail.mRootView = root; - detail.mViewHolder = new RunningProcessesView.ViewHolder(root); - detail.mActiveItem = detail.mViewHolder.bind(mState, pi, mBuilder); - - TextView description = (TextView)root.findViewById(R.id.comp_description); - if (i < 0) { - description.setText(R.string.main_running_process_description); - } else { - int textid = 0; - CharSequence label = null; - ActivityManager.RunningAppProcessInfo rpi = pi.mRunningProcessInfo; - final ComponentName comp = rpi.importanceReasonComponent; - //Log.i(TAG, "Secondary proc: code=" + rpi.importanceReasonCode - // + " pid=" + rpi.importanceReasonPid + " comp=" + comp); - switch (rpi.importanceReasonCode) { - case ActivityManager.RunningAppProcessInfo.REASON_PROVIDER_IN_USE: - textid = R.string.process_provider_in_use_description; - List<ProviderInfo> providers = null; - if (comp != null) { - providers = getPackageManager() - .queryContentProviders(comp.getPackageName(), - rpi.uid, 0); - } - if (providers != null) { - for (int j=0; j<providers.size(); j++) { - ProviderInfo prov = providers.get(i); - if (comp.getClassName().equals(prov.name)) { - label = RunningState.makeLabel(getPackageManager(), - prov.name, prov); - break; - } - } - } - break; - case ActivityManager.RunningAppProcessInfo.REASON_SERVICE_IN_USE: - textid = R.string.process_service_in_use_description; - if (rpi.importanceReasonComponent != null) { - try { - ServiceInfo serv = getPackageManager().getServiceInfo( - rpi.importanceReasonComponent, 0); - label = RunningState.makeLabel(getPackageManager(), - serv.name, serv); - } catch (NameNotFoundException e) { - } - } - break; - } - if (textid != 0 && label != null) { - description.setText(getString(textid, label)); - } - } - - mActiveDetails.add(detail); + addProcessDetailsView(pi, i < 0); } } } diff --git a/src/com/android/settings/applications/RunningState.java b/src/com/android/settings/applications/RunningState.java index 2b51421..b3f56e3 100644 --- a/src/com/android/settings/applications/RunningState.java +++ b/src/com/android/settings/applications/RunningState.java @@ -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; diff --git a/src/com/android/settings/fuelgauge/BatteryHistoryChart.java b/src/com/android/settings/fuelgauge/BatteryHistoryChart.java index 43905dc..dba7809 100644 --- a/src/com/android/settings/fuelgauge/BatteryHistoryChart.java +++ b/src/com/android/settings/fuelgauge/BatteryHistoryChart.java @@ -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); } |