• 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 android.graphics.Rect;
20 import android.os.RemoteException;
21 import android.util.Log;
22 import android.util.Slog;
23 import android.view.InputChannel;
24 import android.view.KeyEvent;
25 import android.view.WindowManager;
26 
27 import java.util.ArrayList;
28 import java.util.Arrays;
29 
30 final class InputMonitor {
31     private final WindowManagerService mService;
32 
33     // Current window with input focus for keys and other non-touch events.  May be null.
34     private WindowState mInputFocus;
35 
36     // When true, prevents input dispatch from proceeding until set to false again.
37     private boolean mInputDispatchFrozen;
38 
39     // When true, input dispatch proceeds normally.  Otherwise all events are dropped.
40     private boolean mInputDispatchEnabled = true;
41 
42     // When true, need to call updateInputWindowsLw().
43     private boolean mUpdateInputWindowsNeeded = true;
44 
45     // Array of window handles to provide to the input dispatcher.
46     private InputWindowHandle[] mInputWindowHandles;
47     private int mInputWindowHandleCount;
48 
49     // Set to true when the first input device configuration change notification
50     // is received to indicate that the input devices are ready.
51     private final Object mInputDevicesReadyMonitor = new Object();
52     private boolean mInputDevicesReady;
53 
InputMonitor(WindowManagerService service)54     public InputMonitor(WindowManagerService service) {
55         mService = service;
56     }
57 
58     /* Notifies the window manager about a broken input channel.
59      *
60      * Called by the InputManager.
61      */
notifyInputChannelBroken(InputWindowHandle inputWindowHandle)62     public void notifyInputChannelBroken(InputWindowHandle inputWindowHandle) {
63         if (inputWindowHandle == null) {
64             return;
65         }
66 
67         synchronized (mService.mWindowMap) {
68             WindowState windowState = (WindowState) inputWindowHandle.windowState;
69             if (windowState != null) {
70                 Slog.i(WindowManagerService.TAG, "WINDOW DIED " + windowState);
71                 mService.removeWindowLocked(windowState.mSession, windowState);
72             }
73         }
74     }
75 
76     /* Notifies the window manager about an application that is not responding.
77      * Returns a new timeout to continue waiting in nanoseconds, or 0 to abort dispatch.
78      *
79      * Called by the InputManager.
80      */
notifyANR(InputApplicationHandle inputApplicationHandle, InputWindowHandle inputWindowHandle)81     public long notifyANR(InputApplicationHandle inputApplicationHandle,
82             InputWindowHandle inputWindowHandle) {
83         AppWindowToken appWindowToken = null;
84         if (inputWindowHandle != null) {
85             synchronized (mService.mWindowMap) {
86                 WindowState windowState = (WindowState) inputWindowHandle.windowState;
87                 if (windowState != null) {
88                     Slog.i(WindowManagerService.TAG, "Input event dispatching timed out sending to "
89                             + windowState.mAttrs.getTitle());
90                     appWindowToken = windowState.mAppToken;
91                 }
92             }
93         }
94 
95         if (appWindowToken == null && inputApplicationHandle != null) {
96             appWindowToken = inputApplicationHandle.appWindowToken;
97             if (appWindowToken != null) {
98                 Slog.i(WindowManagerService.TAG,
99                         "Input event dispatching timed out sending to application "
100                                 + appWindowToken.stringName);
101             }
102         }
103 
104         if (appWindowToken != null && appWindowToken.appToken != null) {
105             try {
106                 // Notify the activity manager about the timeout and let it decide whether
107                 // to abort dispatching or keep waiting.
108                 boolean abort = appWindowToken.appToken.keyDispatchingTimedOut();
109                 if (! abort) {
110                     // The activity manager declined to abort dispatching.
111                     // Wait a bit longer and timeout again later.
112                     return appWindowToken.inputDispatchingTimeoutNanos;
113                 }
114             } catch (RemoteException ex) {
115             }
116         }
117         return 0; // abort dispatching
118     }
119 
addInputWindowHandleLw(InputWindowHandle windowHandle)120     private void addInputWindowHandleLw(InputWindowHandle windowHandle) {
121         if (mInputWindowHandles == null) {
122             mInputWindowHandles = new InputWindowHandle[16];
123         }
124         if (mInputWindowHandleCount >= mInputWindowHandles.length) {
125             mInputWindowHandles = Arrays.copyOf(mInputWindowHandles,
126                     mInputWindowHandleCount * 2);
127         }
128         mInputWindowHandles[mInputWindowHandleCount++] = windowHandle;
129     }
130 
clearInputWindowHandlesLw()131     private void clearInputWindowHandlesLw() {
132         while (mInputWindowHandleCount != 0) {
133             mInputWindowHandles[--mInputWindowHandleCount] = null;
134         }
135     }
136 
setUpdateInputWindowsNeededLw()137     public void setUpdateInputWindowsNeededLw() {
138         mUpdateInputWindowsNeeded = true;
139     }
140 
141     /* Updates the cached window information provided to the input dispatcher. */
updateInputWindowsLw(boolean force)142     public void updateInputWindowsLw(boolean force) {
143         if (!force && !mUpdateInputWindowsNeeded) {
144             return;
145         }
146         mUpdateInputWindowsNeeded = false;
147 
148         if (false) Slog.d(WindowManagerService.TAG, ">>>>>> ENTERED updateInputWindowsLw");
149 
150         // Populate the input window list with information about all of the windows that
151         // could potentially receive input.
152         // As an optimization, we could try to prune the list of windows but this turns
153         // out to be difficult because only the native code knows for sure which window
154         // currently has touch focus.
155         final ArrayList<WindowState> windows = mService.mWindows;
156 
157         // If there's a drag in flight, provide a pseudowindow to catch drag input
158         final boolean inDrag = (mService.mDragState != null);
159         if (inDrag) {
160             if (WindowManagerService.DEBUG_DRAG) {
161                 Log.d(WindowManagerService.TAG, "Inserting drag window");
162             }
163             final InputWindowHandle dragWindowHandle = mService.mDragState.mDragWindowHandle;
164             if (dragWindowHandle != null) {
165                 addInputWindowHandleLw(dragWindowHandle);
166             } else {
167                 Slog.w(WindowManagerService.TAG, "Drag is in progress but there is no "
168                         + "drag window handle.");
169             }
170         }
171 
172         final int NFW = mService.mFakeWindows.size();
173         for (int i = 0; i < NFW; i++) {
174             addInputWindowHandleLw(mService.mFakeWindows.get(i).mWindowHandle);
175         }
176 
177         final int N = windows.size();
178         for (int i = N - 1; i >= 0; i--) {
179             final WindowState child = windows.get(i);
180             final InputChannel inputChannel = child.mInputChannel;
181             final InputWindowHandle inputWindowHandle = child.mInputWindowHandle;
182             if (inputChannel == null || inputWindowHandle == null || child.mRemoved) {
183                 // Skip this window because it cannot possibly receive input.
184                 continue;
185             }
186 
187             final int flags = child.mAttrs.flags;
188             final int type = child.mAttrs.type;
189 
190             final boolean hasFocus = (child == mInputFocus);
191             final boolean isVisible = child.isVisibleLw();
192             final boolean hasWallpaper = (child == mService.mWallpaperTarget)
193                     && (type != WindowManager.LayoutParams.TYPE_KEYGUARD);
194 
195             // If there's a drag in progress and 'child' is a potential drop target,
196             // make sure it's been told about the drag
197             if (inDrag && isVisible) {
198                 mService.mDragState.sendDragStartedIfNeededLw(child);
199             }
200 
201             // Add a window to our list of input windows.
202             inputWindowHandle.name = child.toString();
203             inputWindowHandle.layoutParamsFlags = flags;
204             inputWindowHandle.layoutParamsType = type;
205             inputWindowHandle.dispatchingTimeoutNanos = child.getInputDispatchingTimeoutNanos();
206             inputWindowHandle.visible = isVisible;
207             inputWindowHandle.canReceiveKeys = child.canReceiveKeys();
208             inputWindowHandle.hasFocus = hasFocus;
209             inputWindowHandle.hasWallpaper = hasWallpaper;
210             inputWindowHandle.paused = child.mAppToken != null ? child.mAppToken.paused : false;
211             inputWindowHandle.layer = child.mLayer;
212             inputWindowHandle.ownerPid = child.mSession.mPid;
213             inputWindowHandle.ownerUid = child.mSession.mUid;
214             inputWindowHandle.inputFeatures = child.mAttrs.inputFeatures;
215 
216             final Rect frame = child.mFrame;
217             inputWindowHandle.frameLeft = frame.left;
218             inputWindowHandle.frameTop = frame.top;
219             inputWindowHandle.frameRight = frame.right;
220             inputWindowHandle.frameBottom = frame.bottom;
221 
222             if (child.mGlobalScale != 1) {
223                 // If we are scaling the window, input coordinates need
224                 // to be inversely scaled to map from what is on screen
225                 // to what is actually being touched in the UI.
226                 inputWindowHandle.scaleFactor = 1.0f/child.mGlobalScale;
227             } else {
228                 inputWindowHandle.scaleFactor = 1;
229             }
230 
231             child.getTouchableRegion(inputWindowHandle.touchableRegion);
232 
233             addInputWindowHandleLw(inputWindowHandle);
234         }
235 
236         // Send windows to native code.
237         mService.mInputManager.setInputWindows(mInputWindowHandles);
238 
239         // Clear the list in preparation for the next round.
240         clearInputWindowHandlesLw();
241 
242         if (false) Slog.d(WindowManagerService.TAG, "<<<<<<< EXITED updateInputWindowsLw");
243     }
244 
245     /* Notifies that the input device configuration has changed. */
notifyConfigurationChanged()246     public void notifyConfigurationChanged() {
247         mService.sendNewConfiguration();
248 
249         synchronized (mInputDevicesReadyMonitor) {
250             if (!mInputDevicesReady) {
251                 mInputDevicesReady = true;
252                 mInputDevicesReadyMonitor.notifyAll();
253             }
254         }
255     }
256 
257     /* Waits until the built-in input devices have been configured. */
waitForInputDevicesReady(long timeoutMillis)258     public boolean waitForInputDevicesReady(long timeoutMillis) {
259         synchronized (mInputDevicesReadyMonitor) {
260             if (!mInputDevicesReady) {
261                 try {
262                     mInputDevicesReadyMonitor.wait(timeoutMillis);
263                 } catch (InterruptedException ex) {
264                 }
265             }
266             return mInputDevicesReady;
267         }
268     }
269 
270     /* Notifies that the lid switch changed state. */
notifyLidSwitchChanged(long whenNanos, boolean lidOpen)271     public void notifyLidSwitchChanged(long whenNanos, boolean lidOpen) {
272         mService.mPolicy.notifyLidSwitchChanged(whenNanos, lidOpen);
273     }
274 
275     /* Provides an opportunity for the window manager policy to intercept early key
276      * processing as soon as the key has been read from the device. */
interceptKeyBeforeQueueing( KeyEvent event, int policyFlags, boolean isScreenOn)277     public int interceptKeyBeforeQueueing(
278             KeyEvent event, int policyFlags, boolean isScreenOn) {
279         return mService.mPolicy.interceptKeyBeforeQueueing(event, policyFlags, isScreenOn);
280     }
281 
282     /* Provides an opportunity for the window manager policy to intercept early
283      * motion event processing when the screen is off since these events are normally
284      * dropped. */
interceptMotionBeforeQueueingWhenScreenOff(int policyFlags)285     public int interceptMotionBeforeQueueingWhenScreenOff(int policyFlags) {
286         return mService.mPolicy.interceptMotionBeforeQueueingWhenScreenOff(policyFlags);
287     }
288 
289     /* Provides an opportunity for the window manager policy to process a key before
290      * ordinary dispatch. */
interceptKeyBeforeDispatching( InputWindowHandle focus, KeyEvent event, int policyFlags)291     public long interceptKeyBeforeDispatching(
292             InputWindowHandle focus, KeyEvent event, int policyFlags) {
293         WindowState windowState = focus != null ? (WindowState) focus.windowState : null;
294         return mService.mPolicy.interceptKeyBeforeDispatching(windowState, event, policyFlags);
295     }
296 
297     /* Provides an opportunity for the window manager policy to process a key that
298      * the application did not handle. */
dispatchUnhandledKey( InputWindowHandle focus, KeyEvent event, int policyFlags)299     public KeyEvent dispatchUnhandledKey(
300             InputWindowHandle focus, KeyEvent event, int policyFlags) {
301         WindowState windowState = focus != null ? (WindowState) focus.windowState : null;
302         return mService.mPolicy.dispatchUnhandledKey(windowState, event, policyFlags);
303     }
304 
305     /* Called when the current input focus changes.
306      * Layer assignment is assumed to be complete by the time this is called.
307      */
setInputFocusLw(WindowState newWindow, boolean updateInputWindows)308     public void setInputFocusLw(WindowState newWindow, boolean updateInputWindows) {
309         if (WindowManagerService.DEBUG_INPUT) {
310             Slog.d(WindowManagerService.TAG, "Input focus has changed to " + newWindow);
311         }
312 
313         if (newWindow != mInputFocus) {
314             if (newWindow != null && newWindow.canReceiveKeys()) {
315                 // Displaying a window implicitly causes dispatching to be unpaused.
316                 // This is to protect against bugs if someone pauses dispatching but
317                 // forgets to resume.
318                 newWindow.mToken.paused = false;
319             }
320 
321             mInputFocus = newWindow;
322             setUpdateInputWindowsNeededLw();
323 
324             if (updateInputWindows) {
325                 updateInputWindowsLw(false /*force*/);
326             }
327         }
328     }
329 
setFocusedAppLw(AppWindowToken newApp)330     public void setFocusedAppLw(AppWindowToken newApp) {
331         // Focused app has changed.
332         if (newApp == null) {
333             mService.mInputManager.setFocusedApplication(null);
334         } else {
335             final InputApplicationHandle handle = newApp.mInputApplicationHandle;
336             handle.name = newApp.toString();
337             handle.dispatchingTimeoutNanos = newApp.inputDispatchingTimeoutNanos;
338 
339             mService.mInputManager.setFocusedApplication(handle);
340         }
341     }
342 
pauseDispatchingLw(WindowToken window)343     public void pauseDispatchingLw(WindowToken window) {
344         if (! window.paused) {
345             if (WindowManagerService.DEBUG_INPUT) {
346                 Slog.v(WindowManagerService.TAG, "Pausing WindowToken " + window);
347             }
348 
349             window.paused = true;
350             updateInputWindowsLw(true /*force*/);
351         }
352     }
353 
resumeDispatchingLw(WindowToken window)354     public void resumeDispatchingLw(WindowToken window) {
355         if (window.paused) {
356             if (WindowManagerService.DEBUG_INPUT) {
357                 Slog.v(WindowManagerService.TAG, "Resuming WindowToken " + window);
358             }
359 
360             window.paused = false;
361             updateInputWindowsLw(true /*force*/);
362         }
363     }
364 
freezeInputDispatchingLw()365     public void freezeInputDispatchingLw() {
366         if (! mInputDispatchFrozen) {
367             if (WindowManagerService.DEBUG_INPUT) {
368                 Slog.v(WindowManagerService.TAG, "Freezing input dispatching");
369             }
370 
371             mInputDispatchFrozen = true;
372             updateInputDispatchModeLw();
373         }
374     }
375 
thawInputDispatchingLw()376     public void thawInputDispatchingLw() {
377         if (mInputDispatchFrozen) {
378             if (WindowManagerService.DEBUG_INPUT) {
379                 Slog.v(WindowManagerService.TAG, "Thawing input dispatching");
380             }
381 
382             mInputDispatchFrozen = false;
383             updateInputDispatchModeLw();
384         }
385     }
386 
setEventDispatchingLw(boolean enabled)387     public void setEventDispatchingLw(boolean enabled) {
388         if (mInputDispatchEnabled != enabled) {
389             if (WindowManagerService.DEBUG_INPUT) {
390                 Slog.v(WindowManagerService.TAG, "Setting event dispatching to " + enabled);
391             }
392 
393             mInputDispatchEnabled = enabled;
394             updateInputDispatchModeLw();
395         }
396     }
397 
updateInputDispatchModeLw()398     private void updateInputDispatchModeLw() {
399         mService.mInputManager.setInputDispatchMode(mInputDispatchEnabled, mInputDispatchFrozen);
400     }
401 }
402