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, long whenNanos, int policyFlags)170     public int interceptMotionBeforeQueueingNonInteractive(int displayId, long whenNanos,
171             int policyFlags) {
172         return mService.mPolicy.interceptMotionBeforeQueueingNonInteractive(
173                 displayId, 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                     .build();
290         }
291     }
292 
293     @Override
notifyPointerDisplayIdChanged(int displayId, float x, float y)294     public void notifyPointerDisplayIdChanged(int displayId, float x, float y) {
295         synchronized (mService.mGlobalLock) {
296             mService.setMousePointerDisplayId(displayId);
297             if (displayId == Display.INVALID_DISPLAY) return;
298 
299             final DisplayContent dc = mService.mRoot.getDisplayContent(displayId);
300             if (dc == null) {
301                 Slog.wtf(TAG, "The mouse pointer was moved to display " + displayId
302                         + " that does not have a valid DisplayContent.");
303                 return;
304             }
305             mService.restorePointerIconLocked(dc, x, y);
306         }
307     }
308 
309     /** Waits until the built-in input devices have been configured. */
waitForInputDevicesReady(long timeoutMillis)310     public boolean waitForInputDevicesReady(long timeoutMillis) {
311         synchronized (mInputDevicesReadyMonitor) {
312             if (!mInputDevicesReady) {
313                 try {
314                     mInputDevicesReadyMonitor.wait(timeoutMillis);
315                 } catch (InterruptedException ex) {
316                 }
317             }
318             return mInputDevicesReady;
319         }
320     }
321 
freezeInputDispatchingLw()322     public void freezeInputDispatchingLw() {
323         if (!mInputDispatchFrozen) {
324             if (DEBUG_INPUT) {
325                 Slog.v(TAG_WM, "Freezing input dispatching");
326             }
327 
328             mInputDispatchFrozen = true;
329 
330             if (DEBUG_INPUT) {
331                 mInputFreezeReason = Debug.getCallers(6);
332             }
333             updateInputDispatchModeLw();
334         }
335     }
336 
thawInputDispatchingLw()337     public void thawInputDispatchingLw() {
338         if (mInputDispatchFrozen) {
339             if (DEBUG_INPUT) {
340                 Slog.v(TAG_WM, "Thawing input dispatching");
341             }
342 
343             mInputDispatchFrozen = false;
344             mInputFreezeReason = null;
345             updateInputDispatchModeLw();
346         }
347     }
348 
setEventDispatchingLw(boolean enabled)349     public void setEventDispatchingLw(boolean enabled) {
350         if (mInputDispatchEnabled != enabled) {
351             if (DEBUG_INPUT) {
352                 Slog.v(TAG_WM, "Setting event dispatching to " + enabled);
353             }
354 
355             mInputDispatchEnabled = enabled;
356             updateInputDispatchModeLw();
357         }
358     }
359 
updateInputDispatchModeLw()360     private void updateInputDispatchModeLw() {
361         mService.mInputManager.setInputDispatchMode(mInputDispatchEnabled, mInputDispatchFrozen);
362     }
363 
timeoutMessage(OptionalInt pid, String reason)364     private String timeoutMessage(OptionalInt pid, String reason) {
365         String message = (reason == null) ? "Input dispatching timed out."
366                 : String.format("Input dispatching timed out (%s).", reason);
367         if (pid.isEmpty()) {
368             return message;
369         }
370         StalledTransactionInfo stalledTransactionInfo =
371                 SurfaceControl.getStalledTransactionInfo(pid.getAsInt());
372         if (stalledTransactionInfo == null) {
373             return message;
374         }
375         return String.format("%s Buffer processing for the associated surface is stuck due to an "
376                 + "unsignaled fence (window=%s, bufferId=0x%016X, frameNumber=%s). This "
377                 + "potentially indicates a GPU hang.", message, stalledTransactionInfo.layerName,
378                 stalledTransactionInfo.bufferId, stalledTransactionInfo.frameNumber);
379     }
380 
dump(PrintWriter pw, String prefix)381     void dump(PrintWriter pw, String prefix) {
382         if (mInputFreezeReason != null) {
383             pw.println(prefix + "mInputFreezeReason=" + mInputFreezeReason);
384         }
385     }
386 }
387