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