1 /* 2 * Copyright (C) 2010 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 android.os.Trace.TRACE_TAG_WINDOW_MANAGER; 20 import static android.view.WindowManager.INPUT_CONSUMER_NAVIGATION; 21 import static android.view.WindowManager.INPUT_CONSUMER_PIP; 22 import static android.view.WindowManager.INPUT_CONSUMER_RECENTS_ANIMATION; 23 import static android.view.WindowManager.INPUT_CONSUMER_WALLPAPER; 24 import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL; 25 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_DISABLE_WALLPAPER_TOUCH_EVENTS; 26 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD; 27 import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; 28 29 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS_LIGHT; 30 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_INPUT; 31 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_POSITIONING; 32 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; 33 34 import android.graphics.Rect; 35 import android.os.Handler; 36 import android.os.IBinder; 37 import android.os.Looper; 38 import android.os.Process; 39 import android.os.Trace; 40 import android.os.UserHandle; 41 import android.util.ArrayMap; 42 import android.util.Log; 43 import android.util.Slog; 44 import android.view.InputApplicationHandle; 45 import android.view.InputChannel; 46 import android.view.InputEventReceiver; 47 import android.view.InputWindowHandle; 48 import android.view.SurfaceControl; 49 50 import com.android.server.AnimationThread; 51 import com.android.server.policy.WindowManagerPolicy; 52 53 import java.io.PrintWriter; 54 import java.util.Set; 55 import java.util.function.Consumer; 56 57 final class InputMonitor { 58 private final WindowManagerService mService; 59 60 // Current window with input focus for keys and other non-touch events. May be null. 61 private WindowState mInputFocus; 62 63 // When true, need to call updateInputWindowsLw(). 64 private boolean mUpdateInputWindowsNeeded = true; 65 private boolean mUpdateInputWindowsPending; 66 private boolean mApplyImmediately; 67 68 // Currently focused input window handle. 69 private InputWindowHandle mFocusedInputWindowHandle; 70 71 private boolean mDisableWallpaperTouchEvents; 72 private final Rect mTmpRect = new Rect(); 73 private final UpdateInputForAllWindowsConsumer mUpdateInputForAllWindowsConsumer; 74 75 private final int mDisplayId; 76 private final DisplayContent mDisplayContent; 77 private boolean mDisplayRemoved; 78 79 private final SurfaceControl.Transaction mInputTransaction; 80 private final Handler mHandler; 81 82 /** 83 * The set of input consumer added to the window manager by name, which consumes input events 84 * for the windows below it. 85 */ 86 private final ArrayMap<String, InputConsumerImpl> mInputConsumers = new ArrayMap(); 87 88 private static final class EventReceiverInputConsumer extends InputConsumerImpl 89 implements WindowManagerPolicy.InputConsumer { 90 private InputMonitor mInputMonitor; 91 private final InputEventReceiver mInputEventReceiver; 92 EventReceiverInputConsumer(WindowManagerService service, InputMonitor monitor, Looper looper, String name, InputEventReceiver.Factory inputEventReceiverFactory, int clientPid, UserHandle clientUser, int displayId)93 EventReceiverInputConsumer(WindowManagerService service, InputMonitor monitor, 94 Looper looper, String name, 95 InputEventReceiver.Factory inputEventReceiverFactory, 96 int clientPid, UserHandle clientUser, int displayId) { 97 super(service, null /* token */, name, null /* inputChannel */, clientPid, clientUser, 98 displayId); 99 mInputMonitor = monitor; 100 mInputEventReceiver = inputEventReceiverFactory.createInputEventReceiver( 101 mClientChannel, looper); 102 } 103 104 @Override dismiss()105 public void dismiss() { 106 synchronized (mService.mGlobalLock) { 107 if (mInputMonitor.destroyInputConsumer(mWindowHandle.name)) { 108 mInputEventReceiver.dispose(); 109 } 110 } 111 } 112 } 113 114 private final Runnable mUpdateInputWindows = new Runnable() { 115 @Override 116 public void run() { 117 synchronized (mService.mGlobalLock) { 118 mUpdateInputWindowsPending = false; 119 mUpdateInputWindowsNeeded = false; 120 121 if (mDisplayRemoved) { 122 return; 123 } 124 125 // Populate the input window list with information about all of the windows that 126 // could potentially receive input. 127 // As an optimization, we could try to prune the list of windows but this turns 128 // out to be difficult because only the native code knows for sure which window 129 // currently has touch focus. 130 131 // If there's a drag in flight, provide a pseudo-window to catch drag input 132 final boolean inDrag = mService.mDragDropController.dragDropActiveLocked(); 133 final boolean inPositioning = 134 mService.mTaskPositioningController.isPositioningLocked(); 135 if (inPositioning) { 136 if (DEBUG_TASK_POSITIONING) { 137 Log.d(TAG_WM, "Inserting window handle for repositioning"); 138 } 139 mService.mTaskPositioningController.showInputSurface(mInputTransaction, 140 mDisplayId); 141 } else { 142 mService.mTaskPositioningController.hideInputSurface(mInputTransaction, 143 mDisplayId); 144 } 145 146 // Add all windows on the default display. 147 mUpdateInputForAllWindowsConsumer.updateInputWindows(inDrag); 148 } 149 } 150 }; 151 InputMonitor(WindowManagerService service, int displayId)152 public InputMonitor(WindowManagerService service, int displayId) { 153 mService = service; 154 mDisplayContent = mService.mRoot.getDisplayContent(displayId); 155 mDisplayId = displayId; 156 mInputTransaction = mService.mTransactionFactory.make(); 157 mHandler = AnimationThread.getHandler(); 158 mUpdateInputForAllWindowsConsumer = new UpdateInputForAllWindowsConsumer(); 159 } 160 onDisplayRemoved()161 void onDisplayRemoved() { 162 mHandler.removeCallbacks(mUpdateInputWindows); 163 mService.mInputManager.onDisplayRemoved(mDisplayId); 164 mDisplayRemoved = true; 165 } 166 addInputConsumer(String name, InputConsumerImpl consumer)167 private void addInputConsumer(String name, InputConsumerImpl consumer) { 168 mInputConsumers.put(name, consumer); 169 consumer.linkToDeathRecipient(); 170 updateInputWindowsLw(true /* force */); 171 } 172 destroyInputConsumer(String name)173 boolean destroyInputConsumer(String name) { 174 if (disposeInputConsumer(mInputConsumers.remove(name))) { 175 updateInputWindowsLw(true /* force */); 176 return true; 177 } 178 return false; 179 } 180 disposeInputConsumer(InputConsumerImpl consumer)181 private boolean disposeInputConsumer(InputConsumerImpl consumer) { 182 if (consumer != null) { 183 consumer.disposeChannelsLw(); 184 consumer.hide(mInputTransaction); 185 return true; 186 } 187 return false; 188 } 189 getInputConsumer(String name)190 InputConsumerImpl getInputConsumer(String name) { 191 return mInputConsumers.get(name); 192 } 193 layoutInputConsumers(int dw, int dh)194 void layoutInputConsumers(int dw, int dh) { 195 try { 196 Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "layoutInputConsumer"); 197 for (int i = mInputConsumers.size() - 1; i >= 0; i--) { 198 mInputConsumers.valueAt(i).layout(mInputTransaction, dw, dh); 199 } 200 } finally { 201 Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); 202 } 203 } 204 205 // The visibility of the input consumers is recomputed each time we 206 // update the input windows. We use a model where consumers begin invisible 207 // (set so by this function) and must meet some condition for visibility on each update. resetInputConsumers(SurfaceControl.Transaction t)208 void resetInputConsumers(SurfaceControl.Transaction t) { 209 for (int i = mInputConsumers.size() - 1; i >= 0; i--) { 210 mInputConsumers.valueAt(i).hide(t); 211 } 212 } 213 createInputConsumer(Looper looper, String name, InputEventReceiver.Factory inputEventReceiverFactory)214 WindowManagerPolicy.InputConsumer createInputConsumer(Looper looper, String name, 215 InputEventReceiver.Factory inputEventReceiverFactory) { 216 if (mInputConsumers.containsKey(name)) { 217 throw new IllegalStateException("Existing input consumer found with name: " + name 218 + ", display: " + mDisplayId); 219 } 220 final EventReceiverInputConsumer consumer = new EventReceiverInputConsumer(mService, 221 this, looper, name, inputEventReceiverFactory, Process.myPid(), 222 UserHandle.SYSTEM, mDisplayId); 223 addInputConsumer(name, consumer); 224 return consumer; 225 } 226 createInputConsumer(IBinder token, String name, InputChannel inputChannel, int clientPid, UserHandle clientUser)227 void createInputConsumer(IBinder token, String name, InputChannel inputChannel, int clientPid, 228 UserHandle clientUser) { 229 if (mInputConsumers.containsKey(name)) { 230 throw new IllegalStateException("Existing input consumer found with name: " + name 231 + ", display: " + mDisplayId); 232 } 233 234 final InputConsumerImpl consumer = new InputConsumerImpl(mService, token, name, 235 inputChannel, clientPid, clientUser, mDisplayId); 236 switch (name) { 237 case INPUT_CONSUMER_WALLPAPER: 238 consumer.mWindowHandle.hasWallpaper = true; 239 break; 240 case INPUT_CONSUMER_PIP: 241 // The touchable region of the Pip input window is cropped to the bounds of the 242 // stack, and we need FLAG_NOT_TOUCH_MODAL to ensure other events fall through 243 consumer.mWindowHandle.layoutParamsFlags |= FLAG_NOT_TOUCH_MODAL; 244 break; 245 } 246 addInputConsumer(name, consumer); 247 } 248 249 populateInputWindowHandle(final InputWindowHandle inputWindowHandle, final WindowState child, int flags, final int type, final boolean isVisible, final boolean hasFocus, final boolean hasWallpaper)250 void populateInputWindowHandle(final InputWindowHandle inputWindowHandle, 251 final WindowState child, int flags, final int type, final boolean isVisible, 252 final boolean hasFocus, final boolean hasWallpaper) { 253 // Add a window to our list of input windows. 254 inputWindowHandle.name = child.toString(); 255 flags = child.getSurfaceTouchableRegion(inputWindowHandle, flags); 256 inputWindowHandle.layoutParamsFlags = flags; 257 inputWindowHandle.layoutParamsType = type; 258 inputWindowHandle.dispatchingTimeoutNanos = child.getInputDispatchingTimeoutNanos(); 259 inputWindowHandle.visible = isVisible; 260 inputWindowHandle.canReceiveKeys = child.canReceiveKeys(); 261 inputWindowHandle.hasFocus = hasFocus; 262 inputWindowHandle.hasWallpaper = hasWallpaper; 263 inputWindowHandle.paused = child.mAppToken != null ? child.mAppToken.paused : false; 264 inputWindowHandle.layer = child.mLayer; 265 inputWindowHandle.ownerPid = child.mSession.mPid; 266 inputWindowHandle.ownerUid = child.mSession.mUid; 267 inputWindowHandle.inputFeatures = child.mAttrs.inputFeatures; 268 inputWindowHandle.displayId = child.getDisplayId(); 269 270 final Rect frame = child.getFrameLw(); 271 inputWindowHandle.frameLeft = frame.left; 272 inputWindowHandle.frameTop = frame.top; 273 inputWindowHandle.frameRight = frame.right; 274 inputWindowHandle.frameBottom = frame.bottom; 275 276 // Surface insets are hardcoded to be the same in all directions 277 // and we could probably deprecate the "left/right/top/bottom" concept. 278 // we avoid reintroducing this concept by just choosing one of them here. 279 inputWindowHandle.surfaceInset = child.getAttrs().surfaceInsets.left; 280 281 if (child.mGlobalScale != 1) { 282 // If we are scaling the window, input coordinates need 283 // to be inversely scaled to map from what is on screen 284 // to what is actually being touched in the UI. 285 inputWindowHandle.scaleFactor = 1.0f/child.mGlobalScale; 286 } else { 287 inputWindowHandle.scaleFactor = 1; 288 } 289 290 if (DEBUG_INPUT) { 291 Slog.d(TAG_WM, "addInputWindowHandle: " 292 + child + ", " + inputWindowHandle); 293 } 294 295 if (hasFocus) { 296 mFocusedInputWindowHandle = inputWindowHandle; 297 } 298 } 299 setUpdateInputWindowsNeededLw()300 void setUpdateInputWindowsNeededLw() { 301 mUpdateInputWindowsNeeded = true; 302 } 303 304 /* Updates the cached window information provided to the input dispatcher. */ updateInputWindowsLw(boolean force)305 void updateInputWindowsLw(boolean force) { 306 if (!force && !mUpdateInputWindowsNeeded) { 307 return; 308 } 309 scheduleUpdateInputWindows(); 310 } 311 scheduleUpdateInputWindows()312 private void scheduleUpdateInputWindows() { 313 if (mDisplayRemoved) { 314 return; 315 } 316 317 if (!mUpdateInputWindowsPending) { 318 mUpdateInputWindowsPending = true; 319 mHandler.post(mUpdateInputWindows); 320 } 321 } 322 updateInputWindowsImmediately()323 void updateInputWindowsImmediately() { 324 if (mUpdateInputWindowsPending) { 325 mApplyImmediately = true; 326 mUpdateInputWindows.run(); 327 mApplyImmediately = false; 328 } 329 } 330 331 /* Called when the current input focus changes. 332 * Layer assignment is assumed to be complete by the time this is called. 333 */ setInputFocusLw(WindowState newWindow, boolean updateInputWindows)334 public void setInputFocusLw(WindowState newWindow, boolean updateInputWindows) { 335 if (DEBUG_FOCUS_LIGHT || DEBUG_INPUT) { 336 Slog.d(TAG_WM, "Input focus has changed to " + newWindow); 337 } 338 339 if (newWindow != mInputFocus) { 340 if (newWindow != null && newWindow.canReceiveKeys()) { 341 // Displaying a window implicitly causes dispatching to be unpaused. 342 // This is to protect against bugs if someone pauses dispatching but 343 // forgets to resume. 344 newWindow.mToken.paused = false; 345 } 346 347 mInputFocus = newWindow; 348 setUpdateInputWindowsNeededLw(); 349 350 if (updateInputWindows) { 351 updateInputWindowsLw(false /*force*/); 352 } 353 } 354 } 355 setFocusedAppLw(AppWindowToken newApp)356 public void setFocusedAppLw(AppWindowToken newApp) { 357 // Focused app has changed. 358 if (newApp == null) { 359 mService.mInputManager.setFocusedApplication(mDisplayId, null); 360 } else { 361 final InputApplicationHandle handle = newApp.mInputApplicationHandle; 362 handle.name = newApp.toString(); 363 handle.dispatchingTimeoutNanos = newApp.mInputDispatchingTimeoutNanos; 364 365 mService.mInputManager.setFocusedApplication(mDisplayId, handle); 366 } 367 } 368 pauseDispatchingLw(WindowToken window)369 public void pauseDispatchingLw(WindowToken window) { 370 if (! window.paused) { 371 if (DEBUG_INPUT) { 372 Slog.v(TAG_WM, "Pausing WindowToken " + window); 373 } 374 375 window.paused = true; 376 updateInputWindowsLw(true /*force*/); 377 } 378 } 379 resumeDispatchingLw(WindowToken window)380 public void resumeDispatchingLw(WindowToken window) { 381 if (window.paused) { 382 if (DEBUG_INPUT) { 383 Slog.v(TAG_WM, "Resuming WindowToken " + window); 384 } 385 386 window.paused = false; 387 updateInputWindowsLw(true /*force*/); 388 } 389 } 390 dump(PrintWriter pw, String prefix)391 void dump(PrintWriter pw, String prefix) { 392 final Set<String> inputConsumerKeys = mInputConsumers.keySet(); 393 if (!inputConsumerKeys.isEmpty()) { 394 pw.println(prefix + "InputConsumers:"); 395 for (String key : inputConsumerKeys) { 396 mInputConsumers.get(key).dump(pw, key, prefix); 397 } 398 } 399 } 400 401 private final class UpdateInputForAllWindowsConsumer implements Consumer<WindowState> { 402 InputConsumerImpl navInputConsumer; 403 InputConsumerImpl pipInputConsumer; 404 InputConsumerImpl wallpaperInputConsumer; 405 InputConsumerImpl recentsAnimationInputConsumer; 406 407 private boolean mAddInputConsumerHandle; 408 private boolean mAddPipInputConsumerHandle; 409 private boolean mAddWallpaperInputConsumerHandle; 410 private boolean mAddRecentsAnimationInputConsumerHandle; 411 412 boolean inDrag; 413 WallpaperController wallpaperController; 414 415 // An invalid window handle that tells SurfaceFlinger not update the input info. 416 final InputWindowHandle mInvalidInputWindow = new InputWindowHandle(null, null, mDisplayId); 417 updateInputWindows(boolean inDrag)418 private void updateInputWindows(boolean inDrag) { 419 Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "updateInputWindows"); 420 421 navInputConsumer = getInputConsumer(INPUT_CONSUMER_NAVIGATION); 422 pipInputConsumer = getInputConsumer(INPUT_CONSUMER_PIP); 423 wallpaperInputConsumer = getInputConsumer(INPUT_CONSUMER_WALLPAPER); 424 recentsAnimationInputConsumer = getInputConsumer(INPUT_CONSUMER_RECENTS_ANIMATION); 425 426 mAddInputConsumerHandle = navInputConsumer != null; 427 mAddPipInputConsumerHandle = pipInputConsumer != null; 428 mAddWallpaperInputConsumerHandle = wallpaperInputConsumer != null; 429 mAddRecentsAnimationInputConsumerHandle = recentsAnimationInputConsumer != null; 430 431 mTmpRect.setEmpty(); 432 mDisableWallpaperTouchEvents = false; 433 this.inDrag = inDrag; 434 wallpaperController = mDisplayContent.mWallpaperController; 435 436 resetInputConsumers(mInputTransaction); 437 438 mDisplayContent.forAllWindows(this, 439 true /* traverseTopToBottom */); 440 441 if (mAddWallpaperInputConsumerHandle) { 442 wallpaperInputConsumer.show(mInputTransaction, 0); 443 } 444 445 if (mApplyImmediately) { 446 mInputTransaction.apply(); 447 } else { 448 mDisplayContent.getPendingTransaction().merge(mInputTransaction); 449 mDisplayContent.scheduleAnimation(); 450 } 451 452 Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); 453 } 454 455 @Override accept(WindowState w)456 public void accept(WindowState w) { 457 final InputChannel inputChannel = w.mInputChannel; 458 final InputWindowHandle inputWindowHandle = w.mInputWindowHandle; 459 if (inputChannel == null || inputWindowHandle == null || w.mRemoved 460 || w.cantReceiveTouchInput()) { 461 if (w.mWinAnimator.hasSurface()) { 462 mInputTransaction.setInputWindowInfo( 463 w.mWinAnimator.mSurfaceController.mSurfaceControl, mInvalidInputWindow); 464 } 465 // Skip this window because it cannot possibly receive input. 466 return; 467 } 468 469 final int flags = w.mAttrs.flags; 470 final int privateFlags = w.mAttrs.privateFlags; 471 final int type = w.mAttrs.type; 472 final boolean hasFocus = w.isFocused(); 473 final boolean isVisible = w.isVisibleLw(); 474 475 if (mAddRecentsAnimationInputConsumerHandle) { 476 final RecentsAnimationController recentsAnimationController = 477 mService.getRecentsAnimationController(); 478 if (recentsAnimationController != null 479 && recentsAnimationController.shouldApplyInputConsumer(w.mAppToken)) { 480 if (recentsAnimationController.updateInputConsumerForApp( 481 recentsAnimationInputConsumer.mWindowHandle, hasFocus)) { 482 recentsAnimationInputConsumer.show(mInputTransaction, w); 483 mAddRecentsAnimationInputConsumerHandle = false; 484 } 485 } 486 } 487 488 if (w.inPinnedWindowingMode()) { 489 if (mAddPipInputConsumerHandle) { 490 // Update the bounds of the Pip input consumer to match the window bounds. 491 w.getBounds(mTmpRect); 492 pipInputConsumer.layout(mInputTransaction, mTmpRect); 493 494 // The touchable region is relative to the surface top-left 495 mTmpRect.offsetTo(0, 0); 496 pipInputConsumer.mWindowHandle.touchableRegion.set(mTmpRect); 497 pipInputConsumer.show(mInputTransaction, w); 498 mAddPipInputConsumerHandle = false; 499 } 500 } 501 502 if (mAddInputConsumerHandle 503 && inputWindowHandle.layer <= navInputConsumer.mWindowHandle.layer) { 504 navInputConsumer.show(mInputTransaction, w); 505 mAddInputConsumerHandle = false; 506 } 507 508 if (mAddWallpaperInputConsumerHandle) { 509 if (w.mAttrs.type == TYPE_WALLPAPER && w.isVisibleLw()) { 510 // Add the wallpaper input consumer above the first visible wallpaper. 511 wallpaperInputConsumer.show(mInputTransaction, w); 512 mAddWallpaperInputConsumerHandle = false; 513 } 514 } 515 516 if ((privateFlags & PRIVATE_FLAG_DISABLE_WALLPAPER_TOUCH_EVENTS) != 0) { 517 mDisableWallpaperTouchEvents = true; 518 } 519 final boolean hasWallpaper = wallpaperController.isWallpaperTarget(w) 520 && (privateFlags & PRIVATE_FLAG_KEYGUARD) == 0 521 && !mDisableWallpaperTouchEvents; 522 523 // If there's a drag in progress and 'child' is a potential drop target, 524 // make sure it's been told about the drag 525 if (inDrag && isVisible && w.getDisplayContent().isDefaultDisplay) { 526 mService.mDragDropController.sendDragStartedIfNeededLocked(w); 527 } 528 529 populateInputWindowHandle( 530 inputWindowHandle, w, flags, type, isVisible, hasFocus, hasWallpaper); 531 532 if (w.mWinAnimator.hasSurface()) { 533 mInputTransaction.setInputWindowInfo( 534 w.mWinAnimator.mSurfaceController.mSurfaceControl, inputWindowHandle); 535 } 536 } 537 } 538 } 539