InputMonitor.java revision 3ddc5d60967a5daa2ebf0ede98b71093bb8cc4ae
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.WindowState.BOUNDS_FOR_TOUCH;
21import android.app.ActivityManagerNative;
22import android.graphics.Rect;
23import android.os.RemoteException;
24import android.util.Log;
25import android.util.Slog;
26import android.view.Display;
27import android.view.InputChannel;
28import android.view.KeyEvent;
29import android.view.WindowManager;
30
31import com.android.server.input.InputApplicationHandle;
32import com.android.server.input.InputManagerService;
33import com.android.server.input.InputWindowHandle;
34
35import java.util.Arrays;
36
37final class InputMonitor implements InputManagerService.WindowManagerCallbacks {
38    private final WindowManagerService mService;
39
40    // Current window with input focus for keys and other non-touch events.  May be null.
41    private WindowState mInputFocus;
42
43    // When true, prevents input dispatch from proceeding until set to false again.
44    private boolean mInputDispatchFrozen;
45
46    // When true, input dispatch proceeds normally.  Otherwise all events are dropped.
47    // Initially false, so that input does not get dispatched until boot is finished at
48    // which point the ActivityManager will enable dispatching.
49    private boolean mInputDispatchEnabled;
50
51    // When true, need to call updateInputWindowsLw().
52    private boolean mUpdateInputWindowsNeeded = true;
53
54    // Array of window handles to provide to the input dispatcher.
55    private InputWindowHandle[] mInputWindowHandles;
56    private int mInputWindowHandleCount;
57
58    // Set to true when the first input device configuration change notification
59    // is received to indicate that the input devices are ready.
60    private final Object mInputDevicesReadyMonitor = new Object();
61    private boolean mInputDevicesReady;
62
63    Rect mTmpRect = new Rect();
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        final boolean modal = (flags & (WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
178                | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE)) == 0;
179        if (modal && child.mAppToken != null) {
180            // Limit the outer touch to the activity stack region.
181            flags |= WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
182            child.getVisibleBounds(mTmpRect, BOUNDS_FOR_TOUCH);
183            inputWindowHandle.touchableRegion.set(mTmpRect);
184        } else {
185            // Not modal or full screen modal
186            child.getTouchableRegion(inputWindowHandle.touchableRegion);
187        }
188        inputWindowHandle.layoutParamsFlags = flags;
189        inputWindowHandle.layoutParamsType = type;
190        inputWindowHandle.dispatchingTimeoutNanos = child.getInputDispatchingTimeoutNanos();
191        inputWindowHandle.visible = isVisible;
192        inputWindowHandle.canReceiveKeys = child.canReceiveKeys();
193        inputWindowHandle.hasFocus = hasFocus;
194        inputWindowHandle.hasWallpaper = hasWallpaper;
195        inputWindowHandle.paused = child.mAppToken != null ? child.mAppToken.paused : false;
196        inputWindowHandle.layer = child.mLayer;
197        inputWindowHandle.ownerPid = child.mSession.mPid;
198        inputWindowHandle.ownerUid = child.mSession.mUid;
199        inputWindowHandle.inputFeatures = child.mAttrs.inputFeatures;
200
201        final Rect frame = child.mFrame;
202        inputWindowHandle.frameLeft = frame.left;
203        inputWindowHandle.frameTop = frame.top;
204        inputWindowHandle.frameRight = frame.right;
205        inputWindowHandle.frameBottom = frame.bottom;
206        if (child.mAttrs.type == TYPE_DOCK_DIVIDER) {
207            // We need to determine if the divider is horizontal or vertical and adjust its handle
208            // frame accordingly.
209            int adjustment = displayContent.mDividerControllerLocked.getWidthAdjustment();
210            if (inputWindowHandle.frameRight - inputWindowHandle.frameLeft >
211                    inputWindowHandle.frameTop - inputWindowHandle.frameBottom) {
212                // Horizontal divider.
213                inputWindowHandle.frameTop -= adjustment;
214                inputWindowHandle.frameBottom += adjustment;
215            } else {
216                inputWindowHandle.frameLeft -= adjustment;
217                inputWindowHandle.frameRight += adjustment;
218            }
219        }
220
221        if (child.mGlobalScale != 1) {
222            // If we are scaling the window, input coordinates need
223            // to be inversely scaled to map from what is on screen
224            // to what is actually being touched in the UI.
225            inputWindowHandle.scaleFactor = 1.0f/child.mGlobalScale;
226        } else {
227            inputWindowHandle.scaleFactor = 1;
228        }
229
230
231        addInputWindowHandleLw(inputWindowHandle);
232    }
233
234    private void clearInputWindowHandlesLw() {
235        while (mInputWindowHandleCount != 0) {
236            mInputWindowHandles[--mInputWindowHandleCount] = null;
237        }
238    }
239
240    public void setUpdateInputWindowsNeededLw() {
241        mUpdateInputWindowsNeeded = true;
242    }
243
244    /* Updates the cached window information provided to the input dispatcher. */
245    public void updateInputWindowsLw(boolean force) {
246        if (!force && !mUpdateInputWindowsNeeded) {
247            return;
248        }
249        mUpdateInputWindowsNeeded = false;
250
251        if (false) Slog.d(WindowManagerService.TAG, ">>>>>> ENTERED updateInputWindowsLw");
252
253        // Populate the input window list with information about all of the windows that
254        // could potentially receive input.
255        // As an optimization, we could try to prune the list of windows but this turns
256        // out to be difficult because only the native code knows for sure which window
257        // currently has touch focus.
258        boolean disableWallpaperTouchEvents = false;
259
260        // If there's a drag in flight, provide a pseudowindow to catch drag input
261        final boolean inDrag = (mService.mDragState != null);
262        if (inDrag) {
263            if (WindowManagerService.DEBUG_DRAG) {
264                Log.d(WindowManagerService.TAG, "Inserting drag window");
265            }
266            final InputWindowHandle dragWindowHandle = mService.mDragState.mDragWindowHandle;
267            if (dragWindowHandle != null) {
268                addInputWindowHandleLw(dragWindowHandle);
269            } else {
270                Slog.w(WindowManagerService.TAG, "Drag is in progress but there is no "
271                        + "drag window handle.");
272            }
273        }
274
275        final boolean inPositioning = (mService.mTaskPositioner != null);
276        if (inPositioning) {
277            if (WindowManagerService.DEBUG_TASK_POSITIONING) {
278                Log.d(WindowManagerService.TAG, "Inserting window handle for repositioning");
279            }
280            final InputWindowHandle dragWindowHandle = mService.mTaskPositioner.mDragWindowHandle;
281            if (dragWindowHandle != null) {
282                addInputWindowHandleLw(dragWindowHandle);
283            } else {
284                Slog.e(WindowManagerService.TAG,
285                        "Repositioning is in progress but there is no drag window handle.");
286            }
287        }
288
289        boolean addInputConsumerHandle = mService.mInputConsumer != null;
290
291        // Add all windows on the default display.
292        final int numDisplays = mService.mDisplayContents.size();
293        final WallpaperController wallpaperController = mService.mWallpaperControllerLocked;
294        for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
295            final DisplayContent displayContent = mService.mDisplayContents.valueAt(displayNdx);
296            final WindowList windows = displayContent.getWindowList();
297            for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) {
298                final WindowState child = windows.get(winNdx);
299                final InputChannel inputChannel = child.mInputChannel;
300                final InputWindowHandle inputWindowHandle = child.mInputWindowHandle;
301                if (inputChannel == null || inputWindowHandle == null || child.mRemoved) {
302                    // Skip this window because it cannot possibly receive input.
303                    continue;
304                }
305                if (addInputConsumerHandle
306                        && inputWindowHandle.layer <= mService.mInputConsumer.mWindowHandle.layer) {
307                    addInputWindowHandleLw(mService.mInputConsumer.mWindowHandle);
308                    addInputConsumerHandle = false;
309                }
310
311                final int flags = child.mAttrs.flags;
312                final int privateFlags = child.mAttrs.privateFlags;
313                final int type = child.mAttrs.type;
314
315                final boolean hasFocus = (child == mInputFocus);
316                final boolean isVisible = child.isVisibleLw();
317                if ((privateFlags
318                        & WindowManager.LayoutParams.PRIVATE_FLAG_DISABLE_WALLPAPER_TOUCH_EVENTS)
319                            != 0) {
320                    disableWallpaperTouchEvents = true;
321                }
322                final boolean hasWallpaper = wallpaperController.isWallpaperTarget(child)
323                        && (privateFlags & WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD) == 0
324                        && !disableWallpaperTouchEvents;
325                final boolean onDefaultDisplay = (child.getDisplayId() == Display.DEFAULT_DISPLAY);
326
327                // If there's a drag in progress and 'child' is a potential drop target,
328                // make sure it's been told about the drag
329                if (inDrag && isVisible && onDefaultDisplay) {
330                    mService.mDragState.sendDragStartedIfNeededLw(child);
331                }
332
333                addInputWindowHandleLw(inputWindowHandle, child, flags, type, isVisible, hasFocus,
334                        hasWallpaper, displayContent);
335            }
336        }
337
338        // Send windows to native code.
339        mService.mInputManager.setInputWindows(mInputWindowHandles);
340
341        // Clear the list in preparation for the next round.
342        clearInputWindowHandlesLw();
343
344        if (false) Slog.d(WindowManagerService.TAG, "<<<<<<< EXITED updateInputWindowsLw");
345    }
346
347    /* Notifies that the input device configuration has changed. */
348    @Override
349    public void notifyConfigurationChanged() {
350        mService.sendNewConfiguration();
351
352        synchronized (mInputDevicesReadyMonitor) {
353            if (!mInputDevicesReady) {
354                mInputDevicesReady = true;
355                mInputDevicesReadyMonitor.notifyAll();
356            }
357        }
358    }
359
360    /* Waits until the built-in input devices have been configured. */
361    public boolean waitForInputDevicesReady(long timeoutMillis) {
362        synchronized (mInputDevicesReadyMonitor) {
363            if (!mInputDevicesReady) {
364                try {
365                    mInputDevicesReadyMonitor.wait(timeoutMillis);
366                } catch (InterruptedException ex) {
367                }
368            }
369            return mInputDevicesReady;
370        }
371    }
372
373    /* Notifies that the lid switch changed state. */
374    @Override
375    public void notifyLidSwitchChanged(long whenNanos, boolean lidOpen) {
376        mService.mPolicy.notifyLidSwitchChanged(whenNanos, lidOpen);
377    }
378
379    /* Notifies that the camera lens cover state has changed. */
380    @Override
381    public void notifyCameraLensCoverSwitchChanged(long whenNanos, boolean lensCovered) {
382        mService.mPolicy.notifyCameraLensCoverSwitchChanged(whenNanos, lensCovered);
383    }
384
385    /* Provides an opportunity for the window manager policy to intercept early key
386     * processing as soon as the key has been read from the device. */
387    @Override
388    public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {
389        return mService.mPolicy.interceptKeyBeforeQueueing(event, policyFlags);
390    }
391
392    /* Provides an opportunity for the window manager policy to intercept early motion event
393     * processing when the device is in a non-interactive state since these events are normally
394     * dropped. */
395    @Override
396    public int interceptMotionBeforeQueueingNonInteractive(long whenNanos, int policyFlags) {
397        return mService.mPolicy.interceptMotionBeforeQueueingNonInteractive(
398                whenNanos, policyFlags);
399    }
400
401    /* Provides an opportunity for the window manager policy to process a key before
402     * ordinary dispatch. */
403    @Override
404    public long interceptKeyBeforeDispatching(
405            InputWindowHandle focus, KeyEvent event, int policyFlags) {
406        WindowState windowState = focus != null ? (WindowState) focus.windowState : null;
407        return mService.mPolicy.interceptKeyBeforeDispatching(windowState, event, policyFlags);
408    }
409
410    /* Provides an opportunity for the window manager policy to process a key that
411     * the application did not handle. */
412    @Override
413    public KeyEvent dispatchUnhandledKey(
414            InputWindowHandle focus, KeyEvent event, int policyFlags) {
415        WindowState windowState = focus != null ? (WindowState) focus.windowState : null;
416        return mService.mPolicy.dispatchUnhandledKey(windowState, event, policyFlags);
417    }
418
419    /* Callback to get pointer layer. */
420    @Override
421    public int getPointerLayer() {
422        return mService.mPolicy.windowTypeToLayerLw(WindowManager.LayoutParams.TYPE_POINTER)
423                * WindowManagerService.TYPE_LAYER_MULTIPLIER
424                + WindowManagerService.TYPE_LAYER_OFFSET;
425    }
426
427    /* Called when the current input focus changes.
428     * Layer assignment is assumed to be complete by the time this is called.
429     */
430    public void setInputFocusLw(WindowState newWindow, boolean updateInputWindows) {
431        if (WindowManagerService.DEBUG_FOCUS_LIGHT || WindowManagerService.DEBUG_INPUT) {
432            Slog.d(WindowManagerService.TAG, "Input focus has changed to " + newWindow);
433        }
434
435        if (newWindow != mInputFocus) {
436            if (newWindow != null && newWindow.canReceiveKeys()) {
437                // Displaying a window implicitly causes dispatching to be unpaused.
438                // This is to protect against bugs if someone pauses dispatching but
439                // forgets to resume.
440                newWindow.mToken.paused = false;
441            }
442
443            mInputFocus = newWindow;
444            setUpdateInputWindowsNeededLw();
445
446            if (updateInputWindows) {
447                updateInputWindowsLw(false /*force*/);
448            }
449        }
450    }
451
452    public void setFocusedAppLw(AppWindowToken newApp) {
453        // Focused app has changed.
454        if (newApp == null) {
455            mService.mInputManager.setFocusedApplication(null);
456        } else {
457            final InputApplicationHandle handle = newApp.mInputApplicationHandle;
458            handle.name = newApp.toString();
459            handle.dispatchingTimeoutNanos = newApp.inputDispatchingTimeoutNanos;
460
461            mService.mInputManager.setFocusedApplication(handle);
462        }
463    }
464
465    public void pauseDispatchingLw(WindowToken window) {
466        if (! window.paused) {
467            if (WindowManagerService.DEBUG_INPUT) {
468                Slog.v(WindowManagerService.TAG, "Pausing WindowToken " + window);
469            }
470
471            window.paused = true;
472            updateInputWindowsLw(true /*force*/);
473        }
474    }
475
476    public void resumeDispatchingLw(WindowToken window) {
477        if (window.paused) {
478            if (WindowManagerService.DEBUG_INPUT) {
479                Slog.v(WindowManagerService.TAG, "Resuming WindowToken " + window);
480            }
481
482            window.paused = false;
483            updateInputWindowsLw(true /*force*/);
484        }
485    }
486
487    public void freezeInputDispatchingLw() {
488        if (! mInputDispatchFrozen) {
489            if (WindowManagerService.DEBUG_INPUT) {
490                Slog.v(WindowManagerService.TAG, "Freezing input dispatching");
491            }
492
493            mInputDispatchFrozen = true;
494            updateInputDispatchModeLw();
495        }
496    }
497
498    public void thawInputDispatchingLw() {
499        if (mInputDispatchFrozen) {
500            if (WindowManagerService.DEBUG_INPUT) {
501                Slog.v(WindowManagerService.TAG, "Thawing input dispatching");
502            }
503
504            mInputDispatchFrozen = false;
505            updateInputDispatchModeLw();
506        }
507    }
508
509    public void setEventDispatchingLw(boolean enabled) {
510        if (mInputDispatchEnabled != enabled) {
511            if (WindowManagerService.DEBUG_INPUT) {
512                Slog.v(WindowManagerService.TAG, "Setting event dispatching to " + enabled);
513            }
514
515            mInputDispatchEnabled = enabled;
516            updateInputDispatchModeLw();
517        }
518    }
519
520    private void updateInputDispatchModeLw() {
521        mService.mInputManager.setInputDispatchMode(mInputDispatchEnabled, mInputDispatchFrozen);
522    }
523}
524