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