OSDN Git Service

Merge "No input for windows in minimized docked stack" into nyc-dev
[android-x86/frameworks-base.git] / services / core / java / com / android / server / wm / InputMonitor.java
1 /*
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.
15  */
16
17 package com.android.server.wm;
18
19 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_DRAG;
20 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS_LIGHT;
21 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_INPUT;
22 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_POSITIONING;
23 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
24
25 import android.app.ActivityManagerNative;
26 import android.graphics.Rect;
27 import android.os.Debug;
28 import android.os.RemoteException;
29 import android.util.Log;
30 import android.util.Slog;
31 import android.view.Display;
32 import android.view.InputChannel;
33 import android.view.KeyEvent;
34 import android.view.WindowManager;
35
36 import com.android.server.input.InputApplicationHandle;
37 import com.android.server.input.InputManagerService;
38 import com.android.server.input.InputWindowHandle;
39
40 import java.io.PrintWriter;
41 import java.util.Arrays;
42
43 final class InputMonitor implements InputManagerService.WindowManagerCallbacks {
44     private final WindowManagerService mService;
45
46     // Current window with input focus for keys and other non-touch events.  May be null.
47     private WindowState mInputFocus;
48
49     // When true, prevents input dispatch from proceeding until set to false again.
50     private boolean mInputDispatchFrozen;
51
52     // The reason the input is currently frozen or null if the input isn't frozen.
53     private String mInputFreezeReason = null;
54
55     // When true, input dispatch proceeds normally.  Otherwise all events are dropped.
56     // Initially false, so that input does not get dispatched until boot is finished at
57     // which point the ActivityManager will enable dispatching.
58     private boolean mInputDispatchEnabled;
59
60     // When true, need to call updateInputWindowsLw().
61     private boolean mUpdateInputWindowsNeeded = true;
62
63     // Array of window handles to provide to the input dispatcher.
64     private InputWindowHandle[] mInputWindowHandles;
65     private int mInputWindowHandleCount;
66
67     // Set to true when the first input device configuration change notification
68     // is received to indicate that the input devices are ready.
69     private final Object mInputDevicesReadyMonitor = new Object();
70     private boolean mInputDevicesReady;
71
72     public InputMonitor(WindowManagerService service) {
73         mService = service;
74     }
75
76     /* Notifies the window manager about a broken input channel.
77      *
78      * Called by the InputManager.
79      */
80     @Override
81     public void notifyInputChannelBroken(InputWindowHandle inputWindowHandle) {
82         if (inputWindowHandle == null) {
83             return;
84         }
85
86         synchronized (mService.mWindowMap) {
87             WindowState windowState = (WindowState) inputWindowHandle.windowState;
88             if (windowState != null) {
89                 Slog.i(TAG_WM, "WINDOW DIED " + windowState);
90                 mService.removeWindowLocked(windowState);
91             }
92         }
93     }
94
95     /* Notifies the window manager about an application that is not responding.
96      * Returns a new timeout to continue waiting in nanoseconds, or 0 to abort dispatch.
97      *
98      * Called by the InputManager.
99      */
100     @Override
101     public long notifyANR(InputApplicationHandle inputApplicationHandle,
102             InputWindowHandle inputWindowHandle, String reason) {
103         AppWindowToken appWindowToken = null;
104         WindowState windowState = null;
105         boolean aboveSystem = false;
106         synchronized (mService.mWindowMap) {
107             if (inputWindowHandle != null) {
108                 windowState = (WindowState) inputWindowHandle.windowState;
109                 if (windowState != null) {
110                     appWindowToken = windowState.mAppToken;
111                 }
112             }
113             if (appWindowToken == null && inputApplicationHandle != null) {
114                 appWindowToken = (AppWindowToken)inputApplicationHandle.appWindowToken;
115             }
116
117             if (windowState != null) {
118                 Slog.i(TAG_WM, "Input event dispatching timed out "
119                         + "sending to " + windowState.mAttrs.getTitle()
120                         + ".  Reason: " + reason);
121                 // Figure out whether this window is layered above system windows.
122                 // We need to do this here to help the activity manager know how to
123                 // layer its ANR dialog.
124                 int systemAlertLayer = mService.mPolicy.windowTypeToLayerLw(
125                         WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
126                 aboveSystem = windowState.mBaseLayer > systemAlertLayer;
127             } else if (appWindowToken != null) {
128                 Slog.i(TAG_WM, "Input event dispatching timed out "
129                         + "sending to application " + appWindowToken.stringName
130                         + ".  Reason: " + reason);
131             } else {
132                 Slog.i(TAG_WM, "Input event dispatching timed out "
133                         + ".  Reason: " + reason);
134             }
135
136             mService.saveANRStateLocked(appWindowToken, windowState, reason);
137         }
138
139         if (appWindowToken != null && appWindowToken.appToken != null) {
140             try {
141                 // Notify the activity manager about the timeout and let it decide whether
142                 // to abort dispatching or keep waiting.
143                 boolean abort = appWindowToken.appToken.keyDispatchingTimedOut(reason);
144                 if (! abort) {
145                     // The activity manager declined to abort dispatching.
146                     // Wait a bit longer and timeout again later.
147                     return appWindowToken.inputDispatchingTimeoutNanos;
148                 }
149             } catch (RemoteException ex) {
150             }
151         } else if (windowState != null) {
152             try {
153                 // Notify the activity manager about the timeout and let it decide whether
154                 // to abort dispatching or keep waiting.
155                 long timeout = ActivityManagerNative.getDefault().inputDispatchingTimedOut(
156                         windowState.mSession.mPid, aboveSystem, reason);
157                 if (timeout >= 0) {
158                     // The activity manager declined to abort dispatching.
159                     // Wait a bit longer and timeout again later.
160                     return timeout * 1000000L; // nanoseconds
161                 }
162             } catch (RemoteException ex) {
163             }
164         }
165         return 0; // abort dispatching
166     }
167
168     private void addInputWindowHandleLw(final InputWindowHandle windowHandle) {
169         if (mInputWindowHandles == null) {
170             mInputWindowHandles = new InputWindowHandle[16];
171         }
172         if (mInputWindowHandleCount >= mInputWindowHandles.length) {
173             mInputWindowHandles = Arrays.copyOf(mInputWindowHandles,
174                     mInputWindowHandleCount * 2);
175         }
176         mInputWindowHandles[mInputWindowHandleCount++] = windowHandle;
177     }
178
179     private void addInputWindowHandleLw(final InputWindowHandle inputWindowHandle,
180             final WindowState child, int flags, final int type, final boolean isVisible,
181             final boolean hasFocus, final boolean hasWallpaper) {
182         // Add a window to our list of input windows.
183         inputWindowHandle.name = child.toString();
184         flags = child.getTouchableRegion(inputWindowHandle.touchableRegion, flags);
185         inputWindowHandle.layoutParamsFlags = flags;
186         inputWindowHandle.layoutParamsType = type;
187         inputWindowHandle.dispatchingTimeoutNanos = child.getInputDispatchingTimeoutNanos();
188         inputWindowHandle.visible = isVisible;
189         inputWindowHandle.canReceiveKeys = child.canReceiveKeys();
190         inputWindowHandle.hasFocus = hasFocus;
191         inputWindowHandle.hasWallpaper = hasWallpaper;
192         inputWindowHandle.paused = child.mAppToken != null ? child.mAppToken.paused : false;
193         inputWindowHandle.layer = child.mLayer;
194         inputWindowHandle.ownerPid = child.mSession.mPid;
195         inputWindowHandle.ownerUid = child.mSession.mUid;
196         inputWindowHandle.inputFeatures = child.mAttrs.inputFeatures;
197
198         final Rect frame = child.mFrame;
199         inputWindowHandle.frameLeft = frame.left;
200         inputWindowHandle.frameTop = frame.top;
201         inputWindowHandle.frameRight = frame.right;
202         inputWindowHandle.frameBottom = frame.bottom;
203
204         if (child.isDockedInEffect()) {
205             // Adjust to account for non-resizeable tasks that's scrolled
206             inputWindowHandle.frameLeft += child.mXOffset;
207             inputWindowHandle.frameTop += child.mYOffset;
208             inputWindowHandle.frameRight += child.mXOffset;
209             inputWindowHandle.frameBottom += child.mYOffset;
210         }
211
212         if (child.mGlobalScale != 1) {
213             // If we are scaling the window, input coordinates need
214             // to be inversely scaled to map from what is on screen
215             // to what is actually being touched in the UI.
216             inputWindowHandle.scaleFactor = 1.0f/child.mGlobalScale;
217         } else {
218             inputWindowHandle.scaleFactor = 1;
219         }
220
221         if (DEBUG_INPUT) {
222             Slog.d(TAG_WM, "addInputWindowHandle: "
223                     + child + ", " + inputWindowHandle);
224         }
225         addInputWindowHandleLw(inputWindowHandle);
226     }
227
228     private void clearInputWindowHandlesLw() {
229         while (mInputWindowHandleCount != 0) {
230             mInputWindowHandles[--mInputWindowHandleCount] = null;
231         }
232     }
233
234     public void setUpdateInputWindowsNeededLw() {
235         mUpdateInputWindowsNeeded = true;
236     }
237
238     /* Updates the cached window information provided to the input dispatcher. */
239     public void updateInputWindowsLw(boolean force) {
240         if (!force && !mUpdateInputWindowsNeeded) {
241             return;
242         }
243         mUpdateInputWindowsNeeded = false;
244
245         if (false) Slog.d(TAG_WM, ">>>>>> ENTERED updateInputWindowsLw");
246
247         // Populate the input window list with information about all of the windows that
248         // could potentially receive input.
249         // As an optimization, we could try to prune the list of windows but this turns
250         // out to be difficult because only the native code knows for sure which window
251         // currently has touch focus.
252         boolean disableWallpaperTouchEvents = false;
253
254         // If there's a drag in flight, provide a pseudowindow to catch drag input
255         final boolean inDrag = (mService.mDragState != null);
256         if (inDrag) {
257             if (DEBUG_DRAG) {
258                 Log.d(TAG_WM, "Inserting drag window");
259             }
260             final InputWindowHandle dragWindowHandle = mService.mDragState.mDragWindowHandle;
261             if (dragWindowHandle != null) {
262                 addInputWindowHandleLw(dragWindowHandle);
263             } else {
264                 Slog.w(TAG_WM, "Drag is in progress but there is no "
265                         + "drag window handle.");
266             }
267         }
268
269         final boolean inPositioning = (mService.mTaskPositioner != null);
270         if (inPositioning) {
271             if (DEBUG_TASK_POSITIONING) {
272                 Log.d(TAG_WM, "Inserting window handle for repositioning");
273             }
274             final InputWindowHandle dragWindowHandle = mService.mTaskPositioner.mDragWindowHandle;
275             if (dragWindowHandle != null) {
276                 addInputWindowHandleLw(dragWindowHandle);
277             } else {
278                 Slog.e(TAG_WM,
279                         "Repositioning is in progress but there is no drag window handle.");
280             }
281         }
282
283         boolean addInputConsumerHandle = mService.mInputConsumer != null;
284
285         boolean addWallpaperInputConsumerHandle = mService.mWallpaperInputConsumer != null;
286
287         // Add all windows on the default display.
288         final int numDisplays = mService.mDisplayContents.size();
289         final WallpaperController wallpaperController = mService.mWallpaperControllerLocked;
290         for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
291             final DisplayContent displayContent = mService.mDisplayContents.valueAt(displayNdx);
292             final WindowList windows = displayContent.getWindowList();
293             for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) {
294                 final WindowState child = windows.get(winNdx);
295                 final InputChannel inputChannel = child.mInputChannel;
296                 final InputWindowHandle inputWindowHandle = child.mInputWindowHandle;
297                 if (inputChannel == null || inputWindowHandle == null || child.mRemoved
298                         || child.isAdjustedForMinimizedDock()) {
299                     // Skip this window because it cannot possibly receive input.
300                     continue;
301                 }
302                 if (addInputConsumerHandle
303                         && inputWindowHandle.layer <= mService.mInputConsumer.mWindowHandle.layer) {
304                     addInputWindowHandleLw(mService.mInputConsumer.mWindowHandle);
305                     addInputConsumerHandle = false;
306                 }
307
308                 if (addWallpaperInputConsumerHandle) {
309                     if (child.mAttrs.type == WindowManager.LayoutParams.TYPE_WALLPAPER) {
310                         // Add the wallpaper input consumer above the first wallpaper window.
311                         addInputWindowHandleLw(mService.mWallpaperInputConsumer.mWindowHandle);
312                         addWallpaperInputConsumerHandle = false;
313                     }
314                 }
315
316                 final int flags = child.mAttrs.flags;
317                 final int privateFlags = child.mAttrs.privateFlags;
318                 final int type = child.mAttrs.type;
319
320                 final boolean hasFocus = (child == mInputFocus);
321                 final boolean isVisible = child.isVisibleLw();
322                 if ((privateFlags
323                         & WindowManager.LayoutParams.PRIVATE_FLAG_DISABLE_WALLPAPER_TOUCH_EVENTS)
324                             != 0) {
325                     disableWallpaperTouchEvents = true;
326                 }
327                 final boolean hasWallpaper = wallpaperController.isWallpaperTarget(child)
328                         && (privateFlags & WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD) == 0
329                         && !disableWallpaperTouchEvents;
330                 final boolean onDefaultDisplay = (child.getDisplayId() == Display.DEFAULT_DISPLAY);
331
332                 // If there's a drag in progress and 'child' is a potential drop target,
333                 // make sure it's been told about the drag
334                 if (inDrag && isVisible && onDefaultDisplay) {
335                     mService.mDragState.sendDragStartedIfNeededLw(child);
336                 }
337
338                 addInputWindowHandleLw(
339                         inputWindowHandle, child, flags, type, isVisible, hasFocus, hasWallpaper);
340             }
341         }
342
343         if (addWallpaperInputConsumerHandle) {
344             // No wallpaper found, add the wallpaper input consumer at the end.
345             addInputWindowHandleLw(mService.mWallpaperInputConsumer.mWindowHandle);
346         }
347
348         // Send windows to native code.
349         mService.mInputManager.setInputWindows(mInputWindowHandles);
350
351         // Clear the list in preparation for the next round.
352         clearInputWindowHandlesLw();
353
354         if (false) Slog.d(TAG_WM, "<<<<<<< EXITED updateInputWindowsLw");
355     }
356
357     /* Notifies that the input device configuration has changed. */
358     @Override
359     public void notifyConfigurationChanged() {
360         mService.sendNewConfiguration();
361
362         synchronized (mInputDevicesReadyMonitor) {
363             if (!mInputDevicesReady) {
364                 mInputDevicesReady = true;
365                 mInputDevicesReadyMonitor.notifyAll();
366             }
367         }
368     }
369
370     /* Waits until the built-in input devices have been configured. */
371     public boolean waitForInputDevicesReady(long timeoutMillis) {
372         synchronized (mInputDevicesReadyMonitor) {
373             if (!mInputDevicesReady) {
374                 try {
375                     mInputDevicesReadyMonitor.wait(timeoutMillis);
376                 } catch (InterruptedException ex) {
377                 }
378             }
379             return mInputDevicesReady;
380         }
381     }
382
383     /* Notifies that the lid switch changed state. */
384     @Override
385     public void notifyLidSwitchChanged(long whenNanos, boolean lidOpen) {
386         mService.mPolicy.notifyLidSwitchChanged(whenNanos, lidOpen);
387     }
388
389     /* Notifies that the camera lens cover state has changed. */
390     @Override
391     public void notifyCameraLensCoverSwitchChanged(long whenNanos, boolean lensCovered) {
392         mService.mPolicy.notifyCameraLensCoverSwitchChanged(whenNanos, lensCovered);
393     }
394
395     /* Provides an opportunity for the window manager policy to intercept early key
396      * processing as soon as the key has been read from the device. */
397     @Override
398     public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {
399         return mService.mPolicy.interceptKeyBeforeQueueing(event, policyFlags);
400     }
401
402     /* Provides an opportunity for the window manager policy to intercept early motion event
403      * processing when the device is in a non-interactive state since these events are normally
404      * dropped. */
405     @Override
406     public int interceptMotionBeforeQueueingNonInteractive(long whenNanos, int policyFlags) {
407         return mService.mPolicy.interceptMotionBeforeQueueingNonInteractive(
408                 whenNanos, policyFlags);
409     }
410
411     /* Provides an opportunity for the window manager policy to process a key before
412      * ordinary dispatch. */
413     @Override
414     public long interceptKeyBeforeDispatching(
415             InputWindowHandle focus, KeyEvent event, int policyFlags) {
416         WindowState windowState = focus != null ? (WindowState) focus.windowState : null;
417         return mService.mPolicy.interceptKeyBeforeDispatching(windowState, event, policyFlags);
418     }
419
420     /* Provides an opportunity for the window manager policy to process a key that
421      * the application did not handle. */
422     @Override
423     public KeyEvent dispatchUnhandledKey(
424             InputWindowHandle focus, KeyEvent event, int policyFlags) {
425         WindowState windowState = focus != null ? (WindowState) focus.windowState : null;
426         return mService.mPolicy.dispatchUnhandledKey(windowState, event, policyFlags);
427     }
428
429     /* Callback to get pointer layer. */
430     @Override
431     public int getPointerLayer() {
432         return mService.mPolicy.windowTypeToLayerLw(WindowManager.LayoutParams.TYPE_POINTER)
433                 * WindowManagerService.TYPE_LAYER_MULTIPLIER
434                 + WindowManagerService.TYPE_LAYER_OFFSET;
435     }
436
437     /* Called when the current input focus changes.
438      * Layer assignment is assumed to be complete by the time this is called.
439      */
440     public void setInputFocusLw(WindowState newWindow, boolean updateInputWindows) {
441         if (DEBUG_FOCUS_LIGHT || DEBUG_INPUT) {
442             Slog.d(TAG_WM, "Input focus has changed to " + newWindow);
443         }
444
445         if (newWindow != mInputFocus) {
446             if (newWindow != null && newWindow.canReceiveKeys()) {
447                 // Displaying a window implicitly causes dispatching to be unpaused.
448                 // This is to protect against bugs if someone pauses dispatching but
449                 // forgets to resume.
450                 newWindow.mToken.paused = false;
451             }
452
453             mInputFocus = newWindow;
454             setUpdateInputWindowsNeededLw();
455
456             if (updateInputWindows) {
457                 updateInputWindowsLw(false /*force*/);
458             }
459         }
460     }
461
462     public void setFocusedAppLw(AppWindowToken newApp) {
463         // Focused app has changed.
464         if (newApp == null) {
465             mService.mInputManager.setFocusedApplication(null);
466         } else {
467             final InputApplicationHandle handle = newApp.mInputApplicationHandle;
468             handle.name = newApp.toString();
469             handle.dispatchingTimeoutNanos = newApp.inputDispatchingTimeoutNanos;
470
471             mService.mInputManager.setFocusedApplication(handle);
472         }
473     }
474
475     public void pauseDispatchingLw(WindowToken window) {
476         if (! window.paused) {
477             if (DEBUG_INPUT) {
478                 Slog.v(TAG_WM, "Pausing WindowToken " + window);
479             }
480
481             window.paused = true;
482             updateInputWindowsLw(true /*force*/);
483         }
484     }
485
486     public void resumeDispatchingLw(WindowToken window) {
487         if (window.paused) {
488             if (DEBUG_INPUT) {
489                 Slog.v(TAG_WM, "Resuming WindowToken " + window);
490             }
491
492             window.paused = false;
493             updateInputWindowsLw(true /*force*/);
494         }
495     }
496
497     public void freezeInputDispatchingLw() {
498         if (!mInputDispatchFrozen) {
499             if (DEBUG_INPUT) {
500                 Slog.v(TAG_WM, "Freezing input dispatching");
501             }
502
503             mInputDispatchFrozen = true;
504
505             if (DEBUG_INPUT || true) {
506                 mInputFreezeReason = Debug.getCallers(6);
507             }
508             updateInputDispatchModeLw();
509         }
510     }
511
512     public void thawInputDispatchingLw() {
513         if (mInputDispatchFrozen) {
514             if (DEBUG_INPUT) {
515                 Slog.v(TAG_WM, "Thawing input dispatching");
516             }
517
518             mInputDispatchFrozen = false;
519             mInputFreezeReason = null;
520             updateInputDispatchModeLw();
521         }
522     }
523
524     public void setEventDispatchingLw(boolean enabled) {
525         if (mInputDispatchEnabled != enabled) {
526             if (DEBUG_INPUT) {
527                 Slog.v(TAG_WM, "Setting event dispatching to " + enabled);
528             }
529
530             mInputDispatchEnabled = enabled;
531             updateInputDispatchModeLw();
532         }
533     }
534
535     private void updateInputDispatchModeLw() {
536         mService.mInputManager.setInputDispatchMode(mInputDispatchEnabled, mInputDispatchFrozen);
537     }
538
539     void dump(PrintWriter pw, String prefix) {
540         if (mInputFreezeReason != null) {
541             pw.println(prefix + "mInputFreezeReason=" + mInputFreezeReason);
542         }
543     }
544 }