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