blob: 47cd34a6a6397159f2324c6e7fd88d934070a20c [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
Jeff Brown4532e612012-04-05 14:27:12 -070019import com.android.server.input.InputManagerService;
20import com.android.server.input.InputApplicationHandle;
21import com.android.server.input.InputWindowHandle;
22
Dianne Hackborn6e1eb762011-02-17 16:07:28 -080023import android.graphics.Rect;
Dianne Hackborn6e1eb762011-02-17 16:07:28 -080024import android.os.RemoteException;
25import android.util.Log;
26import android.util.Slog;
Jeff Browncc4f7db2011-08-30 20:34:48 -070027import android.view.InputChannel;
Dianne Hackborn6e1eb762011-02-17 16:07:28 -080028import android.view.KeyEvent;
29import android.view.WindowManager;
30
31import java.util.ArrayList;
Jeff Brown9302c872011-07-13 22:51:29 -070032import java.util.Arrays;
Dianne Hackborn6e1eb762011-02-17 16:07:28 -080033
Jeff Brown4532e612012-04-05 14:27:12 -070034final class InputMonitor implements InputManagerService.Callbacks {
Dianne Hackborn6e1eb762011-02-17 16:07:28 -080035 private final WindowManagerService mService;
36
37 // Current window with input focus for keys and other non-touch events. May be null.
38 private WindowState mInputFocus;
39
40 // When true, prevents input dispatch from proceeding until set to false again.
41 private boolean mInputDispatchFrozen;
42
43 // When true, input dispatch proceeds normally. Otherwise all events are dropped.
Jeff Brownc042ee22012-05-08 13:03:42 -070044 // Initially false, so that input does not get dispatched until boot is finished at
45 // which point the ActivityManager will enable dispatching.
46 private boolean mInputDispatchEnabled;
Dianne Hackborn6e1eb762011-02-17 16:07:28 -080047
48 // When true, need to call updateInputWindowsLw().
49 private boolean mUpdateInputWindowsNeeded = true;
50
Jeff Brown9302c872011-07-13 22:51:29 -070051 // Array of window handles to provide to the input dispatcher.
52 private InputWindowHandle[] mInputWindowHandles;
53 private int mInputWindowHandleCount;
54
Dianne Hackborn6e1eb762011-02-17 16:07:28 -080055 // Set to true when the first input device configuration change notification
56 // is received to indicate that the input devices are ready.
57 private final Object mInputDevicesReadyMonitor = new Object();
58 private boolean mInputDevicesReady;
59
60 public InputMonitor(WindowManagerService service) {
61 mService = service;
62 }
63
64 /* Notifies the window manager about a broken input channel.
65 *
66 * Called by the InputManager.
67 */
68 public void notifyInputChannelBroken(InputWindowHandle inputWindowHandle) {
69 if (inputWindowHandle == null) {
70 return;
71 }
72
73 synchronized (mService.mWindowMap) {
74 WindowState windowState = (WindowState) inputWindowHandle.windowState;
Jeff Brown9302c872011-07-13 22:51:29 -070075 if (windowState != null) {
76 Slog.i(WindowManagerService.TAG, "WINDOW DIED " + windowState);
77 mService.removeWindowLocked(windowState.mSession, windowState);
78 }
Dianne Hackborn6e1eb762011-02-17 16:07:28 -080079 }
80 }
81
82 /* Notifies the window manager about an application that is not responding.
83 * Returns a new timeout to continue waiting in nanoseconds, or 0 to abort dispatch.
84 *
85 * Called by the InputManager.
86 */
87 public long notifyANR(InputApplicationHandle inputApplicationHandle,
88 InputWindowHandle inputWindowHandle) {
89 AppWindowToken appWindowToken = null;
90 if (inputWindowHandle != null) {
91 synchronized (mService.mWindowMap) {
92 WindowState windowState = (WindowState) inputWindowHandle.windowState;
93 if (windowState != null) {
94 Slog.i(WindowManagerService.TAG, "Input event dispatching timed out sending to "
95 + windowState.mAttrs.getTitle());
96 appWindowToken = windowState.mAppToken;
Jeff Brownd7a04de2012-06-17 14:17:52 -070097 mService.saveANRStateLocked(appWindowToken, windowState);
Dianne Hackborn6e1eb762011-02-17 16:07:28 -080098 }
99 }
100 }
101
102 if (appWindowToken == null && inputApplicationHandle != null) {
Jeff Brown4532e612012-04-05 14:27:12 -0700103 appWindowToken = (AppWindowToken)inputApplicationHandle.appWindowToken;
Jeff Brown9302c872011-07-13 22:51:29 -0700104 if (appWindowToken != null) {
105 Slog.i(WindowManagerService.TAG,
106 "Input event dispatching timed out sending to application "
107 + appWindowToken.stringName);
Jeff Brownd7a04de2012-06-17 14:17:52 -0700108 mService.saveANRStateLocked(appWindowToken, null);
Jeff Brown9302c872011-07-13 22:51:29 -0700109 }
Dianne Hackborn6e1eb762011-02-17 16:07:28 -0800110 }
111
112 if (appWindowToken != null && appWindowToken.appToken != null) {
113 try {
114 // Notify the activity manager about the timeout and let it decide whether
115 // to abort dispatching or keep waiting.
116 boolean abort = appWindowToken.appToken.keyDispatchingTimedOut();
117 if (! abort) {
118 // The activity manager declined to abort dispatching.
119 // Wait a bit longer and timeout again later.
120 return appWindowToken.inputDispatchingTimeoutNanos;
121 }
122 } catch (RemoteException ex) {
123 }
124 }
125 return 0; // abort dispatching
126 }
127
Jeff Brown9302c872011-07-13 22:51:29 -0700128 private void addInputWindowHandleLw(InputWindowHandle windowHandle) {
129 if (mInputWindowHandles == null) {
130 mInputWindowHandles = new InputWindowHandle[16];
131 }
132 if (mInputWindowHandleCount >= mInputWindowHandles.length) {
133 mInputWindowHandles = Arrays.copyOf(mInputWindowHandles,
134 mInputWindowHandleCount * 2);
135 }
136 mInputWindowHandles[mInputWindowHandleCount++] = windowHandle;
137 }
138
139 private void clearInputWindowHandlesLw() {
140 while (mInputWindowHandleCount != 0) {
141 mInputWindowHandles[--mInputWindowHandleCount] = null;
142 }
Dianne Hackborn6e1eb762011-02-17 16:07:28 -0800143 }
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");
Jeff Brown9302c872011-07-13 22:51:29 -0700157
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 }
Jeff Browncc4f7db2011-08-30 20:34:48 -0700171 final InputWindowHandle dragWindowHandle = mService.mDragState.mDragWindowHandle;
172 if (dragWindowHandle != null) {
173 addInputWindowHandleLw(dragWindowHandle);
174 } else {
175 Slog.w(WindowManagerService.TAG, "Drag is in progress but there is no "
176 + "drag window handle.");
177 }
Dianne Hackborn6e1eb762011-02-17 16:07:28 -0800178 }
179
Dianne Hackborndf89e652011-10-06 22:35:11 -0700180 final int NFW = mService.mFakeWindows.size();
181 for (int i = 0; i < NFW; i++) {
182 addInputWindowHandleLw(mService.mFakeWindows.get(i).mWindowHandle);
183 }
184
Dianne Hackborn6e1eb762011-02-17 16:07:28 -0800185 final int N = windows.size();
186 for (int i = N - 1; i >= 0; i--) {
187 final WindowState child = windows.get(i);
Jeff Browncc4f7db2011-08-30 20:34:48 -0700188 final InputChannel inputChannel = child.mInputChannel;
189 final InputWindowHandle inputWindowHandle = child.mInputWindowHandle;
190 if (inputChannel == null || inputWindowHandle == null || child.mRemoved) {
Dianne Hackborn6e1eb762011-02-17 16:07:28 -0800191 // Skip this window because it cannot possibly receive input.
192 continue;
193 }
194
195 final int flags = child.mAttrs.flags;
196 final int type = child.mAttrs.type;
197
198 final boolean hasFocus = (child == mInputFocus);
199 final boolean isVisible = child.isVisibleLw();
200 final boolean hasWallpaper = (child == mService.mWallpaperTarget)
201 && (type != WindowManager.LayoutParams.TYPE_KEYGUARD);
202
203 // If there's a drag in progress and 'child' is a potential drop target,
204 // make sure it's been told about the drag
205 if (inDrag && isVisible) {
206 mService.mDragState.sendDragStartedIfNeededLw(child);
207 }
208
209 // Add a window to our list of input windows.
Jeff Brown9302c872011-07-13 22:51:29 -0700210 inputWindowHandle.name = child.toString();
211 inputWindowHandle.layoutParamsFlags = flags;
212 inputWindowHandle.layoutParamsType = type;
213 inputWindowHandle.dispatchingTimeoutNanos = child.getInputDispatchingTimeoutNanos();
214 inputWindowHandle.visible = isVisible;
215 inputWindowHandle.canReceiveKeys = child.canReceiveKeys();
216 inputWindowHandle.hasFocus = hasFocus;
217 inputWindowHandle.hasWallpaper = hasWallpaper;
218 inputWindowHandle.paused = child.mAppToken != null ? child.mAppToken.paused : false;
219 inputWindowHandle.layer = child.mLayer;
220 inputWindowHandle.ownerPid = child.mSession.mPid;
221 inputWindowHandle.ownerUid = child.mSession.mUid;
222 inputWindowHandle.inputFeatures = child.mAttrs.inputFeatures;
Jeff Brown474dcb52011-06-14 20:22:50 -0700223
Dianne Hackbornffb3d932011-05-17 17:44:51 -0700224 final Rect frame = child.mFrame;
Jeff Brown9302c872011-07-13 22:51:29 -0700225 inputWindowHandle.frameLeft = frame.left;
226 inputWindowHandle.frameTop = frame.top;
227 inputWindowHandle.frameRight = frame.right;
228 inputWindowHandle.frameBottom = frame.bottom;
Dianne Hackborn6e1eb762011-02-17 16:07:28 -0800229
Dianne Hackborne2515ee2011-04-27 18:52:56 -0400230 if (child.mGlobalScale != 1) {
231 // If we are scaling the window, input coordinates need
232 // to be inversely scaled to map from what is on screen
233 // to what is actually being touched in the UI.
Jeff Brown9302c872011-07-13 22:51:29 -0700234 inputWindowHandle.scaleFactor = 1.0f/child.mGlobalScale;
Dianne Hackborne2515ee2011-04-27 18:52:56 -0400235 } else {
Jeff Brown9302c872011-07-13 22:51:29 -0700236 inputWindowHandle.scaleFactor = 1;
Dianne Hackborne2515ee2011-04-27 18:52:56 -0400237 }
238
Jeff Brown9302c872011-07-13 22:51:29 -0700239 child.getTouchableRegion(inputWindowHandle.touchableRegion);
240
241 addInputWindowHandleLw(inputWindowHandle);
Dianne Hackborn6e1eb762011-02-17 16:07:28 -0800242 }
243
244 // Send windows to native code.
Jeff Brown9302c872011-07-13 22:51:29 -0700245 mService.mInputManager.setInputWindows(mInputWindowHandles);
246
Dianne Hackborn6e1eb762011-02-17 16:07:28 -0800247 // Clear the list in preparation for the next round.
Jeff Brown9302c872011-07-13 22:51:29 -0700248 clearInputWindowHandlesLw();
249
Dianne Hackbornb961cd22011-06-21 12:13:37 -0700250 if (false) Slog.d(WindowManagerService.TAG, "<<<<<<< EXITED updateInputWindowsLw");
Dianne Hackborn6e1eb762011-02-17 16:07:28 -0800251 }
252
253 /* Notifies that the input device configuration has changed. */
254 public void notifyConfigurationChanged() {
255 mService.sendNewConfiguration();
256
257 synchronized (mInputDevicesReadyMonitor) {
258 if (!mInputDevicesReady) {
259 mInputDevicesReady = true;
260 mInputDevicesReadyMonitor.notifyAll();
261 }
262 }
263 }
264
265 /* Waits until the built-in input devices have been configured. */
266 public boolean waitForInputDevicesReady(long timeoutMillis) {
267 synchronized (mInputDevicesReadyMonitor) {
268 if (!mInputDevicesReady) {
269 try {
270 mInputDevicesReadyMonitor.wait(timeoutMillis);
271 } catch (InterruptedException ex) {
272 }
273 }
274 return mInputDevicesReady;
275 }
276 }
277
278 /* Notifies that the lid switch changed state. */
279 public void notifyLidSwitchChanged(long whenNanos, boolean lidOpen) {
280 mService.mPolicy.notifyLidSwitchChanged(whenNanos, lidOpen);
281 }
282
283 /* Provides an opportunity for the window manager policy to intercept early key
284 * processing as soon as the key has been read from the device. */
285 public int interceptKeyBeforeQueueing(
286 KeyEvent event, int policyFlags, boolean isScreenOn) {
287 return mService.mPolicy.interceptKeyBeforeQueueing(event, policyFlags, isScreenOn);
288 }
Jeff Brown56194eb2011-03-02 19:23:13 -0800289
290 /* Provides an opportunity for the window manager policy to intercept early
291 * motion event processing when the screen is off since these events are normally
292 * dropped. */
293 public int interceptMotionBeforeQueueingWhenScreenOff(int policyFlags) {
294 return mService.mPolicy.interceptMotionBeforeQueueingWhenScreenOff(policyFlags);
295 }
296
Dianne Hackborn6e1eb762011-02-17 16:07:28 -0800297 /* Provides an opportunity for the window manager policy to process a key before
298 * ordinary dispatch. */
Jeff Brown905805a2011-10-12 13:57:59 -0700299 public long interceptKeyBeforeDispatching(
Dianne Hackborn6e1eb762011-02-17 16:07:28 -0800300 InputWindowHandle focus, KeyEvent event, int policyFlags) {
301 WindowState windowState = focus != null ? (WindowState) focus.windowState : null;
302 return mService.mPolicy.interceptKeyBeforeDispatching(windowState, event, policyFlags);
303 }
304
305 /* Provides an opportunity for the window manager policy to process a key that
306 * the application did not handle. */
307 public KeyEvent dispatchUnhandledKey(
308 InputWindowHandle focus, KeyEvent event, int policyFlags) {
309 WindowState windowState = focus != null ? (WindowState) focus.windowState : null;
310 return mService.mPolicy.dispatchUnhandledKey(windowState, event, policyFlags);
311 }
Jeff Brown4532e612012-04-05 14:27:12 -0700312
313 /* Callback to get pointer layer. */
314 public int getPointerLayer() {
315 return mService.mPolicy.windowTypeToLayerLw(WindowManager.LayoutParams.TYPE_POINTER)
316 * WindowManagerService.TYPE_LAYER_MULTIPLIER
317 + WindowManagerService.TYPE_LAYER_OFFSET;
318 }
319
Dianne Hackborn6e1eb762011-02-17 16:07:28 -0800320 /* Called when the current input focus changes.
321 * Layer assignment is assumed to be complete by the time this is called.
322 */
323 public void setInputFocusLw(WindowState newWindow, boolean updateInputWindows) {
324 if (WindowManagerService.DEBUG_INPUT) {
325 Slog.d(WindowManagerService.TAG, "Input focus has changed to " + newWindow);
326 }
327
328 if (newWindow != mInputFocus) {
329 if (newWindow != null && newWindow.canReceiveKeys()) {
330 // Displaying a window implicitly causes dispatching to be unpaused.
331 // This is to protect against bugs if someone pauses dispatching but
332 // forgets to resume.
333 newWindow.mToken.paused = false;
334 }
335
336 mInputFocus = newWindow;
337 setUpdateInputWindowsNeededLw();
338
339 if (updateInputWindows) {
340 updateInputWindowsLw(false /*force*/);
341 }
342 }
343 }
344
345 public void setFocusedAppLw(AppWindowToken newApp) {
346 // Focused app has changed.
347 if (newApp == null) {
348 mService.mInputManager.setFocusedApplication(null);
349 } else {
Jeff Brown9302c872011-07-13 22:51:29 -0700350 final InputApplicationHandle handle = newApp.mInputApplicationHandle;
351 handle.name = newApp.toString();
352 handle.dispatchingTimeoutNanos = newApp.inputDispatchingTimeoutNanos;
Dianne Hackborn6e1eb762011-02-17 16:07:28 -0800353
Jeff Brown9302c872011-07-13 22:51:29 -0700354 mService.mInputManager.setFocusedApplication(handle);
Dianne Hackborn6e1eb762011-02-17 16:07:28 -0800355 }
356 }
357
358 public void pauseDispatchingLw(WindowToken window) {
359 if (! window.paused) {
360 if (WindowManagerService.DEBUG_INPUT) {
361 Slog.v(WindowManagerService.TAG, "Pausing WindowToken " + window);
362 }
363
364 window.paused = true;
365 updateInputWindowsLw(true /*force*/);
366 }
367 }
368
369 public void resumeDispatchingLw(WindowToken window) {
370 if (window.paused) {
371 if (WindowManagerService.DEBUG_INPUT) {
372 Slog.v(WindowManagerService.TAG, "Resuming WindowToken " + window);
373 }
374
375 window.paused = false;
376 updateInputWindowsLw(true /*force*/);
377 }
378 }
379
380 public void freezeInputDispatchingLw() {
381 if (! mInputDispatchFrozen) {
382 if (WindowManagerService.DEBUG_INPUT) {
383 Slog.v(WindowManagerService.TAG, "Freezing input dispatching");
384 }
385
386 mInputDispatchFrozen = true;
387 updateInputDispatchModeLw();
388 }
389 }
390
391 public void thawInputDispatchingLw() {
392 if (mInputDispatchFrozen) {
393 if (WindowManagerService.DEBUG_INPUT) {
394 Slog.v(WindowManagerService.TAG, "Thawing input dispatching");
395 }
396
397 mInputDispatchFrozen = false;
398 updateInputDispatchModeLw();
399 }
400 }
401
402 public void setEventDispatchingLw(boolean enabled) {
403 if (mInputDispatchEnabled != enabled) {
404 if (WindowManagerService.DEBUG_INPUT) {
405 Slog.v(WindowManagerService.TAG, "Setting event dispatching to " + enabled);
406 }
407
408 mInputDispatchEnabled = enabled;
409 updateInputDispatchModeLw();
410 }
411 }
412
413 private void updateInputDispatchModeLw() {
414 mService.mInputManager.setInputDispatchMode(mInputDispatchEnabled, mInputDispatchFrozen);
415 }
Jeff Brownea426552011-07-18 16:53:48 -0700416}