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 22 import android.util.ArraySet; 23 import android.util.Slog; 24 25 import com.android.internal.annotations.GuardedBy; 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 /** The activity the owns this service connection object. */ 42 private final ActivityRecord mActivity; 43 44 /** 45 * The service connection object bounded with the owning activity. They represent 46 * ConnectionRecord on the AM side, however we don't need to know their object representation 47 * on the WM side since we don't perform operations on the object. Mainly here for communication 48 * and booking with the AM side. 49 */ 50 @GuardedBy("mActivity") 51 private ArraySet<T> mConnections; 52 53 /** Whether all connections of {@link #mActivity} are being removed. */ 54 private volatile boolean mIsDisconnecting; 55 ActivityServiceConnectionsHolder(ActivityRecord activity)56 ActivityServiceConnectionsHolder(ActivityRecord activity) { 57 mActivity = activity; 58 } 59 60 /** Adds a connection record that the activity has bound to a specific service. */ addConnection(T c)61 public void addConnection(T c) { 62 synchronized (mActivity) { 63 if (mIsDisconnecting) { 64 // This is unlikely to happen because the caller should create a new holder. 65 if (DEBUG_CLEANUP) { 66 Slog.e(TAG_ATM, "Skip adding connection " + c + " to a disconnecting holder of " 67 + mActivity); 68 } 69 return; 70 } 71 if (mConnections == null) { 72 mConnections = new ArraySet<>(); 73 } 74 mConnections.add(c); 75 } 76 } 77 78 /** Removed a connection record between the activity and a specific service. */ removeConnection(T c)79 public void removeConnection(T c) { 80 synchronized (mActivity) { 81 if (mConnections == null) { 82 return; 83 } 84 if (DEBUG_CLEANUP && mIsDisconnecting) { 85 Slog.v(TAG_ATM, "Remove pending disconnecting " + c + " of " + mActivity); 86 } 87 mConnections.remove(c); 88 } 89 } 90 91 /** @see android.content.Context#BIND_ADJUST_WITH_ACTIVITY */ isActivityVisible()92 public boolean isActivityVisible() { 93 return mActivity.mVisibleForServiceConnection; 94 } 95 getActivityPid()96 public int getActivityPid() { 97 final WindowProcessController wpc = mActivity.app; 98 return wpc != null ? wpc.getPid() : -1; 99 } 100 forEachConnection(Consumer<T> consumer)101 public void forEachConnection(Consumer<T> consumer) { 102 final ArraySet<T> connections; 103 synchronized (mActivity) { 104 if (mConnections == null || mConnections.isEmpty()) { 105 return; 106 } 107 connections = new ArraySet<>(mConnections); 108 } 109 for (int i = connections.size() - 1; i >= 0; i--) { 110 consumer.accept(connections.valueAt(i)); 111 } 112 } 113 114 /** 115 * Removes the connection between the activity and all services that were connected to it. In 116 * general, this method is used to clean up if the activity didn't unbind services before it 117 * is destroyed. 118 */ 119 @GuardedBy("mActivity") disconnectActivityFromServices()120 void disconnectActivityFromServices() { 121 if (mConnections == null || mConnections.isEmpty() || mIsDisconnecting) { 122 return; 123 } 124 // Mark as disconnecting, to guarantee that we process 125 // disconnect of these specific connections exactly once even if 126 // we're racing with rapid activity lifecycle churn and this 127 // method is invoked more than once on this object. 128 // It is possible that {@link #removeConnection} is called while the disconnect-runnable is 129 // still in the message queue, so keep the reference of {@link #mConnections} to make sure 130 // the connection list is up-to-date. 131 mIsDisconnecting = true; 132 mActivity.mAtmService.mH.post(() -> { 133 mActivity.mAtmService.mAmInternal.disconnectActivityFromServices(this); 134 mIsDisconnecting = false; 135 }); 136 } 137 dump(PrintWriter pw, String prefix)138 public void dump(PrintWriter pw, String prefix) { 139 pw.println(prefix + "activity=" + mActivity); 140 } 141 142 /** Used by {@link ActivityRecord#dump}. */ 143 @Override toString()144 public String toString() { 145 return String.valueOf(mConnections); 146 } 147 } 148