1 /*
2  * Copyright (C) 2019 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 
20 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
21 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
22 
23 import android.annotation.Nullable;
24 import android.os.IBinder;
25 import android.os.RemoteException;
26 import android.os.UserHandle;
27 import android.util.ArrayMap;
28 import android.util.Slog;
29 import android.view.IWindow;
30 import android.view.InputApplicationHandle;
31 import android.view.InputChannel;
32 
33 /**
34  * Keeps track of embedded windows.
35  *
36  * If the embedded window does not receive input then Window Manager does not keep track of it.
37  * But if they do receive input, we keep track of the calling PID to blame the right app and
38  * the host window to send pointerDownOutsideFocus.
39  */
40 class EmbeddedWindowController {
41     private static final String TAG = TAG_WITH_CLASS_NAME ? "EmbeddedWindowController" : TAG_WM;
42     /* maps input token to an embedded window */
43     private ArrayMap<IBinder /*input token */, EmbeddedWindow> mWindows = new ArrayMap<>();
44     private final Object mGlobalLock;
45     private final ActivityTaskManagerService mAtmService;
46 
EmbeddedWindowController(ActivityTaskManagerService atmService)47     EmbeddedWindowController(ActivityTaskManagerService atmService) {
48         mAtmService = atmService;
49         mGlobalLock = atmService.getGlobalLock();
50     }
51 
52     /**
53      * Adds a new embedded window.
54      *
55      * @param inputToken input channel token passed in by the embedding process when it requests
56      *                   the server to add an input channel to the embedded surface.
57      * @param window An {@link EmbeddedWindow} object to add to this controller.
58      */
add(IBinder inputToken, EmbeddedWindow window)59     void add(IBinder inputToken, EmbeddedWindow window) {
60         try {
61             mWindows.put(inputToken, window);
62             updateProcessController(window);
63             window.mClient.asBinder().linkToDeath(()-> {
64                 synchronized (mGlobalLock) {
65                     mWindows.remove(inputToken);
66                 }
67             }, 0);
68         } catch (RemoteException e) {
69             // The caller has died, remove from the map
70             mWindows.remove(inputToken);
71         }
72     }
73 
74     /**
75      * Track the host activity in the embedding process so we can determine if the
76      * process is currently showing any UI to the user.
77      */
updateProcessController(EmbeddedWindow window)78     private void updateProcessController(EmbeddedWindow window) {
79         if (window.mHostActivityRecord == null) {
80             return;
81         }
82         final WindowProcessController processController =
83                 mAtmService.getProcessController(window.mOwnerPid, window.mOwnerUid);
84         if (processController == null) {
85             Slog.w(TAG, "Could not find the embedding process.");
86         } else {
87             processController.addHostActivity(window.mHostActivityRecord);
88         }
89     }
90 
getHostWindow(IBinder inputToken)91     WindowState getHostWindow(IBinder inputToken) {
92         EmbeddedWindow embeddedWindow = mWindows.get(inputToken);
93         return embeddedWindow != null ? embeddedWindow.mHostWindowState : null;
94     }
95 
remove(IWindow client)96     void remove(IWindow client) {
97         for (int i = mWindows.size() - 1; i >= 0; i--) {
98             if (mWindows.valueAt(i).mClient.asBinder() == client.asBinder()) {
99                 mWindows.removeAt(i).onRemoved();
100                 return;
101             }
102         }
103     }
104 
onWindowRemoved(WindowState host)105     void onWindowRemoved(WindowState host) {
106         for (int i = mWindows.size() - 1; i >= 0; i--) {
107             if (mWindows.valueAt(i).mHostWindowState == host) {
108                 mWindows.removeAt(i).onRemoved();
109             }
110         }
111     }
112 
get(IBinder inputToken)113     EmbeddedWindow get(IBinder inputToken) {
114         return mWindows.get(inputToken);
115     }
116 
onActivityRemoved(ActivityRecord activityRecord)117     void onActivityRemoved(ActivityRecord activityRecord) {
118         for (int i = mWindows.size() - 1; i >= 0; i--) {
119             final EmbeddedWindow window = mWindows.valueAt(i);
120             if (window.mHostActivityRecord == activityRecord) {
121                 final WindowProcessController processController =
122                         mAtmService.getProcessController(window.mOwnerPid, window.mOwnerUid);
123                 if (processController != null) {
124                     processController.removeHostActivity(activityRecord);
125                 }
126             }
127         }
128     }
129 
130     static class EmbeddedWindow implements InputTarget {
131         final IWindow mClient;
132         @Nullable final WindowState mHostWindowState;
133         @Nullable final ActivityRecord mHostActivityRecord;
134         final int mOwnerUid;
135         final int mOwnerPid;
136         final WindowManagerService mWmService;
137         final int mDisplayId;
138         public Session mSession;
139         InputChannel mInputChannel;
140         final int mWindowType;
141 
142         /**
143          * @param session  calling session to check ownership of the window
144          * @param clientToken client token used to clean up the map if the embedding process dies
145          * @param hostWindowState input channel token belonging to the host window. This is needed
146          *                        to handle input callbacks to wm. It's used when raising ANR and
147          *                        when the user taps out side of the focused region on screen. This
148          *                        can be null if there is no host window.
149          * @param ownerUid  calling uid
150          * @param ownerPid  calling pid used for anr blaming
151          * @param windowType to forward to input
152          * @param displayId used for focus requests
153          */
EmbeddedWindow(Session session, WindowManagerService service, IWindow clientToken, WindowState hostWindowState, int ownerUid, int ownerPid, int windowType, int displayId)154         EmbeddedWindow(Session session, WindowManagerService service, IWindow clientToken,
155                        WindowState hostWindowState, int ownerUid, int ownerPid, int windowType,
156                        int displayId) {
157             mSession = session;
158             mWmService = service;
159             mClient = clientToken;
160             mHostWindowState = hostWindowState;
161             mHostActivityRecord = (mHostWindowState != null) ? mHostWindowState.mActivityRecord
162                     : null;
163             mOwnerUid = ownerUid;
164             mOwnerPid = ownerPid;
165             mWindowType = windowType;
166             mDisplayId = displayId;
167         }
168 
169         @Override
toString()170         public String toString() {
171             final String hostWindowName = (mHostWindowState != null)
172                     ? mHostWindowState.getWindowTag().toString() : "Internal";
173             return "EmbeddedWindow{ u" + UserHandle.getUserId(mOwnerUid) + " " + hostWindowName
174                     + "}";
175         }
176 
getApplicationHandle()177         InputApplicationHandle getApplicationHandle() {
178             if (mHostWindowState == null
179                     || mHostWindowState.mInputWindowHandle.getInputApplicationHandle() == null) {
180                 return null;
181             }
182             return new InputApplicationHandle(
183                     mHostWindowState.mInputWindowHandle.getInputApplicationHandle());
184         }
185 
openInputChannel()186         InputChannel openInputChannel() {
187             final String name = toString();
188             mInputChannel = mWmService.mInputManager.createInputChannel(name);
189             return mInputChannel;
190         }
191 
onRemoved()192         void onRemoved() {
193             if (mInputChannel != null) {
194                 mWmService.mInputManager.removeInputChannel(mInputChannel.getToken());
195                 mInputChannel.dispose();
196                 mInputChannel = null;
197             }
198         }
199 
200         @Override
getWindowState()201         public WindowState getWindowState() {
202             return mHostWindowState;
203         }
204 
205         @Override
getDisplayId()206         public int getDisplayId() {
207             return mDisplayId;
208         }
209 
210         @Override
getIWindow()211         public IWindow getIWindow() {
212             return mClient;
213         }
214 
215         @Override
getPid()216         public int getPid() {
217             return mOwnerPid;
218         }
219     }
220 }
221