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