Fix an issue in how running services are shown.
If a process had started services, but another process was
using it for more important reasons, it would still be shown
as just with running services. Now it will be bundled into
the other process that has a dependency on this.
This fixes an issue where something like a live wallpaper
that say sits forever with a connection to market's licensing
service would not show up to the user as using far more memory
than it should because it is keeping market running (if market
also leaves one of its services running, which it tends to do).
Change-Id: I0fd953b7221f7a3b35400007bb9e8437f09d066e
diff --git a/src/com/android/settings/applications/RunningState.java b/src/com/android/settings/applications/RunningState.java
index 552aa56..beb9605 100644
--- a/src/com/android/settings/applications/RunningState.java
+++ b/src/com/android/settings/applications/RunningState.java
@@ -99,7 +99,20 @@
// All processes, used for retrieving memory information.
final ArrayList<ProcessItem> mAllProcessItems = new ArrayList<ProcessItem>();
-
+
+ static class AppProcessInfo {
+ final ActivityManager.RunningAppProcessInfo info;
+ boolean hasServices;
+ boolean hasForegroundServices;
+
+ AppProcessInfo(ActivityManager.RunningAppProcessInfo _info) {
+ info = _info;
+ }
+ }
+
+ // Temporary structure used when updating above information.
+ final SparseArray<AppProcessInfo> mTmpAppProcesses = new SparseArray<AppProcessInfo>();
+
int mSequence = 0;
// ----- following protected by mLock -----
@@ -641,25 +654,97 @@
mSequence++;
boolean changed = false;
-
+
+ // Retrieve list of services, filtering out anything that definitely
+ // won't be shown in the UI.
List<ActivityManager.RunningServiceInfo> services
= am.getRunningServices(MAX_SERVICES);
- final int NS = services != null ? services.size() : 0;
+ 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) {
+ services.remove(i);
+ i--;
+ NS--;
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) {
+ services.remove(i);
+ i--;
+ NS--;
continue;
}
-
+ }
+
+ // Retrieve list of running processes, organizing them into a sparse
+ // array for easy retrieval.
+ List<ActivityManager.RunningAppProcessInfo> processes
+ = am.getRunningAppProcesses();
+ final int NP = processes != null ? processes.size() : 0;
+ mTmpAppProcesses.clear();
+ for (int i=0; i<NP; i++) {
+ ActivityManager.RunningAppProcessInfo pi = processes.get(i);
+ mTmpAppProcesses.put(pi.pid, new AppProcessInfo(pi));
+ }
+
+ // Initial iteration through running services to collect per-process
+ // info about them.
+ for (int i=0; i<NS; i++) {
+ ActivityManager.RunningServiceInfo si = services.get(i);
+ if (si.restarting == 0 && si.pid > 0) {
+ AppProcessInfo ainfo = mTmpAppProcesses.get(si.pid);
+ if (ainfo != null) {
+ ainfo.hasServices = true;
+ if (si.foreground) {
+ ainfo.hasForegroundServices = true;
+ }
+ }
+ }
+ }
+
+ // Update state we are maintaining about process that are running services.
+ for (int i=0; i<NS; i++) {
+ ActivityManager.RunningServiceInfo si = services.get(i);
+
+ // If this service's process is in use at a higher importance
+ // due to another process bound to one of its services, then we
+ // won't put it in the top-level list of services. Instead we
+ // want it to be included in the set of processes that the other
+ // process needs.
+ if (si.restarting == 0 && si.pid > 0) {
+ AppProcessInfo ainfo = mTmpAppProcesses.get(si.pid);
+ if (ainfo != null && !ainfo.hasForegroundServices) {
+ // This process does not have any foreground services.
+ // If its importance is greater than the service importance
+ // then there is something else more significant that is
+ // keeping it around that it should possibly be included as
+ // a part of instead of being shown by itself.
+ if (ainfo.info.importance
+ < ActivityManager.RunningAppProcessInfo.IMPORTANCE_SERVICE) {
+ // Follow process chain to see if there is something
+ // else that could be shown
+ boolean skip = false;
+ ainfo = mTmpAppProcesses.get(ainfo.info.importanceReasonPid);
+ while (ainfo != null) {
+ if (ainfo.hasServices || isInterestingProcess(ainfo.info)) {
+ skip = true;
+ break;
+ }
+ ainfo = mTmpAppProcesses.get(ainfo.info.importanceReasonPid);
+ }
+ if (skip) {
+ continue;
+ }
+ }
+ }
+ }
+
HashMap<String, ProcessItem> procs = mServiceProcessesByName.get(si.uid);
if (procs == null) {
procs = new HashMap<String, ProcessItem>();
@@ -694,9 +779,6 @@
// 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);