blob: 68066344586a5875eca5046261bf3eac4ab44031 [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 Hackbornb961cd22011-06-21 12:13:37 -070020import android.os.Binder;
Dianne Hackborn6e1eb762011-02-17 16:07:28 -080021import android.os.Process;
22import android.os.RemoteException;
23import android.util.Log;
24import android.util.Slog;
25import android.view.KeyEvent;
26import android.view.WindowManager;
27
28import java.util.ArrayList;
29
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
45 // Temporary list of windows information to provide to the input dispatcher.
46 private InputWindowList mTempInputWindows = new InputWindowList();
47
48 // Temporary input application object to provide to the input dispatcher.
49 private InputApplication mTempInputApplication = new InputApplication();
50
51 // Set to true when the first input device configuration change notification
52 // is received to indicate that the input devices are ready.
53 private final Object mInputDevicesReadyMonitor = new Object();
54 private boolean mInputDevicesReady;
55
56 public InputMonitor(WindowManagerService service) {
57 mService = service;
58 }
59
60 /* Notifies the window manager about a broken input channel.
61 *
62 * Called by the InputManager.
63 */
64 public void notifyInputChannelBroken(InputWindowHandle inputWindowHandle) {
65 if (inputWindowHandle == null) {
66 return;
67 }
68
69 synchronized (mService.mWindowMap) {
70 WindowState windowState = (WindowState) inputWindowHandle.windowState;
71 Slog.i(WindowManagerService.TAG, "WINDOW DIED " + windowState);
72 mService.removeWindowLocked(windowState.mSession, windowState);
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 */
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 Slog.i(WindowManagerService.TAG, "Input event dispatching timed out sending to application "
98 + appWindowToken.stringName);
99 }
100
101 if (appWindowToken != null && appWindowToken.appToken != null) {
102 try {
103 // Notify the activity manager about the timeout and let it decide whether
104 // to abort dispatching or keep waiting.
105 boolean abort = appWindowToken.appToken.keyDispatchingTimedOut();
106 if (! abort) {
107 // The activity manager declined to abort dispatching.
108 // Wait a bit longer and timeout again later.
109 return appWindowToken.inputDispatchingTimeoutNanos;
110 }
111 } catch (RemoteException ex) {
112 }
113 }
114 return 0; // abort dispatching
115 }
116
117 private void addDragInputWindowLw(InputWindowList windowList) {
118 final InputWindow inputWindow = windowList.add();
119 inputWindow.inputChannel = mService.mDragState.mServerChannel;
120 inputWindow.name = "drag";
121 inputWindow.layoutParamsFlags = 0;
122 inputWindow.layoutParamsType = WindowManager.LayoutParams.TYPE_DRAG;
123 inputWindow.dispatchingTimeoutNanos = WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
124 inputWindow.visible = true;
125 inputWindow.canReceiveKeys = false;
126 inputWindow.hasFocus = true;
127 inputWindow.hasWallpaper = false;
128 inputWindow.paused = false;
129 inputWindow.layer = mService.mDragState.getDragLayerLw();
130 inputWindow.ownerPid = Process.myPid();
131 inputWindow.ownerUid = Process.myUid();
Jeff Brown474dcb52011-06-14 20:22:50 -0700132 inputWindow.inputFeatures = 0;
133 inputWindow.scaleFactor = 1.0f;
Dianne Hackborn6e1eb762011-02-17 16:07:28 -0800134
135 // The drag window covers the entire display
136 inputWindow.frameLeft = 0;
137 inputWindow.frameTop = 0;
Dianne Hackbornac8dea12011-04-20 18:18:51 -0700138 inputWindow.frameRight = mService.mDisplay.getRealWidth();
139 inputWindow.frameBottom = mService.mDisplay.getRealHeight();
Dianne Hackborn6e1eb762011-02-17 16:07:28 -0800140
141 // The drag window cannot receive new touches.
142 inputWindow.touchableRegion.setEmpty();
143 }
144
145 public void setUpdateInputWindowsNeededLw() {
146 mUpdateInputWindowsNeeded = true;
147 }
148
149 /* Updates the cached window information provided to the input dispatcher. */
150 public void updateInputWindowsLw(boolean force) {
151 if (!force && !mUpdateInputWindowsNeeded) {
152 return;
153 }
154 mUpdateInputWindowsNeeded = false;
155
Dianne Hackbornb961cd22011-06-21 12:13:37 -0700156 if (false) Slog.d(WindowManagerService.TAG, ">>>>>> ENTERED updateInputWindowsLw");
157
Dianne Hackborn6e1eb762011-02-17 16:07:28 -0800158 // Populate the input window list with information about all of the windows that
159 // could potentially receive input.
160 // As an optimization, we could try to prune the list of windows but this turns
161 // out to be difficult because only the native code knows for sure which window
162 // currently has touch focus.
163 final ArrayList<WindowState> windows = mService.mWindows;
164
165 // If there's a drag in flight, provide a pseudowindow to catch drag input
166 final boolean inDrag = (mService.mDragState != null);
167 if (inDrag) {
168 if (WindowManagerService.DEBUG_DRAG) {
169 Log.d(WindowManagerService.TAG, "Inserting drag window");
170 }
171 addDragInputWindowLw(mTempInputWindows);
172 }
173
174 final int N = windows.size();
175 for (int i = N - 1; i >= 0; i--) {
176 final WindowState child = windows.get(i);
177 if (child.mInputChannel == null || child.mRemoved) {
178 // Skip this window because it cannot possibly receive input.
179 continue;
180 }
181
182 final int flags = child.mAttrs.flags;
183 final int type = child.mAttrs.type;
184
185 final boolean hasFocus = (child == mInputFocus);
186 final boolean isVisible = child.isVisibleLw();
187 final boolean hasWallpaper = (child == mService.mWallpaperTarget)
188 && (type != WindowManager.LayoutParams.TYPE_KEYGUARD);
189
190 // If there's a drag in progress and 'child' is a potential drop target,
191 // make sure it's been told about the drag
192 if (inDrag && isVisible) {
193 mService.mDragState.sendDragStartedIfNeededLw(child);
194 }
195
196 // Add a window to our list of input windows.
197 final InputWindow inputWindow = mTempInputWindows.add();
198 inputWindow.inputWindowHandle = child.mInputWindowHandle;
199 inputWindow.inputChannel = child.mInputChannel;
200 inputWindow.name = child.toString();
201 inputWindow.layoutParamsFlags = flags;
202 inputWindow.layoutParamsType = type;
203 inputWindow.dispatchingTimeoutNanos = child.getInputDispatchingTimeoutNanos();
204 inputWindow.visible = isVisible;
205 inputWindow.canReceiveKeys = child.canReceiveKeys();
206 inputWindow.hasFocus = hasFocus;
207 inputWindow.hasWallpaper = hasWallpaper;
208 inputWindow.paused = child.mAppToken != null ? child.mAppToken.paused : false;
209 inputWindow.layer = child.mLayer;
210 inputWindow.ownerPid = child.mSession.mPid;
211 inputWindow.ownerUid = child.mSession.mUid;
Jeff Brown474dcb52011-06-14 20:22:50 -0700212 inputWindow.inputFeatures = child.mAttrs.inputFeatures;
213
Dianne Hackbornffb3d932011-05-17 17:44:51 -0700214 final Rect frame = child.mFrame;
Dianne Hackborn6e1eb762011-02-17 16:07:28 -0800215 inputWindow.frameLeft = frame.left;
216 inputWindow.frameTop = frame.top;
217 inputWindow.frameRight = frame.right;
218 inputWindow.frameBottom = frame.bottom;
219
Dianne Hackborne2515ee2011-04-27 18:52:56 -0400220 if (child.mGlobalScale != 1) {
221 // If we are scaling the window, input coordinates need
222 // to be inversely scaled to map from what is on screen
223 // to what is actually being touched in the UI.
224 inputWindow.scaleFactor = 1.0f/child.mGlobalScale;
225 } else {
226 inputWindow.scaleFactor = 1;
227 }
228
Dianne Hackborn6e1eb762011-02-17 16:07:28 -0800229 child.getTouchableRegion(inputWindow.touchableRegion);
230 }
231
232 // Send windows to native code.
233 mService.mInputManager.setInputWindows(mTempInputWindows.toNullTerminatedArray());
234
235 // Clear the list in preparation for the next round.
236 // Also avoids keeping InputChannel objects referenced unnecessarily.
237 mTempInputWindows.clear();
Dianne Hackbornb961cd22011-06-21 12:13:37 -0700238
239 if (false) Slog.d(WindowManagerService.TAG, "<<<<<<< EXITED updateInputWindowsLw");
Dianne Hackborn6e1eb762011-02-17 16:07:28 -0800240 }
241
242 /* Notifies that the input device configuration has changed. */
243 public void notifyConfigurationChanged() {
244 mService.sendNewConfiguration();
245
246 synchronized (mInputDevicesReadyMonitor) {
247 if (!mInputDevicesReady) {
248 mInputDevicesReady = true;
249 mInputDevicesReadyMonitor.notifyAll();
250 }
251 }
252 }
253
254 /* Waits until the built-in input devices have been configured. */
255 public boolean waitForInputDevicesReady(long timeoutMillis) {
256 synchronized (mInputDevicesReadyMonitor) {
257 if (!mInputDevicesReady) {
258 try {
259 mInputDevicesReadyMonitor.wait(timeoutMillis);
260 } catch (InterruptedException ex) {
261 }
262 }
263 return mInputDevicesReady;
264 }
265 }
266
267 /* Notifies that the lid switch changed state. */
268 public void notifyLidSwitchChanged(long whenNanos, boolean lidOpen) {
269 mService.mPolicy.notifyLidSwitchChanged(whenNanos, lidOpen);
270 }
271
272 /* Provides an opportunity for the window manager policy to intercept early key
273 * processing as soon as the key has been read from the device. */
274 public int interceptKeyBeforeQueueing(
275 KeyEvent event, int policyFlags, boolean isScreenOn) {
276 return mService.mPolicy.interceptKeyBeforeQueueing(event, policyFlags, isScreenOn);
277 }
Jeff Brown56194eb2011-03-02 19:23:13 -0800278
279 /* Provides an opportunity for the window manager policy to intercept early
280 * motion event processing when the screen is off since these events are normally
281 * dropped. */
282 public int interceptMotionBeforeQueueingWhenScreenOff(int policyFlags) {
283 return mService.mPolicy.interceptMotionBeforeQueueingWhenScreenOff(policyFlags);
284 }
285
Dianne Hackborn6e1eb762011-02-17 16:07:28 -0800286 /* Provides an opportunity for the window manager policy to process a key before
287 * ordinary dispatch. */
288 public boolean interceptKeyBeforeDispatching(
289 InputWindowHandle focus, KeyEvent event, int policyFlags) {
290 WindowState windowState = focus != null ? (WindowState) focus.windowState : null;
291 return mService.mPolicy.interceptKeyBeforeDispatching(windowState, event, policyFlags);
292 }
293
294 /* Provides an opportunity for the window manager policy to process a key that
295 * the application did not handle. */
296 public KeyEvent dispatchUnhandledKey(
297 InputWindowHandle focus, KeyEvent event, int policyFlags) {
298 WindowState windowState = focus != null ? (WindowState) focus.windowState : null;
299 return mService.mPolicy.dispatchUnhandledKey(windowState, event, policyFlags);
300 }
301
302 /* Called when the current input focus changes.
303 * Layer assignment is assumed to be complete by the time this is called.
304 */
305 public void setInputFocusLw(WindowState newWindow, boolean updateInputWindows) {
306 if (WindowManagerService.DEBUG_INPUT) {
307 Slog.d(WindowManagerService.TAG, "Input focus has changed to " + newWindow);
308 }
309
310 if (newWindow != mInputFocus) {
311 if (newWindow != null && newWindow.canReceiveKeys()) {
312 // Displaying a window implicitly causes dispatching to be unpaused.
313 // This is to protect against bugs if someone pauses dispatching but
314 // forgets to resume.
315 newWindow.mToken.paused = false;
316 }
317
318 mInputFocus = newWindow;
319 setUpdateInputWindowsNeededLw();
320
321 if (updateInputWindows) {
322 updateInputWindowsLw(false /*force*/);
323 }
324 }
325 }
326
327 public void setFocusedAppLw(AppWindowToken newApp) {
328 // Focused app has changed.
329 if (newApp == null) {
330 mService.mInputManager.setFocusedApplication(null);
331 } else {
332 mTempInputApplication.inputApplicationHandle = newApp.mInputApplicationHandle;
333 mTempInputApplication.name = newApp.toString();
334 mTempInputApplication.dispatchingTimeoutNanos =
335 newApp.inputDispatchingTimeoutNanos;
336
337 mService.mInputManager.setFocusedApplication(mTempInputApplication);
338
339 mTempInputApplication.recycle();
340 }
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 }
401}