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