• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.IdentifierProto.HASH_CODE;
21 import static com.android.server.wm.IdentifierProto.TITLE;
22 import static com.android.server.wm.WindowContainerProto.IDENTIFIER;
23 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
24 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
25 
26 import android.annotation.Nullable;
27 import android.os.IBinder;
28 import android.os.RemoteException;
29 import android.os.UserHandle;
30 import android.util.ArrayMap;
31 import android.util.proto.ProtoOutputStream;
32 import android.util.Slog;
33 import android.view.IWindow;
34 import android.view.InputApplicationHandle;
35 import android.view.InputChannel;
36 
37 /**
38  * Keeps track of embedded windows.
39  *
40  * If the embedded window does not receive input then Window Manager does not keep track of it.
41  * But if they do receive input, we keep track of the calling PID to blame the right app and
42  * the host window to send pointerDownOutsideFocus.
43  */
44 class EmbeddedWindowController {
45     private static final String TAG = TAG_WITH_CLASS_NAME ? "EmbeddedWindowController" : TAG_WM;
46     /* maps input token to an embedded window */
47     private ArrayMap<IBinder /*input token */, EmbeddedWindow> mWindows = new ArrayMap<>();
48     private ArrayMap<IBinder /*focus grant token */, EmbeddedWindow> mWindowsByFocusToken =
49         new ArrayMap<>();
50     private ArrayMap<IBinder /*window token*/, EmbeddedWindow> mWindowsByWindowToken =
51         new ArrayMap<>();
52     private final Object mGlobalLock;
53     private final ActivityTaskManagerService mAtmService;
54 
EmbeddedWindowController(ActivityTaskManagerService atmService)55     EmbeddedWindowController(ActivityTaskManagerService atmService) {
56         mAtmService = atmService;
57         mGlobalLock = atmService.getGlobalLock();
58     }
59 
60     /**
61      * Adds a new embedded window.
62      *
63      * @param inputToken input channel token passed in by the embedding process when it requests
64      *                   the server to add an input channel to the embedded surface.
65      * @param window An {@link EmbeddedWindow} object to add to this controller.
66      */
add(IBinder inputToken, EmbeddedWindow window)67     void add(IBinder inputToken, EmbeddedWindow window) {
68         try {
69             mWindows.put(inputToken, window);
70             final IBinder focusToken = window.getFocusGrantToken();
71             mWindowsByFocusToken.put(focusToken, window);
72             mWindowsByWindowToken.put(window.getWindowToken(), window);
73             updateProcessController(window);
74             window.mClient.asBinder().linkToDeath(()-> {
75                 synchronized (mGlobalLock) {
76                     mWindows.remove(inputToken);
77                     mWindowsByFocusToken.remove(focusToken);
78                 }
79             }, 0);
80         } catch (RemoteException e) {
81             // The caller has died, remove from the map
82             mWindows.remove(inputToken);
83         }
84     }
85 
86     /**
87      * Track the host activity in the embedding process so we can determine if the
88      * process is currently showing any UI to the user.
89      */
updateProcessController(EmbeddedWindow window)90     private void updateProcessController(EmbeddedWindow window) {
91         if (window.mHostActivityRecord == null) {
92             return;
93         }
94         final WindowProcessController processController =
95                 mAtmService.getProcessController(window.mOwnerPid, window.mOwnerUid);
96         if (processController == null) {
97             Slog.w(TAG, "Could not find the embedding process.");
98         } else {
99             processController.addHostActivity(window.mHostActivityRecord);
100         }
101     }
102 
getHostWindow(IBinder inputToken)103     WindowState getHostWindow(IBinder inputToken) {
104         EmbeddedWindow embeddedWindow = mWindows.get(inputToken);
105         return embeddedWindow != null ? embeddedWindow.mHostWindowState : null;
106     }
107 
isOverlay(IBinder inputToken)108     boolean isOverlay(IBinder inputToken) {
109         EmbeddedWindow embeddedWindow = mWindows.get(inputToken);
110         return embeddedWindow != null ? embeddedWindow.getIsOverlay() : false;
111     }
112 
setIsOverlay(IBinder focusGrantToken)113     void setIsOverlay(IBinder focusGrantToken) {
114         EmbeddedWindow embeddedWindow = mWindowsByFocusToken.get(focusGrantToken);
115         if (embeddedWindow != null) {
116             embeddedWindow.setIsOverlay();
117         }
118     }
119 
remove(IWindow client)120     void remove(IWindow client) {
121         for (int i = mWindows.size() - 1; i >= 0; i--) {
122             EmbeddedWindow ew = mWindows.valueAt(i);
123             if (ew.mClient.asBinder() == client.asBinder()) {
124                 mWindows.removeAt(i).onRemoved();
125                 mWindowsByFocusToken.remove(ew.getFocusGrantToken());
126                 mWindowsByWindowToken.remove(ew.getWindowToken());
127                 return;
128             }
129         }
130     }
131 
onWindowRemoved(WindowState host)132     void onWindowRemoved(WindowState host) {
133         for (int i = mWindows.size() - 1; i >= 0; i--) {
134             EmbeddedWindow ew = mWindows.valueAt(i);
135             if (ew.mHostWindowState == host) {
136                 mWindows.removeAt(i).onRemoved();
137                 mWindowsByFocusToken.remove(ew.getFocusGrantToken());
138                 mWindowsByWindowToken.remove(ew.getWindowToken());
139             }
140         }
141     }
142 
get(IBinder inputToken)143     EmbeddedWindow get(IBinder inputToken) {
144         return mWindows.get(inputToken);
145     }
146 
getByFocusToken(IBinder focusGrantToken)147     EmbeddedWindow getByFocusToken(IBinder focusGrantToken) {
148         return mWindowsByFocusToken.get(focusGrantToken);
149     }
150 
getByWindowToken(IBinder windowToken)151     EmbeddedWindow getByWindowToken(IBinder windowToken) {
152         return mWindowsByWindowToken.get(windowToken);
153     }
154 
onActivityRemoved(ActivityRecord activityRecord)155     void onActivityRemoved(ActivityRecord activityRecord) {
156         for (int i = mWindows.size() - 1; i >= 0; i--) {
157             final EmbeddedWindow window = mWindows.valueAt(i);
158             if (window.mHostActivityRecord == activityRecord) {
159                 final WindowProcessController processController =
160                         mAtmService.getProcessController(window.mOwnerPid, window.mOwnerUid);
161                 if (processController != null) {
162                     processController.removeHostActivity(activityRecord);
163                 }
164             }
165         }
166     }
167 
168     static class EmbeddedWindow implements InputTarget {
169         final IWindow mClient;
170         @Nullable final WindowState mHostWindowState;
171         @Nullable final ActivityRecord mHostActivityRecord;
172         final String mName;
173         final int mOwnerUid;
174         final int mOwnerPid;
175         final WindowManagerService mWmService;
176         final int mDisplayId;
177         public Session mSession;
178         InputChannel mInputChannel;
179         final int mWindowType;
180         // Track whether the EmbeddedWindow is a system hosted overlay via
181         // {@link OverlayHost}. In the case of client hosted overlays, the client
182         // view hierarchy will take care of invoking requestEmbeddedWindowFocus
183         // but for system hosted overlays we have to do this via tapOutsideDetection
184         // and this variable is mostly used for tracking that.
185         boolean mIsOverlay = false;
186 
187         private IBinder mFocusGrantToken;
188 
189         /**
190          * @param session  calling session to check ownership of the window
191          * @param clientToken client token used to clean up the map if the embedding process dies
192          * @param hostWindowState input channel token belonging to the host window. This is needed
193          *                        to handle input callbacks to wm. It's used when raising ANR and
194          *                        when the user taps out side of the focused region on screen. This
195          *                        can be null if there is no host window.
196          * @param ownerUid  calling uid
197          * @param ownerPid  calling pid used for anr blaming
198          * @param windowType to forward to input
199          * @param displayId used for focus requests
200          */
EmbeddedWindow(Session session, WindowManagerService service, IWindow clientToken, WindowState hostWindowState, int ownerUid, int ownerPid, int windowType, int displayId, IBinder focusGrantToken, String inputHandleName)201         EmbeddedWindow(Session session, WindowManagerService service, IWindow clientToken,
202                        WindowState hostWindowState, int ownerUid, int ownerPid, int windowType,
203                        int displayId, IBinder focusGrantToken, String inputHandleName) {
204             mSession = session;
205             mWmService = service;
206             mClient = clientToken;
207             mHostWindowState = hostWindowState;
208             mHostActivityRecord = (mHostWindowState != null) ? mHostWindowState.mActivityRecord
209                     : null;
210             mOwnerUid = ownerUid;
211             mOwnerPid = ownerPid;
212             mWindowType = windowType;
213             mDisplayId = displayId;
214             mFocusGrantToken = focusGrantToken;
215             final String hostWindowName =
216                     (mHostWindowState != null) ? "-" + mHostWindowState.getWindowTag().toString()
217                             : "";
218             mName = "Embedded{" + inputHandleName + hostWindowName + "}";
219         }
220 
221         @Override
toString()222         public String toString() {
223             return mName;
224         }
225 
getApplicationHandle()226         InputApplicationHandle getApplicationHandle() {
227             if (mHostWindowState == null
228                     || mHostWindowState.mInputWindowHandle.getInputApplicationHandle() == null) {
229                 return null;
230             }
231             return new InputApplicationHandle(
232                     mHostWindowState.mInputWindowHandle.getInputApplicationHandle());
233         }
234 
openInputChannel()235         InputChannel openInputChannel() {
236             final String name = toString();
237             mInputChannel = mWmService.mInputManager.createInputChannel(name);
238             return mInputChannel;
239         }
240 
onRemoved()241         void onRemoved() {
242             if (mInputChannel != null) {
243                 mWmService.mInputManager.removeInputChannel(mInputChannel.getToken());
244                 mInputChannel.dispose();
245                 mInputChannel = null;
246             }
247         }
248 
249         @Override
getWindowState()250         public WindowState getWindowState() {
251             return mHostWindowState;
252         }
253 
254         @Override
getDisplayId()255         public int getDisplayId() {
256             return mDisplayId;
257         }
258 
259         @Override
getDisplayContent()260         public DisplayContent getDisplayContent() {
261             return mWmService.mRoot.getDisplayContent(getDisplayId());
262         }
263 
264         @Override
getIWindow()265         public IWindow getIWindow() {
266             return mClient;
267         }
268 
getWindowToken()269         public IBinder getWindowToken() {
270             return mClient.asBinder();
271         }
272 
273         @Override
getPid()274         public int getPid() {
275             return mOwnerPid;
276         }
277 
278         @Override
getUid()279         public int getUid() {
280             return mOwnerUid;
281         }
282 
setIsOverlay()283         void setIsOverlay() {
284             mIsOverlay = true;
285         }
getIsOverlay()286         boolean getIsOverlay() {
287             return mIsOverlay;
288         }
289 
getFocusGrantToken()290         IBinder getFocusGrantToken() {
291             return mFocusGrantToken;
292         }
293 
getInputChannelToken()294         IBinder getInputChannelToken() {
295             if (mInputChannel != null) {
296                 return mInputChannel.getToken();
297             }
298             return null;
299         }
300 
301         /**
302          * System hosted overlays need the WM to invoke grantEmbeddedWindowFocus and
303          * so we need to participate inside handlePointerDownOutsideFocus logic
304          * however client hosted overlays will rely on the hosting view hierarchy
305          * to grant and revoke focus, and so the server side logic is not needed.
306          */
307         @Override
receiveFocusFromTapOutside()308         public boolean receiveFocusFromTapOutside() {
309             return mIsOverlay;
310         }
311 
handleTap(boolean grantFocus)312         private void handleTap(boolean grantFocus) {
313             if (mInputChannel != null) {
314                 mWmService.grantEmbeddedWindowFocus(mSession, mFocusGrantToken, grantFocus);
315             }
316         }
317 
318         @Override
handleTapOutsideFocusOutsideSelf()319         public void handleTapOutsideFocusOutsideSelf() {
320             handleTap(false);
321         }
322 
323         @Override
handleTapOutsideFocusInsideSelf()324         public void handleTapOutsideFocusInsideSelf() {
325             handleTap(true);
326         }
327 
328         @Override
shouldControlIme()329         public boolean shouldControlIme() {
330             return false;
331         }
332 
333         @Override
canScreenshotIme()334         public boolean canScreenshotIme() {
335             return true;
336         }
337 
338         @Override
unfreezeInsetsAfterStartInput()339         public void unfreezeInsetsAfterStartInput() {
340         }
341 
342         @Override
getImeControlTarget()343         public InsetsControlTarget getImeControlTarget() {
344             return mWmService.getDefaultDisplayContentLocked().mRemoteInsetsControlTarget;
345         }
346 
347         @Override
isInputMethodClientFocus(int uid, int pid)348         public boolean isInputMethodClientFocus(int uid, int pid) {
349             return uid == mOwnerUid && pid == mOwnerPid;
350         }
351 
352         @Override
getActivityRecord()353         public ActivityRecord getActivityRecord() {
354             return null;
355         }
356 
357         @Override
dumpProto(ProtoOutputStream proto, long fieldId, @WindowTraceLogLevel int logLevel)358         public void dumpProto(ProtoOutputStream proto, long fieldId,
359                               @WindowTraceLogLevel int logLevel) {
360             final long token = proto.start(fieldId);
361 
362             final long token2 = proto.start(IDENTIFIER);
363             proto.write(HASH_CODE, System.identityHashCode(this));
364             proto.write(TITLE, "EmbeddedWindow");
365             proto.end(token2);
366             proto.end(token);
367         }
368     }
369 }
370