diff options
author | 2018-11-20 15:58:15 -0800 | |
---|---|---|
committer | 2018-11-21 09:13:21 -0800 | |
commit | a631d56cf37917121f26c5e70a67f1730840ef68 (patch) | |
tree | 4ea65c72521a472436e000ac9577fa5f0b888bc1 | |
parent | f66699ae165ce30f1cda1cfd704027a75bf457d2 (diff) |
Add new API to performing grouping and ordering of bindings.
This will allow apps to better manage many isolated processes,
telling the system how they are grouped together (so should be
managed as one related entity within the LRU cache) and fine-tune
the ordering within that group.
The API is not yet tested... though in theory it is implemented.
But the implementation done for that also fixes a *lot* of problems
with activity LRU management that, even without groups, should
make the ordering of processes in the LRU list much more consistently
match how recently the user has interacted with it.
Also clean up some of the new dumpsys output in the activity manager:
move the new sections to before the process output (so it is still
easy to see the process state at the end of the output), and add and
document the command line options for controlling them. And add a
new "lru" section that gives a clear view of what is going on with
the raw LRU list.
An upcoming change will add tests for the new grouping functionality,
and probably some fixes resulting from that.
Test: atest CtsAppTestCases:ServiceTest
Bug: 111434506
Change-Id: I1f6b6b9de66ccde1573e1a0e9615e8c5f8e6c0d7
-rwxr-xr-x | api/current.txt | 2 | ||||
-rw-r--r-- | core/java/android/app/ContextImpl.java | 18 | ||||
-rw-r--r-- | core/java/android/app/IActivityManager.aidl | 1 | ||||
-rw-r--r-- | core/java/android/content/Context.java | 34 | ||||
-rw-r--r-- | core/java/android/content/ContextWrapper.java | 5 | ||||
-rw-r--r-- | services/core/java/com/android/server/am/ActiveServices.java | 33 | ||||
-rw-r--r-- | services/core/java/com/android/server/am/ActivityManagerService.java | 149 | ||||
-rw-r--r-- | services/core/java/com/android/server/am/ActivityManagerShellCommand.java | 3 | ||||
-rw-r--r-- | services/core/java/com/android/server/am/ConnectionRecord.java | 4 | ||||
-rw-r--r-- | services/core/java/com/android/server/am/ProcessList.java | 130 | ||||
-rw-r--r-- | services/core/java/com/android/server/am/ProcessRecord.java | 8 | ||||
-rw-r--r-- | test-mock/api/current.txt | 1 | ||||
-rw-r--r-- | test-mock/src/android/test/mock/MockContext.java | 5 |
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(); } |