summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xapi/current.txt2
-rw-r--r--core/java/android/app/ContextImpl.java18
-rw-r--r--core/java/android/app/IActivityManager.aidl1
-rw-r--r--core/java/android/content/Context.java34
-rw-r--r--core/java/android/content/ContextWrapper.java5
-rw-r--r--services/core/java/com/android/server/am/ActiveServices.java33
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java149
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerShellCommand.java3
-rw-r--r--services/core/java/com/android/server/am/ConnectionRecord.java4
-rw-r--r--services/core/java/com/android/server/am/ProcessList.java130
-rw-r--r--services/core/java/com/android/server/am/ProcessRecord.java8
-rw-r--r--test-mock/api/current.txt1
-rw-r--r--test-mock/src/android/test/mock/MockContext.java5
13 files changed, 345 insertions, 48 deletions
diff --git a/api/current.txt b/api/current.txt
index ff78d481f4de..4c32d04bcbcb 100755
--- a/api/current.txt
+++ b/api/current.txt
@@ -9598,6 +9598,7 @@ package android.content {
method public abstract void unbindService(android.content.ServiceConnection);
method public void unregisterComponentCallbacks(android.content.ComponentCallbacks);
method public abstract void unregisterReceiver(android.content.BroadcastReceiver);
+ method public abstract void updateServiceGroup(android.content.ServiceConnection, int, int);
field public static final java.lang.String ACCESSIBILITY_SERVICE = "accessibility";
field public static final java.lang.String ACCOUNT_SERVICE = "account";
field public static final java.lang.String ACTIVITY_SERVICE = "activity";
@@ -9796,6 +9797,7 @@ package android.content {
method public boolean stopService(android.content.Intent);
method public void unbindService(android.content.ServiceConnection);
method public void unregisterReceiver(android.content.BroadcastReceiver);
+ method public void updateServiceGroup(android.content.ServiceConnection, int, int);
}
public deprecated class CursorLoader extends android.content.AsyncTaskLoader {
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 9837deb6143c..28ecb27b53d2 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -1724,6 +1724,24 @@ class ContextImpl extends Context {
}
@Override
+ public void updateServiceGroup(@NonNull ServiceConnection conn, int group, int importance) {
+ if (conn == null) {
+ throw new IllegalArgumentException("connection is null");
+ }
+ if (mPackageInfo != null) {
+ IServiceConnection sd = mPackageInfo.forgetServiceDispatcher(
+ getOuterContext(), conn);
+ try {
+ ActivityManager.getService().updateServiceGroup(sd, group, importance);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ } else {
+ throw new RuntimeException("Not supported in system context");
+ }
+ }
+
+ @Override
public void unbindService(ServiceConnection conn) {
if (conn == null) {
throw new IllegalArgumentException("connection is null");
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index f27c6677ed22..e83bcd0e6647 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -140,6 +140,7 @@ interface IActivityManager {
int bindIsolatedService(in IApplicationThread caller, in IBinder token, in Intent service,
in String resolvedType, in IServiceConnection connection, int flags,
in String instanceName, in String callingPackage, int userId);
+ void updateServiceGroup(in IServiceConnection connection, int group, int importance);
boolean unbindService(in IServiceConnection connection);
void publishService(in IBinder token, in Intent intent, in IBinder service);
void setDebugApp(in String packageName, boolean waitForDebugger, boolean persistent);
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 004417b80e26..cec8ef59b961 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -425,6 +425,15 @@ public abstract class Context {
*/
public static final int BIND_EXTERNAL_SERVICE = 0x80000000;
+ /**
+ * These bind flags reduce the strength of the binding such that we shouldn't
+ * consider it as pulling the process up to the level of the one that is bound to it.
+ * @hide
+ */
+ public static final int BIND_REDUCTION_FLAGS =
+ Context.BIND_ALLOW_OOM_MANAGEMENT | Context.BIND_WAIVE_PRIORITY
+ | Context.BIND_ADJUST_BELOW_PERCEPTIBLE | Context.BIND_NOT_VISIBLE;
+
/** @hide */
@IntDef(flag = true, prefix = { "RECEIVER_VISIBLE_" }, value = {
RECEIVER_VISIBLE_TO_INSTANT_APPS
@@ -2982,6 +2991,31 @@ public abstract class Context {
}
/**
+ * For a service previously bound with {@link #bindService} or a related method, change
+ * how the system manages that service's process in relation to other processes. This
+ * doesn't modify the original bind flags that were passed in when binding, but adjusts
+ * how the process will be managed in some cases based on those flags. Currently only
+ * works on isolated processes (will be ignored for non-isolated processes).
+ *
+ * @param conn The connection interface previously supplied to bindService(). This
+ * parameter must not be null.
+ * @param group A group to put this connection's process in. Upon calling here, this
+ * will override any previous group that was set for that process. The group
+ * tells the system about processes that are logically grouped together, so
+ * should be managed as one unit of importance (such as when being considered
+ * a recently used app). All processes in the same app with the same group
+ * are considered to be related. Supplying 0 reverts to the default behavior
+ * of not grouping.
+ * @param importance Additional importance of the processes within a group. Upon calling
+ * here, this will override any previous group that was set for that
+ * process. This fine-tunes process killing of all processes within
+ * a related groups -- higher importance values will be killed before
+ * lower ones.
+ */
+ public abstract void updateServiceGroup(@NonNull ServiceConnection conn, int group,
+ int importance);
+
+ /**
* Disconnect from an application service. You will no longer receive
* calls as the service is restarted, and the service is now allowed to
* stop at any time.
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index 88696b0e1e4d..2db44b44fd6c 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -726,6 +726,11 @@ public class ContextWrapper extends Context {
}
@Override
+ public void updateServiceGroup(ServiceConnection conn, int group, int importance) {
+ mBase.updateServiceGroup(conn, group, importance);
+ }
+
+ @Override
public void unbindService(ServiceConnection conn) {
mBase.unbindService(conn);
}
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index c660cc6c6f75..a19e9287aa6c 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -1702,8 +1702,11 @@ public final class ActiveServices {
s.app.whitelistManager = true;
}
// This could have made the service more important.
- mAm.updateLruProcessLocked(s.app, s.app.hasClientActivities()
- || s.app.treatLikeActivity, b.client);
+ mAm.updateLruProcessLocked(s.app,
+ (callerApp.hasActivitiesOrRecentTasks() && s.app.hasClientActivities())
+ || (callerApp.getCurProcState() <= ActivityManager.PROCESS_STATE_TOP
+ && (flags & Context.BIND_TREAT_LIKE_ACTIVITY) != 0),
+ b.client);
mAm.updateOomAdjLocked(s.app, true);
}
@@ -1787,6 +1790,32 @@ public final class ActiveServices {
}
}
+ void updateServiceGroupLocked(IServiceConnection connection, int group, int importance) {
+ final IBinder binder = connection.asBinder();
+ if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "updateServiceGroup: conn=" + binder);
+ final ArrayList<ConnectionRecord> clist = mServiceConnections.get(binder);
+ if (clist == null) {
+ throw new IllegalArgumentException("Could not find connection for "
+ + connection.asBinder());
+ }
+ for (int i = clist.size() - 1; i >= 0; i--) {
+ final ConnectionRecord crec = clist.get(i);
+ final ServiceRecord srec = crec.binding.service;
+ if (srec != null && srec.app != null
+ && (srec.serviceInfo.flags & ServiceInfo.FLAG_ISOLATED_PROCESS) != 0) {
+ if (group > 0) {
+ srec.app.connectionService = srec;
+ srec.app.connectionGroup = group;
+ srec.app.connectionImportance = importance;
+ } else {
+ srec.app.connectionService = null;
+ srec.app.connectionGroup = 0;
+ srec.app.connectionImportance = 0;
+ }
+ }
+ }
+ }
+
boolean unbindServiceLocked(IServiceConnection connection) {
IBinder binder = connection.asBinder();
if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "unbindService: conn=" + binder);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 4e417bab7d6a..7e9e83cf17d0 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -9207,26 +9207,33 @@ public class ActivityManagerService extends IActivityManager.Stub
}
dumpAssociationsLocked(fd, pw, args, opti, dumpAll, dumpClient, dumpPackage);
}
- pw.println();
- if (dumpAll) {
- pw.println("-------------------------------------------------------------------------------");
- }
- dumpProcessesLocked(fd, pw, args, opti, dumpAll, dumpPackage, dumpAppId);
- pw.println();
- if (dumpAll) {
- pw.println("-------------------------------------------------------------------------------");
+ if (dumpPackage == null) {
+ pw.println();
+ if (dumpAll) {
+ pw.println("-------------------------------------------------------------------------------");
+ }
+ mOomAdjProfiler.dump(pw);
+ pw.println();
+ if (dumpAll) {
+ pw.println("-------------------------------------------------------------------------------");
+ }
+ dumpBinderProxies(pw);
+ pw.println();
+ if (dumpAll) {
+ pw.println("-------------------------------------------------------------------------------");
+ }
+ dumpLmkLocked(pw);
}
- mOomAdjProfiler.dump(pw);
pw.println();
if (dumpAll) {
pw.println("-------------------------------------------------------------------------------");
}
- dumpBinderProxies(pw);
+ dumpLruLocked(pw, dumpPackage);
pw.println();
if (dumpAll) {
pw.println("-------------------------------------------------------------------------------");
}
- dumpLmkLocked(pw);
+ dumpProcessesLocked(fd, pw, args, opti, dumpAll, dumpPackage, dumpAppId);
}
}
@@ -9421,6 +9428,10 @@ public class ActivityManagerService extends IActivityManager.Stub
synchronized (this) {
dumpLmkLocked(pw);
}
+ } else if ("lru".equals(cmd)) {
+ synchronized (this) {
+ dumpLruLocked(pw, null);
+ }
} else if ("permissions".equals(cmd) || "perm".equals(cmd)) {
synchronized (this) {
dumpPermissionsLocked(fd, pw, args, opti, true, null);
@@ -9698,17 +9709,102 @@ public class ActivityManagerService extends IActivityManager.Stub
}
pw.println();
}
- pw.println();
return true;
}
return false;
}
void dumpBinderProxies(PrintWriter pw) {
+ pw.println("ACTIVITY MANAGER BINDER PROXY STATE (dumpsys activity binder-proxies)");
dumpBinderProxyInterfaceCounts(pw,
- "Top proxy interface names held by SYSTEM");
+ " Top proxy interface names held by SYSTEM");
dumpBinderProxiesCounts(pw,
- "Counts of Binder Proxies held by SYSTEM");
+ " Counts of Binder Proxies held by SYSTEM");
+ }
+
+ void dumpLruEntryLocked(PrintWriter pw, int index, ProcessRecord proc) {
+ pw.print(" #");
+ pw.print(index);
+ pw.print(": ");
+ pw.print(ProcessList.makeOomAdjString(proc.setAdj));
+ pw.print(" ");
+ pw.print(ProcessList.makeProcStateString(proc.getCurProcState()));
+ pw.print(" ");
+ pw.print(proc.toShortString());
+ pw.print(" ");
+ if (proc.hasActivitiesOrRecentTasks() || proc.hasClientActivities()
+ || proc.treatLikeActivity) {
+ pw.print(" activity=");
+ boolean printed = false;
+ if (proc.hasActivities()) {
+ pw.print("activities");
+ printed = true;
+ }
+ if (proc.hasRecentTasks()) {
+ if (printed) {
+ pw.print("|");
+ }
+ pw.print("recents");
+ printed = true;
+ }
+ if (proc.hasClientActivities()) {
+ if (printed) {
+ pw.print("|");
+ }
+ pw.print("client");
+ printed = true;
+ }
+ if (proc.treatLikeActivity) {
+ if (printed) {
+ pw.print("|");
+ }
+ pw.print("treated");
+ }
+ }
+ pw.println();
+ }
+
+ // TODO: Move to ProcessList?
+ void dumpLruLocked(PrintWriter pw, String dumpPackage) {
+ pw.println("ACTIVITY MANAGER LRU PROCESSES (dumpsys activity lru)");
+ final int N = mProcessList.mLruProcesses.size();
+ int i;
+ boolean first = true;
+ for (i = N - 1; i >= mProcessList.mLruProcessActivityStart; i--) {
+ final ProcessRecord r = mProcessList.mLruProcesses.get(i);
+ if (dumpPackage != null && !r.pkgList.containsKey(dumpPackage)) {
+ continue;
+ }
+ if (first) {
+ pw.println(" Activities:");
+ first = false;
+ }
+ dumpLruEntryLocked(pw, i, r);
+ }
+ first = true;
+ for (; i >= mProcessList.mLruProcessServiceStart; i--) {
+ final ProcessRecord r = mProcessList.mLruProcesses.get(i);
+ if (dumpPackage != null && !r.pkgList.containsKey(dumpPackage)) {
+ continue;
+ }
+ if (first) {
+ pw.println(" Services:");
+ first = false;
+ }
+ dumpLruEntryLocked(pw, i, r);
+ }
+ first = true;
+ for (; i >= 0; i--) {
+ final ProcessRecord r = mProcessList.mLruProcesses.get(i);
+ if (dumpPackage != null && !r.pkgList.containsKey(dumpPackage)) {
+ continue;
+ }
+ if (first) {
+ pw.println(" Other:");
+ first = false;
+ }
+ dumpLruEntryLocked(pw, i, r);
+ }
}
// TODO: Move to ProcessList?
@@ -13214,6 +13310,12 @@ public class ActivityManagerService extends IActivityManager.Stub
}
}
+ public void updateServiceGroup(IServiceConnection connection, int group, int importance) {
+ synchronized (this) {
+ mServices.updateServiceGroupLocked(connection, group, importance);
+ }
+ }
+
public boolean unbindService(IServiceConnection connection) {
synchronized (this) {
return mServices.unbindServiceLocked(connection);
@@ -17398,8 +17500,11 @@ public class ActivityManagerService extends IActivityManager.Stub
int stepCached = 0;
int stepEmpty = 0;
int numCached = 0;
+ int numCachedExtraGroup = 0;
int numEmpty = 0;
int numTrimming = 0;
+ int lastCachedGroup = 0;
+ int lastCachedGroupUid = 0;
mNumNonCachedProcs = 0;
mNumCachedHiddenProcs = 0;
@@ -17523,7 +17628,21 @@ public class ActivityManagerService extends IActivityManager.Stub
case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT:
mNumCachedHiddenProcs++;
numCached++;
- if (numCached > cachedProcessLimit) {
+ if (app.connectionGroup != 0) {
+ if (lastCachedGroupUid == app.uid
+ && lastCachedGroup == app.connectionGroup) {
+ // If this process is the next in the same group, we don't
+ // want it to count against our limit of the number of cached
+ // processes, so bump up the group count to account for it.
+ numCachedExtraGroup++;
+ } else {
+ lastCachedGroupUid = app.uid;
+ lastCachedGroup = app.connectionGroup;
+ }
+ } else {
+ lastCachedGroupUid = lastCachedGroup = 0;
+ }
+ if ((numCached - numCachedExtraGroup) > cachedProcessLimit) {
app.kill("cached #" + numCached, true);
}
break;
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 8f8d5abca1d8..67a4d14a6edb 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -2853,6 +2853,9 @@ final class ActivityManagerShellCommand extends ShellCommand {
pw.println(" provider [COMP_SPEC]: provider client-side state");
pw.println(" s[ervices] [COMP_SPEC ...]: service state");
pw.println(" as[sociations]: tracked app associations");
+ pw.println(" lmk: stats on low memory killer");
+ pw.println(" lru: raw LRU process list");
+ pw.println(" binder-proxies: stats on binder objects and IPCs");
pw.println(" settings: currently applied config settings");
pw.println(" service [COMP_SPEC]: service client-side state");
pw.println(" package [PACKAGE_NAME]: all state related to given package");
diff --git a/services/core/java/com/android/server/am/ConnectionRecord.java b/services/core/java/com/android/server/am/ConnectionRecord.java
index bfa3f66ce726..aa76b3d6b856 100644
--- a/services/core/java/com/android/server/am/ConnectionRecord.java
+++ b/services/core/java/com/android/server/am/ConnectionRecord.java
@@ -48,7 +48,7 @@ final class ConnectionRecord {
boolean serviceDead; // Well is it?
// Please keep the following two enum list synced.
- private static int[] BIND_ORIG_ENUMS = new int[] {
+ private static final int[] BIND_ORIG_ENUMS = new int[] {
Context.BIND_AUTO_CREATE,
Context.BIND_DEBUG_UNBIND,
Context.BIND_NOT_FOREGROUND,
@@ -65,7 +65,7 @@ final class ConnectionRecord {
Context.BIND_SHOWING_UI,
Context.BIND_NOT_VISIBLE,
};
- private static int[] BIND_PROTO_ENUMS = new int[] {
+ private static final int[] BIND_PROTO_ENUMS = new int[] {
ConnectionRecordProto.AUTO_CREATE,
ConnectionRecordProto.DEBUG_UNBIND,
ConnectionRecordProto.NOT_FG,
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 84b364bd20a8..4b19398cc73d 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -50,6 +50,7 @@ import android.app.AppGlobals;
import android.app.AppProtoEnums;
import android.app.IApplicationThread;
import android.content.ComponentName;
+import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
@@ -2204,7 +2205,7 @@ public final class ProcessList {
@GuardedBy("mService")
int updateLruProcessInternalLocked(ProcessRecord app, long now, int index,
- String what, Object obj, ProcessRecord srcApp) {
+ int lruSeq, String what, Object obj, ProcessRecord srcApp) {
app.lastActivityTime = now;
if (app.hasActivitiesOrRecentTasks()) {
@@ -2225,7 +2226,7 @@ public final class ProcessList {
return index;
}
- if (lrui >= mLruProcessActivityStart) {
+ if (lrui >= mLruProcessActivityStart && index < mLruProcessActivityStart) {
// Don't want to touch dependent processes that are hosting activities.
return index;
}
@@ -2237,6 +2238,7 @@ public final class ProcessList {
if (DEBUG_LRU) Slog.d(TAG_LRU, "Moving dep from " + lrui + " to " + index
+ " in LRU list: " + app);
mLruProcesses.add(index, app);
+ app.lruSeq = lruSeq;
return index;
}
@@ -2345,9 +2347,11 @@ public final class ProcessList {
*/
int nextIndex;
+ int nextActivityIndex = -1;
if (hasActivity) {
final int N = mLruProcesses.size();
- if ((!app.hasActivities() || app.hasRecentTasks())
+ nextIndex = mLruProcessServiceStart;
+ if (!app.hasActivitiesOrRecentTasks() && !app.treatLikeActivity
&& mLruProcessActivityStart < (N - 1)) {
// Process doesn't have activities, but has clients with
// activities... move it up, but one below the top (the top
@@ -2355,36 +2359,92 @@ public final class ProcessList {
if (DEBUG_LRU) Slog.d(TAG_LRU,
"Adding to second-top of LRU activity list: " + app);
mLruProcesses.add(N - 1, app);
- // To keep it from spamming the LRU list (by making a bunch of clients),
- // we will push down any other entries owned by the app.
+ // If this process is part of a group, need to pull up any other processes
+ // in that group to be with it.
final int uid = app.info.uid;
- for (int i = N - 2; i > mLruProcessActivityStart; i--) {
- ProcessRecord subProc = mLruProcesses.get(i);
- if (subProc.info.uid == uid) {
- // We want to push this one down the list. If the process after
- // it is for the same uid, however, don't do so, because we don't
- // want them internally to be re-ordered.
- if (mLruProcesses.get(i - 1).info.uid != uid) {
- if (DEBUG_LRU) Slog.d(TAG_LRU,
- "Pushing uid " + uid + " swapping at " + i + ": "
- + mLruProcesses.get(i) + " : "
- + mLruProcesses.get(i - 1));
- ProcessRecord tmp = mLruProcesses.get(i);
- mLruProcesses.set(i, mLruProcesses.get(i - 1));
- mLruProcesses.set(i - 1, tmp);
- i--;
+ int endIndex = N - 2;
+ nextActivityIndex = N - 2;
+ if (app.connectionGroup > 0) {
+ int endImportance = app.connectionImportance;
+ for (int i = endIndex; i >= mLruProcessActivityStart; i--) {
+ final ProcessRecord subProc = mLruProcesses.get(i);
+ if (subProc.info.uid == uid
+ && subProc.connectionGroup == subProc.connectionGroup) {
+ if (i == endIndex && subProc.connectionImportance >= endImportance) {
+ // This process is already in the group, and its importance
+ // is not as strong as the process before it, so it keep it
+ // correctly positioned in the group.
+ endIndex--;
+ endImportance = subProc.connectionImportance;
+ } else {
+ // We want to pull this up to be with the rest of the group,
+ // and order within the group by importance.
+ boolean moved = false;
+ for (int pos = N - 1; pos > endIndex; pos--) {
+ final ProcessRecord posProc = mLruProcesses.get(pos);
+ if (subProc.connectionImportance
+ <= posProc.connectionImportance) {
+ mLruProcesses.remove(i);
+ mLruProcesses.add(pos, subProc);
+ moved = true;
+ endIndex--;
+ break;
+ }
+ }
+ if (!moved) {
+ // Goes to the end of the group.
+ mLruProcesses.remove(i);
+ mLruProcesses.add(endIndex - 1, subProc);
+ endIndex--;
+ endImportance = subProc.connectionImportance;
+ }
+ }
+ }
+ }
+
+ }
+ // To keep it from spamming the LRU list (by making a bunch of clients),
+ // we will distribute other entries owned by it to be in-between other apps.
+ for (int i = endIndex; i >= mLruProcessActivityStart; i--) {
+ final ProcessRecord subProc = mLruProcesses.get(i);
+ if (subProc.info.uid != uid) {
+ // This is a different app... if we have gone through some of the
+ // target app, pull this up to be before them.
+ if (i < endIndex) {
+ mLruProcesses.remove(i);
+ mLruProcesses.add(endIndex, subProc);
+ }
+ // Find the end of the next group of processes for target app. This
+ // is after any entries of different apps (so we don't change the existing
+ // relative order of apps) and then after the next last group of processes
+ // of the target app.
+ for (endIndex--; endIndex >= mLruProcessActivityStart; endIndex--) {
+ final ProcessRecord endProc = mLruProcesses.get(endIndex);
+ if (endProc.info.uid == uid) {
+ break;
+ }
+ }
+ if (endIndex >= mLruProcessActivityStart) {
+ final ProcessRecord endProc = mLruProcesses.get(endIndex);
+ for (endIndex--; endIndex >= mLruProcessActivityStart; endIndex--) {
+ final ProcessRecord nextEndProc = mLruProcesses.get(endIndex);
+ if (nextEndProc.info.uid != uid
+ || nextEndProc.connectionGroup != endProc.connectionGroup) {
+ break;
+ }
+ }
+ }
+ if (i > endIndex) {
+ i = endIndex;
}
- } else {
- // A gap, we can stop here.
- break;
}
}
} else {
// Process has activities, put it at the very tipsy-top.
if (DEBUG_LRU) Slog.d(TAG_LRU, "Adding to top of LRU activity list: " + app);
mLruProcesses.add(app);
+ nextActivityIndex = mLruProcesses.size() - 1;
}
- nextIndex = mLruProcessServiceStart;
} else if (hasService) {
// Process has services, put it at the top of the service list.
if (DEBUG_LRU) Slog.d(TAG_LRU, "Adding to top of LRU service list: " + app);
@@ -2416,6 +2476,8 @@ public final class ProcessList {
mLruProcessServiceStart++;
}
+ app.lruSeq = mLruSeq;
+
// If the app is currently using a content provider or service,
// bump those processes as well.
for (int j = app.connections.size() - 1; j >= 0; j--) {
@@ -2423,17 +2485,27 @@ public final class ProcessList {
if (cr.binding != null && !cr.serviceDead && cr.binding.service != null
&& cr.binding.service.app != null
&& cr.binding.service.app.lruSeq != mLruSeq
+ && (cr.flags & Context.BIND_REDUCTION_FLAGS) == 0
&& !cr.binding.service.app.isPersistent()) {
- nextIndex = updateLruProcessInternalLocked(cr.binding.service.app,
- now,
- nextIndex,
- "service connection", cr, app);
+ if (cr.binding.service.app.hasClientActivities()) {
+ if (nextActivityIndex >= 0) {
+ nextActivityIndex = updateLruProcessInternalLocked(cr.binding.service.app,
+ now,
+ nextActivityIndex, mLruSeq,
+ "service connection", cr, app);
+ }
+ } else {
+ nextIndex = updateLruProcessInternalLocked(cr.binding.service.app,
+ now,
+ nextIndex, mLruSeq,
+ "service connection", cr, app);
+ }
}
}
for (int j = app.conProviders.size() - 1; j >= 0; j--) {
ContentProviderRecord cpr = app.conProviders.get(j).provider;
if (cpr.proc != null && cpr.proc.lruSeq != mLruSeq && !cpr.proc.isPersistent()) {
- nextIndex = updateLruProcessInternalLocked(cpr.proc, now, nextIndex,
+ nextIndex = updateLruProcessInternalLocked(cpr.proc, now, nextIndex, mLruSeq,
"provider reference", cpr, app);
}
}
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index faf85615eb0d..013de93c7f24 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -155,6 +155,9 @@ final class ProcessRecord implements WindowProcessListener {
int pssStatType; // The type of stat collection that we are currently requesting
int savedPriority; // Previous priority value if we're switching to non-SCHED_OTHER
int renderThreadTid; // TID for RenderThread
+ ServiceRecord connectionService; // Service that applied current connectionGroup/Importance
+ int connectionGroup; // Last group set by a connection
+ int connectionImportance; // Last importance set by a connection
boolean serviceb; // Process currently is on the service B list
boolean serviceHighRam; // We are forcing to service B list due to its RAM use
boolean notCachedSinceIdle; // Has this process not been in a cached state since last idle?
@@ -396,6 +399,11 @@ final class ProcessRecord implements WindowProcessListener {
pw.print(" hasAboveClient="); pw.print(hasAboveClient);
pw.print(" treatLikeActivity="); pw.println(treatLikeActivity);
}
+ if (connectionService != null || connectionGroup != 0) {
+ pw.print(prefix); pw.print("connectionGroup="); pw.print(connectionGroup);
+ pw.print(" Importance="); pw.print(connectionImportance);
+ pw.print(" Service="); pw.println(connectionService);
+ }
if (hasTopUi() || hasOverlayUi() || runningRemoteAnimation) {
pw.print(prefix); pw.print("hasTopUi="); pw.print(hasTopUi());
pw.print(" hasOverlayUi="); pw.print(hasOverlayUi());
diff --git a/test-mock/api/current.txt b/test-mock/api/current.txt
index f91d74a30693..7842a1cd03b3 100644
--- a/test-mock/api/current.txt
+++ b/test-mock/api/current.txt
@@ -135,6 +135,7 @@ package android.test.mock {
method public boolean stopService(android.content.Intent);
method public void unbindService(android.content.ServiceConnection);
method public void unregisterReceiver(android.content.BroadcastReceiver);
+ method public void updateServiceGroup(android.content.ServiceConnection, int, int);
}
public deprecated class MockCursor implements android.database.Cursor {
diff --git a/test-mock/src/android/test/mock/MockContext.java b/test-mock/src/android/test/mock/MockContext.java
index 66be6d9d9840..ae6cd29fb2de 100644
--- a/test-mock/src/android/test/mock/MockContext.java
+++ b/test-mock/src/android/test/mock/MockContext.java
@@ -591,6 +591,11 @@ public class MockContext extends Context {
}
@Override
+ public void updateServiceGroup(ServiceConnection conn, int group, int importance) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
public void unbindService(ServiceConnection conn) {
throw new UnsupportedOperationException();
}