1 /* 2 * Copyright (C) 2021 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 17 package com.android.server.companion.virtual; 18 19 import android.annotation.IntDef; 20 import android.annotation.NonNull; 21 import android.annotation.StringDef; 22 import android.graphics.PointF; 23 import android.hardware.display.DisplayManagerInternal; 24 import android.hardware.input.InputDeviceIdentifier; 25 import android.hardware.input.InputManager; 26 import android.hardware.input.InputManagerGlobal; 27 import android.hardware.input.VirtualKeyEvent; 28 import android.hardware.input.VirtualMouseButtonEvent; 29 import android.hardware.input.VirtualMouseRelativeEvent; 30 import android.hardware.input.VirtualMouseScrollEvent; 31 import android.hardware.input.VirtualTouchEvent; 32 import android.os.Handler; 33 import android.os.IBinder; 34 import android.os.IInputConstants; 35 import android.os.RemoteException; 36 import android.util.ArrayMap; 37 import android.util.Slog; 38 import android.view.Display; 39 import android.view.InputDevice; 40 import android.view.WindowManager; 41 42 import com.android.internal.annotations.GuardedBy; 43 import com.android.internal.annotations.VisibleForTesting; 44 import com.android.server.LocalServices; 45 import com.android.server.input.InputManagerInternal; 46 47 import java.io.PrintWriter; 48 import java.lang.annotation.Retention; 49 import java.lang.annotation.RetentionPolicy; 50 import java.nio.charset.StandardCharsets; 51 import java.util.Iterator; 52 import java.util.Map; 53 import java.util.Objects; 54 import java.util.concurrent.CountDownLatch; 55 import java.util.concurrent.TimeUnit; 56 import java.util.concurrent.atomic.AtomicLong; 57 import java.util.function.Supplier; 58 59 /** Controls virtual input devices, including device lifecycle and event dispatch. */ 60 class InputController { 61 62 private static final String TAG = "VirtualInputController"; 63 64 private static final AtomicLong sNextPhysId = new AtomicLong(1); 65 66 static final String NAVIGATION_TOUCHPAD_DEVICE_TYPE = "touchNavigation"; 67 68 static final String PHYS_TYPE_DPAD = "Dpad"; 69 static final String PHYS_TYPE_KEYBOARD = "Keyboard"; 70 static final String PHYS_TYPE_MOUSE = "Mouse"; 71 static final String PHYS_TYPE_TOUCHSCREEN = "Touchscreen"; 72 static final String PHYS_TYPE_NAVIGATION_TOUCHPAD = "NavigationTouchpad"; 73 @StringDef(prefix = { "PHYS_TYPE_" }, value = { 74 PHYS_TYPE_DPAD, 75 PHYS_TYPE_KEYBOARD, 76 PHYS_TYPE_MOUSE, 77 PHYS_TYPE_TOUCHSCREEN, 78 PHYS_TYPE_NAVIGATION_TOUCHPAD, 79 }) 80 @Retention(RetentionPolicy.SOURCE) 81 @interface PhysType { 82 } 83 84 /** 85 * The maximum length of a device name (in bytes in UTF-8 encoding). 86 * 87 * This limitation comes directly from uinput. 88 * See also UINPUT_MAX_NAME_SIZE in linux/uinput.h 89 */ 90 private static final int DEVICE_NAME_MAX_LENGTH = 80; 91 92 final Object mLock = new Object(); 93 94 /* Token -> file descriptor associations. */ 95 @GuardedBy("mLock") 96 private final ArrayMap<IBinder, InputDeviceDescriptor> mInputDeviceDescriptors = 97 new ArrayMap<>(); 98 99 private final Handler mHandler; 100 private final NativeWrapper mNativeWrapper; 101 private final DisplayManagerInternal mDisplayManagerInternal; 102 private final InputManagerInternal mInputManagerInternal; 103 private final WindowManager mWindowManager; 104 private final DeviceCreationThreadVerifier mThreadVerifier; 105 InputController(@onNull Handler handler, @NonNull WindowManager windowManager)106 InputController(@NonNull Handler handler, 107 @NonNull WindowManager windowManager) { 108 this(new NativeWrapper(), handler, windowManager, 109 // Verify that virtual devices are not created on the handler thread. 110 () -> !handler.getLooper().isCurrentThread()); 111 } 112 113 @VisibleForTesting InputController(@onNull NativeWrapper nativeWrapper, @NonNull Handler handler, @NonNull WindowManager windowManager, @NonNull DeviceCreationThreadVerifier threadVerifier)114 InputController(@NonNull NativeWrapper nativeWrapper, 115 @NonNull Handler handler, @NonNull WindowManager windowManager, 116 @NonNull DeviceCreationThreadVerifier threadVerifier) { 117 mHandler = handler; 118 mNativeWrapper = nativeWrapper; 119 mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class); 120 mInputManagerInternal = LocalServices.getService(InputManagerInternal.class); 121 mWindowManager = windowManager; 122 mThreadVerifier = threadVerifier; 123 } 124 close()125 void close() { 126 synchronized (mLock) { 127 final Iterator<Map.Entry<IBinder, InputDeviceDescriptor>> iterator = 128 mInputDeviceDescriptors.entrySet().iterator(); 129 if (iterator.hasNext()) { 130 final Map.Entry<IBinder, InputDeviceDescriptor> entry = iterator.next(); 131 final IBinder token = entry.getKey(); 132 final InputDeviceDescriptor inputDeviceDescriptor = entry.getValue(); 133 iterator.remove(); 134 closeInputDeviceDescriptorLocked(token, inputDeviceDescriptor); 135 } 136 } 137 } 138 createDpad(@onNull String deviceName, int vendorId, int productId, @NonNull IBinder deviceToken, int displayId)139 void createDpad(@NonNull String deviceName, 140 int vendorId, 141 int productId, 142 @NonNull IBinder deviceToken, 143 int displayId) { 144 final String phys = createPhys(PHYS_TYPE_DPAD); 145 try { 146 createDeviceInternal(InputDeviceDescriptor.TYPE_DPAD, deviceName, vendorId, 147 productId, deviceToken, displayId, phys, 148 () -> mNativeWrapper.openUinputDpad(deviceName, vendorId, productId, phys)); 149 } catch (DeviceCreationException e) { 150 throw new RuntimeException( 151 "Failed to create virtual dpad device '" + deviceName + "'.", e); 152 } 153 } 154 createKeyboard(@onNull String deviceName, int vendorId, int productId, @NonNull IBinder deviceToken, int displayId, @NonNull String languageTag, @NonNull String layoutType)155 void createKeyboard(@NonNull String deviceName, int vendorId, int productId, 156 @NonNull IBinder deviceToken, int displayId, @NonNull String languageTag, 157 @NonNull String layoutType) { 158 final String phys = createPhys(PHYS_TYPE_KEYBOARD); 159 mInputManagerInternal.addKeyboardLayoutAssociation(phys, languageTag, 160 layoutType); 161 try { 162 createDeviceInternal(InputDeviceDescriptor.TYPE_KEYBOARD, deviceName, vendorId, 163 productId, deviceToken, displayId, phys, 164 () -> mNativeWrapper.openUinputKeyboard(deviceName, vendorId, productId, phys)); 165 } catch (DeviceCreationException e) { 166 mInputManagerInternal.removeKeyboardLayoutAssociation(phys); 167 throw new RuntimeException( 168 "Failed to create virtual keyboard device '" + deviceName + "'.", e); 169 } 170 } 171 createMouse(@onNull String deviceName, int vendorId, int productId, @NonNull IBinder deviceToken, int displayId)172 void createMouse(@NonNull String deviceName, 173 int vendorId, 174 int productId, 175 @NonNull IBinder deviceToken, 176 int displayId) { 177 final String phys = createPhys(PHYS_TYPE_MOUSE); 178 try { 179 createDeviceInternal(InputDeviceDescriptor.TYPE_MOUSE, deviceName, vendorId, productId, 180 deviceToken, displayId, phys, 181 () -> mNativeWrapper.openUinputMouse(deviceName, vendorId, productId, phys)); 182 } catch (DeviceCreationException e) { 183 throw new RuntimeException( 184 "Failed to create virtual mouse device: '" + deviceName + "'.", e); 185 } 186 mInputManagerInternal.setVirtualMousePointerDisplayId(displayId); 187 } 188 createTouchscreen(@onNull String deviceName, int vendorId, int productId, @NonNull IBinder deviceToken, int displayId, int height, int width)189 void createTouchscreen(@NonNull String deviceName, 190 int vendorId, 191 int productId, 192 @NonNull IBinder deviceToken, 193 int displayId, 194 int height, 195 int width) { 196 final String phys = createPhys(PHYS_TYPE_TOUCHSCREEN); 197 try { 198 createDeviceInternal(InputDeviceDescriptor.TYPE_TOUCHSCREEN, deviceName, vendorId, 199 productId, deviceToken, displayId, phys, 200 () -> mNativeWrapper.openUinputTouchscreen(deviceName, vendorId, productId, 201 phys, height, width)); 202 } catch (DeviceCreationException e) { 203 throw new RuntimeException( 204 "Failed to create virtual touchscreen device '" + deviceName + "'.", e); 205 } 206 } 207 createNavigationTouchpad( @onNull String deviceName, int vendorId, int productId, @NonNull IBinder deviceToken, int displayId, int touchpadHeight, int touchpadWidth)208 void createNavigationTouchpad( 209 @NonNull String deviceName, 210 int vendorId, 211 int productId, 212 @NonNull IBinder deviceToken, 213 int displayId, 214 int touchpadHeight, 215 int touchpadWidth) { 216 final String phys = createPhys(PHYS_TYPE_NAVIGATION_TOUCHPAD); 217 mInputManagerInternal.setTypeAssociation(phys, NAVIGATION_TOUCHPAD_DEVICE_TYPE); 218 try { 219 createDeviceInternal(InputDeviceDescriptor.TYPE_NAVIGATION_TOUCHPAD, deviceName, 220 vendorId, productId, deviceToken, displayId, phys, 221 () -> mNativeWrapper.openUinputTouchscreen(deviceName, vendorId, productId, 222 phys, touchpadHeight, touchpadWidth)); 223 } catch (DeviceCreationException e) { 224 mInputManagerInternal.unsetTypeAssociation(phys); 225 throw new RuntimeException( 226 "Failed to create virtual navigation touchpad device '" + deviceName + "'.", e); 227 } 228 } 229 unregisterInputDevice(@onNull IBinder token)230 void unregisterInputDevice(@NonNull IBinder token) { 231 synchronized (mLock) { 232 final InputDeviceDescriptor inputDeviceDescriptor = mInputDeviceDescriptors.remove( 233 token); 234 if (inputDeviceDescriptor == null) { 235 throw new IllegalArgumentException( 236 "Could not unregister input device for given token"); 237 } 238 closeInputDeviceDescriptorLocked(token, inputDeviceDescriptor); 239 } 240 } 241 242 @GuardedBy("mLock") closeInputDeviceDescriptorLocked(IBinder token, InputDeviceDescriptor inputDeviceDescriptor)243 private void closeInputDeviceDescriptorLocked(IBinder token, 244 InputDeviceDescriptor inputDeviceDescriptor) { 245 token.unlinkToDeath(inputDeviceDescriptor.getDeathRecipient(), /* flags= */ 0); 246 mNativeWrapper.closeUinput(inputDeviceDescriptor.getNativePointer()); 247 String phys = inputDeviceDescriptor.getPhys(); 248 InputManagerGlobal.getInstance().removeUniqueIdAssociation(phys); 249 // Type associations are added in the case of navigation touchpads. Those should be removed 250 // once the input device gets closed. 251 if (inputDeviceDescriptor.getType() == InputDeviceDescriptor.TYPE_NAVIGATION_TOUCHPAD) { 252 mInputManagerInternal.unsetTypeAssociation(phys); 253 } 254 255 if (inputDeviceDescriptor.getType() == InputDeviceDescriptor.TYPE_KEYBOARD) { 256 mInputManagerInternal.removeKeyboardLayoutAssociation(phys); 257 } 258 259 // Reset values to the default if all virtual mice are unregistered, or set display 260 // id if there's another mouse (choose the most recent). The inputDeviceDescriptor must be 261 // removed from the mInputDeviceDescriptors instance variable prior to this point. 262 if (inputDeviceDescriptor.isMouse()) { 263 if (mInputManagerInternal.getVirtualMousePointerDisplayId() 264 == inputDeviceDescriptor.getDisplayId()) { 265 updateActivePointerDisplayIdLocked(); 266 } 267 } 268 } 269 270 /** 271 * @return the device id for a given token (identifiying a device) 272 */ getInputDeviceId(IBinder token)273 int getInputDeviceId(IBinder token) { 274 synchronized (mLock) { 275 final InputDeviceDescriptor inputDeviceDescriptor = mInputDeviceDescriptors.get(token); 276 if (inputDeviceDescriptor == null) { 277 throw new IllegalArgumentException("Could not get device id for given token"); 278 } 279 return inputDeviceDescriptor.getInputDeviceId(); 280 } 281 } 282 setShowPointerIcon(boolean visible, int displayId)283 void setShowPointerIcon(boolean visible, int displayId) { 284 mInputManagerInternal.setPointerIconVisible(visible, displayId); 285 } 286 setPointerAcceleration(float pointerAcceleration, int displayId)287 void setPointerAcceleration(float pointerAcceleration, int displayId) { 288 mInputManagerInternal.setPointerAcceleration(pointerAcceleration, displayId); 289 } 290 setDisplayEligibilityForPointerCapture(boolean isEligible, int displayId)291 void setDisplayEligibilityForPointerCapture(boolean isEligible, int displayId) { 292 mInputManagerInternal.setDisplayEligibilityForPointerCapture(displayId, isEligible); 293 } 294 setLocalIme(int displayId)295 void setLocalIme(int displayId) { 296 // WM throws a SecurityException if the display is untrusted. 297 if ((mDisplayManagerInternal.getDisplayInfo(displayId).flags & Display.FLAG_TRUSTED) 298 == Display.FLAG_TRUSTED) { 299 mWindowManager.setDisplayImePolicy(displayId, 300 WindowManager.DISPLAY_IME_POLICY_LOCAL); 301 } 302 } 303 304 @GuardedBy("mLock") updateActivePointerDisplayIdLocked()305 private void updateActivePointerDisplayIdLocked() { 306 InputDeviceDescriptor mostRecentlyCreatedMouse = null; 307 for (int i = 0; i < mInputDeviceDescriptors.size(); ++i) { 308 InputDeviceDescriptor otherInputDeviceDescriptor = mInputDeviceDescriptors.valueAt(i); 309 if (otherInputDeviceDescriptor.isMouse()) { 310 if (mostRecentlyCreatedMouse == null 311 || (otherInputDeviceDescriptor.getCreationOrderNumber() 312 > mostRecentlyCreatedMouse.getCreationOrderNumber())) { 313 mostRecentlyCreatedMouse = otherInputDeviceDescriptor; 314 } 315 } 316 } 317 if (mostRecentlyCreatedMouse != null) { 318 mInputManagerInternal.setVirtualMousePointerDisplayId( 319 mostRecentlyCreatedMouse.getDisplayId()); 320 } else { 321 // All mice have been unregistered 322 mInputManagerInternal.setVirtualMousePointerDisplayId(Display.INVALID_DISPLAY); 323 } 324 } 325 326 /** 327 * Validates a device name by checking length and whether a device with the same name 328 * already exists. Throws exceptions if the validation fails. 329 * @param deviceName The name of the device to be validated 330 * @throws DeviceCreationException if {@code deviceName} is not valid. 331 */ validateDeviceName(String deviceName)332 private void validateDeviceName(String deviceName) throws DeviceCreationException { 333 // Comparison is greater or equal because the device name must fit into a const char* 334 // including the \0-terminator. Therefore the actual number of bytes that can be used 335 // for device name is DEVICE_NAME_MAX_LENGTH - 1 336 if (deviceName.getBytes(StandardCharsets.UTF_8).length >= DEVICE_NAME_MAX_LENGTH) { 337 throw new DeviceCreationException( 338 "Input device name exceeds maximum length of " + DEVICE_NAME_MAX_LENGTH 339 + "bytes: " + deviceName); 340 } 341 342 synchronized (mLock) { 343 for (int i = 0; i < mInputDeviceDescriptors.size(); ++i) { 344 if (mInputDeviceDescriptors.valueAt(i).mName.equals(deviceName)) { 345 throw new DeviceCreationException( 346 "Input device name already in use: " + deviceName); 347 } 348 } 349 } 350 } 351 createPhys(@hysType String type)352 private static String createPhys(@PhysType String type) { 353 return String.format("virtual%s:%d", type, sNextPhysId.getAndIncrement()); 354 } 355 setUniqueIdAssociation(int displayId, String phys)356 private void setUniqueIdAssociation(int displayId, String phys) { 357 final String displayUniqueId = mDisplayManagerInternal.getDisplayInfo(displayId).uniqueId; 358 InputManagerGlobal.getInstance().addUniqueIdAssociation(phys, displayUniqueId); 359 } 360 sendDpadKeyEvent(@onNull IBinder token, @NonNull VirtualKeyEvent event)361 boolean sendDpadKeyEvent(@NonNull IBinder token, @NonNull VirtualKeyEvent event) { 362 synchronized (mLock) { 363 final InputDeviceDescriptor inputDeviceDescriptor = mInputDeviceDescriptors.get( 364 token); 365 if (inputDeviceDescriptor == null) { 366 throw new IllegalArgumentException( 367 "Could not send key event to input device for given token"); 368 } 369 return mNativeWrapper.writeDpadKeyEvent(inputDeviceDescriptor.getNativePointer(), 370 event.getKeyCode(), event.getAction(), event.getEventTimeNanos()); 371 } 372 } 373 sendKeyEvent(@onNull IBinder token, @NonNull VirtualKeyEvent event)374 boolean sendKeyEvent(@NonNull IBinder token, @NonNull VirtualKeyEvent event) { 375 synchronized (mLock) { 376 final InputDeviceDescriptor inputDeviceDescriptor = mInputDeviceDescriptors.get( 377 token); 378 if (inputDeviceDescriptor == null) { 379 throw new IllegalArgumentException( 380 "Could not send key event to input device for given token"); 381 } 382 return mNativeWrapper.writeKeyEvent(inputDeviceDescriptor.getNativePointer(), 383 event.getKeyCode(), event.getAction(), event.getEventTimeNanos()); 384 } 385 } 386 sendButtonEvent(@onNull IBinder token, @NonNull VirtualMouseButtonEvent event)387 boolean sendButtonEvent(@NonNull IBinder token, @NonNull VirtualMouseButtonEvent event) { 388 synchronized (mLock) { 389 final InputDeviceDescriptor inputDeviceDescriptor = mInputDeviceDescriptors.get( 390 token); 391 if (inputDeviceDescriptor == null) { 392 throw new IllegalArgumentException( 393 "Could not send button event to input device for given token"); 394 } 395 if (inputDeviceDescriptor.getDisplayId() 396 != mInputManagerInternal.getVirtualMousePointerDisplayId()) { 397 throw new IllegalStateException( 398 "Display id associated with this mouse is not currently targetable"); 399 } 400 return mNativeWrapper.writeButtonEvent(inputDeviceDescriptor.getNativePointer(), 401 event.getButtonCode(), event.getAction(), event.getEventTimeNanos()); 402 } 403 } 404 sendTouchEvent(@onNull IBinder token, @NonNull VirtualTouchEvent event)405 boolean sendTouchEvent(@NonNull IBinder token, @NonNull VirtualTouchEvent event) { 406 synchronized (mLock) { 407 final InputDeviceDescriptor inputDeviceDescriptor = mInputDeviceDescriptors.get( 408 token); 409 if (inputDeviceDescriptor == null) { 410 throw new IllegalArgumentException( 411 "Could not send touch event to input device for given token"); 412 } 413 return mNativeWrapper.writeTouchEvent(inputDeviceDescriptor.getNativePointer(), 414 event.getPointerId(), event.getToolType(), event.getAction(), event.getX(), 415 event.getY(), event.getPressure(), event.getMajorAxisSize(), 416 event.getEventTimeNanos()); 417 } 418 } 419 sendRelativeEvent(@onNull IBinder token, @NonNull VirtualMouseRelativeEvent event)420 boolean sendRelativeEvent(@NonNull IBinder token, @NonNull VirtualMouseRelativeEvent event) { 421 synchronized (mLock) { 422 final InputDeviceDescriptor inputDeviceDescriptor = mInputDeviceDescriptors.get( 423 token); 424 if (inputDeviceDescriptor == null) { 425 throw new IllegalArgumentException( 426 "Could not send relative event to input device for given token"); 427 } 428 if (inputDeviceDescriptor.getDisplayId() 429 != mInputManagerInternal.getVirtualMousePointerDisplayId()) { 430 throw new IllegalStateException( 431 "Display id associated with this mouse is not currently targetable"); 432 } 433 return mNativeWrapper.writeRelativeEvent(inputDeviceDescriptor.getNativePointer(), 434 event.getRelativeX(), event.getRelativeY(), event.getEventTimeNanos()); 435 } 436 } 437 sendScrollEvent(@onNull IBinder token, @NonNull VirtualMouseScrollEvent event)438 boolean sendScrollEvent(@NonNull IBinder token, @NonNull VirtualMouseScrollEvent event) { 439 synchronized (mLock) { 440 final InputDeviceDescriptor inputDeviceDescriptor = mInputDeviceDescriptors.get( 441 token); 442 if (inputDeviceDescriptor == null) { 443 throw new IllegalArgumentException( 444 "Could not send scroll event to input device for given token"); 445 } 446 if (inputDeviceDescriptor.getDisplayId() 447 != mInputManagerInternal.getVirtualMousePointerDisplayId()) { 448 throw new IllegalStateException( 449 "Display id associated with this mouse is not currently targetable"); 450 } 451 return mNativeWrapper.writeScrollEvent(inputDeviceDescriptor.getNativePointer(), 452 event.getXAxisMovement(), event.getYAxisMovement(), event.getEventTimeNanos()); 453 } 454 } 455 getCursorPosition(@onNull IBinder token)456 public PointF getCursorPosition(@NonNull IBinder token) { 457 synchronized (mLock) { 458 final InputDeviceDescriptor inputDeviceDescriptor = mInputDeviceDescriptors.get( 459 token); 460 if (inputDeviceDescriptor == null) { 461 throw new IllegalArgumentException( 462 "Could not get cursor position for input device for given token"); 463 } 464 if (inputDeviceDescriptor.getDisplayId() 465 != mInputManagerInternal.getVirtualMousePointerDisplayId()) { 466 throw new IllegalStateException( 467 "Display id associated with this mouse is not currently targetable"); 468 } 469 return LocalServices.getService(InputManagerInternal.class).getCursorPosition(); 470 } 471 } 472 dump(@onNull PrintWriter fout)473 public void dump(@NonNull PrintWriter fout) { 474 fout.println(" InputController: "); 475 synchronized (mLock) { 476 fout.println(" Active descriptors: "); 477 for (int i = 0; i < mInputDeviceDescriptors.size(); ++i) { 478 InputDeviceDescriptor inputDeviceDescriptor = mInputDeviceDescriptors.valueAt(i); 479 fout.println(" ptr: " + inputDeviceDescriptor.getNativePointer()); 480 fout.println(" displayId: " + inputDeviceDescriptor.getDisplayId()); 481 fout.println(" creationOrder: " 482 + inputDeviceDescriptor.getCreationOrderNumber()); 483 fout.println(" type: " + inputDeviceDescriptor.getType()); 484 fout.println(" phys: " + inputDeviceDescriptor.getPhys()); 485 fout.println( 486 " inputDeviceId: " + inputDeviceDescriptor.getInputDeviceId()); 487 } 488 } 489 } 490 491 @VisibleForTesting addDeviceForTesting(IBinder deviceToken, long ptr, int type, int displayId, String phys, String deviceName, int inputDeviceId)492 void addDeviceForTesting(IBinder deviceToken, long ptr, int type, int displayId, String phys, 493 String deviceName, int inputDeviceId) { 494 synchronized (mLock) { 495 mInputDeviceDescriptors.put(deviceToken, new InputDeviceDescriptor(ptr, () -> { 496 }, type, displayId, phys, deviceName, inputDeviceId)); 497 } 498 } 499 500 @VisibleForTesting getInputDeviceDescriptors()501 Map<IBinder, InputDeviceDescriptor> getInputDeviceDescriptors() { 502 final Map<IBinder, InputDeviceDescriptor> inputDeviceDescriptors = new ArrayMap<>(); 503 synchronized (mLock) { 504 inputDeviceDescriptors.putAll(mInputDeviceDescriptors); 505 } 506 return inputDeviceDescriptors; 507 } 508 nativeOpenUinputDpad(String deviceName, int vendorId, int productId, String phys)509 private static native long nativeOpenUinputDpad(String deviceName, int vendorId, int productId, 510 String phys); nativeOpenUinputKeyboard(String deviceName, int vendorId, int productId, String phys)511 private static native long nativeOpenUinputKeyboard(String deviceName, int vendorId, 512 int productId, String phys); nativeOpenUinputMouse(String deviceName, int vendorId, int productId, String phys)513 private static native long nativeOpenUinputMouse(String deviceName, int vendorId, int productId, 514 String phys); nativeOpenUinputTouchscreen(String deviceName, int vendorId, int productId, String phys, int height, int width)515 private static native long nativeOpenUinputTouchscreen(String deviceName, int vendorId, 516 int productId, String phys, int height, int width); nativeCloseUinput(long ptr)517 private static native void nativeCloseUinput(long ptr); nativeWriteDpadKeyEvent(long ptr, int androidKeyCode, int action, long eventTimeNanos)518 private static native boolean nativeWriteDpadKeyEvent(long ptr, int androidKeyCode, int action, 519 long eventTimeNanos); nativeWriteKeyEvent(long ptr, int androidKeyCode, int action, long eventTimeNanos)520 private static native boolean nativeWriteKeyEvent(long ptr, int androidKeyCode, int action, 521 long eventTimeNanos); nativeWriteButtonEvent(long ptr, int buttonCode, int action, long eventTimeNanos)522 private static native boolean nativeWriteButtonEvent(long ptr, int buttonCode, int action, 523 long eventTimeNanos); nativeWriteTouchEvent(long ptr, int pointerId, int toolType, int action, float locationX, float locationY, float pressure, float majorAxisSize, long eventTimeNanos)524 private static native boolean nativeWriteTouchEvent(long ptr, int pointerId, int toolType, 525 int action, float locationX, float locationY, float pressure, float majorAxisSize, 526 long eventTimeNanos); nativeWriteRelativeEvent(long ptr, float relativeX, float relativeY, long eventTimeNanos)527 private static native boolean nativeWriteRelativeEvent(long ptr, float relativeX, 528 float relativeY, long eventTimeNanos); nativeWriteScrollEvent(long ptr, float xAxisMovement, float yAxisMovement, long eventTimeNanos)529 private static native boolean nativeWriteScrollEvent(long ptr, float xAxisMovement, 530 float yAxisMovement, long eventTimeNanos); 531 532 /** Wrapper around the static native methods for tests. */ 533 @VisibleForTesting 534 protected static class NativeWrapper { openUinputDpad(String deviceName, int vendorId, int productId, String phys)535 public long openUinputDpad(String deviceName, int vendorId, int productId, String phys) { 536 return nativeOpenUinputDpad(deviceName, vendorId, productId, phys); 537 } 538 openUinputKeyboard(String deviceName, int vendorId, int productId, String phys)539 public long openUinputKeyboard(String deviceName, int vendorId, int productId, 540 String phys) { 541 return nativeOpenUinputKeyboard(deviceName, vendorId, productId, phys); 542 } 543 openUinputMouse(String deviceName, int vendorId, int productId, String phys)544 public long openUinputMouse(String deviceName, int vendorId, int productId, String phys) { 545 return nativeOpenUinputMouse(deviceName, vendorId, productId, phys); 546 } 547 openUinputTouchscreen(String deviceName, int vendorId, int productId, String phys, int height, int width)548 public long openUinputTouchscreen(String deviceName, int vendorId, 549 int productId, String phys, int height, int width) { 550 return nativeOpenUinputTouchscreen(deviceName, vendorId, productId, phys, height, 551 width); 552 } 553 closeUinput(long ptr)554 public void closeUinput(long ptr) { 555 nativeCloseUinput(ptr); 556 } 557 writeDpadKeyEvent(long ptr, int androidKeyCode, int action, long eventTimeNanos)558 public boolean writeDpadKeyEvent(long ptr, int androidKeyCode, int action, 559 long eventTimeNanos) { 560 return nativeWriteDpadKeyEvent(ptr, androidKeyCode, action, eventTimeNanos); 561 } 562 writeKeyEvent(long ptr, int androidKeyCode, int action, long eventTimeNanos)563 public boolean writeKeyEvent(long ptr, int androidKeyCode, int action, 564 long eventTimeNanos) { 565 return nativeWriteKeyEvent(ptr, androidKeyCode, action, eventTimeNanos); 566 } 567 writeButtonEvent(long ptr, int buttonCode, int action, long eventTimeNanos)568 public boolean writeButtonEvent(long ptr, int buttonCode, int action, 569 long eventTimeNanos) { 570 return nativeWriteButtonEvent(ptr, buttonCode, action, eventTimeNanos); 571 } 572 writeTouchEvent(long ptr, int pointerId, int toolType, int action, float locationX, float locationY, float pressure, float majorAxisSize, long eventTimeNanos)573 public boolean writeTouchEvent(long ptr, int pointerId, int toolType, int action, 574 float locationX, float locationY, float pressure, float majorAxisSize, 575 long eventTimeNanos) { 576 return nativeWriteTouchEvent(ptr, pointerId, toolType, 577 action, locationX, locationY, 578 pressure, majorAxisSize, eventTimeNanos); 579 } 580 writeRelativeEvent(long ptr, float relativeX, float relativeY, long eventTimeNanos)581 public boolean writeRelativeEvent(long ptr, float relativeX, float relativeY, 582 long eventTimeNanos) { 583 return nativeWriteRelativeEvent(ptr, relativeX, relativeY, eventTimeNanos); 584 } 585 writeScrollEvent(long ptr, float xAxisMovement, float yAxisMovement, long eventTimeNanos)586 public boolean writeScrollEvent(long ptr, float xAxisMovement, float yAxisMovement, 587 long eventTimeNanos) { 588 return nativeWriteScrollEvent(ptr, xAxisMovement, yAxisMovement, eventTimeNanos); 589 } 590 } 591 592 @VisibleForTesting static final class InputDeviceDescriptor { 593 594 static final int TYPE_KEYBOARD = 1; 595 static final int TYPE_MOUSE = 2; 596 static final int TYPE_TOUCHSCREEN = 3; 597 static final int TYPE_DPAD = 4; 598 static final int TYPE_NAVIGATION_TOUCHPAD = 5; 599 @IntDef(prefix = { "TYPE_" }, value = { 600 TYPE_KEYBOARD, 601 TYPE_MOUSE, 602 TYPE_TOUCHSCREEN, 603 TYPE_DPAD, 604 TYPE_NAVIGATION_TOUCHPAD, 605 }) 606 @Retention(RetentionPolicy.SOURCE) 607 @interface Type { 608 } 609 610 private static final AtomicLong sNextCreationOrderNumber = new AtomicLong(1); 611 612 // Pointer to the native input device object. 613 private final long mPtr; 614 private final IBinder.DeathRecipient mDeathRecipient; 615 private final @Type int mType; 616 private final int mDisplayId; 617 private final String mPhys; 618 // The name given to this device by the client. Enforced to be unique within 619 // InputController. 620 private final String mName; 621 // The input device id that was associated to the device by the InputReader on device 622 // creation. 623 private final int mInputDeviceId; 624 // Monotonically increasing number; devices with lower numbers were created earlier. 625 private final long mCreationOrderNumber; 626 InputDeviceDescriptor(long ptr, IBinder.DeathRecipient deathRecipient, @Type int type, int displayId, String phys, String name, int inputDeviceId)627 InputDeviceDescriptor(long ptr, IBinder.DeathRecipient deathRecipient, @Type int type, 628 int displayId, String phys, String name, int inputDeviceId) { 629 mPtr = ptr; 630 mDeathRecipient = deathRecipient; 631 mType = type; 632 mDisplayId = displayId; 633 mPhys = phys; 634 mName = name; 635 mInputDeviceId = inputDeviceId; 636 mCreationOrderNumber = sNextCreationOrderNumber.getAndIncrement(); 637 } 638 getNativePointer()639 public long getNativePointer() { 640 return mPtr; 641 } 642 getType()643 public int getType() { 644 return mType; 645 } 646 isMouse()647 public boolean isMouse() { 648 return mType == TYPE_MOUSE; 649 } 650 getDeathRecipient()651 public IBinder.DeathRecipient getDeathRecipient() { 652 return mDeathRecipient; 653 } 654 getDisplayId()655 public int getDisplayId() { 656 return mDisplayId; 657 } 658 getCreationOrderNumber()659 public long getCreationOrderNumber() { 660 return mCreationOrderNumber; 661 } 662 getPhys()663 public String getPhys() { 664 return mPhys; 665 } 666 getInputDeviceId()667 public int getInputDeviceId() { 668 return mInputDeviceId; 669 } 670 } 671 672 private final class BinderDeathRecipient implements IBinder.DeathRecipient { 673 674 private final IBinder mDeviceToken; 675 BinderDeathRecipient(IBinder deviceToken)676 BinderDeathRecipient(IBinder deviceToken) { 677 mDeviceToken = deviceToken; 678 } 679 680 @Override binderDied()681 public void binderDied() { 682 // All callers are expected to call {@link VirtualDevice#unregisterInputDevice} before 683 // quitting, which removes this death recipient. If this is invoked, the remote end 684 // died, or they disposed of the object without properly unregistering. 685 Slog.e(TAG, "Virtual input controller binder died"); 686 unregisterInputDevice(mDeviceToken); 687 } 688 } 689 690 /** A helper class used to wait for an input device to be registered. */ 691 private class WaitForDevice implements AutoCloseable { 692 private final CountDownLatch mDeviceAddedLatch = new CountDownLatch(1); 693 private final InputManager.InputDeviceListener mListener; 694 695 private int mInputDeviceId = IInputConstants.INVALID_INPUT_DEVICE_ID; 696 WaitForDevice(String deviceName, int vendorId, int productId)697 WaitForDevice(String deviceName, int vendorId, int productId) { 698 mListener = new InputManager.InputDeviceListener() { 699 @Override 700 public void onInputDeviceAdded(int deviceId) { 701 final InputDevice device = InputManagerGlobal.getInstance().getInputDevice( 702 deviceId); 703 Objects.requireNonNull(device, "Newly added input device was null."); 704 if (!device.getName().equals(deviceName)) { 705 return; 706 } 707 final InputDeviceIdentifier id = device.getIdentifier(); 708 if (id.getVendorId() != vendorId || id.getProductId() != productId) { 709 return; 710 } 711 mInputDeviceId = deviceId; 712 mDeviceAddedLatch.countDown(); 713 } 714 715 @Override 716 public void onInputDeviceRemoved(int deviceId) { 717 718 } 719 720 @Override 721 public void onInputDeviceChanged(int deviceId) { 722 723 } 724 }; 725 InputManagerGlobal.getInstance().registerInputDeviceListener(mListener, mHandler); 726 } 727 728 /** 729 * Note: This must not be called from {@link #mHandler}'s thread. 730 * @throws DeviceCreationException if the device was not created successfully within the 731 * timeout. 732 * @return The id of the created input device. 733 */ waitForDeviceCreation()734 int waitForDeviceCreation() throws DeviceCreationException { 735 try { 736 if (!mDeviceAddedLatch.await(1, TimeUnit.MINUTES)) { 737 throw new DeviceCreationException( 738 "Timed out waiting for virtual device to be created."); 739 } 740 } catch (InterruptedException e) { 741 throw new DeviceCreationException( 742 "Interrupted while waiting for virtual device to be created.", e); 743 } 744 if (mInputDeviceId == IInputConstants.INVALID_INPUT_DEVICE_ID) { 745 throw new IllegalStateException( 746 "Virtual input device was created with an invalid " 747 + "id=" + mInputDeviceId); 748 } 749 return mInputDeviceId; 750 } 751 752 @Override close()753 public void close() { 754 InputManagerGlobal.getInstance().unregisterInputDeviceListener(mListener); 755 } 756 } 757 758 /** An internal exception that is thrown to indicate an error when opening a virtual device. */ 759 private static class DeviceCreationException extends Exception { DeviceCreationException(String message)760 DeviceCreationException(String message) { 761 super(message); 762 } DeviceCreationException(String message, Exception cause)763 DeviceCreationException(String message, Exception cause) { 764 super(message, cause); 765 } 766 } 767 768 /** 769 * Creates a virtual input device synchronously, and waits for the notification that the device 770 * was added. 771 * 772 * Note: Input device creation is expected to happen on a binder thread, and the calling thread 773 * will be blocked until the input device creation is successful. This should not be called on 774 * the handler's thread. 775 * 776 * @throws DeviceCreationException Throws this exception if anything unexpected happens in the 777 * process of creating the device. This method will take care 778 * to restore the state of the system in the event of any 779 * unexpected behavior. 780 */ createDeviceInternal(@nputDeviceDescriptor.Type int type, String deviceName, int vendorId, int productId, IBinder deviceToken, int displayId, String phys, Supplier<Long> deviceOpener)781 private void createDeviceInternal(@InputDeviceDescriptor.Type int type, String deviceName, 782 int vendorId, int productId, IBinder deviceToken, int displayId, String phys, 783 Supplier<Long> deviceOpener) 784 throws DeviceCreationException { 785 if (!mThreadVerifier.isValidThread()) { 786 throw new IllegalStateException( 787 "Virtual device creation should happen on an auxiliary thread (e.g. binder " 788 + "thread) and not from the handler's thread."); 789 } 790 validateDeviceName(deviceName); 791 792 final long ptr; 793 final BinderDeathRecipient binderDeathRecipient; 794 795 final int inputDeviceId; 796 797 setUniqueIdAssociation(displayId, phys); 798 try (WaitForDevice waiter = new WaitForDevice(deviceName, vendorId, productId)) { 799 ptr = deviceOpener.get(); 800 // See INVALID_PTR in libs/input/VirtualInputDevice.cpp. 801 if (ptr == 0) { 802 throw new DeviceCreationException( 803 "A native error occurred when creating virtual input device: " 804 + deviceName); 805 } 806 // The pointer to the native input device is valid from here, so ensure that all 807 // failures close the device after this point. 808 try { 809 inputDeviceId = waiter.waitForDeviceCreation(); 810 811 binderDeathRecipient = new BinderDeathRecipient(deviceToken); 812 try { 813 deviceToken.linkToDeath(binderDeathRecipient, /* flags= */ 0); 814 } catch (RemoteException e) { 815 throw new DeviceCreationException( 816 "Client died before virtual device could be created.", e); 817 } 818 } catch (DeviceCreationException e) { 819 mNativeWrapper.closeUinput(ptr); 820 throw e; 821 } 822 } catch (DeviceCreationException e) { 823 InputManagerGlobal.getInstance().removeUniqueIdAssociation(phys); 824 throw e; 825 } 826 827 synchronized (mLock) { 828 mInputDeviceDescriptors.put(deviceToken, 829 new InputDeviceDescriptor(ptr, binderDeathRecipient, type, displayId, phys, 830 deviceName, inputDeviceId)); 831 } 832 } 833 834 @VisibleForTesting 835 interface DeviceCreationThreadVerifier { 836 /** Returns true if the calling thread is a valid thread for device creation. */ isValidThread()837 boolean isValidThread(); 838 } 839 } 840