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