• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
InputMonitor(WindowManagerService service)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
notifyInputChannelBroken(InputWindowHandle inputWindowHandle)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
notifyANR(InputApplicationHandle inputApplicationHandle, InputWindowHandle inputWindowHandle, String reason)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 
addInputWindowHandleLw(final InputWindowHandle windowHandle)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 
addInputWindowHandleLw(final InputWindowHandle inputWindowHandle, final WindowState child, int flags, int privateFlags, final int type, final boolean isVisible, final boolean hasFocus, final boolean hasWallpaper)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 
clearInputWindowHandlesLw()216     private void clearInputWindowHandlesLw() {
217         while (mInputWindowHandleCount != 0) {
218             mInputWindowHandles[--mInputWindowHandleCount] = null;
219         }
220     }
221 
setUpdateInputWindowsNeededLw()222     public void setUpdateInputWindowsNeededLw() {
223         mUpdateInputWindowsNeeded = true;
224     }
225 
226     /* Updates the cached window information provided to the input dispatcher. */
updateInputWindowsLw(boolean force)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
notifyConfigurationChanged()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. */
waitForInputDevicesReady(long timeoutMillis)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
notifyLidSwitchChanged(long whenNanos, boolean lidOpen)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
interceptKeyBeforeQueueing( KeyEvent event, int policyFlags, boolean isScreenOn)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
interceptMotionBeforeQueueingWhenScreenOff(int policyFlags)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
interceptKeyBeforeDispatching( InputWindowHandle focus, KeyEvent event, int policyFlags)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
dispatchUnhandledKey( InputWindowHandle focus, KeyEvent event, int policyFlags)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
getPointerLayer()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      */
setInputFocusLw(WindowState newWindow, boolean updateInputWindows)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 
setFocusedAppLw(AppWindowToken newApp)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 
pauseDispatchingLw(WindowToken window)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 
resumeDispatchingLw(WindowToken window)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 
freezeInputDispatchingLw()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 
thawInputDispatchingLw()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 
setEventDispatchingLw(boolean enabled)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 
updateInputDispatchModeLw()487     private void updateInputDispatchModeLw() {
488         mService.mInputManager.setInputDispatchMode(mInputDispatchEnabled, mInputDispatchFrozen);
489     }
490 }
491