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