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.os.Debug;
28 import android.os.IBinder;
29 import android.util.Slog;
30 import android.view.InputApplicationHandle;
31 import android.view.KeyEvent;
32 import android.view.WindowManager;
33 import android.view.WindowManagerPolicyConstants;
34 
35 import com.android.internal.util.function.pooled.PooledLambda;
36 import com.android.server.input.InputManagerService;
37 
38 import java.io.PrintWriter;
39 
40 final class InputManagerCallback implements InputManagerService.WindowManagerCallbacks {
41     private static final String TAG = TAG_WITH_CLASS_NAME ? "InputManagerCallback" : TAG_WM;
42 
43     private final WindowManagerService mService;
44 
45     // Set to true when the first input device configuration change notification
46     // is received to indicate that the input devices are ready.
47     private final Object mInputDevicesReadyMonitor = new Object();
48     private boolean mInputDevicesReady;
49 
50     // When true, prevents input dispatch from proceeding until set to false again.
51     private boolean mInputDispatchFrozen;
52 
53     // The reason the input is currently frozen or null if the input isn't frozen.
54     private String mInputFreezeReason = null;
55 
56     // When true, input dispatch proceeds normally.  Otherwise all events are dropped.
57     // Initially false, so that input does not get dispatched until boot is finished at
58     // which point the ActivityManager will enable dispatching.
59     private boolean mInputDispatchEnabled;
60 
InputManagerCallback(WindowManagerService service)61     public InputManagerCallback(WindowManagerService service) {
62         mService = service;
63     }
64 
65     /**
66      * Notifies the window manager about a broken input channel.
67      *
68      * Called by the InputManager.
69      */
70     @Override
notifyInputChannelBroken(IBinder token)71     public void notifyInputChannelBroken(IBinder token) {
72         if (token == null) {
73             return;
74         }
75 
76         synchronized (mService.mGlobalLock) {
77             WindowState windowState = mService.mInputToWindowMap.get(token);
78             if (windowState != null) {
79                 Slog.i(TAG_WM, "WINDOW DIED " + windowState);
80                 windowState.removeIfPossible();
81             }
82         }
83     }
84 
85     /**
86      * Notifies the window manager about an application that is not responding because it has
87      * no focused window.
88      *
89      * Called by the InputManager.
90      */
91     @Override
notifyNoFocusedWindowAnr(@onNull InputApplicationHandle applicationHandle)92     public void notifyNoFocusedWindowAnr(@NonNull InputApplicationHandle applicationHandle) {
93         mService.mAnrController.notifyAppUnresponsive(
94                 applicationHandle, "Application does not have a focused window");
95     }
96 
97     @Override
notifyGestureMonitorUnresponsive(int pid, @NonNull String reason)98     public void notifyGestureMonitorUnresponsive(int pid, @NonNull String reason) {
99         mService.mAnrController.notifyGestureMonitorUnresponsive(pid, reason);
100     }
101 
102     @Override
notifyWindowUnresponsive(@onNull IBinder token, String reason)103     public void notifyWindowUnresponsive(@NonNull IBinder token, String reason) {
104         mService.mAnrController.notifyWindowUnresponsive(token, reason);
105     }
106 
107     @Override
notifyGestureMonitorResponsive(int pid)108     public void notifyGestureMonitorResponsive(int pid) {
109         mService.mAnrController.notifyGestureMonitorResponsive(pid);
110     }
111 
112     @Override
notifyWindowResponsive(@onNull IBinder token)113     public void notifyWindowResponsive(@NonNull IBinder token) {
114         mService.mAnrController.notifyWindowResponsive(token);
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 lid switch changed state. */
133     @Override
notifyLidSwitchChanged(long whenNanos, boolean lidOpen)134     public void notifyLidSwitchChanged(long whenNanos, boolean lidOpen) {
135         mService.mPolicy.notifyLidSwitchChanged(whenNanos, lidOpen);
136     }
137 
138     /** Notifies that the camera lens cover state has changed. */
139     @Override
notifyCameraLensCoverSwitchChanged(long whenNanos, boolean lensCovered)140     public void notifyCameraLensCoverSwitchChanged(long whenNanos, boolean lensCovered) {
141         mService.mPolicy.notifyCameraLensCoverSwitchChanged(whenNanos, lensCovered);
142     }
143 
144     /**
145      * Provides an opportunity for the window manager policy to intercept early key
146      * processing as soon as the key has been read from the device.
147      */
148     @Override
interceptKeyBeforeQueueing(KeyEvent event, int policyFlags)149     public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {
150         return mService.mPolicy.interceptKeyBeforeQueueing(event, policyFlags);
151     }
152 
153     /** {@inheritDoc} */
154     @Override
interceptMotionBeforeQueueingNonInteractive(int displayId, long whenNanos, int policyFlags)155     public int interceptMotionBeforeQueueingNonInteractive(int displayId, long whenNanos,
156             int policyFlags) {
157         return mService.mPolicy.interceptMotionBeforeQueueingNonInteractive(
158                 displayId, whenNanos, policyFlags);
159     }
160 
161     /**
162      * Provides an opportunity for the window manager policy to process a key before
163      * ordinary dispatch.
164      */
165     @Override
interceptKeyBeforeDispatching( IBinder focusedToken, KeyEvent event, int policyFlags)166     public long interceptKeyBeforeDispatching(
167             IBinder focusedToken, KeyEvent event, int policyFlags) {
168         return mService.mPolicy.interceptKeyBeforeDispatching(focusedToken, event, policyFlags);
169     }
170 
171     /**
172      * Provides an opportunity for the window manager policy to process a key that
173      * the application did not handle.
174      */
175     @Override
dispatchUnhandledKey( IBinder focusedToken, KeyEvent event, int policyFlags)176     public KeyEvent dispatchUnhandledKey(
177             IBinder focusedToken, KeyEvent event, int policyFlags) {
178         return mService.mPolicy.dispatchUnhandledKey(focusedToken, event, policyFlags);
179     }
180 
181     /** Callback to get pointer layer. */
182     @Override
getPointerLayer()183     public int getPointerLayer() {
184         return mService.mPolicy.getWindowLayerFromTypeLw(WindowManager.LayoutParams.TYPE_POINTER)
185                 * WindowManagerPolicyConstants.TYPE_LAYER_MULTIPLIER
186                 + WindowManagerPolicyConstants.TYPE_LAYER_OFFSET;
187     }
188 
189     /** Callback to get pointer display id. */
190     @Override
getPointerDisplayId()191     public int getPointerDisplayId() {
192         synchronized (mService.mGlobalLock) {
193             // If desktop mode is not enabled, show on the default display.
194             if (!mService.mForceDesktopModeOnExternalDisplays) {
195                 return DEFAULT_DISPLAY;
196             }
197 
198             // Look for the topmost freeform display.
199             int firstExternalDisplayId = DEFAULT_DISPLAY;
200             for (int i = mService.mRoot.mChildren.size() - 1; i >= 0; --i) {
201                 final DisplayContent displayContent = mService.mRoot.mChildren.get(i);
202                 // Heuristic solution here. Currently when "Freeform windows" developer option is
203                 // enabled we automatically put secondary displays in freeform mode and emulating
204                 // "desktop mode". It also makes sense to show the pointer on the same display.
205                 if (displayContent.getWindowingMode() == WINDOWING_MODE_FREEFORM) {
206                     return displayContent.getDisplayId();
207                 }
208 
209                 if (firstExternalDisplayId == DEFAULT_DISPLAY
210                         && displayContent.getDisplayId() != DEFAULT_DISPLAY) {
211                     firstExternalDisplayId = displayContent.getDisplayId();
212                 }
213             }
214 
215             // Look for the topmost non-default display
216             return firstExternalDisplayId;
217         }
218     }
219 
220     @Override
onPointerDownOutsideFocus(IBinder touchedToken)221     public void onPointerDownOutsideFocus(IBinder touchedToken) {
222         mService.mH.obtainMessage(ON_POINTER_DOWN_OUTSIDE_FOCUS, touchedToken).sendToTarget();
223     }
224 
225     @Override
notifyFocusChanged(IBinder oldToken, IBinder newToken)226     public void notifyFocusChanged(IBinder oldToken, IBinder newToken) {
227         mService.mH.sendMessage(PooledLambda.obtainMessage(
228                 mService::reportFocusChanged, oldToken, newToken));
229     }
230 
231     @Override
notifyDropWindow(IBinder token, float x, float y)232     public void notifyDropWindow(IBinder token, float x, float y) {
233         mService.mH.sendMessage(PooledLambda.obtainMessage(
234                 mService.mDragDropController::reportDropWindow, token, x, y));
235     }
236 
237     /** Waits until the built-in input devices have been configured. */
waitForInputDevicesReady(long timeoutMillis)238     public boolean waitForInputDevicesReady(long timeoutMillis) {
239         synchronized (mInputDevicesReadyMonitor) {
240             if (!mInputDevicesReady) {
241                 try {
242                     mInputDevicesReadyMonitor.wait(timeoutMillis);
243                 } catch (InterruptedException ex) {
244                 }
245             }
246             return mInputDevicesReady;
247         }
248     }
249 
freezeInputDispatchingLw()250     public void freezeInputDispatchingLw() {
251         if (!mInputDispatchFrozen) {
252             if (DEBUG_INPUT) {
253                 Slog.v(TAG_WM, "Freezing input dispatching");
254             }
255 
256             mInputDispatchFrozen = true;
257 
258             if (DEBUG_INPUT) {
259                 mInputFreezeReason = Debug.getCallers(6);
260             }
261             updateInputDispatchModeLw();
262         }
263     }
264 
thawInputDispatchingLw()265     public void thawInputDispatchingLw() {
266         if (mInputDispatchFrozen) {
267             if (DEBUG_INPUT) {
268                 Slog.v(TAG_WM, "Thawing input dispatching");
269             }
270 
271             mInputDispatchFrozen = false;
272             mInputFreezeReason = null;
273             updateInputDispatchModeLw();
274         }
275     }
276 
setEventDispatchingLw(boolean enabled)277     public void setEventDispatchingLw(boolean enabled) {
278         if (mInputDispatchEnabled != enabled) {
279             if (DEBUG_INPUT) {
280                 Slog.v(TAG_WM, "Setting event dispatching to " + enabled);
281             }
282 
283             mInputDispatchEnabled = enabled;
284             updateInputDispatchModeLw();
285         }
286     }
287 
updateInputDispatchModeLw()288     private void updateInputDispatchModeLw() {
289         mService.mInputManager.setInputDispatchMode(mInputDispatchEnabled, mInputDispatchFrozen);
290     }
291 
dump(PrintWriter pw, String prefix)292     void dump(PrintWriter pw, String prefix) {
293         if (mInputFreezeReason != null) {
294             pw.println(prefix + "mInputFreezeReason=" + mInputFreezeReason);
295         }
296     }
297 }
298