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