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