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