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.graphics.Rect; 20 import android.os.RemoteException; 21 import android.util.Log; 22 import android.util.Slog; 23 import android.view.InputChannel; 24 import android.view.KeyEvent; 25 import android.view.WindowManager; 26 27 import java.util.ArrayList; 28 import java.util.Arrays; 29 30 final class InputMonitor { 31 private final WindowManagerService mService; 32 33 // Current window with input focus for keys and other non-touch events. May be null. 34 private WindowState mInputFocus; 35 36 // When true, prevents input dispatch from proceeding until set to false again. 37 private boolean mInputDispatchFrozen; 38 39 // When true, input dispatch proceeds normally. Otherwise all events are dropped. 40 private boolean mInputDispatchEnabled = true; 41 42 // When true, need to call updateInputWindowsLw(). 43 private boolean mUpdateInputWindowsNeeded = true; 44 45 // Array of window handles to provide to the input dispatcher. 46 private InputWindowHandle[] mInputWindowHandles; 47 private int mInputWindowHandleCount; 48 49 // Set to true when the first input device configuration change notification 50 // is received to indicate that the input devices are ready. 51 private final Object mInputDevicesReadyMonitor = new Object(); 52 private boolean mInputDevicesReady; 53 InputMonitor(WindowManagerService service)54 public InputMonitor(WindowManagerService service) { 55 mService = service; 56 } 57 58 /* Notifies the window manager about a broken input channel. 59 * 60 * Called by the InputManager. 61 */ notifyInputChannelBroken(InputWindowHandle inputWindowHandle)62 public void notifyInputChannelBroken(InputWindowHandle inputWindowHandle) { 63 if (inputWindowHandle == null) { 64 return; 65 } 66 67 synchronized (mService.mWindowMap) { 68 WindowState windowState = (WindowState) inputWindowHandle.windowState; 69 if (windowState != null) { 70 Slog.i(WindowManagerService.TAG, "WINDOW DIED " + windowState); 71 mService.removeWindowLocked(windowState.mSession, windowState); 72 } 73 } 74 } 75 76 /* Notifies the window manager about an application that is not responding. 77 * Returns a new timeout to continue waiting in nanoseconds, or 0 to abort dispatch. 78 * 79 * Called by the InputManager. 80 */ notifyANR(InputApplicationHandle inputApplicationHandle, InputWindowHandle inputWindowHandle)81 public long notifyANR(InputApplicationHandle inputApplicationHandle, 82 InputWindowHandle inputWindowHandle) { 83 AppWindowToken appWindowToken = null; 84 if (inputWindowHandle != null) { 85 synchronized (mService.mWindowMap) { 86 WindowState windowState = (WindowState) inputWindowHandle.windowState; 87 if (windowState != null) { 88 Slog.i(WindowManagerService.TAG, "Input event dispatching timed out sending to " 89 + windowState.mAttrs.getTitle()); 90 appWindowToken = windowState.mAppToken; 91 } 92 } 93 } 94 95 if (appWindowToken == null && inputApplicationHandle != null) { 96 appWindowToken = inputApplicationHandle.appWindowToken; 97 if (appWindowToken != null) { 98 Slog.i(WindowManagerService.TAG, 99 "Input event dispatching timed out sending to application " 100 + appWindowToken.stringName); 101 } 102 } 103 104 if (appWindowToken != null && appWindowToken.appToken != null) { 105 try { 106 // Notify the activity manager about the timeout and let it decide whether 107 // to abort dispatching or keep waiting. 108 boolean abort = appWindowToken.appToken.keyDispatchingTimedOut(); 109 if (! abort) { 110 // The activity manager declined to abort dispatching. 111 // Wait a bit longer and timeout again later. 112 return appWindowToken.inputDispatchingTimeoutNanos; 113 } 114 } catch (RemoteException ex) { 115 } 116 } 117 return 0; // abort dispatching 118 } 119 addInputWindowHandleLw(InputWindowHandle windowHandle)120 private void addInputWindowHandleLw(InputWindowHandle windowHandle) { 121 if (mInputWindowHandles == null) { 122 mInputWindowHandles = new InputWindowHandle[16]; 123 } 124 if (mInputWindowHandleCount >= mInputWindowHandles.length) { 125 mInputWindowHandles = Arrays.copyOf(mInputWindowHandles, 126 mInputWindowHandleCount * 2); 127 } 128 mInputWindowHandles[mInputWindowHandleCount++] = windowHandle; 129 } 130 clearInputWindowHandlesLw()131 private void clearInputWindowHandlesLw() { 132 while (mInputWindowHandleCount != 0) { 133 mInputWindowHandles[--mInputWindowHandleCount] = null; 134 } 135 } 136 setUpdateInputWindowsNeededLw()137 public void setUpdateInputWindowsNeededLw() { 138 mUpdateInputWindowsNeeded = true; 139 } 140 141 /* Updates the cached window information provided to the input dispatcher. */ updateInputWindowsLw(boolean force)142 public void updateInputWindowsLw(boolean force) { 143 if (!force && !mUpdateInputWindowsNeeded) { 144 return; 145 } 146 mUpdateInputWindowsNeeded = false; 147 148 if (false) Slog.d(WindowManagerService.TAG, ">>>>>> ENTERED updateInputWindowsLw"); 149 150 // Populate the input window list with information about all of the windows that 151 // could potentially receive input. 152 // As an optimization, we could try to prune the list of windows but this turns 153 // out to be difficult because only the native code knows for sure which window 154 // currently has touch focus. 155 final ArrayList<WindowState> windows = mService.mWindows; 156 157 // If there's a drag in flight, provide a pseudowindow to catch drag input 158 final boolean inDrag = (mService.mDragState != null); 159 if (inDrag) { 160 if (WindowManagerService.DEBUG_DRAG) { 161 Log.d(WindowManagerService.TAG, "Inserting drag window"); 162 } 163 final InputWindowHandle dragWindowHandle = mService.mDragState.mDragWindowHandle; 164 if (dragWindowHandle != null) { 165 addInputWindowHandleLw(dragWindowHandle); 166 } else { 167 Slog.w(WindowManagerService.TAG, "Drag is in progress but there is no " 168 + "drag window handle."); 169 } 170 } 171 172 final int NFW = mService.mFakeWindows.size(); 173 for (int i = 0; i < NFW; i++) { 174 addInputWindowHandleLw(mService.mFakeWindows.get(i).mWindowHandle); 175 } 176 177 final int N = windows.size(); 178 for (int i = N - 1; i >= 0; i--) { 179 final WindowState child = windows.get(i); 180 final InputChannel inputChannel = child.mInputChannel; 181 final InputWindowHandle inputWindowHandle = child.mInputWindowHandle; 182 if (inputChannel == null || inputWindowHandle == null || child.mRemoved) { 183 // Skip this window because it cannot possibly receive input. 184 continue; 185 } 186 187 final int flags = child.mAttrs.flags; 188 final int type = child.mAttrs.type; 189 190 final boolean hasFocus = (child == mInputFocus); 191 final boolean isVisible = child.isVisibleLw(); 192 final boolean hasWallpaper = (child == mService.mWallpaperTarget) 193 && (type != WindowManager.LayoutParams.TYPE_KEYGUARD); 194 195 // If there's a drag in progress and 'child' is a potential drop target, 196 // make sure it's been told about the drag 197 if (inDrag && isVisible) { 198 mService.mDragState.sendDragStartedIfNeededLw(child); 199 } 200 201 // Add a window to our list of input windows. 202 inputWindowHandle.name = child.toString(); 203 inputWindowHandle.layoutParamsFlags = flags; 204 inputWindowHandle.layoutParamsType = type; 205 inputWindowHandle.dispatchingTimeoutNanos = child.getInputDispatchingTimeoutNanos(); 206 inputWindowHandle.visible = isVisible; 207 inputWindowHandle.canReceiveKeys = child.canReceiveKeys(); 208 inputWindowHandle.hasFocus = hasFocus; 209 inputWindowHandle.hasWallpaper = hasWallpaper; 210 inputWindowHandle.paused = child.mAppToken != null ? child.mAppToken.paused : false; 211 inputWindowHandle.layer = child.mLayer; 212 inputWindowHandle.ownerPid = child.mSession.mPid; 213 inputWindowHandle.ownerUid = child.mSession.mUid; 214 inputWindowHandle.inputFeatures = child.mAttrs.inputFeatures; 215 216 final Rect frame = child.mFrame; 217 inputWindowHandle.frameLeft = frame.left; 218 inputWindowHandle.frameTop = frame.top; 219 inputWindowHandle.frameRight = frame.right; 220 inputWindowHandle.frameBottom = frame.bottom; 221 222 if (child.mGlobalScale != 1) { 223 // If we are scaling the window, input coordinates need 224 // to be inversely scaled to map from what is on screen 225 // to what is actually being touched in the UI. 226 inputWindowHandle.scaleFactor = 1.0f/child.mGlobalScale; 227 } else { 228 inputWindowHandle.scaleFactor = 1; 229 } 230 231 child.getTouchableRegion(inputWindowHandle.touchableRegion); 232 233 addInputWindowHandleLw(inputWindowHandle); 234 } 235 236 // Send windows to native code. 237 mService.mInputManager.setInputWindows(mInputWindowHandles); 238 239 // Clear the list in preparation for the next round. 240 clearInputWindowHandlesLw(); 241 242 if (false) Slog.d(WindowManagerService.TAG, "<<<<<<< EXITED updateInputWindowsLw"); 243 } 244 245 /* Notifies that the input device configuration has changed. */ notifyConfigurationChanged()246 public void notifyConfigurationChanged() { 247 mService.sendNewConfiguration(); 248 249 synchronized (mInputDevicesReadyMonitor) { 250 if (!mInputDevicesReady) { 251 mInputDevicesReady = true; 252 mInputDevicesReadyMonitor.notifyAll(); 253 } 254 } 255 } 256 257 /* Waits until the built-in input devices have been configured. */ waitForInputDevicesReady(long timeoutMillis)258 public boolean waitForInputDevicesReady(long timeoutMillis) { 259 synchronized (mInputDevicesReadyMonitor) { 260 if (!mInputDevicesReady) { 261 try { 262 mInputDevicesReadyMonitor.wait(timeoutMillis); 263 } catch (InterruptedException ex) { 264 } 265 } 266 return mInputDevicesReady; 267 } 268 } 269 270 /* Notifies that the lid switch changed state. */ notifyLidSwitchChanged(long whenNanos, boolean lidOpen)271 public void notifyLidSwitchChanged(long whenNanos, boolean lidOpen) { 272 mService.mPolicy.notifyLidSwitchChanged(whenNanos, lidOpen); 273 } 274 275 /* Provides an opportunity for the window manager policy to intercept early key 276 * processing as soon as the key has been read from the device. */ interceptKeyBeforeQueueing( KeyEvent event, int policyFlags, boolean isScreenOn)277 public int interceptKeyBeforeQueueing( 278 KeyEvent event, int policyFlags, boolean isScreenOn) { 279 return mService.mPolicy.interceptKeyBeforeQueueing(event, policyFlags, isScreenOn); 280 } 281 282 /* Provides an opportunity for the window manager policy to intercept early 283 * motion event processing when the screen is off since these events are normally 284 * dropped. */ interceptMotionBeforeQueueingWhenScreenOff(int policyFlags)285 public int interceptMotionBeforeQueueingWhenScreenOff(int policyFlags) { 286 return mService.mPolicy.interceptMotionBeforeQueueingWhenScreenOff(policyFlags); 287 } 288 289 /* Provides an opportunity for the window manager policy to process a key before 290 * ordinary dispatch. */ interceptKeyBeforeDispatching( InputWindowHandle focus, KeyEvent event, int policyFlags)291 public long interceptKeyBeforeDispatching( 292 InputWindowHandle focus, KeyEvent event, int policyFlags) { 293 WindowState windowState = focus != null ? (WindowState) focus.windowState : null; 294 return mService.mPolicy.interceptKeyBeforeDispatching(windowState, event, policyFlags); 295 } 296 297 /* Provides an opportunity for the window manager policy to process a key that 298 * the application did not handle. */ dispatchUnhandledKey( InputWindowHandle focus, KeyEvent event, int policyFlags)299 public KeyEvent dispatchUnhandledKey( 300 InputWindowHandle focus, KeyEvent event, int policyFlags) { 301 WindowState windowState = focus != null ? (WindowState) focus.windowState : null; 302 return mService.mPolicy.dispatchUnhandledKey(windowState, event, policyFlags); 303 } 304 305 /* Called when the current input focus changes. 306 * Layer assignment is assumed to be complete by the time this is called. 307 */ setInputFocusLw(WindowState newWindow, boolean updateInputWindows)308 public void setInputFocusLw(WindowState newWindow, boolean updateInputWindows) { 309 if (WindowManagerService.DEBUG_INPUT) { 310 Slog.d(WindowManagerService.TAG, "Input focus has changed to " + newWindow); 311 } 312 313 if (newWindow != mInputFocus) { 314 if (newWindow != null && newWindow.canReceiveKeys()) { 315 // Displaying a window implicitly causes dispatching to be unpaused. 316 // This is to protect against bugs if someone pauses dispatching but 317 // forgets to resume. 318 newWindow.mToken.paused = false; 319 } 320 321 mInputFocus = newWindow; 322 setUpdateInputWindowsNeededLw(); 323 324 if (updateInputWindows) { 325 updateInputWindowsLw(false /*force*/); 326 } 327 } 328 } 329 setFocusedAppLw(AppWindowToken newApp)330 public void setFocusedAppLw(AppWindowToken newApp) { 331 // Focused app has changed. 332 if (newApp == null) { 333 mService.mInputManager.setFocusedApplication(null); 334 } else { 335 final InputApplicationHandle handle = newApp.mInputApplicationHandle; 336 handle.name = newApp.toString(); 337 handle.dispatchingTimeoutNanos = newApp.inputDispatchingTimeoutNanos; 338 339 mService.mInputManager.setFocusedApplication(handle); 340 } 341 } 342 pauseDispatchingLw(WindowToken window)343 public void pauseDispatchingLw(WindowToken window) { 344 if (! window.paused) { 345 if (WindowManagerService.DEBUG_INPUT) { 346 Slog.v(WindowManagerService.TAG, "Pausing WindowToken " + window); 347 } 348 349 window.paused = true; 350 updateInputWindowsLw(true /*force*/); 351 } 352 } 353 resumeDispatchingLw(WindowToken window)354 public void resumeDispatchingLw(WindowToken window) { 355 if (window.paused) { 356 if (WindowManagerService.DEBUG_INPUT) { 357 Slog.v(WindowManagerService.TAG, "Resuming WindowToken " + window); 358 } 359 360 window.paused = false; 361 updateInputWindowsLw(true /*force*/); 362 } 363 } 364 freezeInputDispatchingLw()365 public void freezeInputDispatchingLw() { 366 if (! mInputDispatchFrozen) { 367 if (WindowManagerService.DEBUG_INPUT) { 368 Slog.v(WindowManagerService.TAG, "Freezing input dispatching"); 369 } 370 371 mInputDispatchFrozen = true; 372 updateInputDispatchModeLw(); 373 } 374 } 375 thawInputDispatchingLw()376 public void thawInputDispatchingLw() { 377 if (mInputDispatchFrozen) { 378 if (WindowManagerService.DEBUG_INPUT) { 379 Slog.v(WindowManagerService.TAG, "Thawing input dispatching"); 380 } 381 382 mInputDispatchFrozen = false; 383 updateInputDispatchModeLw(); 384 } 385 } 386 setEventDispatchingLw(boolean enabled)387 public void setEventDispatchingLw(boolean enabled) { 388 if (mInputDispatchEnabled != enabled) { 389 if (WindowManagerService.DEBUG_INPUT) { 390 Slog.v(WindowManagerService.TAG, "Setting event dispatching to " + enabled); 391 } 392 393 mInputDispatchEnabled = enabled; 394 updateInputDispatchModeLw(); 395 } 396 } 397 updateInputDispatchModeLw()398 private void updateInputDispatchModeLw() { 399 mService.mInputManager.setInputDispatchMode(mInputDispatchEnabled, mInputDispatchFrozen); 400 } 401 } 402