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