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