• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 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 package com.android.server.wm;
17 
18 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
19 import static android.view.Display.DEFAULT_DISPLAY;
20 
21 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_INPUT;
22 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
23 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
24 import static com.android.server.wm.WindowManagerService.H.ON_POINTER_DOWN_OUTSIDE_FOCUS;
25 
26 import android.annotation.NonNull;
27 import android.annotation.Nullable;
28 import android.graphics.PointF;
29 import android.os.Debug;
30 import android.os.IBinder;
31 import android.util.Slog;
32 import android.view.Display;
33 import android.view.InputApplicationHandle;
34 import android.view.KeyEvent;
35 import android.view.SurfaceControl;
36 import android.view.WindowManager;
37 import android.view.WindowManagerPolicyConstants;
38 
39 import com.android.internal.util.function.pooled.PooledLambda;
40 import com.android.server.input.InputManagerService;
41 
42 import java.io.PrintWriter;
43 import java.util.OptionalInt;
44 
45 final class InputManagerCallback implements InputManagerService.WindowManagerCallbacks {
46     private static final String TAG = TAG_WITH_CLASS_NAME ? "InputManagerCallback" : TAG_WM;
47 
48     private final WindowManagerService mService;
49 
50     // Set to true when the first input device configuration change notification
51     // is received to indicate that the input devices are ready.
52     private final Object mInputDevicesReadyMonitor = new Object();
53     private boolean mInputDevicesReady;
54 
55     // When true, prevents input dispatch from proceeding until set to false again.
56     private boolean mInputDispatchFrozen;
57 
58     // The reason the input is currently frozen or null if the input isn't frozen.
59     private String mInputFreezeReason = null;
60 
61     // When true, input dispatch proceeds normally.  Otherwise all events are dropped.
62     // Initially false, so that input does not get dispatched until boot is finished at
63     // which point the ActivityManager will enable dispatching.
64     private boolean mInputDispatchEnabled;
65 
InputManagerCallback(WindowManagerService service)66     public InputManagerCallback(WindowManagerService service) {
67         mService = service;
68     }
69 
70     /**
71      * Notifies the window manager about a broken input channel.
72      *
73      * Called by the InputManager.
74      */
75     @Override
notifyInputChannelBroken(IBinder token)76     public void notifyInputChannelBroken(IBinder token) {
77         if (token == null) {
78             return;
79         }
80 
81         synchronized (mService.mGlobalLock) {
82             WindowState windowState = mService.mInputToWindowMap.get(token);
83             if (windowState != null) {
84                 Slog.i(TAG_WM, "WINDOW DIED " + windowState);
85                 windowState.removeIfPossible();
86             }
87         }
88     }
89 
90     /**
91      * Notifies the window manager about an application that is not responding because it has
92      * no focused window.
93      *
94      * Called by the InputManager.
95      */
96     @Override
notifyNoFocusedWindowAnr(@onNull InputApplicationHandle applicationHandle)97     public void notifyNoFocusedWindowAnr(@NonNull InputApplicationHandle applicationHandle) {
98         mService.mAnrController.notifyAppUnresponsive(
99                 applicationHandle, "Application does not have a focused window");
100     }
101 
102     @Override
notifyWindowUnresponsive(@onNull IBinder token, @NonNull OptionalInt pid, @NonNull String reason)103     public void notifyWindowUnresponsive(@NonNull IBinder token, @NonNull OptionalInt pid,
104             @NonNull String reason) {
105         mService.mAnrController.notifyWindowUnresponsive(token, pid, reason);
106     }
107 
108     @Override
notifyWindowResponsive(@onNull IBinder token, @NonNull OptionalInt pid)109     public void notifyWindowResponsive(@NonNull IBinder token, @NonNull OptionalInt pid) {
110         mService.mAnrController.notifyWindowResponsive(token, pid);
111     }
112 
113     /** Notifies that the input device configuration has changed. */
114     @Override
notifyConfigurationChanged()115     public void notifyConfigurationChanged() {
116         synchronized (mService.mGlobalLock) {
117             mService.mRoot.forAllDisplays(DisplayContent::sendNewConfiguration);
118         }
119 
120         synchronized (mInputDevicesReadyMonitor) {
121             if (!mInputDevicesReady) {
122                 mInputDevicesReady = true;
123                 mInputDevicesReadyMonitor.notifyAll();
124             }
125         }
126     }
127 
128     /** Notifies that the lid switch changed state. */
129     @Override
notifyLidSwitchChanged(long whenNanos, boolean lidOpen)130     public void notifyLidSwitchChanged(long whenNanos, boolean lidOpen) {
131         mService.mPolicy.notifyLidSwitchChanged(whenNanos, lidOpen);
132     }
133 
134     /** Notifies that the camera lens cover state has changed. */
135     @Override
notifyCameraLensCoverSwitchChanged(long whenNanos, boolean lensCovered)136     public void notifyCameraLensCoverSwitchChanged(long whenNanos, boolean lensCovered) {
137         mService.mPolicy.notifyCameraLensCoverSwitchChanged(whenNanos, lensCovered);
138     }
139 
140     /**
141      * Provides an opportunity for the window manager policy to intercept early key
142      * processing as soon as the key has been read from the device.
143      */
144     @Override
interceptKeyBeforeQueueing(KeyEvent event, int policyFlags)145     public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {
146         return mService.mPolicy.interceptKeyBeforeQueueing(event, policyFlags);
147     }
148 
149     /** {@inheritDoc} */
150     @Override
interceptMotionBeforeQueueingNonInteractive(int displayId, long whenNanos, int policyFlags)151     public int interceptMotionBeforeQueueingNonInteractive(int displayId, long whenNanos,
152             int policyFlags) {
153         return mService.mPolicy.interceptMotionBeforeQueueingNonInteractive(
154                 displayId, whenNanos, policyFlags);
155     }
156 
157     /**
158      * Provides an opportunity for the window manager policy to process a key before
159      * ordinary dispatch.
160      */
161     @Override
interceptKeyBeforeDispatching( IBinder focusedToken, KeyEvent event, int policyFlags)162     public long interceptKeyBeforeDispatching(
163             IBinder focusedToken, KeyEvent event, int policyFlags) {
164         return mService.mPolicy.interceptKeyBeforeDispatching(focusedToken, event, policyFlags);
165     }
166 
167     /**
168      * Provides an opportunity for the window manager policy to process a key that
169      * the application did not handle.
170      */
171     @Override
dispatchUnhandledKey( IBinder focusedToken, KeyEvent event, int policyFlags)172     public KeyEvent dispatchUnhandledKey(
173             IBinder focusedToken, KeyEvent event, int policyFlags) {
174         return mService.mPolicy.dispatchUnhandledKey(focusedToken, event, policyFlags);
175     }
176 
177     /** Callback to get pointer layer. */
178     @Override
getPointerLayer()179     public int getPointerLayer() {
180         return mService.mPolicy.getWindowLayerFromTypeLw(WindowManager.LayoutParams.TYPE_POINTER)
181                 * WindowManagerPolicyConstants.TYPE_LAYER_MULTIPLIER
182                 + WindowManagerPolicyConstants.TYPE_LAYER_OFFSET;
183     }
184 
185     /** Callback to get pointer display id. */
186     @Override
getPointerDisplayId()187     public int getPointerDisplayId() {
188         synchronized (mService.mGlobalLock) {
189             // If desktop mode is not enabled, show on the default display.
190             if (!mService.mForceDesktopModeOnExternalDisplays) {
191                 return DEFAULT_DISPLAY;
192             }
193 
194             // Look for the topmost freeform display.
195             int firstExternalDisplayId = DEFAULT_DISPLAY;
196             for (int i = mService.mRoot.mChildren.size() - 1; i >= 0; --i) {
197                 final DisplayContent displayContent = mService.mRoot.mChildren.get(i);
198                 if (displayContent.getDisplayInfo().state == Display.STATE_OFF) {
199                     continue;
200                 }
201                 // Heuristic solution here. Currently when "Freeform windows" developer option is
202                 // enabled we automatically put secondary displays in freeform mode and emulating
203                 // "desktop mode". It also makes sense to show the pointer on the same display.
204                 if (displayContent.getWindowingMode() == WINDOWING_MODE_FREEFORM) {
205                     return displayContent.getDisplayId();
206                 }
207 
208                 if (firstExternalDisplayId == DEFAULT_DISPLAY
209                         && displayContent.getDisplayId() != DEFAULT_DISPLAY) {
210                     firstExternalDisplayId = displayContent.getDisplayId();
211                 }
212             }
213 
214             // Look for the topmost non-default display
215             return firstExternalDisplayId;
216         }
217     }
218 
219     @Override
getCursorPosition()220     public PointF getCursorPosition() {
221         return mService.getLatestMousePosition();
222     }
223 
224     @Override
onPointerDownOutsideFocus(IBinder touchedToken)225     public void onPointerDownOutsideFocus(IBinder touchedToken) {
226         mService.mH.obtainMessage(ON_POINTER_DOWN_OUTSIDE_FOCUS, touchedToken).sendToTarget();
227     }
228 
229     @Override
notifyFocusChanged(IBinder oldToken, IBinder newToken)230     public void notifyFocusChanged(IBinder oldToken, IBinder newToken) {
231         mService.mH.sendMessage(PooledLambda.obtainMessage(
232                 mService::reportFocusChanged, oldToken, newToken));
233     }
234 
235     @Override
notifyDropWindow(IBinder token, float x, float y)236     public void notifyDropWindow(IBinder token, float x, float y) {
237         mService.mH.sendMessage(PooledLambda.obtainMessage(
238                 mService.mDragDropController::reportDropWindow, token, x, y));
239     }
240 
241     @Override
getParentSurfaceForPointers(int displayId)242     public SurfaceControl getParentSurfaceForPointers(int displayId) {
243         synchronized (mService.mGlobalLock) {
244             final DisplayContent dc = mService.mRoot.getDisplayContent(displayId);
245             if (dc == null) {
246                 Slog.e(TAG, "Failed to get parent surface for pointers on display " + displayId
247                         + " - DisplayContent not found.");
248                 return null;
249             }
250             return dc.getOverlayLayer();
251         }
252     }
253 
254     @Override
255     @Nullable
createSurfaceForGestureMonitor(String name, int displayId)256     public SurfaceControl createSurfaceForGestureMonitor(String name, int displayId) {
257         synchronized (mService.mGlobalLock) {
258             final DisplayContent dc = mService.mRoot.getDisplayContent(displayId);
259             if (dc == null) {
260                 Slog.e(TAG, "Failed to create a gesture monitor on display: " + displayId
261                         + " - DisplayContent not found.");
262                 return null;
263             }
264             return mService.makeSurfaceBuilder(dc.getSession())
265                     .setContainerLayer()
266                     .setName(name)
267                     .setCallsite("createSurfaceForGestureMonitor")
268                     .setParent(dc.getSurfaceControl())
269                     .build();
270         }
271     }
272 
273     @Override
notifyPointerDisplayIdChanged(int displayId, float x, float y)274     public void notifyPointerDisplayIdChanged(int displayId, float x, float y) {
275         synchronized (mService.mGlobalLock) {
276             mService.setMousePointerDisplayId(displayId);
277             if (displayId == Display.INVALID_DISPLAY) return;
278 
279             final DisplayContent dc = mService.mRoot.getDisplayContent(displayId);
280             if (dc == null) {
281                 Slog.wtf(TAG, "The mouse pointer was moved to display " + displayId
282                         + " that does not have a valid DisplayContent.");
283                 return;
284             }
285             mService.restorePointerIconLocked(dc, x, y);
286         }
287     }
288 
289     /** Waits until the built-in input devices have been configured. */
waitForInputDevicesReady(long timeoutMillis)290     public boolean waitForInputDevicesReady(long timeoutMillis) {
291         synchronized (mInputDevicesReadyMonitor) {
292             if (!mInputDevicesReady) {
293                 try {
294                     mInputDevicesReadyMonitor.wait(timeoutMillis);
295                 } catch (InterruptedException ex) {
296                 }
297             }
298             return mInputDevicesReady;
299         }
300     }
301 
freezeInputDispatchingLw()302     public void freezeInputDispatchingLw() {
303         if (!mInputDispatchFrozen) {
304             if (DEBUG_INPUT) {
305                 Slog.v(TAG_WM, "Freezing input dispatching");
306             }
307 
308             mInputDispatchFrozen = true;
309 
310             if (DEBUG_INPUT) {
311                 mInputFreezeReason = Debug.getCallers(6);
312             }
313             updateInputDispatchModeLw();
314         }
315     }
316 
thawInputDispatchingLw()317     public void thawInputDispatchingLw() {
318         if (mInputDispatchFrozen) {
319             if (DEBUG_INPUT) {
320                 Slog.v(TAG_WM, "Thawing input dispatching");
321             }
322 
323             mInputDispatchFrozen = false;
324             mInputFreezeReason = null;
325             updateInputDispatchModeLw();
326         }
327     }
328 
setEventDispatchingLw(boolean enabled)329     public void setEventDispatchingLw(boolean enabled) {
330         if (mInputDispatchEnabled != enabled) {
331             if (DEBUG_INPUT) {
332                 Slog.v(TAG_WM, "Setting event dispatching to " + enabled);
333             }
334 
335             mInputDispatchEnabled = enabled;
336             updateInputDispatchModeLw();
337         }
338     }
339 
updateInputDispatchModeLw()340     private void updateInputDispatchModeLw() {
341         mService.mInputManager.setInputDispatchMode(mInputDispatchEnabled, mInputDispatchFrozen);
342     }
343 
dump(PrintWriter pw, String prefix)344     void dump(PrintWriter pw, String prefix) {
345         if (mInputFreezeReason != null) {
346             pw.println(prefix + "mInputFreezeReason=" + mInputFreezeReason);
347         }
348     }
349 }
350