• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package com.android.server.wm;
2 
3 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
4 import static android.view.Display.DEFAULT_DISPLAY;
5 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
6 
7 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_INPUT;
8 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
9 import static com.android.server.wm.WindowManagerService.H.ON_POINTER_DOWN_OUTSIDE_FOCUS;
10 
11 import android.os.Debug;
12 import android.os.IBinder;
13 import android.util.Slog;
14 import android.view.KeyEvent;
15 import android.view.WindowManager;
16 
17 import com.android.server.input.InputManagerService;
18 
19 import java.io.PrintWriter;
20 
21 final class InputManagerCallback implements InputManagerService.WindowManagerCallbacks {
22     private final WindowManagerService mService;
23 
24     // Set to true when the first input device configuration change notification
25     // is received to indicate that the input devices are ready.
26     private final Object mInputDevicesReadyMonitor = new Object();
27     private boolean mInputDevicesReady;
28 
29     // When true, prevents input dispatch from proceeding until set to false again.
30     private boolean mInputDispatchFrozen;
31 
32     // The reason the input is currently frozen or null if the input isn't frozen.
33     private String mInputFreezeReason = null;
34 
35     // When true, input dispatch proceeds normally.  Otherwise all events are dropped.
36     // Initially false, so that input does not get dispatched until boot is finished at
37     // which point the ActivityManager will enable dispatching.
38     private boolean mInputDispatchEnabled;
39 
InputManagerCallback(WindowManagerService service)40     public InputManagerCallback(WindowManagerService service) {
41         mService = service;
42     }
43 
44     /**
45      * Notifies the window manager about a broken input channel.
46      *
47      * Called by the InputManager.
48      */
49     @Override
notifyInputChannelBroken(IBinder token)50     public void notifyInputChannelBroken(IBinder token) {
51         if (token == null) {
52             return;
53         }
54 
55         synchronized (mService.mGlobalLock) {
56             WindowState windowState = mService.windowForClientLocked(null, token, false);
57             if (windowState != null) {
58                 Slog.i(TAG_WM, "WINDOW DIED " + windowState);
59                 windowState.removeIfPossible();
60             }
61         }
62     }
63 
64     /**
65      * Notifies the window manager about an application that is not responding.
66      * Returns a new timeout to continue waiting in nanoseconds, or 0 to abort dispatch.
67      *
68      * Called by the InputManager.
69      */
70     @Override
notifyANR(IBinder token, String reason)71     public long notifyANR(IBinder token, String reason) {
72         AppWindowToken appWindowToken = null;
73         WindowState windowState = null;
74         boolean aboveSystem = false;
75         synchronized (mService.mGlobalLock) {
76             if (token != null) {
77                 windowState = mService.windowForClientLocked(null, token, false);
78                 if (windowState != null) {
79                     appWindowToken = windowState.mAppToken;
80                 }
81             }
82 
83             if (windowState != null) {
84                 Slog.i(TAG_WM, "Input event dispatching timed out "
85                         + "sending to " + windowState.mAttrs.getTitle()
86                         + ".  Reason: " + reason);
87                 // Figure out whether this window is layered above system windows.
88                 // We need to do this here to help the activity manager know how to
89                 // layer its ANR dialog.
90                 int systemAlertLayer = mService.mPolicy.getWindowLayerFromTypeLw(
91                         TYPE_APPLICATION_OVERLAY, windowState.mOwnerCanAddInternalSystemWindow);
92                 aboveSystem = windowState.mBaseLayer > systemAlertLayer;
93             } else if (appWindowToken != null) {
94                 Slog.i(TAG_WM, "Input event dispatching timed out "
95                         + "sending to application " + appWindowToken.stringName
96                         + ".  Reason: " + reason);
97             } else {
98                 Slog.i(TAG_WM, "Input event dispatching timed out "
99                         + ".  Reason: " + reason);
100             }
101 
102             mService.saveANRStateLocked(appWindowToken, windowState, reason);
103         }
104 
105         // All the calls below need to happen without the WM lock held since they call into AM.
106         mService.mAtmInternal.saveANRState(reason);
107 
108         if (appWindowToken != null && appWindowToken.appToken != null) {
109             // Notify the activity manager about the timeout and let it decide whether
110             // to abort dispatching or keep waiting.
111             final boolean abort = appWindowToken.keyDispatchingTimedOut(reason,
112                     (windowState != null) ? windowState.mSession.mPid : -1);
113             if (!abort) {
114                 // The activity manager declined to abort dispatching.
115                 // Wait a bit longer and timeout again later.
116                 return appWindowToken.mInputDispatchingTimeoutNanos;
117             }
118         } else if (windowState != null) {
119             // Notify the activity manager about the timeout and let it decide whether
120             // to abort dispatching or keep waiting.
121             long timeout = mService.mAmInternal.inputDispatchingTimedOut(
122                     windowState.mSession.mPid, aboveSystem, reason);
123             if (timeout >= 0) {
124                 // The activity manager declined to abort dispatching.
125                 // Wait a bit longer and timeout again later.
126                 return timeout * 1000000L; // nanoseconds
127             }
128         }
129         return 0; // abort dispatching
130     }
131 
132     /** Notifies that the input device configuration has changed. */
133     @Override
notifyConfigurationChanged()134     public void notifyConfigurationChanged() {
135         // TODO(multi-display): Notify proper displays that are associated with this input device.
136         mService.sendNewConfiguration(DEFAULT_DISPLAY);
137 
138         synchronized (mInputDevicesReadyMonitor) {
139             if (!mInputDevicesReady) {
140                 mInputDevicesReady = true;
141                 mInputDevicesReadyMonitor.notifyAll();
142             }
143         }
144     }
145 
146     /** Notifies that the lid switch changed state. */
147     @Override
notifyLidSwitchChanged(long whenNanos, boolean lidOpen)148     public void notifyLidSwitchChanged(long whenNanos, boolean lidOpen) {
149         mService.mPolicy.notifyLidSwitchChanged(whenNanos, lidOpen);
150     }
151 
152     /** Notifies that the camera lens cover state has changed. */
153     @Override
notifyCameraLensCoverSwitchChanged(long whenNanos, boolean lensCovered)154     public void notifyCameraLensCoverSwitchChanged(long whenNanos, boolean lensCovered) {
155         mService.mPolicy.notifyCameraLensCoverSwitchChanged(whenNanos, lensCovered);
156     }
157 
158     /**
159      * Provides an opportunity for the window manager policy to intercept early key
160      * processing as soon as the key has been read from the device.
161      */
162     @Override
interceptKeyBeforeQueueing(KeyEvent event, int policyFlags)163     public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {
164         return mService.mPolicy.interceptKeyBeforeQueueing(event, policyFlags);
165     }
166 
167     /** {@inheritDoc} */
168     @Override
interceptMotionBeforeQueueingNonInteractive(int displayId, long whenNanos, int policyFlags)169     public int interceptMotionBeforeQueueingNonInteractive(int displayId, long whenNanos,
170             int policyFlags) {
171         return mService.mPolicy.interceptMotionBeforeQueueingNonInteractive(
172                 displayId, whenNanos, policyFlags);
173     }
174 
175     /**
176      * Provides an opportunity for the window manager policy to process a key before
177      * ordinary dispatch.
178      */
179     @Override
interceptKeyBeforeDispatching( IBinder focus, KeyEvent event, int policyFlags)180     public long interceptKeyBeforeDispatching(
181             IBinder focus, KeyEvent event, int policyFlags) {
182         WindowState windowState = mService.windowForClientLocked(null, focus, false);
183         return mService.mPolicy.interceptKeyBeforeDispatching(windowState, event, policyFlags);
184     }
185 
186     /**
187      * Provides an opportunity for the window manager policy to process a key that
188      * the application did not handle.
189      */
190     @Override
dispatchUnhandledKey( IBinder focus, KeyEvent event, int policyFlags)191     public KeyEvent dispatchUnhandledKey(
192             IBinder focus, KeyEvent event, int policyFlags) {
193         WindowState windowState = mService.windowForClientLocked(null, focus, false);
194         return mService.mPolicy.dispatchUnhandledKey(windowState, event, policyFlags);
195     }
196 
197     /** Callback to get pointer layer. */
198     @Override
getPointerLayer()199     public int getPointerLayer() {
200         return mService.mPolicy.getWindowLayerFromTypeLw(WindowManager.LayoutParams.TYPE_POINTER)
201                 * WindowManagerService.TYPE_LAYER_MULTIPLIER
202                 + WindowManagerService.TYPE_LAYER_OFFSET;
203     }
204 
205     /** Callback to get pointer display id. */
206     @Override
getPointerDisplayId()207     public int getPointerDisplayId() {
208         synchronized (mService.mGlobalLock) {
209             // If desktop mode is not enabled, show on the default display.
210             if (!mService.mForceDesktopModeOnExternalDisplays) {
211                 return DEFAULT_DISPLAY;
212             }
213 
214             // Look for the topmost freeform display.
215             int firstExternalDisplayId = DEFAULT_DISPLAY;
216             for (int i = mService.mRoot.mChildren.size() - 1; i >= 0; --i) {
217                 final DisplayContent displayContent = mService.mRoot.mChildren.get(i);
218                 // Heuristic solution here. Currently when "Freeform windows" developer option is
219                 // enabled we automatically put secondary displays in freeform mode and emulating
220                 // "desktop mode". It also makes sense to show the pointer on the same display.
221                 if (displayContent.getWindowingMode() == WINDOWING_MODE_FREEFORM) {
222                     return displayContent.getDisplayId();
223                 }
224 
225                 if (firstExternalDisplayId == DEFAULT_DISPLAY
226                         && displayContent.getDisplayId() != DEFAULT_DISPLAY) {
227                     firstExternalDisplayId = displayContent.getDisplayId();
228                 }
229             }
230 
231             // Look for the topmost non-default display
232             return firstExternalDisplayId;
233         }
234     }
235 
236     @Override
onPointerDownOutsideFocus(IBinder touchedToken)237     public void onPointerDownOutsideFocus(IBinder touchedToken) {
238         mService.mH.obtainMessage(ON_POINTER_DOWN_OUTSIDE_FOCUS, touchedToken).sendToTarget();
239     }
240 
241     /** Waits until the built-in input devices have been configured. */
waitForInputDevicesReady(long timeoutMillis)242     public boolean waitForInputDevicesReady(long timeoutMillis) {
243         synchronized (mInputDevicesReadyMonitor) {
244             if (!mInputDevicesReady) {
245                 try {
246                     mInputDevicesReadyMonitor.wait(timeoutMillis);
247                 } catch (InterruptedException ex) {
248                 }
249             }
250             return mInputDevicesReady;
251         }
252     }
253 
freezeInputDispatchingLw()254     public void freezeInputDispatchingLw() {
255         if (!mInputDispatchFrozen) {
256             if (DEBUG_INPUT) {
257                 Slog.v(TAG_WM, "Freezing input dispatching");
258             }
259 
260             mInputDispatchFrozen = true;
261 
262             if (DEBUG_INPUT) {
263                 mInputFreezeReason = Debug.getCallers(6);
264             }
265             updateInputDispatchModeLw();
266         }
267     }
268 
thawInputDispatchingLw()269     public void thawInputDispatchingLw() {
270         if (mInputDispatchFrozen) {
271             if (DEBUG_INPUT) {
272                 Slog.v(TAG_WM, "Thawing input dispatching");
273             }
274 
275             mInputDispatchFrozen = false;
276             mInputFreezeReason = null;
277             updateInputDispatchModeLw();
278         }
279     }
280 
setEventDispatchingLw(boolean enabled)281     public void setEventDispatchingLw(boolean enabled) {
282         if (mInputDispatchEnabled != enabled) {
283             if (DEBUG_INPUT) {
284                 Slog.v(TAG_WM, "Setting event dispatching to " + enabled);
285             }
286 
287             mInputDispatchEnabled = enabled;
288             updateInputDispatchModeLw();
289         }
290     }
291 
updateInputDispatchModeLw()292     private void updateInputDispatchModeLw() {
293         mService.mInputManager.setInputDispatchMode(mInputDispatchEnabled, mInputDispatchFrozen);
294     }
295 
dump(PrintWriter pw, String prefix)296     void dump(PrintWriter pw, String prefix) {
297         if (mInputFreezeReason != null) {
298             pw.println(prefix + "mInputFreezeReason=" + mInputFreezeReason);
299         }
300     }
301 }
302