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.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; 20 import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; 21 import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION; 22 import static android.view.WindowManager.INPUT_CONSUMER_PIP; 23 import static android.view.WindowManager.INPUT_CONSUMER_RECENTS_ANIMATION; 24 import static android.view.WindowManager.INPUT_CONSUMER_WALLPAPER; 25 import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; 26 import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL; 27 import static android.view.WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL; 28 import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY; 29 import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY; 30 import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER; 31 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_CONSUMER; 32 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; 33 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG; 34 import static android.view.WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY; 35 import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR; 36 import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL; 37 import static android.view.WindowManager.LayoutParams.TYPE_NOTIFICATION_SHADE; 38 import static android.view.WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY; 39 import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR; 40 import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_ADDITIONAL; 41 import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION; 42 import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; 43 44 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_FOCUS_LIGHT; 45 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_INPUT; 46 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; 47 import static com.android.server.wm.WindowManagerService.LOGTAG_INPUT_FOCUS; 48 49 import static java.lang.Integer.MAX_VALUE; 50 51 import android.annotation.Nullable; 52 import android.graphics.Rect; 53 import android.graphics.Region; 54 import android.os.Handler; 55 import android.os.IBinder; 56 import android.os.InputConfig; 57 import android.os.SystemClock; 58 import android.os.Trace; 59 import android.os.UserHandle; 60 import android.util.ArrayMap; 61 import android.util.EventLog; 62 import android.util.Slog; 63 import android.view.InputChannel; 64 import android.view.InputWindowHandle; 65 import android.view.SurfaceControl; 66 import android.view.WindowManager; 67 68 import com.android.internal.annotations.VisibleForTesting; 69 import com.android.internal.inputmethod.SoftInputShowHideReason; 70 import com.android.internal.protolog.common.ProtoLog; 71 import com.android.server.LocalServices; 72 import com.android.server.inputmethod.InputMethodManagerInternal; 73 74 import java.io.PrintWriter; 75 import java.lang.ref.WeakReference; 76 import java.util.Set; 77 import java.util.function.Consumer; 78 79 final class InputMonitor { 80 private final WindowManagerService mService; 81 82 // Current input focus token for keys and other non-touch events. May be null. 83 IBinder mInputFocus = null; 84 long mInputFocusRequestTimeMillis = 0; 85 86 // When true, need to call updateInputWindowsLw(). 87 private boolean mUpdateInputWindowsNeeded = true; 88 private boolean mUpdateInputWindowsPending; 89 private boolean mUpdateInputWindowsImmediately; 90 91 private final Region mTmpRegion = new Region(); 92 private final UpdateInputForAllWindowsConsumer mUpdateInputForAllWindowsConsumer; 93 94 private final int mDisplayId; 95 private final DisplayContent mDisplayContent; 96 private boolean mDisplayRemoved; 97 private int mDisplayWidth; 98 private int mDisplayHeight; 99 100 private final SurfaceControl.Transaction mInputTransaction; 101 private final Handler mHandler; 102 103 /** 104 * The set of input consumer added to the window manager by name, which consumes input events 105 * for the windows below it. 106 */ 107 private final ArrayMap<String, InputConsumerImpl> mInputConsumers = new ArrayMap(); 108 109 /** 110 * Set when recents (overview) is active as part of a shell transition. While set, any focus 111 * going to the recents activity will be redirected to the Recents input consumer. Since we 112 * draw the live-tile above the recents activity, we also need to provide that activity as a 113 * z-layering reference so that we can place the recents input consumer above it. 114 */ 115 private WeakReference<ActivityRecord> mActiveRecentsActivity = null; 116 private WeakReference<ActivityRecord> mActiveRecentsLayerRef = null; 117 118 private class UpdateInputWindows implements Runnable { 119 @Override run()120 public void run() { 121 synchronized (mService.mGlobalLock) { 122 mUpdateInputWindowsPending = false; 123 mUpdateInputWindowsNeeded = false; 124 125 if (mDisplayRemoved) { 126 return; 127 } 128 129 // Populate the input window list with information about all of the windows that 130 // could potentially receive input. 131 // As an optimization, we could try to prune the list of windows but this turns 132 // out to be difficult because only the native code knows for sure which window 133 // currently has touch focus. 134 135 // If there's a drag in flight, provide a pseudo-window to catch drag input 136 final boolean inDrag = mService.mDragDropController.dragDropActiveLocked(); 137 138 // Add all windows on the default display. 139 mUpdateInputForAllWindowsConsumer.updateInputWindows(inDrag); 140 } 141 } 142 } 143 144 private final UpdateInputWindows mUpdateInputWindows = new UpdateInputWindows(); 145 InputMonitor(WindowManagerService service, DisplayContent displayContent)146 InputMonitor(WindowManagerService service, DisplayContent displayContent) { 147 mService = service; 148 mDisplayContent = displayContent; 149 mDisplayId = displayContent.getDisplayId(); 150 mInputTransaction = mService.mTransactionFactory.get(); 151 mHandler = mService.mAnimationHandler; 152 mUpdateInputForAllWindowsConsumer = new UpdateInputForAllWindowsConsumer(); 153 } 154 onDisplayRemoved()155 void onDisplayRemoved() { 156 mHandler.removeCallbacks(mUpdateInputWindows); 157 mService.mTransactionFactory.get() 158 // Make sure any pending setInputWindowInfo transactions are completed. That 159 // prevents the timing of updating input info of removed display after cleanup. 160 .addWindowInfosReportedListener(() -> 161 // It calls InputDispatcher::setInputWindows directly. 162 mService.mInputManager.onDisplayRemoved(mDisplayId)) 163 .apply(); 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 consumer.layout(mInputTransaction, mDisplayWidth, mDisplayHeight); 171 updateInputWindowsLw(true /* force */); 172 } 173 destroyInputConsumer(String name)174 boolean destroyInputConsumer(String name) { 175 if (disposeInputConsumer(mInputConsumers.remove(name))) { 176 updateInputWindowsLw(true /* force */); 177 return true; 178 } 179 return false; 180 } 181 disposeInputConsumer(InputConsumerImpl consumer)182 private boolean disposeInputConsumer(InputConsumerImpl consumer) { 183 if (consumer != null) { 184 consumer.disposeChannelsLw(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 if (mDisplayWidth == dw && mDisplayHeight == dh) { 196 return; 197 } 198 mDisplayWidth = dw; 199 mDisplayHeight = dh; 200 try { 201 Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "layoutInputConsumer"); 202 for (int i = mInputConsumers.size() - 1; i >= 0; i--) { 203 mInputConsumers.valueAt(i).layout(mInputTransaction, dw, dh); 204 } 205 } finally { 206 Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); 207 } 208 } 209 210 // The visibility of the input consumers is recomputed each time we 211 // update the input windows. We use a model where consumers begin invisible 212 // (set so by this function) and must meet some condition for visibility on each update. resetInputConsumers(SurfaceControl.Transaction t)213 void resetInputConsumers(SurfaceControl.Transaction t) { 214 for (int i = mInputConsumers.size() - 1; i >= 0; i--) { 215 mInputConsumers.valueAt(i).hide(t); 216 } 217 } 218 createInputConsumer(IBinder token, String name, InputChannel inputChannel, int clientPid, UserHandle clientUser)219 void createInputConsumer(IBinder token, String name, InputChannel inputChannel, int clientPid, 220 UserHandle clientUser) { 221 if (mInputConsumers.containsKey(name)) { 222 throw new IllegalStateException("Existing input consumer found with name: " + name 223 + ", display: " + mDisplayId); 224 } 225 226 final InputConsumerImpl consumer = new InputConsumerImpl(mService, token, name, 227 inputChannel, clientPid, clientUser, mDisplayId); 228 switch (name) { 229 case INPUT_CONSUMER_WALLPAPER: 230 consumer.mWindowHandle.inputConfig |= InputConfig.DUPLICATE_TOUCH_TO_WALLPAPER; 231 break; 232 case INPUT_CONSUMER_PIP: 233 // This is a valid consumer type, but we don't need any additional configurations. 234 break; 235 case INPUT_CONSUMER_RECENTS_ANIMATION: 236 consumer.mWindowHandle.inputConfig &= ~InputConfig.NOT_FOCUSABLE; 237 break; 238 default: 239 throw new IllegalArgumentException("Illegal input consumer : " + name 240 + ", display: " + mDisplayId); 241 } 242 addInputConsumer(name, consumer); 243 } 244 245 @VisibleForTesting populateInputWindowHandle(final InputWindowHandleWrapper inputWindowHandle, final WindowState w)246 void populateInputWindowHandle(final InputWindowHandleWrapper inputWindowHandle, 247 final WindowState w) { 248 // Add a window to our list of input windows. 249 inputWindowHandle.setInputApplicationHandle(w.mActivityRecord != null 250 ? w.mActivityRecord.getInputApplicationHandle(false /* update */) : null); 251 inputWindowHandle.setToken(w.mInputChannelToken); 252 inputWindowHandle.setDispatchingTimeoutMillis(w.getInputDispatchingTimeoutMillis()); 253 inputWindowHandle.setTouchOcclusionMode(w.getTouchOcclusionMode()); 254 inputWindowHandle.setPaused(w.mActivityRecord != null && w.mActivityRecord.paused); 255 inputWindowHandle.setWindowToken(w.mClient); 256 257 inputWindowHandle.setName(w.getName()); 258 259 // Update layout params flags to force the window to be not touch modal. We do this to 260 // restrict the window's touchable region to the task even if it requests touches outside 261 // its window bounds. An example is a dialog in primary split should get touches outside its 262 // window within the primary task but should not get any touches going to the secondary 263 // task. 264 int flags = w.mAttrs.flags; 265 if (w.mAttrs.isModal()) { 266 flags = flags | FLAG_NOT_TOUCH_MODAL; 267 } 268 inputWindowHandle.setLayoutParamsFlags(flags); 269 inputWindowHandle.setInputConfigMasked( 270 InputConfigAdapter.getInputConfigFromWindowParams( 271 w.mAttrs.type, flags, w.mAttrs.inputFeatures), 272 InputConfigAdapter.getMask()); 273 274 final boolean focusable = w.canReceiveKeys() 275 && (mDisplayContent.hasOwnFocus() || mDisplayContent.isOnTop()); 276 inputWindowHandle.setFocusable(focusable); 277 278 final boolean hasWallpaper = mDisplayContent.mWallpaperController.isWallpaperTarget(w) 279 && !mService.mPolicy.isKeyguardShowing() 280 && w.mAttrs.areWallpaperTouchEventsEnabled(); 281 inputWindowHandle.setHasWallpaper(hasWallpaper); 282 283 // Surface insets are hardcoded to be the same in all directions 284 // and we could probably deprecate the "left/right/top/bottom" concept. 285 // we avoid reintroducing this concept by just choosing one of them here. 286 inputWindowHandle.setSurfaceInset(w.mAttrs.surfaceInsets.left); 287 288 // If we are scaling the window, input coordinates need to be inversely scaled to map from 289 // what is on screen to what is actually being touched in the UI. 290 inputWindowHandle.setScaleFactor(w.mGlobalScale != 1f ? (1f / w.mGlobalScale) : 1f); 291 292 boolean useSurfaceBoundsAsTouchRegion = false; 293 SurfaceControl touchableRegionCrop = null; 294 final Task task = w.getTask(); 295 if (task != null) { 296 if (task.isOrganized() && task.getWindowingMode() != WINDOWING_MODE_FULLSCREEN) { 297 // If the window is in a TaskManaged by a TaskOrganizer then most cropping will 298 // be applied using the SurfaceControl hierarchy from the Organizer. This means 299 // we need to make sure that these changes in crop are reflected in the input 300 // windows, and so ensure this flag is set so that the input crop always reflects 301 // the surface hierarchy. However, we only want to set this when the client did 302 // not already provide a touchable region, so that we don't ignore the one provided. 303 if (w.mTouchableInsets != TOUCHABLE_INSETS_REGION) { 304 useSurfaceBoundsAsTouchRegion = true; 305 } 306 307 if (w.mAttrs.isModal()) { 308 TaskFragment parent = w.getTaskFragment(); 309 touchableRegionCrop = parent != null ? parent.getSurfaceControl() : null; 310 } 311 } else if (task.cropWindowsToRootTaskBounds() && !w.inFreeformWindowingMode()) { 312 touchableRegionCrop = task.getRootTask().getSurfaceControl(); 313 } 314 } 315 inputWindowHandle.setReplaceTouchableRegionWithCrop(useSurfaceBoundsAsTouchRegion); 316 inputWindowHandle.setTouchableRegionCrop(touchableRegionCrop); 317 318 if (!useSurfaceBoundsAsTouchRegion) { 319 w.getSurfaceTouchableRegion(mTmpRegion, w.mAttrs); 320 inputWindowHandle.setTouchableRegion(mTmpRegion); 321 } 322 } 323 setUpdateInputWindowsNeededLw()324 void setUpdateInputWindowsNeededLw() { 325 mUpdateInputWindowsNeeded = true; 326 } 327 328 /* Updates the cached window information provided to the input dispatcher. */ updateInputWindowsLw(boolean force)329 void updateInputWindowsLw(boolean force) { 330 if (!force && !mUpdateInputWindowsNeeded) { 331 return; 332 } 333 scheduleUpdateInputWindows(); 334 } 335 scheduleUpdateInputWindows()336 private void scheduleUpdateInputWindows() { 337 if (mDisplayRemoved) { 338 return; 339 } 340 341 if (!mUpdateInputWindowsPending) { 342 mUpdateInputWindowsPending = true; 343 mHandler.post(mUpdateInputWindows); 344 } 345 } 346 347 /** 348 * Immediately update the input transaction and merge into the passing Transaction that could be 349 * collected and applied later. 350 */ updateInputWindowsImmediately(SurfaceControl.Transaction t)351 void updateInputWindowsImmediately(SurfaceControl.Transaction t) { 352 mHandler.removeCallbacks(mUpdateInputWindows); 353 mUpdateInputWindowsImmediately = true; 354 mUpdateInputWindows.run(); 355 mUpdateInputWindowsImmediately = false; 356 t.merge(mInputTransaction); 357 } 358 359 /** 360 * Called when the current input focus changes. Will apply it in next updateInputWindows. 361 * Layer assignment is assumed to be complete by the time this is called. 362 */ setInputFocusLw(WindowState newWindow, boolean updateInputWindows)363 void setInputFocusLw(WindowState newWindow, boolean updateInputWindows) { 364 ProtoLog.v(WM_DEBUG_FOCUS_LIGHT, "Input focus has changed to %s display=%d", 365 newWindow, mDisplayId); 366 final IBinder focus = newWindow != null ? newWindow.mInputChannelToken : null; 367 if (focus == mInputFocus) { 368 return; 369 } 370 371 if (newWindow != null && newWindow.canReceiveKeys()) { 372 // Displaying a window implicitly causes dispatching to be unpaused. 373 // This is to protect against bugs if someone pauses dispatching but 374 // forgets to resume. 375 newWindow.mToken.paused = false; 376 } 377 378 setUpdateInputWindowsNeededLw(); 379 380 if (updateInputWindows) { 381 updateInputWindowsLw(false /*force*/); 382 } 383 } 384 385 /** 386 * Inform InputMonitor when recents is active so it can enable the recents input consumer. 387 * @param activity The active recents activity. {@code null} means recents is not active. 388 * @param layer An activity whose Z-layer is used as a reference for how to sort the consumer. 389 */ setActiveRecents(@ullable ActivityRecord activity, @Nullable ActivityRecord layer)390 void setActiveRecents(@Nullable ActivityRecord activity, @Nullable ActivityRecord layer) { 391 final boolean clear = activity == null; 392 mActiveRecentsActivity = clear ? null : new WeakReference<>(activity); 393 mActiveRecentsLayerRef = clear ? null : new WeakReference<>(layer); 394 } 395 getWeak(WeakReference<T> ref)396 private static <T> T getWeak(WeakReference<T> ref) { 397 return ref != null ? ref.get() : null; 398 } 399 400 /** 401 * Called when the current input focus changes. 402 */ updateInputFocusRequest(InputConsumerImpl recentsAnimationInputConsumer)403 private void updateInputFocusRequest(InputConsumerImpl recentsAnimationInputConsumer) { 404 final WindowState focus = mDisplayContent.mCurrentFocus; 405 // Request focus for the recents animation input consumer if an input consumer should 406 // be applied for the window. 407 if (recentsAnimationInputConsumer != null && focus != null) { 408 final RecentsAnimationController recentsAnimationController = 409 mService.getRecentsAnimationController(); 410 // Apply recents input consumer when the focusing window is in recents animation. 411 final boolean shouldApplyRecentsInputConsumer = (recentsAnimationController != null 412 && recentsAnimationController.shouldApplyInputConsumer(focus.mActivityRecord)) 413 // Shell transitions doesn't use RecentsAnimationController but we still 414 // have carryover legacy logic that relies on the consumer. 415 || (getWeak(mActiveRecentsActivity) != null && focus.inTransition() 416 // only take focus from the recents activity to avoid intercepting 417 // events before the gesture officially starts. 418 && focus.isActivityTypeHomeOrRecents()); 419 if (shouldApplyRecentsInputConsumer) { 420 if (mInputFocus != recentsAnimationInputConsumer.mWindowHandle.token) { 421 requestFocus(recentsAnimationInputConsumer.mWindowHandle.token, 422 recentsAnimationInputConsumer.mName); 423 } 424 if (mDisplayContent.mInputMethodWindow != null 425 && mDisplayContent.mInputMethodWindow.isVisible()) { 426 // Hiding IME/IME icon when recents input consumer gain focus. 427 final boolean isImeAttachedToApp = mDisplayContent.isImeAttachedToApp(); 428 if (!isImeAttachedToApp) { 429 // Hiding IME if IME window is not attached to app since it's not proper to 430 // snapshot Task with IME window to animate together in this case. 431 final InputMethodManagerInternal inputMethodManagerInternal = 432 LocalServices.getService(InputMethodManagerInternal.class); 433 if (inputMethodManagerInternal != null) { 434 inputMethodManagerInternal.hideCurrentInputMethod( 435 SoftInputShowHideReason.HIDE_RECENTS_ANIMATION); 436 } 437 // Ensure removing the IME snapshot when the app no longer to show on the 438 // task snapshot (also taking the new task snaphot to update the overview). 439 final ActivityRecord app = mDisplayContent.getImeInputTarget() != null 440 ? mDisplayContent.getImeInputTarget().getActivityRecord() : null; 441 if (app != null) { 442 mDisplayContent.removeImeSurfaceImmediately(); 443 if (app.getTask() != null) { 444 mDisplayContent.mAtmService.takeTaskSnapshot(app.getTask().mTaskId, 445 true /* updateCache */); 446 } 447 } 448 } else { 449 // Disable IME icon explicitly when IME attached to the app in case 450 // IME icon might flickering while swiping to the next app task still 451 // in animating before the next app window focused, or IME icon 452 // persists on the bottom when swiping the task to recents. 453 InputMethodManagerInternal.get().updateImeWindowStatus( 454 true /* disableImeIcon */); 455 } 456 } 457 return; 458 } 459 } 460 461 final IBinder focusToken = focus != null ? focus.mInputChannelToken : null; 462 if (focusToken == null) { 463 if (recentsAnimationInputConsumer != null 464 && recentsAnimationInputConsumer.mWindowHandle != null 465 && mInputFocus == recentsAnimationInputConsumer.mWindowHandle.token) { 466 // Avoid removing input focus from recentsAnimationInputConsumer. 467 // When the recents animation input consumer has the input focus, 468 // mInputFocus does not match to mDisplayContent.mCurrentFocus. Making it to be 469 // a special case, that do not remove the input focus from it when 470 // mDisplayContent.mCurrentFocus is null. This special case should be removed 471 // once recentAnimationInputConsumer is removed. 472 return; 473 } 474 // When an app is focused, but its window is not showing yet, remove the input focus 475 // from the current window. This enforces the input focus to match 476 // mDisplayContent.mCurrentFocus. However, if more special cases are discovered that 477 // the input focus and mDisplayContent.mCurrentFocus are expected to mismatch, 478 // the whole logic of how and when to revoke focus needs to be checked. 479 if (mDisplayContent.mFocusedApp != null && mInputFocus != null) { 480 ProtoLog.v(WM_DEBUG_FOCUS_LIGHT, "App %s is focused," 481 + " but the window is not ready. Start a transaction to remove focus from" 482 + " the window of non-focused apps.", 483 mDisplayContent.mFocusedApp.getName()); 484 EventLog.writeEvent(LOGTAG_INPUT_FOCUS, "Requesting to set focus to null window", 485 "reason=UpdateInputWindows"); 486 mInputTransaction.removeCurrentInputFocus(mDisplayId); 487 } 488 mInputFocus = null; 489 return; 490 } 491 492 if (!focus.mWinAnimator.hasSurface() || !focus.mInputWindowHandle.isFocusable()) { 493 ProtoLog.v(WM_DEBUG_FOCUS_LIGHT, "Focus not requested for window=%s" 494 + " because it has no surface or is not focusable.", focus); 495 mInputFocus = null; 496 return; 497 } 498 499 requestFocus(focusToken, focus.getName()); 500 } 501 requestFocus(IBinder focusToken, String windowName)502 private void requestFocus(IBinder focusToken, String windowName) { 503 if (focusToken == mInputFocus) { 504 return; 505 } 506 507 mInputFocus = focusToken; 508 mInputFocusRequestTimeMillis = SystemClock.uptimeMillis(); 509 mInputTransaction.setFocusedWindow(mInputFocus, windowName, mDisplayId); 510 EventLog.writeEvent(LOGTAG_INPUT_FOCUS, "Focus request " + windowName, 511 "reason=UpdateInputWindows"); 512 ProtoLog.v(WM_DEBUG_FOCUS_LIGHT, "Focus requested for window=%s", windowName); 513 } 514 setFocusedAppLw(ActivityRecord newApp)515 void setFocusedAppLw(ActivityRecord newApp) { 516 // Focused app has changed. 517 mService.mInputManager.setFocusedApplication(mDisplayId, 518 newApp != null ? newApp.getInputApplicationHandle(true /* update */) : null); 519 } 520 pauseDispatchingLw(WindowToken window)521 public void pauseDispatchingLw(WindowToken window) { 522 if (! window.paused) { 523 if (DEBUG_INPUT) { 524 Slog.v(TAG_WM, "Pausing WindowToken " + window); 525 } 526 527 window.paused = true; 528 updateInputWindowsLw(true /*force*/); 529 } 530 } 531 resumeDispatchingLw(WindowToken window)532 public void resumeDispatchingLw(WindowToken window) { 533 if (window.paused) { 534 if (DEBUG_INPUT) { 535 Slog.v(TAG_WM, "Resuming WindowToken " + window); 536 } 537 538 window.paused = false; 539 updateInputWindowsLw(true /*force*/); 540 } 541 } 542 dump(PrintWriter pw, String prefix)543 void dump(PrintWriter pw, String prefix) { 544 final Set<String> inputConsumerKeys = mInputConsumers.keySet(); 545 if (!inputConsumerKeys.isEmpty()) { 546 pw.println(prefix + "InputConsumers:"); 547 for (String key : inputConsumerKeys) { 548 mInputConsumers.get(key).dump(pw, key, prefix); 549 } 550 } 551 } 552 553 private final class UpdateInputForAllWindowsConsumer implements Consumer<WindowState> { 554 InputConsumerImpl mPipInputConsumer; 555 InputConsumerImpl mWallpaperInputConsumer; 556 InputConsumerImpl mRecentsAnimationInputConsumer; 557 558 private boolean mAddPipInputConsumerHandle; 559 private boolean mAddWallpaperInputConsumerHandle; 560 private boolean mAddRecentsAnimationInputConsumerHandle; 561 562 private boolean mInDrag; 563 private final Rect mTmpRect = new Rect(); 564 updateInputWindows(boolean inDrag)565 private void updateInputWindows(boolean inDrag) { 566 Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "updateInputWindows"); 567 568 mPipInputConsumer = getInputConsumer(INPUT_CONSUMER_PIP); 569 mWallpaperInputConsumer = getInputConsumer(INPUT_CONSUMER_WALLPAPER); 570 mRecentsAnimationInputConsumer = getInputConsumer(INPUT_CONSUMER_RECENTS_ANIMATION); 571 572 mAddPipInputConsumerHandle = mPipInputConsumer != null; 573 mAddWallpaperInputConsumerHandle = mWallpaperInputConsumer != null; 574 mAddRecentsAnimationInputConsumerHandle = mRecentsAnimationInputConsumer != null; 575 576 mInDrag = inDrag; 577 578 resetInputConsumers(mInputTransaction); 579 // Update recents input consumer layer if active 580 final ActivityRecord activeRecents = getWeak(mActiveRecentsActivity); 581 if (mAddRecentsAnimationInputConsumerHandle && activeRecents != null 582 && activeRecents.getSurfaceControl() != null) { 583 WindowContainer layer = getWeak(mActiveRecentsLayerRef); 584 layer = layer != null ? layer : activeRecents; 585 // Handle edge-case for SUW where windows don't exist yet 586 if (layer.getSurfaceControl() != null) { 587 final WindowState targetAppMainWindow = activeRecents.findMainWindow(); 588 if (targetAppMainWindow != null) { 589 targetAppMainWindow.getBounds(mTmpRect); 590 mRecentsAnimationInputConsumer.mWindowHandle.touchableRegion.set(mTmpRect); 591 } 592 mRecentsAnimationInputConsumer.show(mInputTransaction, layer); 593 mAddRecentsAnimationInputConsumerHandle = false; 594 } 595 } 596 mDisplayContent.forAllWindows(this, true /* traverseTopToBottom */); 597 updateInputFocusRequest(mRecentsAnimationInputConsumer); 598 599 if (!mUpdateInputWindowsImmediately) { 600 mDisplayContent.getPendingTransaction().merge(mInputTransaction); 601 mDisplayContent.scheduleAnimation(); 602 } 603 604 Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); 605 } 606 607 @Override accept(WindowState w)608 public void accept(WindowState w) { 609 final InputWindowHandleWrapper inputWindowHandle = w.mInputWindowHandle; 610 if (w.mInputChannelToken == null || w.mRemoved || !w.canReceiveTouchInput()) { 611 if (w.mWinAnimator.hasSurface()) { 612 // Make sure the input info can't receive input event. It may be omitted from 613 // occlusion detection depending on the type or if it's a trusted overlay. 614 populateOverlayInputInfo(inputWindowHandle, w); 615 setInputWindowInfoIfNeeded(mInputTransaction, 616 w.mWinAnimator.mSurfaceController.mSurfaceControl, inputWindowHandle); 617 return; 618 } 619 // Skip this window because it cannot possibly receive input. 620 return; 621 } 622 623 // This only works for legacy transitions. 624 final RecentsAnimationController recentsAnimationController = 625 mService.getRecentsAnimationController(); 626 final boolean shouldApplyRecentsInputConsumer = recentsAnimationController != null 627 && recentsAnimationController.shouldApplyInputConsumer(w.mActivityRecord); 628 if (mAddRecentsAnimationInputConsumerHandle && shouldApplyRecentsInputConsumer) { 629 if (recentsAnimationController.updateInputConsumerForApp( 630 mRecentsAnimationInputConsumer.mWindowHandle)) { 631 final DisplayArea targetDA = 632 recentsAnimationController.getTargetAppDisplayArea(); 633 if (targetDA != null) { 634 mRecentsAnimationInputConsumer.reparent(mInputTransaction, targetDA); 635 mRecentsAnimationInputConsumer.show(mInputTransaction, MAX_VALUE - 2); 636 mAddRecentsAnimationInputConsumerHandle = false; 637 } 638 } 639 } 640 641 if (w.inPinnedWindowingMode()) { 642 if (mAddPipInputConsumerHandle) { 643 final Task rootTask = w.getTask().getRootTask(); 644 mPipInputConsumer.mWindowHandle.replaceTouchableRegionWithCrop( 645 rootTask.getSurfaceControl()); 646 final DisplayArea targetDA = rootTask.getDisplayArea(); 647 // We set the layer to z=MAX-1 so that it's always on top. 648 if (targetDA != null) { 649 mPipInputConsumer.layout(mInputTransaction, rootTask.getBounds()); 650 mPipInputConsumer.reparent(mInputTransaction, targetDA); 651 mPipInputConsumer.show(mInputTransaction, MAX_VALUE - 1); 652 mAddPipInputConsumerHandle = false; 653 } 654 } 655 } 656 657 if (mAddWallpaperInputConsumerHandle) { 658 if (w.mAttrs.type == TYPE_WALLPAPER && w.isVisible()) { 659 mWallpaperInputConsumer.mWindowHandle 660 .replaceTouchableRegionWithCrop(null /* use this surface's bounds */); 661 // Add the wallpaper input consumer above the first visible wallpaper. 662 mWallpaperInputConsumer.show(mInputTransaction, w); 663 mAddWallpaperInputConsumerHandle = false; 664 } 665 } 666 667 // If there's a drag in progress and 'child' is a potential drop target, 668 // make sure it's been told about the drag 669 if (mInDrag && w.isVisible() && w.getDisplayContent().isDefaultDisplay) { 670 mService.mDragDropController.sendDragStartedIfNeededLocked(w); 671 } 672 673 // register key interception info 674 mService.mKeyInterceptionInfoForToken.put(w.mInputChannelToken, 675 w.getKeyInterceptionInfo()); 676 677 if (w.mWinAnimator.hasSurface()) { 678 populateInputWindowHandle(inputWindowHandle, w); 679 setInputWindowInfoIfNeeded(mInputTransaction, 680 w.mWinAnimator.mSurfaceController.mSurfaceControl, inputWindowHandle); 681 } 682 } 683 } 684 685 @VisibleForTesting setInputWindowInfoIfNeeded(SurfaceControl.Transaction t, SurfaceControl sc, InputWindowHandleWrapper inputWindowHandle)686 static void setInputWindowInfoIfNeeded(SurfaceControl.Transaction t, SurfaceControl sc, 687 InputWindowHandleWrapper inputWindowHandle) { 688 if (DEBUG_INPUT) { 689 Slog.d(TAG_WM, "Update InputWindowHandle: " + inputWindowHandle); 690 } 691 if (inputWindowHandle.isChanged()) { 692 inputWindowHandle.applyChangesToSurface(t, sc); 693 } 694 } 695 populateOverlayInputInfo(InputWindowHandleWrapper inputWindowHandle, WindowState w)696 static void populateOverlayInputInfo(InputWindowHandleWrapper inputWindowHandle, 697 WindowState w) { 698 populateOverlayInputInfo(inputWindowHandle); 699 inputWindowHandle.setTouchOcclusionMode(w.getTouchOcclusionMode()); 700 } 701 702 // This would reset InputWindowHandle fields to prevent it could be found by input event. 703 // We need to check if any new field of InputWindowHandle could impact the result. 704 @VisibleForTesting populateOverlayInputInfo(InputWindowHandleWrapper inputWindowHandle)705 static void populateOverlayInputInfo(InputWindowHandleWrapper inputWindowHandle) { 706 inputWindowHandle.setDispatchingTimeoutMillis(0); // It should never receive input. 707 inputWindowHandle.setFocusable(false); 708 // The input window handle without input channel must not have a token. 709 inputWindowHandle.setToken(null); 710 inputWindowHandle.setScaleFactor(1f); 711 final int defaultType = WindowManager.LayoutParams.TYPE_APPLICATION; 712 inputWindowHandle.setLayoutParamsType(defaultType); 713 inputWindowHandle.setInputConfigMasked( 714 InputConfigAdapter.getInputConfigFromWindowParams( 715 defaultType, 716 FLAG_NOT_TOUCHABLE, 717 INPUT_FEATURE_NO_INPUT_CHANNEL), 718 InputConfigAdapter.getMask()); 719 inputWindowHandle.clearTouchableRegion(); 720 inputWindowHandle.setTouchableRegionCrop(null); 721 } 722 723 /** 724 * Helper function to generate an InputInfo with type SECURE_SYSTEM_OVERLAY. This input 725 * info will not have an input channel or be touchable, but is used to omit Surfaces 726 * from occlusion detection, so that System global overlays like the Watermark aren't 727 * counted by the InputDispatcher as occluding applications below. 728 */ setTrustedOverlayInputInfo(SurfaceControl sc, SurfaceControl.Transaction t, int displayId, String name)729 static void setTrustedOverlayInputInfo(SurfaceControl sc, SurfaceControl.Transaction t, 730 int displayId, String name) { 731 final InputWindowHandleWrapper inputWindowHandle = new InputWindowHandleWrapper( 732 new InputWindowHandle(null /* inputApplicationHandle */, displayId)); 733 inputWindowHandle.setName(name); 734 inputWindowHandle.setLayoutParamsType(TYPE_SECURE_SYSTEM_OVERLAY); 735 inputWindowHandle.setTrustedOverlay(true); 736 populateOverlayInputInfo(inputWindowHandle); 737 setInputWindowInfoIfNeeded(t, sc, inputWindowHandle); 738 } 739 isTrustedOverlay(int type)740 static boolean isTrustedOverlay(int type) { 741 return type == TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY 742 || type == TYPE_INPUT_METHOD || type == TYPE_INPUT_METHOD_DIALOG 743 || type == TYPE_MAGNIFICATION_OVERLAY || type == TYPE_STATUS_BAR 744 || type == TYPE_NOTIFICATION_SHADE 745 || type == TYPE_NAVIGATION_BAR 746 || type == TYPE_NAVIGATION_BAR_PANEL 747 || type == TYPE_SECURE_SYSTEM_OVERLAY 748 || type == TYPE_DOCK_DIVIDER 749 || type == TYPE_ACCESSIBILITY_OVERLAY 750 || type == TYPE_INPUT_CONSUMER 751 || type == TYPE_VOICE_INTERACTION 752 || type == TYPE_STATUS_BAR_ADDITIONAL; 753 } 754 } 755