blob: 08a35603381517ae1e89c77c0ed32fdebd051f8d [file] [log] [blame]
Dianne Hackbornf56e1022011-02-22 10:47:13 -08001/*
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.
Dianne Hackborn6e1eb762011-02-17 16:07:28 -080015 */
Dianne Hackbornf56e1022011-02-22 10:47:13 -080016
Dianne Hackborn6e1eb762011-02-17 16:07:28 -080017package com.android.server.wm;
18
19import android.graphics.Rect;
20import android.os.Process;
21import android.os.RemoteException;
22import android.util.Log;
23import android.util.Slog;
24import android.view.KeyEvent;
25import android.view.WindowManager;
26
27import java.util.ArrayList;
Jeff Brown9302c872011-07-13 22:51:29 -070028import java.util.Arrays;
Dianne Hackborn6e1eb762011-02-17 16:07:28 -080029
30final 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
Jeff Brown9302c872011-07-13 22:51:29 -070045 // Fake handles for the drag surface, lazily initialized.
46 private InputApplicationHandle mDragApplicationHandle;
47 private InputWindowHandle mDragWindowHandle;
48
49 // Array of window handles to provide to the input dispatcher.
50 private InputWindowHandle[] mInputWindowHandles;
51 private int mInputWindowHandleCount;
52
Dianne Hackborn6e1eb762011-02-17 16:07:28 -080053 // Set to true when the first input device configuration change notification
54 // is received to indicate that the input devices are ready.
55 private final Object mInputDevicesReadyMonitor = new Object();
56 private boolean mInputDevicesReady;
57
58 public InputMonitor(WindowManagerService service) {
59 mService = service;
60 }
61
62 /* Notifies the window manager about a broken input channel.
63 *
64 * Called by the InputManager.
65 */
66 public void notifyInputChannelBroken(InputWindowHandle inputWindowHandle) {
67 if (inputWindowHandle == null) {
68 return;
69 }
70
71 synchronized (mService.mWindowMap) {
72 WindowState windowState = (WindowState) inputWindowHandle.windowState;
Jeff Brown9302c872011-07-13 22:51:29 -070073 if (windowState != null) {
74 Slog.i(WindowManagerService.TAG, "WINDOW DIED " + windowState);
75 mService.removeWindowLocked(windowState.mSession, windowState);
76 }
Dianne Hackborn6e1eb762011-02-17 16:07:28 -080077 }
78 }
79
80 /* Notifies the window manager about an application that is not responding.
81 * Returns a new timeout to continue waiting in nanoseconds, or 0 to abort dispatch.
82 *
83 * Called by the InputManager.
84 */
85 public long notifyANR(InputApplicationHandle inputApplicationHandle,
86 InputWindowHandle inputWindowHandle) {
87 AppWindowToken appWindowToken = null;
88 if (inputWindowHandle != null) {
89 synchronized (mService.mWindowMap) {
90 WindowState windowState = (WindowState) inputWindowHandle.windowState;
91 if (windowState != null) {
92 Slog.i(WindowManagerService.TAG, "Input event dispatching timed out sending to "
93 + windowState.mAttrs.getTitle());
94 appWindowToken = windowState.mAppToken;
95 }
96 }
97 }
98
99 if (appWindowToken == null && inputApplicationHandle != null) {
100 appWindowToken = inputApplicationHandle.appWindowToken;
Jeff Brown9302c872011-07-13 22:51:29 -0700101 if (appWindowToken != null) {
102 Slog.i(WindowManagerService.TAG,
103 "Input event dispatching timed out sending to application "
104 + appWindowToken.stringName);
105 }
Dianne Hackborn6e1eb762011-02-17 16:07:28 -0800106 }
107
108 if (appWindowToken != null && appWindowToken.appToken != null) {
109 try {
110 // Notify the activity manager about the timeout and let it decide whether
111 // to abort dispatching or keep waiting.
112 boolean abort = appWindowToken.appToken.keyDispatchingTimedOut();
113 if (! abort) {
114 // The activity manager declined to abort dispatching.
115 // Wait a bit longer and timeout again later.
116 return appWindowToken.inputDispatchingTimeoutNanos;
117 }
118 } catch (RemoteException ex) {
119 }
120 }
121 return 0; // abort dispatching
122 }
123
Jeff Brown9302c872011-07-13 22:51:29 -0700124 private void addDragInputWindowLw() {
125 if (mDragWindowHandle == null) {
126 mDragApplicationHandle = new InputApplicationHandle(null);
127 mDragApplicationHandle.name = "drag";
128 mDragApplicationHandle.dispatchingTimeoutNanos =
129 WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
130
131 mDragWindowHandle = new InputWindowHandle(mDragApplicationHandle, null);
132 mDragWindowHandle.name = "drag";
133 mDragWindowHandle.layoutParamsFlags = 0;
134 mDragWindowHandle.layoutParamsType = WindowManager.LayoutParams.TYPE_DRAG;
135 mDragWindowHandle.dispatchingTimeoutNanos =
136 WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
137 mDragWindowHandle.visible = true;
138 mDragWindowHandle.canReceiveKeys = false;
139 mDragWindowHandle.hasFocus = true;
140 mDragWindowHandle.hasWallpaper = false;
141 mDragWindowHandle.paused = false;
142 mDragWindowHandle.ownerPid = Process.myPid();
143 mDragWindowHandle.ownerUid = Process.myUid();
144 mDragWindowHandle.inputFeatures = 0;
145 mDragWindowHandle.scaleFactor = 1.0f;
146
147 // The drag window cannot receive new touches.
148 mDragWindowHandle.touchableRegion.setEmpty();
149 }
150
151 mDragWindowHandle.layer = mService.mDragState.getDragLayerLw();
Dianne Hackborn6e1eb762011-02-17 16:07:28 -0800152
153 // The drag window covers the entire display
Jeff Brown9302c872011-07-13 22:51:29 -0700154 mDragWindowHandle.frameLeft = 0;
155 mDragWindowHandle.frameTop = 0;
156 mDragWindowHandle.frameRight = mService.mDisplay.getRealWidth();
157 mDragWindowHandle.frameBottom = mService.mDisplay.getRealHeight();
Dianne Hackborn6e1eb762011-02-17 16:07:28 -0800158
Jeff Brown9302c872011-07-13 22:51:29 -0700159 addInputWindowHandleLw(mDragWindowHandle);
160 }
161
162 private void addInputWindowHandleLw(InputWindowHandle windowHandle) {
163 if (mInputWindowHandles == null) {
164 mInputWindowHandles = new InputWindowHandle[16];
165 }
166 if (mInputWindowHandleCount >= mInputWindowHandles.length) {
167 mInputWindowHandles = Arrays.copyOf(mInputWindowHandles,
168 mInputWindowHandleCount * 2);
169 }
170 mInputWindowHandles[mInputWindowHandleCount++] = windowHandle;
171 }
172
173 private void clearInputWindowHandlesLw() {
174 while (mInputWindowHandleCount != 0) {
175 mInputWindowHandles[--mInputWindowHandleCount] = null;
176 }
Dianne Hackborn6e1eb762011-02-17 16:07:28 -0800177 }
178
179 public void setUpdateInputWindowsNeededLw() {
180 mUpdateInputWindowsNeeded = true;
181 }
182
183 /* Updates the cached window information provided to the input dispatcher. */
184 public void updateInputWindowsLw(boolean force) {
185 if (!force && !mUpdateInputWindowsNeeded) {
186 return;
187 }
188 mUpdateInputWindowsNeeded = false;
189
Dianne Hackbornb961cd22011-06-21 12:13:37 -0700190 if (false) Slog.d(WindowManagerService.TAG, ">>>>>> ENTERED updateInputWindowsLw");
Jeff Brown9302c872011-07-13 22:51:29 -0700191
Dianne Hackborn6e1eb762011-02-17 16:07:28 -0800192 // Populate the input window list with information about all of the windows that
193 // could potentially receive input.
194 // As an optimization, we could try to prune the list of windows but this turns
195 // out to be difficult because only the native code knows for sure which window
196 // currently has touch focus.
197 final ArrayList<WindowState> windows = mService.mWindows;
198
199 // If there's a drag in flight, provide a pseudowindow to catch drag input
200 final boolean inDrag = (mService.mDragState != null);
201 if (inDrag) {
202 if (WindowManagerService.DEBUG_DRAG) {
203 Log.d(WindowManagerService.TAG, "Inserting drag window");
204 }
Jeff Brown9302c872011-07-13 22:51:29 -0700205 addDragInputWindowLw();
Dianne Hackborn6e1eb762011-02-17 16:07:28 -0800206 }
207
208 final int N = windows.size();
209 for (int i = N - 1; i >= 0; i--) {
210 final WindowState child = windows.get(i);
211 if (child.mInputChannel == null || child.mRemoved) {
212 // Skip this window because it cannot possibly receive input.
213 continue;
214 }
215
216 final int flags = child.mAttrs.flags;
217 final int type = child.mAttrs.type;
218
219 final boolean hasFocus = (child == mInputFocus);
220 final boolean isVisible = child.isVisibleLw();
221 final boolean hasWallpaper = (child == mService.mWallpaperTarget)
222 && (type != WindowManager.LayoutParams.TYPE_KEYGUARD);
223
224 // If there's a drag in progress and 'child' is a potential drop target,
225 // make sure it's been told about the drag
226 if (inDrag && isVisible) {
227 mService.mDragState.sendDragStartedIfNeededLw(child);
228 }
229
230 // Add a window to our list of input windows.
Jeff Brown9302c872011-07-13 22:51:29 -0700231 final InputWindowHandle inputWindowHandle = child.mInputWindowHandle;
232 inputWindowHandle.inputChannel = child.mInputChannel;
233 inputWindowHandle.name = child.toString();
234 inputWindowHandle.layoutParamsFlags = flags;
235 inputWindowHandle.layoutParamsType = type;
236 inputWindowHandle.dispatchingTimeoutNanos = child.getInputDispatchingTimeoutNanos();
237 inputWindowHandle.visible = isVisible;
238 inputWindowHandle.canReceiveKeys = child.canReceiveKeys();
239 inputWindowHandle.hasFocus = hasFocus;
240 inputWindowHandle.hasWallpaper = hasWallpaper;
241 inputWindowHandle.paused = child.mAppToken != null ? child.mAppToken.paused : false;
242 inputWindowHandle.layer = child.mLayer;
243 inputWindowHandle.ownerPid = child.mSession.mPid;
244 inputWindowHandle.ownerUid = child.mSession.mUid;
245 inputWindowHandle.inputFeatures = child.mAttrs.inputFeatures;
Jeff Brown474dcb52011-06-14 20:22:50 -0700246
Dianne Hackbornffb3d932011-05-17 17:44:51 -0700247 final Rect frame = child.mFrame;
Jeff Brown9302c872011-07-13 22:51:29 -0700248 inputWindowHandle.frameLeft = frame.left;
249 inputWindowHandle.frameTop = frame.top;
250 inputWindowHandle.frameRight = frame.right;
251 inputWindowHandle.frameBottom = frame.bottom;
Dianne Hackborn6e1eb762011-02-17 16:07:28 -0800252
Dianne Hackborne2515ee2011-04-27 18:52:56 -0400253 if (child.mGlobalScale != 1) {
254 // If we are scaling the window, input coordinates need
255 // to be inversely scaled to map from what is on screen
256 // to what is actually being touched in the UI.
Jeff Brown9302c872011-07-13 22:51:29 -0700257 inputWindowHandle.scaleFactor = 1.0f/child.mGlobalScale;
Dianne Hackborne2515ee2011-04-27 18:52:56 -0400258 } else {
Jeff Brown9302c872011-07-13 22:51:29 -0700259 inputWindowHandle.scaleFactor = 1;
Dianne Hackborne2515ee2011-04-27 18:52:56 -0400260 }
261
Jeff Brown9302c872011-07-13 22:51:29 -0700262 child.getTouchableRegion(inputWindowHandle.touchableRegion);
263
264 addInputWindowHandleLw(inputWindowHandle);
Dianne Hackborn6e1eb762011-02-17 16:07:28 -0800265 }
266
267 // Send windows to native code.
Jeff Brown9302c872011-07-13 22:51:29 -0700268 mService.mInputManager.setInputWindows(mInputWindowHandles);
269
Dianne Hackborn6e1eb762011-02-17 16:07:28 -0800270 // Clear the list in preparation for the next round.
Jeff Brown9302c872011-07-13 22:51:29 -0700271 clearInputWindowHandlesLw();
272
Dianne Hackbornb961cd22011-06-21 12:13:37 -0700273 if (false) Slog.d(WindowManagerService.TAG, "<<<<<<< EXITED updateInputWindowsLw");
Dianne Hackborn6e1eb762011-02-17 16:07:28 -0800274 }
275
276 /* Notifies that the input device configuration has changed. */
277 public void notifyConfigurationChanged() {
278 mService.sendNewConfiguration();
279
280 synchronized (mInputDevicesReadyMonitor) {
281 if (!mInputDevicesReady) {
282 mInputDevicesReady = true;
283 mInputDevicesReadyMonitor.notifyAll();
284 }
285 }
286 }
287
288 /* Waits until the built-in input devices have been configured. */
289 public boolean waitForInputDevicesReady(long timeoutMillis) {
290 synchronized (mInputDevicesReadyMonitor) {
291 if (!mInputDevicesReady) {
292 try {
293 mInputDevicesReadyMonitor.wait(timeoutMillis);
294 } catch (InterruptedException ex) {
295 }
296 }
297 return mInputDevicesReady;
298 }
299 }
300
301 /* Notifies that the lid switch changed state. */
302 public void notifyLidSwitchChanged(long whenNanos, boolean lidOpen) {
303 mService.mPolicy.notifyLidSwitchChanged(whenNanos, lidOpen);
304 }
305
306 /* Provides an opportunity for the window manager policy to intercept early key
307 * processing as soon as the key has been read from the device. */
308 public int interceptKeyBeforeQueueing(
309 KeyEvent event, int policyFlags, boolean isScreenOn) {
310 return mService.mPolicy.interceptKeyBeforeQueueing(event, policyFlags, isScreenOn);
311 }
Jeff Brown56194eb2011-03-02 19:23:13 -0800312
313 /* Provides an opportunity for the window manager policy to intercept early
314 * motion event processing when the screen is off since these events are normally
315 * dropped. */
316 public int interceptMotionBeforeQueueingWhenScreenOff(int policyFlags) {
317 return mService.mPolicy.interceptMotionBeforeQueueingWhenScreenOff(policyFlags);
318 }
319
Dianne Hackborn6e1eb762011-02-17 16:07:28 -0800320 /* Provides an opportunity for the window manager policy to process a key before
321 * ordinary dispatch. */
322 public boolean interceptKeyBeforeDispatching(
323 InputWindowHandle focus, KeyEvent event, int policyFlags) {
324 WindowState windowState = focus != null ? (WindowState) focus.windowState : null;
325 return mService.mPolicy.interceptKeyBeforeDispatching(windowState, event, policyFlags);
326 }
327
328 /* Provides an opportunity for the window manager policy to process a key that
329 * the application did not handle. */
330 public KeyEvent dispatchUnhandledKey(
331 InputWindowHandle focus, KeyEvent event, int policyFlags) {
332 WindowState windowState = focus != null ? (WindowState) focus.windowState : null;
333 return mService.mPolicy.dispatchUnhandledKey(windowState, event, policyFlags);
334 }
335
336 /* Called when the current input focus changes.
337 * Layer assignment is assumed to be complete by the time this is called.
338 */
339 public void setInputFocusLw(WindowState newWindow, boolean updateInputWindows) {
340 if (WindowManagerService.DEBUG_INPUT) {
341 Slog.d(WindowManagerService.TAG, "Input focus has changed to " + newWindow);
342 }
343
344 if (newWindow != mInputFocus) {
345 if (newWindow != null && newWindow.canReceiveKeys()) {
346 // Displaying a window implicitly causes dispatching to be unpaused.
347 // This is to protect against bugs if someone pauses dispatching but
348 // forgets to resume.
349 newWindow.mToken.paused = false;
350 }
351
352 mInputFocus = newWindow;
353 setUpdateInputWindowsNeededLw();
354
355 if (updateInputWindows) {
356 updateInputWindowsLw(false /*force*/);
357 }
358 }
359 }
360
361 public void setFocusedAppLw(AppWindowToken newApp) {
362 // Focused app has changed.
363 if (newApp == null) {
364 mService.mInputManager.setFocusedApplication(null);
365 } else {
Jeff Brown9302c872011-07-13 22:51:29 -0700366 final InputApplicationHandle handle = newApp.mInputApplicationHandle;
367 handle.name = newApp.toString();
368 handle.dispatchingTimeoutNanos = newApp.inputDispatchingTimeoutNanos;
Dianne Hackborn6e1eb762011-02-17 16:07:28 -0800369
Jeff Brown9302c872011-07-13 22:51:29 -0700370 mService.mInputManager.setFocusedApplication(handle);
Dianne Hackborn6e1eb762011-02-17 16:07:28 -0800371 }
372 }
373
374 public void pauseDispatchingLw(WindowToken window) {
375 if (! window.paused) {
376 if (WindowManagerService.DEBUG_INPUT) {
377 Slog.v(WindowManagerService.TAG, "Pausing WindowToken " + window);
378 }
379
380 window.paused = true;
381 updateInputWindowsLw(true /*force*/);
382 }
383 }
384
385 public void resumeDispatchingLw(WindowToken window) {
386 if (window.paused) {
387 if (WindowManagerService.DEBUG_INPUT) {
388 Slog.v(WindowManagerService.TAG, "Resuming WindowToken " + window);
389 }
390
391 window.paused = false;
392 updateInputWindowsLw(true /*force*/);
393 }
394 }
395
396 public void freezeInputDispatchingLw() {
397 if (! mInputDispatchFrozen) {
398 if (WindowManagerService.DEBUG_INPUT) {
399 Slog.v(WindowManagerService.TAG, "Freezing input dispatching");
400 }
401
402 mInputDispatchFrozen = true;
403 updateInputDispatchModeLw();
404 }
405 }
406
407 public void thawInputDispatchingLw() {
408 if (mInputDispatchFrozen) {
409 if (WindowManagerService.DEBUG_INPUT) {
410 Slog.v(WindowManagerService.TAG, "Thawing input dispatching");
411 }
412
413 mInputDispatchFrozen = false;
414 updateInputDispatchModeLw();
415 }
416 }
417
418 public void setEventDispatchingLw(boolean enabled) {
419 if (mInputDispatchEnabled != enabled) {
420 if (WindowManagerService.DEBUG_INPUT) {
421 Slog.v(WindowManagerService.TAG, "Setting event dispatching to " + enabled);
422 }
423
424 mInputDispatchEnabled = enabled;
425 updateInputDispatchModeLw();
426 }
427 }
428
429 private void updateInputDispatchModeLw() {
430 mService.mInputManager.setInputDispatchMode(mInputDispatchEnabled, mInputDispatchFrozen);
431 }
432}