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