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 android.app.ActivityManagerNative; 20 import android.graphics.Rect; 21 import android.os.RemoteException; 22 import android.util.Log; 23 import android.util.Slog; 24 import android.view.Display; 25 import android.view.InputChannel; 26 import android.view.KeyEvent; 27 import android.view.WindowManager; 28 29 import com.android.server.input.InputApplicationHandle; 30 import com.android.server.input.InputManagerService; 31 import com.android.server.input.InputWindowHandle; 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); 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 * 1000000L; // nanoseconds 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 boolean disableWallpaperTouchEvents = 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 boolean addInputConsumerHandle = mService.mInputConsumer != null; 260 261 // Add all windows on the default display. 262 final int numDisplays = mService.mDisplayContents.size(); 263 for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) { 264 WindowList windows = mService.mDisplayContents.valueAt(displayNdx).getWindowList(); 265 for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) { 266 final WindowState child = windows.get(winNdx); 267 final InputChannel inputChannel = child.mInputChannel; 268 final InputWindowHandle inputWindowHandle = child.mInputWindowHandle; 269 if (inputChannel == null || inputWindowHandle == null || child.mRemoved) { 270 // Skip this window because it cannot possibly receive input. 271 continue; 272 } 273 if (addInputConsumerHandle 274 && inputWindowHandle.layer <= mService.mInputConsumer.mWindowHandle.layer) { 275 addInputWindowHandleLw(mService.mInputConsumer.mWindowHandle); 276 addInputConsumerHandle = false; 277 } 278 279 final int flags = child.mAttrs.flags; 280 final int privateFlags = child.mAttrs.privateFlags; 281 final int type = child.mAttrs.type; 282 283 final boolean hasFocus = (child == mInputFocus); 284 final boolean isVisible = child.isVisibleLw(); 285 if ((privateFlags 286 & WindowManager.LayoutParams.PRIVATE_FLAG_DISABLE_WALLPAPER_TOUCH_EVENTS) 287 != 0) { 288 disableWallpaperTouchEvents = true; 289 } 290 final boolean hasWallpaper = (child == mService.mWallpaperTarget) 291 && (privateFlags & WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD) == 0 292 && !disableWallpaperTouchEvents; 293 final boolean onDefaultDisplay = (child.getDisplayId() == Display.DEFAULT_DISPLAY); 294 295 // If there's a drag in progress and 'child' is a potential drop target, 296 // make sure it's been told about the drag 297 if (inDrag && isVisible && onDefaultDisplay) { 298 mService.mDragState.sendDragStartedIfNeededLw(child); 299 } 300 301 addInputWindowHandleLw(inputWindowHandle, child, flags, type, isVisible, hasFocus, 302 hasWallpaper); 303 } 304 } 305 306 // Send windows to native code. 307 mService.mInputManager.setInputWindows(mInputWindowHandles); 308 309 // Clear the list in preparation for the next round. 310 clearInputWindowHandlesLw(); 311 312 if (false) Slog.d(WindowManagerService.TAG, "<<<<<<< EXITED updateInputWindowsLw"); 313 } 314 315 /* Notifies that the input device configuration has changed. */ 316 @Override notifyConfigurationChanged()317 public void notifyConfigurationChanged() { 318 mService.sendNewConfiguration(); 319 320 synchronized (mInputDevicesReadyMonitor) { 321 if (!mInputDevicesReady) { 322 mInputDevicesReady = true; 323 mInputDevicesReadyMonitor.notifyAll(); 324 } 325 } 326 } 327 328 /* Waits until the built-in input devices have been configured. */ waitForInputDevicesReady(long timeoutMillis)329 public boolean waitForInputDevicesReady(long timeoutMillis) { 330 synchronized (mInputDevicesReadyMonitor) { 331 if (!mInputDevicesReady) { 332 try { 333 mInputDevicesReadyMonitor.wait(timeoutMillis); 334 } catch (InterruptedException ex) { 335 } 336 } 337 return mInputDevicesReady; 338 } 339 } 340 341 /* Notifies that the lid switch changed state. */ 342 @Override notifyLidSwitchChanged(long whenNanos, boolean lidOpen)343 public void notifyLidSwitchChanged(long whenNanos, boolean lidOpen) { 344 mService.mPolicy.notifyLidSwitchChanged(whenNanos, lidOpen); 345 } 346 347 /* Notifies that the camera lens cover state has changed. */ 348 @Override notifyCameraLensCoverSwitchChanged(long whenNanos, boolean lensCovered)349 public void notifyCameraLensCoverSwitchChanged(long whenNanos, boolean lensCovered) { 350 mService.mPolicy.notifyCameraLensCoverSwitchChanged(whenNanos, lensCovered); 351 } 352 353 /* Provides an opportunity for the window manager policy to intercept early key 354 * processing as soon as the key has been read from the device. */ 355 @Override interceptKeyBeforeQueueing(KeyEvent event, int policyFlags)356 public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) { 357 return mService.mPolicy.interceptKeyBeforeQueueing(event, policyFlags); 358 } 359 360 /* Provides an opportunity for the window manager policy to intercept early motion event 361 * processing when the device is in a non-interactive state since these events are normally 362 * dropped. */ 363 @Override interceptMotionBeforeQueueingNonInteractive(long whenNanos, int policyFlags)364 public int interceptMotionBeforeQueueingNonInteractive(long whenNanos, int policyFlags) { 365 return mService.mPolicy.interceptMotionBeforeQueueingNonInteractive( 366 whenNanos, policyFlags); 367 } 368 369 /* Provides an opportunity for the window manager policy to process a key before 370 * ordinary dispatch. */ 371 @Override interceptKeyBeforeDispatching( InputWindowHandle focus, KeyEvent event, int policyFlags)372 public long interceptKeyBeforeDispatching( 373 InputWindowHandle focus, KeyEvent event, int policyFlags) { 374 WindowState windowState = focus != null ? (WindowState) focus.windowState : null; 375 return mService.mPolicy.interceptKeyBeforeDispatching(windowState, event, policyFlags); 376 } 377 378 /* Provides an opportunity for the window manager policy to process a key that 379 * the application did not handle. */ 380 @Override dispatchUnhandledKey( InputWindowHandle focus, KeyEvent event, int policyFlags)381 public KeyEvent dispatchUnhandledKey( 382 InputWindowHandle focus, KeyEvent event, int policyFlags) { 383 WindowState windowState = focus != null ? (WindowState) focus.windowState : null; 384 return mService.mPolicy.dispatchUnhandledKey(windowState, event, policyFlags); 385 } 386 387 /* Callback to get pointer layer. */ 388 @Override getPointerLayer()389 public int getPointerLayer() { 390 return mService.mPolicy.windowTypeToLayerLw(WindowManager.LayoutParams.TYPE_POINTER) 391 * WindowManagerService.TYPE_LAYER_MULTIPLIER 392 + WindowManagerService.TYPE_LAYER_OFFSET; 393 } 394 395 /* Called when the current input focus changes. 396 * Layer assignment is assumed to be complete by the time this is called. 397 */ setInputFocusLw(WindowState newWindow, boolean updateInputWindows)398 public void setInputFocusLw(WindowState newWindow, boolean updateInputWindows) { 399 if (WindowManagerService.DEBUG_FOCUS_LIGHT || WindowManagerService.DEBUG_INPUT) { 400 Slog.d(WindowManagerService.TAG, "Input focus has changed to " + newWindow); 401 } 402 403 if (newWindow != mInputFocus) { 404 if (newWindow != null && newWindow.canReceiveKeys()) { 405 // Displaying a window implicitly causes dispatching to be unpaused. 406 // This is to protect against bugs if someone pauses dispatching but 407 // forgets to resume. 408 newWindow.mToken.paused = false; 409 } 410 411 mInputFocus = newWindow; 412 setUpdateInputWindowsNeededLw(); 413 414 if (updateInputWindows) { 415 updateInputWindowsLw(false /*force*/); 416 } 417 } 418 } 419 setFocusedAppLw(AppWindowToken newApp)420 public void setFocusedAppLw(AppWindowToken newApp) { 421 // Focused app has changed. 422 if (newApp == null) { 423 mService.mInputManager.setFocusedApplication(null); 424 } else { 425 final InputApplicationHandle handle = newApp.mInputApplicationHandle; 426 handle.name = newApp.toString(); 427 handle.dispatchingTimeoutNanos = newApp.inputDispatchingTimeoutNanos; 428 429 mService.mInputManager.setFocusedApplication(handle); 430 } 431 } 432 pauseDispatchingLw(WindowToken window)433 public void pauseDispatchingLw(WindowToken window) { 434 if (! window.paused) { 435 if (WindowManagerService.DEBUG_INPUT) { 436 Slog.v(WindowManagerService.TAG, "Pausing WindowToken " + window); 437 } 438 439 window.paused = true; 440 updateInputWindowsLw(true /*force*/); 441 } 442 } 443 resumeDispatchingLw(WindowToken window)444 public void resumeDispatchingLw(WindowToken window) { 445 if (window.paused) { 446 if (WindowManagerService.DEBUG_INPUT) { 447 Slog.v(WindowManagerService.TAG, "Resuming WindowToken " + window); 448 } 449 450 window.paused = false; 451 updateInputWindowsLw(true /*force*/); 452 } 453 } 454 freezeInputDispatchingLw()455 public void freezeInputDispatchingLw() { 456 if (! mInputDispatchFrozen) { 457 if (WindowManagerService.DEBUG_INPUT) { 458 Slog.v(WindowManagerService.TAG, "Freezing input dispatching"); 459 } 460 461 mInputDispatchFrozen = true; 462 updateInputDispatchModeLw(); 463 } 464 } 465 thawInputDispatchingLw()466 public void thawInputDispatchingLw() { 467 if (mInputDispatchFrozen) { 468 if (WindowManagerService.DEBUG_INPUT) { 469 Slog.v(WindowManagerService.TAG, "Thawing input dispatching"); 470 } 471 472 mInputDispatchFrozen = false; 473 updateInputDispatchModeLw(); 474 } 475 } 476 setEventDispatchingLw(boolean enabled)477 public void setEventDispatchingLw(boolean enabled) { 478 if (mInputDispatchEnabled != enabled) { 479 if (WindowManagerService.DEBUG_INPUT) { 480 Slog.v(WindowManagerService.TAG, "Setting event dispatching to " + enabled); 481 } 482 483 mInputDispatchEnabled = enabled; 484 updateInputDispatchModeLw(); 485 } 486 } 487 updateInputDispatchModeLw()488 private void updateInputDispatchModeLw() { 489 mService.mInputManager.setInputDispatchMode(mInputDispatchEnabled, mInputDispatchFrozen); 490 } 491 } 492