• 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.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 {
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         InputChannel mInputChannel;
138         final int mWindowType;
139 
140         /**
141          * @param clientToken client token used to clean up the map if the embedding process dies
142          * @param hostWindowState input channel token belonging to the host window. This is needed
143          *                        to handle input callbacks to wm. It's used when raising ANR and
144          *                        when the user taps out side of the focused region on screen. This
145          *                        can be null if there is no host window.
146          * @param ownerUid  calling uid
147          * @param ownerPid  calling pid used for anr blaming
148          */
EmbeddedWindow(WindowManagerService service, IWindow clientToken, WindowState hostWindowState, int ownerUid, int ownerPid, int windowType)149         EmbeddedWindow(WindowManagerService service, IWindow clientToken,
150                 WindowState hostWindowState, int ownerUid, int ownerPid, int windowType) {
151             mWmService = service;
152             mClient = clientToken;
153             mHostWindowState = hostWindowState;
154             mHostActivityRecord = (mHostWindowState != null) ? mHostWindowState.mActivityRecord
155                     : null;
156             mOwnerUid = ownerUid;
157             mOwnerPid = ownerPid;
158             mWindowType = windowType;
159         }
160 
getName()161         String getName() {
162             final String hostWindowName = (mHostWindowState != null)
163                     ? mHostWindowState.getWindowTag().toString() : "Internal";
164             return "EmbeddedWindow{ u" + UserHandle.getUserId(mOwnerUid) + " " + hostWindowName
165                     + "}";
166         }
167 
getApplicationHandle()168         InputApplicationHandle getApplicationHandle() {
169             if (mHostWindowState == null
170                     || mHostWindowState.mInputWindowHandle.inputApplicationHandle == null) {
171                 return null;
172             }
173             return new InputApplicationHandle(
174                     mHostWindowState.mInputWindowHandle.inputApplicationHandle);
175         }
176 
openInputChannel()177         InputChannel openInputChannel() {
178             final String name = getName();
179 
180             final InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);
181             mInputChannel = inputChannels[0];
182             final InputChannel clientChannel = inputChannels[1];
183             mWmService.mInputManager.registerInputChannel(mInputChannel);
184 
185             if (mInputChannel.getToken() != clientChannel.getToken()) {
186                 throw new IllegalStateException("Client and Server tokens are expected to"
187                         + "be the same");
188             }
189 
190             return clientChannel;
191         }
192 
onRemoved()193         void onRemoved() {
194             if (mInputChannel != null) {
195                 mWmService.mInputManager.unregisterInputChannel(mInputChannel);
196                 mInputChannel.dispose();
197                 mInputChannel = null;
198             }
199         }
200     }
201 }
202