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