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