• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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