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.ActivityStack.ActivityState.PAUSING;
20 import static com.android.server.wm.ActivityStack.ActivityState.RESUMED;
21 
22 import java.io.PrintWriter;
23 import java.util.HashSet;
24 import java.util.Iterator;
25 import java.util.function.Consumer;
26 
27 /**
28  * Class for tracking the connections to services on the AM side that activities on the
29  * WM side (in the future) bind with for things like oom score adjustment. Would normally be one
30  * instance of this per activity for tracking all services connected to that activity. AM will
31  * sometimes query this to bump the OOM score for the processes with services connected to visible
32  * activities.
33  */
34 public class ActivityServiceConnectionsHolder<T> {
35 
36     private final ActivityTaskManagerService mService;
37 
38     /** The activity the owns this service connection object. */
39     private final ActivityRecord mActivity;
40 
41     /**
42      * The service connection object bounded with the owning activity. They represent
43      * ConnectionRecord on the AM side, however we don't need to know their object representation
44      * on the WM side since we don't perform operations on the object. Mainly here for communication
45      * and booking with the AM side.
46      */
47     private HashSet<T> mConnections;
48 
ActivityServiceConnectionsHolder(ActivityTaskManagerService service, ActivityRecord activity)49     ActivityServiceConnectionsHolder(ActivityTaskManagerService service, ActivityRecord activity) {
50         mService = service;
51         mActivity = activity;
52     }
53 
54     /** Adds a connection record that the activity has bound to a specific service. */
addConnection(T c)55     public void addConnection(T c) {
56         synchronized (mService.mGlobalLock) {
57             if (mConnections == null) {
58                 mConnections = new HashSet<>();
59             }
60             mConnections.add(c);
61         }
62     }
63 
64     /** Removed a connection record between the activity and a specific service. */
removeConnection(T c)65     public void removeConnection(T c) {
66         synchronized (mService.mGlobalLock) {
67             if (mConnections == null) {
68                 return;
69             }
70             mConnections.remove(c);
71         }
72     }
73 
isActivityVisible()74     public boolean isActivityVisible() {
75         synchronized (mService.mGlobalLock) {
76             return mActivity.visible || mActivity.isState(RESUMED, PAUSING);
77         }
78     }
79 
getActivityPid()80     public int getActivityPid() {
81         synchronized (mService.mGlobalLock) {
82             return mActivity.hasProcess() ? mActivity.app.getPid() : -1;
83         }
84     }
85 
forEachConnection(Consumer<T> consumer)86     public void forEachConnection(Consumer<T> consumer) {
87         synchronized (mService.mGlobalLock) {
88             if (mConnections == null || mConnections.isEmpty()) {
89                 return;
90             }
91             final Iterator<T> it = mConnections.iterator();
92             while (it.hasNext()) {
93                 T c = it.next();
94                 consumer.accept(c);
95             }
96         }
97     }
98 
99     /** Removes the connection between the activity and all services that were connected to it. */
disconnectActivityFromServices()100     void disconnectActivityFromServices() {
101         if (mConnections == null || mConnections.isEmpty()) {
102             return;
103         }
104         // Capture and null out mConnections, to guarantee that we process
105         // disconnect of these specific connections exactly once even if
106         // we're racing with rapid activity lifecycle churn and this
107         // method is invoked more than once on this object.
108         final Object disc = mConnections;
109         mConnections = null;
110         mService.mH.post(() -> mService.mAmInternal.disconnectActivityFromServices(this, disc));
111     }
112 
dump(PrintWriter pw, String prefix)113     public void dump(PrintWriter pw, String prefix) {
114         synchronized (mService.mGlobalLock) {
115             pw.println(prefix + "activity=" + mActivity);
116         }
117     }
118 
119 }
120