blob: fb74d27642807740dc65571b8d1056a4e373bc87 [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;
Dianne Hackborn6e1eb762011-02-17 16:07:28 -080020import android.os.RemoteException;
21import android.util.Log;
22import android.util.Slog;
Jeff Browncc4f7db2011-08-30 20:34:48 -070023import android.view.InputChannel;
Dianne Hackborn6e1eb762011-02-17 16:07:28 -080024import 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 // Array of window handles to provide to the input dispatcher.
46 private InputWindowHandle[] mInputWindowHandles;
47 private int mInputWindowHandleCount;
48
Dianne Hackborn6e1eb762011-02-17 16:07:28 -080049 // 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
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 */
62 public void notifyInputChannelBroken(InputWindowHandle inputWindowHandle) {
63 if (inputWindowHandle == null) {
64 return;
65 }
66
67 synchronized (mService.mWindowMap) {
68 WindowState windowState = (WindowState) inputWindowHandle.windowState;
Jeff Brown9302c872011-07-13 22:51:29 -070069 if (windowState != null) {
70 Slog.i(WindowManagerService.TAG, "WINDOW DIED " + windowState);
71 mService.removeWindowLocked(windowState.mSession, windowState);
72 }
Dianne Hackborn6e1eb762011-02-17 16:07:28 -080073 }
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 */
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;
Jeff Brown9302c872011-07-13 22:51:29 -070097 if (appWindowToken != null) {
98 Slog.i(WindowManagerService.TAG,
99 "Input event dispatching timed out sending to application "
100 + appWindowToken.stringName);
101 }
Dianne Hackborn6e1eb762011-02-17 16:07:28 -0800102 }
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
Jeff Brown9302c872011-07-13 22:51:29 -0700120 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
131 private void clearInputWindowHandlesLw() {
132 while (mInputWindowHandleCount != 0) {
133 mInputWindowHandles[--mInputWindowHandleCount] = null;
134 }
Dianne Hackborn6e1eb762011-02-17 16:07:28 -0800135 }
136
137 public void setUpdateInputWindowsNeededLw() {
138 mUpdateInputWindowsNeeded = true;
139 }
140
141 /* Updates the cached window information provided to the input dispatcher. */
142 public void updateInputWindowsLw(boolean force) {
143 if (!force && !mUpdateInputWindowsNeeded) {
144 return;
145 }
146 mUpdateInputWindowsNeeded = false;
147
Dianne Hackbornb961cd22011-06-21 12:13:37 -0700148 if (false) Slog.d(WindowManagerService.TAG, ">>>>>> ENTERED updateInputWindowsLw");
Jeff Brown9302c872011-07-13 22:51:29 -0700149
Dianne Hackborn6e1eb762011-02-17 16:07:28 -0800150 // 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 }
Jeff Browncc4f7db2011-08-30 20:34:48 -0700163 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 }
Dianne Hackborn6e1eb762011-02-17 16:07:28 -0800170 }
171
Dianne Hackborndf89e652011-10-06 22:35:11 -0700172 final int NFW = mService.mFakeWindows.size();
173 for (int i = 0; i < NFW; i++) {
174 addInputWindowHandleLw(mService.mFakeWindows.get(i).mWindowHandle);
175 }
176
Dianne Hackborn6e1eb762011-02-17 16:07:28 -0800177 final int N = windows.size();
178 for (int i = N - 1; i >= 0; i--) {
179 final WindowState child = windows.get(i);
Jeff Browncc4f7db2011-08-30 20:34:48 -0700180 final InputChannel inputChannel = child.mInputChannel;
181 final InputWindowHandle inputWindowHandle = child.mInputWindowHandle;
182 if (inputChannel == null || inputWindowHandle == null || child.mRemoved) {
Dianne Hackborn6e1eb762011-02-17 16:07:28 -0800183 // 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.
Jeff Brown9302c872011-07-13 22:51:29 -0700202 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;
Jeff Brown474dcb52011-06-14 20:22:50 -0700215
Dianne Hackbornffb3d932011-05-17 17:44:51 -0700216 final Rect frame = child.mFrame;
Jeff Brown9302c872011-07-13 22:51:29 -0700217 inputWindowHandle.frameLeft = frame.left;
218 inputWindowHandle.frameTop = frame.top;
219 inputWindowHandle.frameRight = frame.right;
220 inputWindowHandle.frameBottom = frame.bottom;
Dianne Hackborn6e1eb762011-02-17 16:07:28 -0800221
Dianne Hackborne2515ee2011-04-27 18:52:56 -0400222 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.
Jeff Brown9302c872011-07-13 22:51:29 -0700226 inputWindowHandle.scaleFactor = 1.0f/child.mGlobalScale;
Dianne Hackborne2515ee2011-04-27 18:52:56 -0400227 } else {
Jeff Brown9302c872011-07-13 22:51:29 -0700228 inputWindowHandle.scaleFactor = 1;
Dianne Hackborne2515ee2011-04-27 18:52:56 -0400229 }
230
Jeff Brown9302c872011-07-13 22:51:29 -0700231 child.getTouchableRegion(inputWindowHandle.touchableRegion);
232
233 addInputWindowHandleLw(inputWindowHandle);
Dianne Hackborn6e1eb762011-02-17 16:07:28 -0800234 }
235
236 // Send windows to native code.
Jeff Brown9302c872011-07-13 22:51:29 -0700237 mService.mInputManager.setInputWindows(mInputWindowHandles);
238
Dianne Hackborn6e1eb762011-02-17 16:07:28 -0800239 // Clear the list in preparation for the next round.
Jeff Brown9302c872011-07-13 22:51:29 -0700240 clearInputWindowHandlesLw();
241
Dianne Hackbornb961cd22011-06-21 12:13:37 -0700242 if (false) Slog.d(WindowManagerService.TAG, "<<<<<<< EXITED updateInputWindowsLw");
Dianne Hackborn6e1eb762011-02-17 16:07:28 -0800243 }
244
245 /* Notifies that the input device configuration has changed. */
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. */
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. */
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. */
277 public int interceptKeyBeforeQueueing(
278 KeyEvent event, int policyFlags, boolean isScreenOn) {
279 return mService.mPolicy.interceptKeyBeforeQueueing(event, policyFlags, isScreenOn);
280 }
Jeff Brown56194eb2011-03-02 19:23:13 -0800281
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. */
285 public int interceptMotionBeforeQueueingWhenScreenOff(int policyFlags) {
286 return mService.mPolicy.interceptMotionBeforeQueueingWhenScreenOff(policyFlags);
287 }
288
Dianne Hackborn6e1eb762011-02-17 16:07:28 -0800289 /* Provides an opportunity for the window manager policy to process a key before
290 * ordinary dispatch. */
Jeff Brown905805a2011-10-12 13:57:59 -0700291 public long interceptKeyBeforeDispatching(
Dianne Hackborn6e1eb762011-02-17 16:07:28 -0800292 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. */
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 */
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
330 public void setFocusedAppLw(AppWindowToken newApp) {
331 // Focused app has changed.
332 if (newApp == null) {
333 mService.mInputManager.setFocusedApplication(null);
334 } else {
Jeff Brown9302c872011-07-13 22:51:29 -0700335 final InputApplicationHandle handle = newApp.mInputApplicationHandle;
336 handle.name = newApp.toString();
337 handle.dispatchingTimeoutNanos = newApp.inputDispatchingTimeoutNanos;
Dianne Hackborn6e1eb762011-02-17 16:07:28 -0800338
Jeff Brown9302c872011-07-13 22:51:29 -0700339 mService.mInputManager.setFocusedApplication(handle);
Dianne Hackborn6e1eb762011-02-17 16:07:28 -0800340 }
341 }
342
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
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
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
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
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
398 private void updateInputDispatchModeLw() {
399 mService.mInputManager.setInputDispatchMode(mInputDispatchEnabled, mInputDispatchFrozen);
400 }
Jeff Brownea426552011-07-18 16:53:48 -0700401}