1 /* 2 * Copyright (C) 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License 15 */ 16 17 package com.android.server.wm; 18 19 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_CLEANUP; 20 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM; 21 import static com.android.server.wm.Task.ActivityState.PAUSING; 22 import static com.android.server.wm.Task.ActivityState.RESUMED; 23 24 import android.util.ArraySet; 25 import android.util.Slog; 26 27 import java.io.PrintWriter; 28 import java.util.function.Consumer; 29 30 /** 31 * Class for tracking the connections to services on the AM side that activities on the 32 * WM side (in the future) bind with for things like oom score adjustment. Would normally be one 33 * instance of this per activity for tracking all services connected to that activity. AM will 34 * sometimes query this to bump the OOM score for the processes with services connected to visible 35 * activities. 36 * <p> 37 * Public methods are called in AM lock, otherwise in WM lock. 38 */ 39 public class ActivityServiceConnectionsHolder<T> { 40 41 private final ActivityTaskManagerService mService; 42 43 /** The activity the owns this service connection object. */ 44 private final ActivityRecord mActivity; 45 46 /** 47 * The service connection object bounded with the owning activity. They represent 48 * ConnectionRecord on the AM side, however we don't need to know their object representation 49 * on the WM side since we don't perform operations on the object. Mainly here for communication 50 * and booking with the AM side. 51 */ 52 private ArraySet<T> mConnections; 53 54 /** Whether all connections of {@link #mActivity} are being removed. */ 55 private volatile boolean mIsDisconnecting; 56 ActivityServiceConnectionsHolder(ActivityTaskManagerService service, ActivityRecord activity)57 ActivityServiceConnectionsHolder(ActivityTaskManagerService service, ActivityRecord activity) { 58 mService = service; 59 mActivity = activity; 60 } 61 62 /** Adds a connection record that the activity has bound to a specific service. */ addConnection(T c)63 public void addConnection(T c) { 64 synchronized (mService.mGlobalLock) { 65 if (mIsDisconnecting) { 66 // This is unlikely to happen because the caller should create a new holder. 67 if (DEBUG_CLEANUP) { 68 Slog.e(TAG_ATM, "Skip adding connection " + c + " to a disconnecting holder of " 69 + mActivity); 70 } 71 return; 72 } 73 if (mConnections == null) { 74 mConnections = new ArraySet<>(); 75 } 76 mConnections.add(c); 77 } 78 } 79 80 /** Removed a connection record between the activity and a specific service. */ removeConnection(T c)81 public void removeConnection(T c) { 82 synchronized (mService.mGlobalLock) { 83 if (mConnections == null) { 84 return; 85 } 86 if (DEBUG_CLEANUP && mIsDisconnecting) { 87 Slog.v(TAG_ATM, "Remove pending disconnecting " + c + " of " + mActivity); 88 } 89 mConnections.remove(c); 90 } 91 } 92 isActivityVisible()93 public boolean isActivityVisible() { 94 synchronized (mService.mGlobalLock) { 95 return mActivity.mVisibleRequested || mActivity.isState(RESUMED, PAUSING); 96 } 97 } 98 getActivityPid()99 public int getActivityPid() { 100 synchronized (mService.mGlobalLock) { 101 return mActivity.hasProcess() ? mActivity.app.getPid() : -1; 102 } 103 } 104 forEachConnection(Consumer<T> consumer)105 public void forEachConnection(Consumer<T> consumer) { 106 synchronized (mService.mGlobalLock) { 107 if (mConnections == null || mConnections.isEmpty()) { 108 return; 109 } 110 for (int i = mConnections.size() - 1; i >= 0; i--) { 111 consumer.accept(mConnections.valueAt(i)); 112 } 113 } 114 } 115 116 /** 117 * Removes the connection between the activity and all services that were connected to it. In 118 * general, this method is used to clean up if the activity didn't unbind services before it 119 * is destroyed. 120 */ disconnectActivityFromServices()121 void disconnectActivityFromServices() { 122 if (mConnections == null || mConnections.isEmpty() || mIsDisconnecting) { 123 return; 124 } 125 // Mark as disconnecting, to guarantee that we process 126 // disconnect of these specific connections exactly once even if 127 // we're racing with rapid activity lifecycle churn and this 128 // method is invoked more than once on this object. 129 // It is possible that {@link #removeConnection} is called while the disconnect-runnable is 130 // still in the message queue, so keep the reference of {@link #mConnections} to make sure 131 // the connection list is up-to-date. 132 mIsDisconnecting = true; 133 mService.mH.post(() -> { 134 mService.mAmInternal.disconnectActivityFromServices(this); 135 mIsDisconnecting = false; 136 }); 137 } 138 dump(PrintWriter pw, String prefix)139 public void dump(PrintWriter pw, String prefix) { 140 synchronized (mService.mGlobalLock) { 141 pw.println(prefix + "activity=" + mActivity); 142 } 143 } 144 145 /** Used by {@link ActivityRecord#dump}. */ 146 @Override toString()147 public String toString() { 148 return String.valueOf(mConnections); 149 } 150 } 151