From c4e63a46a5cc0f13357098ea09aa20c96ce5f08d Mon Sep 17 00:00:00 2001 From: Wale Ogunwale Date: Tue, 2 Oct 2018 13:19:54 -0700 Subject: [PATCH] Introduced ActivityServiceConnectionsHolder (18/n) Class for managing the connections to services on the AM side that activities on the WM side bind with for things like oom score adjustment. Bug: 80414790 Test: Existing tests pass. Change-Id: I4ab5140dd7f888f448ce19107bda92c01066a3dc --- .../android/app/ActivityManagerInternal.java | 2 + .../com/android/server/am/ActiveServices.java | 17 +-- .../server/am/ActivityManagerService.java | 26 +++- .../com/android/server/am/ActivityRecord.java | 6 +- .../am/ActivityServiceConnectionsHolder.java | 113 ++++++++++++++++++ .../com/android/server/am/ActivityStack.java | 12 +- .../server/am/ActivityTaskManagerService.java | 16 +++ .../android/server/am/ConnectionRecord.java | 7 +- .../wm/ActivityTaskManagerInternal.java | 4 + 9 files changed, 172 insertions(+), 31 deletions(-) create mode 100644 services/core/java/com/android/server/am/ActivityServiceConnectionsHolder.java diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java index 294a3ec73efdc..76b90f5354b30 100644 --- a/core/java/android/app/ActivityManagerInternal.java +++ b/core/java/android/app/ActivityManagerInternal.java @@ -245,4 +245,6 @@ public abstract class ActivityManagerInternal { public abstract ComponentName startServiceInPackage(int uid, Intent service, String resolvedType, boolean fgRequired, String callingPackage, int userId) throws TransactionTooLargeException; + + public abstract void disconnectActivityFromServices(Object connectionHolder); } diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index 461d39d0a29a2..8e64b50633028 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -1466,9 +1466,9 @@ public final class ActiveServices { + ") when binding service " + service); } - ActivityRecord activity = null; + ActivityServiceConnectionsHolder activity = null; if (token != null) { - activity = ActivityRecord.isInStackLocked(token); + activity = mAm.mAtmInternal.getServiceConnectionsHolder(token); if (activity == null) { Slog.w(TAG, "Binding with unknown activity: " + token); return 0; @@ -1644,10 +1644,7 @@ public final class ActiveServices { clist.add(c); b.connections.add(c); if (activity != null) { - if (activity.connections == null) { - activity.connections = new HashSet(); - } - activity.connections.add(c); + activity.addConnection(c); } b.client.connections.add(c); c.startAssociationIfNeeded(); @@ -2861,8 +2858,8 @@ public final class ActiveServices { smap.ensureNotStartingBackgroundLocked(r); } - void removeConnectionLocked( - ConnectionRecord c, ProcessRecord skipApp, ActivityRecord skipAct) { + void removeConnectionLocked(ConnectionRecord c, ProcessRecord skipApp, + ActivityServiceConnectionsHolder skipAct) { IBinder binder = c.conn.asBinder(); AppBindRecord b = c.binding; ServiceRecord s = b.service; @@ -2876,9 +2873,7 @@ public final class ActiveServices { b.connections.remove(c); c.stopAssociation(); if (c.activity != null && c.activity != skipAct) { - if (c.activity.connections != null) { - c.activity.connections.remove(c); - } + c.activity.removeConnection(c); } if (b.client != skipApp) { b.client.connections.remove(c); diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index f49c50a686744..5f0a89bb7db62 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -10609,9 +10609,13 @@ public class ActivityManagerService extends IActivityManager.Stub currApp.importanceReasonImportance = ActivityManager.RunningAppProcessInfo.procStateToImportance( app.adjSourceProcState); - } else if (app.adjSource instanceof ActivityRecord) { - ActivityRecord r = (ActivityRecord)app.adjSource; - if (r.app != null) currApp.importanceReasonPid = r.app.getPid(); + } else if (app.adjSource instanceof ActivityServiceConnectionsHolder) { + ActivityServiceConnectionsHolder r = + (ActivityServiceConnectionsHolder) app.adjSource; + final int pid = r.getActivityPid(); + if (pid != -1) { + currApp.importanceReasonPid = pid; + } } if (app.adjTarget instanceof ComponentName) { currApp.importanceReasonComponent = (ComponentName)app.adjTarget; @@ -17786,10 +17790,10 @@ public class ActivityManagerService extends IActivityManager.Stub if ((cr.flags&Context.BIND_TREAT_LIKE_ACTIVITY) != 0) { app.treatLikeActivity = true; } - final ActivityRecord a = cr.activity; + final ActivityServiceConnectionsHolder a = cr.activity; if ((cr.flags&Context.BIND_ADJUST_WITH_ACTIVITY) != 0) { - if (a != null && adj > ProcessList.FOREGROUND_APP_ADJ && (a.visible - || a.isState(ActivityState.RESUMED, ActivityState.PAUSING))) { + if (a != null && adj > ProcessList.FOREGROUND_APP_ADJ + && a.isActivityVisible()) { adj = ProcessList.FOREGROUND_APP_ADJ; if ((cr.flags&Context.BIND_NOT_FOREGROUND) == 0) { if ((cr.flags&Context.BIND_IMPORTANT) != 0) { @@ -20904,6 +20908,16 @@ public class ActivityManagerService extends IActivityManager.Stub return res; } } + + @Override + public void disconnectActivityFromServices(Object connectionHolder) { + synchronized(ActivityManagerService.this) { + final ActivityServiceConnectionsHolder c = + (ActivityServiceConnectionsHolder) connectionHolder; + c.forEachConnection(cr -> mServices.removeConnectionLocked( + (ConnectionRecord) cr, null, c)); + } + } } /** diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java index 77cfb124ec808..5e1d9acbb1199 100644 --- a/services/core/java/com/android/server/am/ActivityRecord.java +++ b/services/core/java/com/android/server/am/ActivityRecord.java @@ -288,7 +288,7 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo ActivityOptions pendingOptions; // most recently given options ActivityOptions returningOptions; // options that are coming back via convertToTranslucent AppTimeTracker appTimeTracker; // set if we are tracking the time in this app/task/activity - HashSet connections; // All ConnectionRecord we hold + ActivityServiceConnectionsHolder mServiceConnectionsHolder; // Service connections. UriPermissionOwner uriPermissions; // current special URI access perms. WindowProcessController app; // if non-null, hosting application private ActivityState mState; // current state we are in @@ -563,8 +563,8 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo pw.print(" configChangeFlags="); pw.println(Integer.toHexString(configChangeFlags)); } - if (connections != null) { - pw.print(prefix); pw.print("connections="); pw.println(connections); + if (mServiceConnectionsHolder != null) { + pw.print(prefix); pw.print("connections="); pw.println(mServiceConnectionsHolder); } if (info != null) { pw.println(prefix + "resizeMode=" + ActivityInfo.resizeModeToString(info.resizeMode)); diff --git a/services/core/java/com/android/server/am/ActivityServiceConnectionsHolder.java b/services/core/java/com/android/server/am/ActivityServiceConnectionsHolder.java new file mode 100644 index 0000000000000..b1ced29da4c20 --- /dev/null +++ b/services/core/java/com/android/server/am/ActivityServiceConnectionsHolder.java @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.server.am; + +import static com.android.server.am.ActivityStack.ActivityState.PAUSING; +import static com.android.server.am.ActivityStack.ActivityState.RESUMED; + +import java.io.PrintWriter; +import java.util.HashSet; +import java.util.Iterator; +import java.util.function.Consumer; + +/** + * Class for tracking the connections to services on the AM side that activities on the + * WM side (in the future) bind with for things like oom score adjustment. Would normally be one + * instance of this per activity for tracking all services connected to that activity. AM will + * sometimes query this to bump the OOM score for the processes with services connected to visible + * activities. + */ +public class ActivityServiceConnectionsHolder { + + private final ActivityTaskManagerService mService; + + /** The activity the owns this service connection object. */ + private final ActivityRecord mActivity; + + /** + * The service connection object bounded with the owning activity. They represent + * ConnectionRecord on the AM side, however we don't need to know their object representation + * on the WM side since we don't perform operations on the object. Mainly here for communication + * and booking with the AM side. + */ + private HashSet mConnections; + + ActivityServiceConnectionsHolder(ActivityTaskManagerService service, ActivityRecord activity) { + mService = service; + mActivity = activity; + } + + /** Adds a connection record that the activity has bound to a specific service. */ + public void addConnection(T c) { + synchronized (mService.mGlobalLock) { + if (mConnections == null) { + mConnections = new HashSet<>(); + } + mConnections.add(c); + } + } + + /** Removed a connection record between the activity and a specific service. */ + public void removeConnection(T c) { + synchronized (mService.mGlobalLock) { + if (mConnections == null) { + return; + } + mConnections.remove(c); + } + } + + public boolean isActivityVisible() { + synchronized (mService.mGlobalLock) { + return mActivity.visible || mActivity.isState(RESUMED, PAUSING); + } + } + + public int getActivityPid() { + synchronized (mService.mGlobalLock) { + return mActivity.hasProcess() ? mActivity.app.getPid() : -1; + } + } + + public void forEachConnection(Consumer consumer) { + synchronized (mService.mGlobalLock) { + if (mConnections == null || mConnections.isEmpty()) { + return; + } + final Iterator it = mConnections.iterator(); + while (it.hasNext()) { + T c = it.next(); + consumer.accept(c); + } + } + } + + /** Removes the connection between the activity and all services that were connected to it. */ + void disconnectActivityFromServices() { + if (mConnections == null || mConnections.isEmpty()) { + return; + } + mService.mH.post(() -> mService.mAmInternal.disconnectActivityFromServices(this)); + } + + public void dump(PrintWriter pw, String prefix) { + synchronized (mService.mGlobalLock) { + pw.println(prefix + "activity=" + mActivity); + } + } + +} diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java index 9f59bd8db62aa..7de5824e8ca67 100644 --- a/services/core/java/com/android/server/am/ActivityStack.java +++ b/services/core/java/com/android/server/am/ActivityStack.java @@ -4237,15 +4237,11 @@ class ActivityStack extends ConfigurationContai * Perform clean-up of service connections in an activity record. */ private void cleanUpActivityServicesLocked(ActivityRecord r) { - // Throw away any services that have been bound by this activity. - if (r.connections != null) { - Iterator it = r.connections.iterator(); - while (it.hasNext()) { - ConnectionRecord c = it.next(); - mService.mAm.mServices.removeConnectionLocked(c, null, r); - } - r.connections = null; + if (r.mServiceConnectionsHolder == null) { + return; } + // Throw away any services that have been bound by this activity. + r.mServiceConnectionsHolder.disconnectActivityFromServices(); } final void scheduleDestroyActivities(WindowProcessController owner, String reason) { diff --git a/services/core/java/com/android/server/am/ActivityTaskManagerService.java b/services/core/java/com/android/server/am/ActivityTaskManagerService.java index 36261b505a942..b4f062d7fe603 100644 --- a/services/core/java/com/android/server/am/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/am/ActivityTaskManagerService.java @@ -5781,5 +5781,21 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { resultWho, requestCode, intents, resolvedTypes, flags, bOptions); } } + + @Override + public ActivityServiceConnectionsHolder getServiceConnectionsHolder(IBinder token) { + synchronized (mGlobalLock) { + final ActivityRecord r = ActivityRecord.isInStackLocked(token); + if (r == null) { + return null; + } + if (r.mServiceConnectionsHolder == null) { + r.mServiceConnectionsHolder = new ActivityServiceConnectionsHolder( + ActivityTaskManagerService.this, r); + } + + return r.mServiceConnectionsHolder; + } + } } } diff --git a/services/core/java/com/android/server/am/ConnectionRecord.java b/services/core/java/com/android/server/am/ConnectionRecord.java index fa8e6c4317701..1242ed626a189 100644 --- a/services/core/java/com/android/server/am/ConnectionRecord.java +++ b/services/core/java/com/android/server/am/ConnectionRecord.java @@ -35,7 +35,7 @@ import java.io.PrintWriter; */ final class ConnectionRecord { final AppBindRecord binding; // The application/service binding. - final ActivityRecord activity; // If non-null, the owning activity. + final ActivityServiceConnectionsHolder activity; // If non-null, the owning activity. final IServiceConnection conn; // The client connection. final int flags; // Binding options. final int clientLabel; // String resource labeling this client. @@ -85,13 +85,14 @@ final class ConnectionRecord { void dump(PrintWriter pw, String prefix) { pw.println(prefix + "binding=" + binding); if (activity != null) { - pw.println(prefix + "activity=" + activity); + activity.dump(pw, prefix); } pw.println(prefix + "conn=" + conn.asBinder() + " flags=0x" + Integer.toHexString(flags)); } - ConnectionRecord(AppBindRecord _binding, ActivityRecord _activity, + ConnectionRecord(AppBindRecord _binding, + ActivityServiceConnectionsHolder _activity, IServiceConnection _conn, int _flags, int _clientLabel, PendingIntent _clientIntent, int _clientUid, String _clientProcessName) { diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java index bcf9212464db7..db0bd4f72fd47 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java @@ -33,6 +33,7 @@ import android.service.voice.IVoiceInteractionSession; import android.util.SparseIntArray; import com.android.internal.app.IVoiceInteractor; +import com.android.server.am.ActivityServiceConnectionsHolder; import com.android.server.am.PendingIntentRecord; import com.android.server.am.SafeActivityOptions; import com.android.server.am.TaskRecord; @@ -332,4 +333,7 @@ public abstract class ActivityTaskManagerInternal { int callingUid, int userId, IBinder token, String resultWho, int requestCode, Intent[] intents, String[] resolvedTypes, int flags, Bundle bOptions); + + /** @return the service connection holder for a given activity token. */ + public abstract ActivityServiceConnectionsHolder getServiceConnectionsHolder(IBinder token); }