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 android.app.ActivityManagerNative;
20 import android.graphics.Rect;
21 import android.os.RemoteException;
22 import android.util.Log;
23 import android.util.Slog;
24 import android.view.Display;
25 import android.view.InputChannel;
26 import android.view.KeyEvent;
27 import android.view.WindowManager;
28 
29 import com.android.server.input.InputApplicationHandle;
30 import com.android.server.input.InputManagerService;
31 import com.android.server.input.InputWindowHandle;
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);
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 * 1000000L; // nanoseconds
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         boolean disableWallpaperTouchEvents = 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         boolean addInputConsumerHandle = mService.mInputConsumer != null;
260 
261         // Add all windows on the default display.
262         final int numDisplays = mService.mDisplayContents.size();
263         for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
264             WindowList windows = mService.mDisplayContents.valueAt(displayNdx).getWindowList();
265             for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) {
266                 final WindowState child = windows.get(winNdx);
267                 final InputChannel inputChannel = child.mInputChannel;
268                 final InputWindowHandle inputWindowHandle = child.mInputWindowHandle;
269                 if (inputChannel == null || inputWindowHandle == null || child.mRemoved) {
270                     // Skip this window because it cannot possibly receive input.
271                     continue;
272                 }
273                 if (addInputConsumerHandle
274                         && inputWindowHandle.layer <= mService.mInputConsumer.mWindowHandle.layer) {
275                     addInputWindowHandleLw(mService.mInputConsumer.mWindowHandle);
276                     addInputConsumerHandle = false;
277                 }
278 
279                 final int flags = child.mAttrs.flags;
280                 final int privateFlags = child.mAttrs.privateFlags;
281                 final int type = child.mAttrs.type;
282 
283                 final boolean hasFocus = (child == mInputFocus);
284                 final boolean isVisible = child.isVisibleLw();
285                 if ((privateFlags
286                         & WindowManager.LayoutParams.PRIVATE_FLAG_DISABLE_WALLPAPER_TOUCH_EVENTS)
287                             != 0) {
288                     disableWallpaperTouchEvents = true;
289                 }
290                 final boolean hasWallpaper = (child == mService.mWallpaperTarget)
291                         && (privateFlags & WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD) == 0
292                         && !disableWallpaperTouchEvents;
293                 final boolean onDefaultDisplay = (child.getDisplayId() == Display.DEFAULT_DISPLAY);
294 
295                 // If there's a drag in progress and 'child' is a potential drop target,
296                 // make sure it's been told about the drag
297                 if (inDrag && isVisible && onDefaultDisplay) {
298                     mService.mDragState.sendDragStartedIfNeededLw(child);
299                 }
300 
301                 addInputWindowHandleLw(inputWindowHandle, child, flags, type, isVisible, hasFocus,
302                         hasWallpaper);
303             }
304         }
305 
306         // Send windows to native code.
307         mService.mInputManager.setInputWindows(mInputWindowHandles);
308 
309         // Clear the list in preparation for the next round.
310         clearInputWindowHandlesLw();
311 
312         if (false) Slog.d(WindowManagerService.TAG, "<<<<<<< EXITED updateInputWindowsLw");
313     }
314 
315     /* Notifies that the input device configuration has changed. */
316     @Override
notifyConfigurationChanged()317     public void notifyConfigurationChanged() {
318         mService.sendNewConfiguration();
319 
320         synchronized (mInputDevicesReadyMonitor) {
321             if (!mInputDevicesReady) {
322                 mInputDevicesReady = true;
323                 mInputDevicesReadyMonitor.notifyAll();
324             }
325         }
326     }
327 
328     /* Waits until the built-in input devices have been configured. */
waitForInputDevicesReady(long timeoutMillis)329     public boolean waitForInputDevicesReady(long timeoutMillis) {
330         synchronized (mInputDevicesReadyMonitor) {
331             if (!mInputDevicesReady) {
332                 try {
333                     mInputDevicesReadyMonitor.wait(timeoutMillis);
334                 } catch (InterruptedException ex) {
335                 }
336             }
337             return mInputDevicesReady;
338         }
339     }
340 
341     /* Notifies that the lid switch changed state. */
342     @Override
notifyLidSwitchChanged(long whenNanos, boolean lidOpen)343     public void notifyLidSwitchChanged(long whenNanos, boolean lidOpen) {
344         mService.mPolicy.notifyLidSwitchChanged(whenNanos, lidOpen);
345     }
346 
347     /* Notifies that the camera lens cover state has changed. */
348     @Override
notifyCameraLensCoverSwitchChanged(long whenNanos, boolean lensCovered)349     public void notifyCameraLensCoverSwitchChanged(long whenNanos, boolean lensCovered) {
350         mService.mPolicy.notifyCameraLensCoverSwitchChanged(whenNanos, lensCovered);
351     }
352 
353     /* Provides an opportunity for the window manager policy to intercept early key
354      * processing as soon as the key has been read from the device. */
355     @Override
interceptKeyBeforeQueueing(KeyEvent event, int policyFlags)356     public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {
357         return mService.mPolicy.interceptKeyBeforeQueueing(event, policyFlags);
358     }
359 
360     /* Provides an opportunity for the window manager policy to intercept early motion event
361      * processing when the device is in a non-interactive state since these events are normally
362      * dropped. */
363     @Override
interceptMotionBeforeQueueingNonInteractive(long whenNanos, int policyFlags)364     public int interceptMotionBeforeQueueingNonInteractive(long whenNanos, int policyFlags) {
365         return mService.mPolicy.interceptMotionBeforeQueueingNonInteractive(
366                 whenNanos, policyFlags);
367     }
368 
369     /* Provides an opportunity for the window manager policy to process a key before
370      * ordinary dispatch. */
371     @Override
interceptKeyBeforeDispatching( InputWindowHandle focus, KeyEvent event, int policyFlags)372     public long interceptKeyBeforeDispatching(
373             InputWindowHandle focus, KeyEvent event, int policyFlags) {
374         WindowState windowState = focus != null ? (WindowState) focus.windowState : null;
375         return mService.mPolicy.interceptKeyBeforeDispatching(windowState, event, policyFlags);
376     }
377 
378     /* Provides an opportunity for the window manager policy to process a key that
379      * the application did not handle. */
380     @Override
dispatchUnhandledKey( InputWindowHandle focus, KeyEvent event, int policyFlags)381     public KeyEvent dispatchUnhandledKey(
382             InputWindowHandle focus, KeyEvent event, int policyFlags) {
383         WindowState windowState = focus != null ? (WindowState) focus.windowState : null;
384         return mService.mPolicy.dispatchUnhandledKey(windowState, event, policyFlags);
385     }
386 
387     /* Callback to get pointer layer. */
388     @Override
getPointerLayer()389     public int getPointerLayer() {
390         return mService.mPolicy.windowTypeToLayerLw(WindowManager.LayoutParams.TYPE_POINTER)
391                 * WindowManagerService.TYPE_LAYER_MULTIPLIER
392                 + WindowManagerService.TYPE_LAYER_OFFSET;
393     }
394 
395     /* Called when the current input focus changes.
396      * Layer assignment is assumed to be complete by the time this is called.
397      */
setInputFocusLw(WindowState newWindow, boolean updateInputWindows)398     public void setInputFocusLw(WindowState newWindow, boolean updateInputWindows) {
399         if (WindowManagerService.DEBUG_FOCUS_LIGHT || WindowManagerService.DEBUG_INPUT) {
400             Slog.d(WindowManagerService.TAG, "Input focus has changed to " + newWindow);
401         }
402 
403         if (newWindow != mInputFocus) {
404             if (newWindow != null && newWindow.canReceiveKeys()) {
405                 // Displaying a window implicitly causes dispatching to be unpaused.
406                 // This is to protect against bugs if someone pauses dispatching but
407                 // forgets to resume.
408                 newWindow.mToken.paused = false;
409             }
410 
411             mInputFocus = newWindow;
412             setUpdateInputWindowsNeededLw();
413 
414             if (updateInputWindows) {
415                 updateInputWindowsLw(false /*force*/);
416             }
417         }
418     }
419 
setFocusedAppLw(AppWindowToken newApp)420     public void setFocusedAppLw(AppWindowToken newApp) {
421         // Focused app has changed.
422         if (newApp == null) {
423             mService.mInputManager.setFocusedApplication(null);
424         } else {
425             final InputApplicationHandle handle = newApp.mInputApplicationHandle;
426             handle.name = newApp.toString();
427             handle.dispatchingTimeoutNanos = newApp.inputDispatchingTimeoutNanos;
428 
429             mService.mInputManager.setFocusedApplication(handle);
430         }
431     }
432 
pauseDispatchingLw(WindowToken window)433     public void pauseDispatchingLw(WindowToken window) {
434         if (! window.paused) {
435             if (WindowManagerService.DEBUG_INPUT) {
436                 Slog.v(WindowManagerService.TAG, "Pausing WindowToken " + window);
437             }
438 
439             window.paused = true;
440             updateInputWindowsLw(true /*force*/);
441         }
442     }
443 
resumeDispatchingLw(WindowToken window)444     public void resumeDispatchingLw(WindowToken window) {
445         if (window.paused) {
446             if (WindowManagerService.DEBUG_INPUT) {
447                 Slog.v(WindowManagerService.TAG, "Resuming WindowToken " + window);
448             }
449 
450             window.paused = false;
451             updateInputWindowsLw(true /*force*/);
452         }
453     }
454 
freezeInputDispatchingLw()455     public void freezeInputDispatchingLw() {
456         if (! mInputDispatchFrozen) {
457             if (WindowManagerService.DEBUG_INPUT) {
458                 Slog.v(WindowManagerService.TAG, "Freezing input dispatching");
459             }
460 
461             mInputDispatchFrozen = true;
462             updateInputDispatchModeLw();
463         }
464     }
465 
thawInputDispatchingLw()466     public void thawInputDispatchingLw() {
467         if (mInputDispatchFrozen) {
468             if (WindowManagerService.DEBUG_INPUT) {
469                 Slog.v(WindowManagerService.TAG, "Thawing input dispatching");
470             }
471 
472             mInputDispatchFrozen = false;
473             updateInputDispatchModeLw();
474         }
475     }
476 
setEventDispatchingLw(boolean enabled)477     public void setEventDispatchingLw(boolean enabled) {
478         if (mInputDispatchEnabled != enabled) {
479             if (WindowManagerService.DEBUG_INPUT) {
480                 Slog.v(WindowManagerService.TAG, "Setting event dispatching to " + enabled);
481             }
482 
483             mInputDispatchEnabled = enabled;
484             updateInputDispatchModeLw();
485         }
486     }
487 
updateInputDispatchModeLw()488     private void updateInputDispatchModeLw() {
489         mService.mInputManager.setInputDispatchMode(mInputDispatchEnabled, mInputDispatchFrozen);
490     }
491 }
492