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