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