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