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