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 com.android.server.wm.WindowManagerDebugConfig.DEBUG_DRAG; 20 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS_LIGHT; 21 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_INPUT; 22 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_POSITIONING; 23 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; 24 25 import android.app.ActivityManagerNative; 26 import android.graphics.Rect; 27 import android.os.Debug; 28 import android.os.RemoteException; 29 import android.util.Log; 30 import android.util.Slog; 31 import android.view.Display; 32 import android.view.InputChannel; 33 import android.view.KeyEvent; 34 import android.view.WindowManager; 35 36 import com.android.server.input.InputApplicationHandle; 37 import com.android.server.input.InputManagerService; 38 import com.android.server.input.InputWindowHandle; 39 40 import java.io.PrintWriter; 41 import java.util.Arrays; 42 43 final class InputMonitor implements InputManagerService.WindowManagerCallbacks { 44 private final WindowManagerService mService; 45 46 // Current window with input focus for keys and other non-touch events. May be null. 47 private WindowState mInputFocus; 48 49 // When true, prevents input dispatch from proceeding until set to false again. 50 private boolean mInputDispatchFrozen; 51 52 // The reason the input is currently frozen or null if the input isn't frozen. 53 private String mInputFreezeReason = null; 54 55 // When true, input dispatch proceeds normally. Otherwise all events are dropped. 56 // Initially false, so that input does not get dispatched until boot is finished at 57 // which point the ActivityManager will enable dispatching. 58 private boolean mInputDispatchEnabled; 59 60 // When true, need to call updateInputWindowsLw(). 61 private boolean mUpdateInputWindowsNeeded = true; 62 63 // Array of window handles to provide to the input dispatcher. 64 private InputWindowHandle[] mInputWindowHandles; 65 private int mInputWindowHandleCount; 66 67 // Set to true when the first input device configuration change notification 68 // is received to indicate that the input devices are ready. 69 private final Object mInputDevicesReadyMonitor = new Object(); 70 private boolean mInputDevicesReady; 71 InputMonitor(WindowManagerService service)72 public InputMonitor(WindowManagerService service) { 73 mService = service; 74 } 75 76 /* Notifies the window manager about a broken input channel. 77 * 78 * Called by the InputManager. 79 */ 80 @Override notifyInputChannelBroken(InputWindowHandle inputWindowHandle)81 public void notifyInputChannelBroken(InputWindowHandle inputWindowHandle) { 82 if (inputWindowHandle == null) { 83 return; 84 } 85 86 synchronized (mService.mWindowMap) { 87 WindowState windowState = (WindowState) inputWindowHandle.windowState; 88 if (windowState != null) { 89 Slog.i(TAG_WM, "WINDOW DIED " + windowState); 90 mService.removeWindowLocked(windowState); 91 } 92 } 93 } 94 95 /* Notifies the window manager about an application that is not responding. 96 * Returns a new timeout to continue waiting in nanoseconds, or 0 to abort dispatch. 97 * 98 * Called by the InputManager. 99 */ 100 @Override notifyANR(InputApplicationHandle inputApplicationHandle, InputWindowHandle inputWindowHandle, String reason)101 public long notifyANR(InputApplicationHandle inputApplicationHandle, 102 InputWindowHandle inputWindowHandle, String reason) { 103 AppWindowToken appWindowToken = null; 104 WindowState windowState = null; 105 boolean aboveSystem = false; 106 synchronized (mService.mWindowMap) { 107 if (inputWindowHandle != null) { 108 windowState = (WindowState) inputWindowHandle.windowState; 109 if (windowState != null) { 110 appWindowToken = windowState.mAppToken; 111 } 112 } 113 if (appWindowToken == null && inputApplicationHandle != null) { 114 appWindowToken = (AppWindowToken)inputApplicationHandle.appWindowToken; 115 } 116 117 if (windowState != null) { 118 Slog.i(TAG_WM, "Input event dispatching timed out " 119 + "sending to " + windowState.mAttrs.getTitle() 120 + ". Reason: " + reason); 121 // Figure out whether this window is layered above system windows. 122 // We need to do this here to help the activity manager know how to 123 // layer its ANR dialog. 124 int systemAlertLayer = mService.mPolicy.windowTypeToLayerLw( 125 WindowManager.LayoutParams.TYPE_SYSTEM_ALERT); 126 aboveSystem = windowState.mBaseLayer > systemAlertLayer; 127 } else if (appWindowToken != null) { 128 Slog.i(TAG_WM, "Input event dispatching timed out " 129 + "sending to application " + appWindowToken.stringName 130 + ". Reason: " + reason); 131 } else { 132 Slog.i(TAG_WM, "Input event dispatching timed out " 133 + ". Reason: " + reason); 134 } 135 136 mService.saveANRStateLocked(appWindowToken, windowState, reason); 137 } 138 139 if (appWindowToken != null && appWindowToken.appToken != null) { 140 try { 141 // Notify the activity manager about the timeout and let it decide whether 142 // to abort dispatching or keep waiting. 143 boolean abort = appWindowToken.appToken.keyDispatchingTimedOut(reason); 144 if (! abort) { 145 // The activity manager declined to abort dispatching. 146 // Wait a bit longer and timeout again later. 147 return appWindowToken.inputDispatchingTimeoutNanos; 148 } 149 } catch (RemoteException ex) { 150 } 151 } else if (windowState != null) { 152 try { 153 // Notify the activity manager about the timeout and let it decide whether 154 // to abort dispatching or keep waiting. 155 long timeout = ActivityManagerNative.getDefault().inputDispatchingTimedOut( 156 windowState.mSession.mPid, aboveSystem, reason); 157 if (timeout >= 0) { 158 // The activity manager declined to abort dispatching. 159 // Wait a bit longer and timeout again later. 160 return timeout * 1000000L; // nanoseconds 161 } 162 } catch (RemoteException ex) { 163 } 164 } 165 return 0; // abort dispatching 166 } 167 addInputWindowHandleLw(final InputWindowHandle windowHandle)168 private void addInputWindowHandleLw(final InputWindowHandle windowHandle) { 169 if (mInputWindowHandles == null) { 170 mInputWindowHandles = new InputWindowHandle[16]; 171 } 172 if (mInputWindowHandleCount >= mInputWindowHandles.length) { 173 mInputWindowHandles = Arrays.copyOf(mInputWindowHandles, 174 mInputWindowHandleCount * 2); 175 } 176 mInputWindowHandles[mInputWindowHandleCount++] = windowHandle; 177 } 178 addInputWindowHandleLw(final InputWindowHandle inputWindowHandle, final WindowState child, int flags, final int type, final boolean isVisible, final boolean hasFocus, final boolean hasWallpaper)179 private void addInputWindowHandleLw(final InputWindowHandle inputWindowHandle, 180 final WindowState child, int flags, final int type, final boolean isVisible, 181 final boolean hasFocus, final boolean hasWallpaper) { 182 // Add a window to our list of input windows. 183 inputWindowHandle.name = child.toString(); 184 flags = child.getTouchableRegion(inputWindowHandle.touchableRegion, flags); 185 inputWindowHandle.layoutParamsFlags = flags; 186 inputWindowHandle.layoutParamsType = type; 187 inputWindowHandle.dispatchingTimeoutNanos = child.getInputDispatchingTimeoutNanos(); 188 inputWindowHandle.visible = isVisible; 189 inputWindowHandle.canReceiveKeys = child.canReceiveKeys(); 190 inputWindowHandle.hasFocus = hasFocus; 191 inputWindowHandle.hasWallpaper = hasWallpaper; 192 inputWindowHandle.paused = child.mAppToken != null ? child.mAppToken.paused : false; 193 inputWindowHandle.layer = child.mLayer; 194 inputWindowHandle.ownerPid = child.mSession.mPid; 195 inputWindowHandle.ownerUid = child.mSession.mUid; 196 inputWindowHandle.inputFeatures = child.mAttrs.inputFeatures; 197 198 final Rect frame = child.mFrame; 199 inputWindowHandle.frameLeft = frame.left; 200 inputWindowHandle.frameTop = frame.top; 201 inputWindowHandle.frameRight = frame.right; 202 inputWindowHandle.frameBottom = frame.bottom; 203 204 if (child.isDockedInEffect()) { 205 // Adjust to account for non-resizeable tasks that's scrolled 206 inputWindowHandle.frameLeft += child.mXOffset; 207 inputWindowHandle.frameTop += child.mYOffset; 208 inputWindowHandle.frameRight += child.mXOffset; 209 inputWindowHandle.frameBottom += child.mYOffset; 210 } 211 212 if (child.mGlobalScale != 1) { 213 // If we are scaling the window, input coordinates need 214 // to be inversely scaled to map from what is on screen 215 // to what is actually being touched in the UI. 216 inputWindowHandle.scaleFactor = 1.0f/child.mGlobalScale; 217 } else { 218 inputWindowHandle.scaleFactor = 1; 219 } 220 221 if (DEBUG_INPUT) { 222 Slog.d(TAG_WM, "addInputWindowHandle: " 223 + child + ", " + inputWindowHandle); 224 } 225 addInputWindowHandleLw(inputWindowHandle); 226 } 227 clearInputWindowHandlesLw()228 private void clearInputWindowHandlesLw() { 229 while (mInputWindowHandleCount != 0) { 230 mInputWindowHandles[--mInputWindowHandleCount] = null; 231 } 232 } 233 setUpdateInputWindowsNeededLw()234 public void setUpdateInputWindowsNeededLw() { 235 mUpdateInputWindowsNeeded = true; 236 } 237 238 /* Updates the cached window information provided to the input dispatcher. */ updateInputWindowsLw(boolean force)239 public void updateInputWindowsLw(boolean force) { 240 if (!force && !mUpdateInputWindowsNeeded) { 241 return; 242 } 243 mUpdateInputWindowsNeeded = false; 244 245 if (false) Slog.d(TAG_WM, ">>>>>> ENTERED updateInputWindowsLw"); 246 247 // Populate the input window list with information about all of the windows that 248 // could potentially receive input. 249 // As an optimization, we could try to prune the list of windows but this turns 250 // out to be difficult because only the native code knows for sure which window 251 // currently has touch focus. 252 boolean disableWallpaperTouchEvents = false; 253 254 // If there's a drag in flight, provide a pseudowindow to catch drag input 255 final boolean inDrag = (mService.mDragState != null); 256 if (inDrag) { 257 if (DEBUG_DRAG) { 258 Log.d(TAG_WM, "Inserting drag window"); 259 } 260 final InputWindowHandle dragWindowHandle = mService.mDragState.mDragWindowHandle; 261 if (dragWindowHandle != null) { 262 addInputWindowHandleLw(dragWindowHandle); 263 } else { 264 Slog.w(TAG_WM, "Drag is in progress but there is no " 265 + "drag window handle."); 266 } 267 } 268 269 final boolean inPositioning = (mService.mTaskPositioner != null); 270 if (inPositioning) { 271 if (DEBUG_TASK_POSITIONING) { 272 Log.d(TAG_WM, "Inserting window handle for repositioning"); 273 } 274 final InputWindowHandle dragWindowHandle = mService.mTaskPositioner.mDragWindowHandle; 275 if (dragWindowHandle != null) { 276 addInputWindowHandleLw(dragWindowHandle); 277 } else { 278 Slog.e(TAG_WM, 279 "Repositioning is in progress but there is no drag window handle."); 280 } 281 } 282 283 boolean addInputConsumerHandle = mService.mInputConsumer != null; 284 285 boolean addWallpaperInputConsumerHandle = mService.mWallpaperInputConsumer != null; 286 287 // Add all windows on the default display. 288 final int numDisplays = mService.mDisplayContents.size(); 289 final WallpaperController wallpaperController = mService.mWallpaperControllerLocked; 290 for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) { 291 final DisplayContent displayContent = mService.mDisplayContents.valueAt(displayNdx); 292 final WindowList windows = displayContent.getWindowList(); 293 for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) { 294 final WindowState child = windows.get(winNdx); 295 final InputChannel inputChannel = child.mInputChannel; 296 final InputWindowHandle inputWindowHandle = child.mInputWindowHandle; 297 if (inputChannel == null || inputWindowHandle == null || child.mRemoved 298 || child.isAdjustedForMinimizedDock()) { 299 // Skip this window because it cannot possibly receive input. 300 continue; 301 } 302 if (addInputConsumerHandle 303 && inputWindowHandle.layer <= mService.mInputConsumer.mWindowHandle.layer) { 304 addInputWindowHandleLw(mService.mInputConsumer.mWindowHandle); 305 addInputConsumerHandle = false; 306 } 307 308 if (addWallpaperInputConsumerHandle) { 309 if (child.mAttrs.type == WindowManager.LayoutParams.TYPE_WALLPAPER) { 310 // Add the wallpaper input consumer above the first wallpaper window. 311 addInputWindowHandleLw(mService.mWallpaperInputConsumer.mWindowHandle); 312 addWallpaperInputConsumerHandle = false; 313 } 314 } 315 316 final int flags = child.mAttrs.flags; 317 final int privateFlags = child.mAttrs.privateFlags; 318 final int type = child.mAttrs.type; 319 320 final boolean hasFocus = (child == mInputFocus); 321 final boolean isVisible = child.isVisibleLw(); 322 if ((privateFlags 323 & WindowManager.LayoutParams.PRIVATE_FLAG_DISABLE_WALLPAPER_TOUCH_EVENTS) 324 != 0) { 325 disableWallpaperTouchEvents = true; 326 } 327 final boolean hasWallpaper = wallpaperController.isWallpaperTarget(child) 328 && (privateFlags & WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD) == 0 329 && !disableWallpaperTouchEvents; 330 final boolean onDefaultDisplay = (child.getDisplayId() == Display.DEFAULT_DISPLAY); 331 332 // If there's a drag in progress and 'child' is a potential drop target, 333 // make sure it's been told about the drag 334 if (inDrag && isVisible && onDefaultDisplay) { 335 mService.mDragState.sendDragStartedIfNeededLw(child); 336 } 337 338 addInputWindowHandleLw( 339 inputWindowHandle, child, flags, type, isVisible, hasFocus, hasWallpaper); 340 } 341 } 342 343 if (addWallpaperInputConsumerHandle) { 344 // No wallpaper found, add the wallpaper input consumer at the end. 345 addInputWindowHandleLw(mService.mWallpaperInputConsumer.mWindowHandle); 346 } 347 348 // Send windows to native code. 349 mService.mInputManager.setInputWindows(mInputWindowHandles); 350 351 // Clear the list in preparation for the next round. 352 clearInputWindowHandlesLw(); 353 354 if (false) Slog.d(TAG_WM, "<<<<<<< EXITED updateInputWindowsLw"); 355 } 356 357 /* Notifies that the input device configuration has changed. */ 358 @Override notifyConfigurationChanged()359 public void notifyConfigurationChanged() { 360 mService.sendNewConfiguration(); 361 362 synchronized (mInputDevicesReadyMonitor) { 363 if (!mInputDevicesReady) { 364 mInputDevicesReady = true; 365 mInputDevicesReadyMonitor.notifyAll(); 366 } 367 } 368 } 369 370 /* Waits until the built-in input devices have been configured. */ waitForInputDevicesReady(long timeoutMillis)371 public boolean waitForInputDevicesReady(long timeoutMillis) { 372 synchronized (mInputDevicesReadyMonitor) { 373 if (!mInputDevicesReady) { 374 try { 375 mInputDevicesReadyMonitor.wait(timeoutMillis); 376 } catch (InterruptedException ex) { 377 } 378 } 379 return mInputDevicesReady; 380 } 381 } 382 383 /* Notifies that the lid switch changed state. */ 384 @Override notifyLidSwitchChanged(long whenNanos, boolean lidOpen)385 public void notifyLidSwitchChanged(long whenNanos, boolean lidOpen) { 386 mService.mPolicy.notifyLidSwitchChanged(whenNanos, lidOpen); 387 } 388 389 /* Notifies that the camera lens cover state has changed. */ 390 @Override notifyCameraLensCoverSwitchChanged(long whenNanos, boolean lensCovered)391 public void notifyCameraLensCoverSwitchChanged(long whenNanos, boolean lensCovered) { 392 mService.mPolicy.notifyCameraLensCoverSwitchChanged(whenNanos, lensCovered); 393 } 394 395 /* Provides an opportunity for the window manager policy to intercept early key 396 * processing as soon as the key has been read from the device. */ 397 @Override interceptKeyBeforeQueueing(KeyEvent event, int policyFlags)398 public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) { 399 return mService.mPolicy.interceptKeyBeforeQueueing(event, policyFlags); 400 } 401 402 /* Provides an opportunity for the window manager policy to intercept early motion event 403 * processing when the device is in a non-interactive state since these events are normally 404 * dropped. */ 405 @Override interceptMotionBeforeQueueingNonInteractive(long whenNanos, int policyFlags)406 public int interceptMotionBeforeQueueingNonInteractive(long whenNanos, int policyFlags) { 407 return mService.mPolicy.interceptMotionBeforeQueueingNonInteractive( 408 whenNanos, policyFlags); 409 } 410 411 /* Provides an opportunity for the window manager policy to process a key before 412 * ordinary dispatch. */ 413 @Override interceptKeyBeforeDispatching( InputWindowHandle focus, KeyEvent event, int policyFlags)414 public long interceptKeyBeforeDispatching( 415 InputWindowHandle focus, KeyEvent event, int policyFlags) { 416 WindowState windowState = focus != null ? (WindowState) focus.windowState : null; 417 return mService.mPolicy.interceptKeyBeforeDispatching(windowState, event, policyFlags); 418 } 419 420 /* Provides an opportunity for the window manager policy to process a key that 421 * the application did not handle. */ 422 @Override dispatchUnhandledKey( InputWindowHandle focus, KeyEvent event, int policyFlags)423 public KeyEvent dispatchUnhandledKey( 424 InputWindowHandle focus, KeyEvent event, int policyFlags) { 425 WindowState windowState = focus != null ? (WindowState) focus.windowState : null; 426 return mService.mPolicy.dispatchUnhandledKey(windowState, event, policyFlags); 427 } 428 429 /* Callback to get pointer layer. */ 430 @Override getPointerLayer()431 public int getPointerLayer() { 432 return mService.mPolicy.windowTypeToLayerLw(WindowManager.LayoutParams.TYPE_POINTER) 433 * WindowManagerService.TYPE_LAYER_MULTIPLIER 434 + WindowManagerService.TYPE_LAYER_OFFSET; 435 } 436 437 /* Called when the current input focus changes. 438 * Layer assignment is assumed to be complete by the time this is called. 439 */ setInputFocusLw(WindowState newWindow, boolean updateInputWindows)440 public void setInputFocusLw(WindowState newWindow, boolean updateInputWindows) { 441 if (DEBUG_FOCUS_LIGHT || DEBUG_INPUT) { 442 Slog.d(TAG_WM, "Input focus has changed to " + newWindow); 443 } 444 445 if (newWindow != mInputFocus) { 446 if (newWindow != null && newWindow.canReceiveKeys()) { 447 // Displaying a window implicitly causes dispatching to be unpaused. 448 // This is to protect against bugs if someone pauses dispatching but 449 // forgets to resume. 450 newWindow.mToken.paused = false; 451 } 452 453 mInputFocus = newWindow; 454 setUpdateInputWindowsNeededLw(); 455 456 if (updateInputWindows) { 457 updateInputWindowsLw(false /*force*/); 458 } 459 } 460 } 461 setFocusedAppLw(AppWindowToken newApp)462 public void setFocusedAppLw(AppWindowToken newApp) { 463 // Focused app has changed. 464 if (newApp == null) { 465 mService.mInputManager.setFocusedApplication(null); 466 } else { 467 final InputApplicationHandle handle = newApp.mInputApplicationHandle; 468 handle.name = newApp.toString(); 469 handle.dispatchingTimeoutNanos = newApp.inputDispatchingTimeoutNanos; 470 471 mService.mInputManager.setFocusedApplication(handle); 472 } 473 } 474 pauseDispatchingLw(WindowToken window)475 public void pauseDispatchingLw(WindowToken window) { 476 if (! window.paused) { 477 if (DEBUG_INPUT) { 478 Slog.v(TAG_WM, "Pausing WindowToken " + window); 479 } 480 481 window.paused = true; 482 updateInputWindowsLw(true /*force*/); 483 } 484 } 485 resumeDispatchingLw(WindowToken window)486 public void resumeDispatchingLw(WindowToken window) { 487 if (window.paused) { 488 if (DEBUG_INPUT) { 489 Slog.v(TAG_WM, "Resuming WindowToken " + window); 490 } 491 492 window.paused = false; 493 updateInputWindowsLw(true /*force*/); 494 } 495 } 496 freezeInputDispatchingLw()497 public void freezeInputDispatchingLw() { 498 if (!mInputDispatchFrozen) { 499 if (DEBUG_INPUT) { 500 Slog.v(TAG_WM, "Freezing input dispatching"); 501 } 502 503 mInputDispatchFrozen = true; 504 505 if (DEBUG_INPUT || true) { 506 mInputFreezeReason = Debug.getCallers(6); 507 } 508 updateInputDispatchModeLw(); 509 } 510 } 511 thawInputDispatchingLw()512 public void thawInputDispatchingLw() { 513 if (mInputDispatchFrozen) { 514 if (DEBUG_INPUT) { 515 Slog.v(TAG_WM, "Thawing input dispatching"); 516 } 517 518 mInputDispatchFrozen = false; 519 mInputFreezeReason = null; 520 updateInputDispatchModeLw(); 521 } 522 } 523 setEventDispatchingLw(boolean enabled)524 public void setEventDispatchingLw(boolean enabled) { 525 if (mInputDispatchEnabled != enabled) { 526 if (DEBUG_INPUT) { 527 Slog.v(TAG_WM, "Setting event dispatching to " + enabled); 528 } 529 530 mInputDispatchEnabled = enabled; 531 updateInputDispatchModeLw(); 532 } 533 } 534 updateInputDispatchModeLw()535 private void updateInputDispatchModeLw() { 536 mService.mInputManager.setInputDispatchMode(mInputDispatchEnabled, mInputDispatchFrozen); 537 } 538 dump(PrintWriter pw, String prefix)539 void dump(PrintWriter pw, String prefix) { 540 if (mInputFreezeReason != null) { 541 pw.println(prefix + "mInputFreezeReason=" + mInputFreezeReason); 542 } 543 } 544 } 545