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