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.Point; 23 import android.graphics.PointF; 24 import android.hardware.display.DisplayManagerInternal; 25 import android.hardware.input.InputDeviceIdentifier; 26 import android.hardware.input.InputManager; 27 import android.hardware.input.InputManagerInternal; 28 import android.hardware.input.VirtualKeyEvent; 29 import android.hardware.input.VirtualMouseButtonEvent; 30 import android.hardware.input.VirtualMouseRelativeEvent; 31 import android.hardware.input.VirtualMouseScrollEvent; 32 import android.hardware.input.VirtualTouchEvent; 33 import android.os.Handler; 34 import android.os.IBinder; 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 46 import java.io.PrintWriter; 47 import java.lang.annotation.Retention; 48 import java.lang.annotation.RetentionPolicy; 49 import java.util.Iterator; 50 import java.util.Map; 51 import java.util.Objects; 52 import java.util.concurrent.CountDownLatch; 53 import java.util.concurrent.TimeUnit; 54 import java.util.concurrent.atomic.AtomicLong; 55 import java.util.function.Supplier; 56 57 /** Controls virtual input devices, including device lifecycle and event dispatch. */ 58 class InputController { 59 60 private static final String TAG = "VirtualInputController"; 61 62 private static final AtomicLong sNextPhysId = new AtomicLong(1); 63 64 static final String PHYS_TYPE_KEYBOARD = "Keyboard"; 65 static final String PHYS_TYPE_MOUSE = "Mouse"; 66 static final String PHYS_TYPE_TOUCHSCREEN = "Touchscreen"; 67 @StringDef(prefix = { "PHYS_TYPE_" }, value = { 68 PHYS_TYPE_KEYBOARD, 69 PHYS_TYPE_MOUSE, 70 PHYS_TYPE_TOUCHSCREEN, 71 }) 72 @Retention(RetentionPolicy.SOURCE) 73 @interface PhysType { 74 } 75 76 private final Object mLock; 77 78 /* Token -> file descriptor associations. */ 79 @VisibleForTesting 80 @GuardedBy("mLock") 81 final Map<IBinder, InputDeviceDescriptor> mInputDeviceDescriptors = new ArrayMap<>(); 82 83 private final Handler mHandler; 84 private final NativeWrapper mNativeWrapper; 85 private final DisplayManagerInternal mDisplayManagerInternal; 86 private final InputManagerInternal mInputManagerInternal; 87 private final WindowManager mWindowManager; 88 private final DeviceCreationThreadVerifier mThreadVerifier; 89 InputController(@onNull Object lock, @NonNull Handler handler, @NonNull WindowManager windowManager)90 InputController(@NonNull Object lock, @NonNull Handler handler, 91 @NonNull WindowManager windowManager) { 92 this(lock, new NativeWrapper(), handler, windowManager, 93 // Verify that virtual devices are not created on the handler thread. 94 () -> !handler.getLooper().isCurrentThread()); 95 } 96 97 @VisibleForTesting InputController(@onNull Object lock, @NonNull NativeWrapper nativeWrapper, @NonNull Handler handler, @NonNull WindowManager windowManager, @NonNull DeviceCreationThreadVerifier threadVerifier)98 InputController(@NonNull Object lock, @NonNull NativeWrapper nativeWrapper, 99 @NonNull Handler handler, @NonNull WindowManager windowManager, 100 @NonNull DeviceCreationThreadVerifier threadVerifier) { 101 mLock = lock; 102 mHandler = handler; 103 mNativeWrapper = nativeWrapper; 104 mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class); 105 mInputManagerInternal = LocalServices.getService(InputManagerInternal.class); 106 mWindowManager = windowManager; 107 mThreadVerifier = threadVerifier; 108 } 109 close()110 void close() { 111 synchronized (mLock) { 112 final Iterator<Map.Entry<IBinder, InputDeviceDescriptor>> iterator = 113 mInputDeviceDescriptors.entrySet().iterator(); 114 if (iterator.hasNext()) { 115 final Map.Entry<IBinder, InputDeviceDescriptor> entry = iterator.next(); 116 final IBinder token = entry.getKey(); 117 final InputDeviceDescriptor inputDeviceDescriptor = entry.getValue(); 118 iterator.remove(); 119 closeInputDeviceDescriptorLocked(token, inputDeviceDescriptor); 120 } 121 } 122 } 123 createKeyboard(@onNull String deviceName, int vendorId, int productId, @NonNull IBinder deviceToken, int displayId)124 void createKeyboard(@NonNull String deviceName, 125 int vendorId, 126 int productId, 127 @NonNull IBinder deviceToken, 128 int displayId) { 129 final String phys = createPhys(PHYS_TYPE_KEYBOARD); 130 try { 131 createDeviceInternal(InputDeviceDescriptor.TYPE_KEYBOARD, deviceName, vendorId, 132 productId, deviceToken, displayId, phys, 133 () -> mNativeWrapper.openUinputKeyboard(deviceName, vendorId, productId, phys)); 134 } catch (DeviceCreationException e) { 135 throw new RuntimeException( 136 "Failed to create virtual keyboard device '" + deviceName + "'.", e); 137 } 138 } 139 createMouse(@onNull String deviceName, int vendorId, int productId, @NonNull IBinder deviceToken, int displayId)140 void createMouse(@NonNull String deviceName, 141 int vendorId, 142 int productId, 143 @NonNull IBinder deviceToken, 144 int displayId) { 145 final String phys = createPhys(PHYS_TYPE_MOUSE); 146 try { 147 createDeviceInternal(InputDeviceDescriptor.TYPE_MOUSE, deviceName, vendorId, productId, 148 deviceToken, displayId, phys, 149 () -> mNativeWrapper.openUinputMouse(deviceName, vendorId, productId, phys)); 150 } catch (DeviceCreationException e) { 151 throw new RuntimeException( 152 "Failed to create virtual mouse device: '" + deviceName + "'.", e); 153 } 154 mInputManagerInternal.setVirtualMousePointerDisplayId(displayId); 155 } 156 createTouchscreen(@onNull String deviceName, int vendorId, int productId, @NonNull IBinder deviceToken, int displayId, @NonNull Point screenSize)157 void createTouchscreen(@NonNull String deviceName, 158 int vendorId, 159 int productId, 160 @NonNull IBinder deviceToken, 161 int displayId, 162 @NonNull Point screenSize) { 163 final String phys = createPhys(PHYS_TYPE_TOUCHSCREEN); 164 try { 165 createDeviceInternal(InputDeviceDescriptor.TYPE_TOUCHSCREEN, deviceName, vendorId, 166 productId, deviceToken, displayId, phys, 167 () -> mNativeWrapper.openUinputTouchscreen(deviceName, vendorId, productId, 168 phys, screenSize.y, screenSize.x)); 169 } catch (DeviceCreationException e) { 170 throw new RuntimeException( 171 "Failed to create virtual touchscreen device '" + deviceName + "'.", e); 172 } 173 } 174 unregisterInputDevice(@onNull IBinder token)175 void unregisterInputDevice(@NonNull IBinder token) { 176 synchronized (mLock) { 177 final InputDeviceDescriptor inputDeviceDescriptor = mInputDeviceDescriptors.remove( 178 token); 179 if (inputDeviceDescriptor == null) { 180 throw new IllegalArgumentException( 181 "Could not unregister input device for given token"); 182 } 183 closeInputDeviceDescriptorLocked(token, inputDeviceDescriptor); 184 } 185 } 186 187 @GuardedBy("mLock") closeInputDeviceDescriptorLocked(IBinder token, InputDeviceDescriptor inputDeviceDescriptor)188 private void closeInputDeviceDescriptorLocked(IBinder token, 189 InputDeviceDescriptor inputDeviceDescriptor) { 190 token.unlinkToDeath(inputDeviceDescriptor.getDeathRecipient(), /* flags= */ 0); 191 mNativeWrapper.closeUinput(inputDeviceDescriptor.getFileDescriptor()); 192 InputManager.getInstance().removeUniqueIdAssociation(inputDeviceDescriptor.getPhys()); 193 194 // Reset values to the default if all virtual mice are unregistered, or set display 195 // id if there's another mouse (choose the most recent). The inputDeviceDescriptor must be 196 // removed from the mInputDeviceDescriptors instance variable prior to this point. 197 if (inputDeviceDescriptor.isMouse()) { 198 if (mInputManagerInternal.getVirtualMousePointerDisplayId() 199 == inputDeviceDescriptor.getDisplayId()) { 200 updateActivePointerDisplayIdLocked(); 201 } 202 } 203 } 204 setShowPointerIcon(boolean visible, int displayId)205 void setShowPointerIcon(boolean visible, int displayId) { 206 mInputManagerInternal.setPointerIconVisible(visible, displayId); 207 } 208 setPointerAcceleration(float pointerAcceleration, int displayId)209 void setPointerAcceleration(float pointerAcceleration, int displayId) { 210 mInputManagerInternal.setPointerAcceleration(pointerAcceleration, displayId); 211 } 212 setDisplayEligibilityForPointerCapture(boolean isEligible, int displayId)213 void setDisplayEligibilityForPointerCapture(boolean isEligible, int displayId) { 214 mInputManagerInternal.setDisplayEligibilityForPointerCapture(displayId, isEligible); 215 } 216 setLocalIme(int displayId)217 void setLocalIme(int displayId) { 218 // WM throws a SecurityException if the display is untrusted. 219 if ((mDisplayManagerInternal.getDisplayInfo(displayId).flags & Display.FLAG_TRUSTED) 220 == Display.FLAG_TRUSTED) { 221 mWindowManager.setDisplayImePolicy(displayId, 222 WindowManager.DISPLAY_IME_POLICY_LOCAL); 223 } 224 } 225 226 @GuardedBy("mLock") updateActivePointerDisplayIdLocked()227 private void updateActivePointerDisplayIdLocked() { 228 InputDeviceDescriptor mostRecentlyCreatedMouse = null; 229 for (InputDeviceDescriptor otherInputDeviceDescriptor : mInputDeviceDescriptors.values()) { 230 if (otherInputDeviceDescriptor.isMouse()) { 231 if (mostRecentlyCreatedMouse == null 232 || (otherInputDeviceDescriptor.getCreationOrderNumber() 233 > mostRecentlyCreatedMouse.getCreationOrderNumber())) { 234 mostRecentlyCreatedMouse = otherInputDeviceDescriptor; 235 } 236 } 237 } 238 if (mostRecentlyCreatedMouse != null) { 239 mInputManagerInternal.setVirtualMousePointerDisplayId( 240 mostRecentlyCreatedMouse.getDisplayId()); 241 } else { 242 // All mice have been unregistered 243 mInputManagerInternal.setVirtualMousePointerDisplayId(Display.INVALID_DISPLAY); 244 } 245 } 246 createPhys(@hysType String type)247 private static String createPhys(@PhysType String type) { 248 return String.format("virtual%s:%d", type, sNextPhysId.getAndIncrement()); 249 } 250 setUniqueIdAssociation(int displayId, String phys)251 private void setUniqueIdAssociation(int displayId, String phys) { 252 final String displayUniqueId = mDisplayManagerInternal.getDisplayInfo(displayId).uniqueId; 253 InputManager.getInstance().addUniqueIdAssociation(phys, displayUniqueId); 254 } 255 sendKeyEvent(@onNull IBinder token, @NonNull VirtualKeyEvent event)256 boolean sendKeyEvent(@NonNull IBinder token, @NonNull VirtualKeyEvent event) { 257 synchronized (mLock) { 258 final InputDeviceDescriptor inputDeviceDescriptor = mInputDeviceDescriptors.get( 259 token); 260 if (inputDeviceDescriptor == null) { 261 throw new IllegalArgumentException( 262 "Could not send key event to input device for given token"); 263 } 264 return mNativeWrapper.writeKeyEvent(inputDeviceDescriptor.getFileDescriptor(), 265 event.getKeyCode(), event.getAction()); 266 } 267 } 268 sendButtonEvent(@onNull IBinder token, @NonNull VirtualMouseButtonEvent event)269 boolean sendButtonEvent(@NonNull IBinder token, @NonNull VirtualMouseButtonEvent event) { 270 synchronized (mLock) { 271 final InputDeviceDescriptor inputDeviceDescriptor = mInputDeviceDescriptors.get( 272 token); 273 if (inputDeviceDescriptor == null) { 274 throw new IllegalArgumentException( 275 "Could not send button event to input device for given token"); 276 } 277 if (inputDeviceDescriptor.getDisplayId() 278 != mInputManagerInternal.getVirtualMousePointerDisplayId()) { 279 throw new IllegalStateException( 280 "Display id associated with this mouse is not currently targetable"); 281 } 282 return mNativeWrapper.writeButtonEvent(inputDeviceDescriptor.getFileDescriptor(), 283 event.getButtonCode(), event.getAction()); 284 } 285 } 286 sendTouchEvent(@onNull IBinder token, @NonNull VirtualTouchEvent event)287 boolean sendTouchEvent(@NonNull IBinder token, @NonNull VirtualTouchEvent event) { 288 synchronized (mLock) { 289 final InputDeviceDescriptor inputDeviceDescriptor = mInputDeviceDescriptors.get( 290 token); 291 if (inputDeviceDescriptor == null) { 292 throw new IllegalArgumentException( 293 "Could not send touch event to input device for given token"); 294 } 295 return mNativeWrapper.writeTouchEvent(inputDeviceDescriptor.getFileDescriptor(), 296 event.getPointerId(), event.getToolType(), event.getAction(), event.getX(), 297 event.getY(), event.getPressure(), event.getMajorAxisSize()); 298 } 299 } 300 sendRelativeEvent(@onNull IBinder token, @NonNull VirtualMouseRelativeEvent event)301 boolean sendRelativeEvent(@NonNull IBinder token, @NonNull VirtualMouseRelativeEvent event) { 302 synchronized (mLock) { 303 final InputDeviceDescriptor inputDeviceDescriptor = mInputDeviceDescriptors.get( 304 token); 305 if (inputDeviceDescriptor == null) { 306 throw new IllegalArgumentException( 307 "Could not send relative event to input device for given token"); 308 } 309 if (inputDeviceDescriptor.getDisplayId() 310 != mInputManagerInternal.getVirtualMousePointerDisplayId()) { 311 throw new IllegalStateException( 312 "Display id associated with this mouse is not currently targetable"); 313 } 314 return mNativeWrapper.writeRelativeEvent(inputDeviceDescriptor.getFileDescriptor(), 315 event.getRelativeX(), event.getRelativeY()); 316 } 317 } 318 sendScrollEvent(@onNull IBinder token, @NonNull VirtualMouseScrollEvent event)319 boolean sendScrollEvent(@NonNull IBinder token, @NonNull VirtualMouseScrollEvent event) { 320 synchronized (mLock) { 321 final InputDeviceDescriptor inputDeviceDescriptor = mInputDeviceDescriptors.get( 322 token); 323 if (inputDeviceDescriptor == null) { 324 throw new IllegalArgumentException( 325 "Could not send scroll event to input device for given token"); 326 } 327 if (inputDeviceDescriptor.getDisplayId() 328 != mInputManagerInternal.getVirtualMousePointerDisplayId()) { 329 throw new IllegalStateException( 330 "Display id associated with this mouse is not currently targetable"); 331 } 332 return mNativeWrapper.writeScrollEvent(inputDeviceDescriptor.getFileDescriptor(), 333 event.getXAxisMovement(), event.getYAxisMovement()); 334 } 335 } 336 getCursorPosition(@onNull IBinder token)337 public PointF getCursorPosition(@NonNull IBinder token) { 338 synchronized (mLock) { 339 final InputDeviceDescriptor inputDeviceDescriptor = mInputDeviceDescriptors.get( 340 token); 341 if (inputDeviceDescriptor == null) { 342 throw new IllegalArgumentException( 343 "Could not get cursor position for input device for given token"); 344 } 345 if (inputDeviceDescriptor.getDisplayId() 346 != mInputManagerInternal.getVirtualMousePointerDisplayId()) { 347 throw new IllegalStateException( 348 "Display id associated with this mouse is not currently targetable"); 349 } 350 return LocalServices.getService(InputManagerInternal.class).getCursorPosition(); 351 } 352 } 353 dump(@onNull PrintWriter fout)354 public void dump(@NonNull PrintWriter fout) { 355 fout.println(" InputController: "); 356 synchronized (mLock) { 357 fout.println(" Active descriptors: "); 358 for (InputDeviceDescriptor inputDeviceDescriptor : mInputDeviceDescriptors.values()) { 359 fout.println(" fd: " + inputDeviceDescriptor.getFileDescriptor()); 360 fout.println(" displayId: " + inputDeviceDescriptor.getDisplayId()); 361 fout.println(" creationOrder: " 362 + inputDeviceDescriptor.getCreationOrderNumber()); 363 fout.println(" type: " + inputDeviceDescriptor.getType()); 364 fout.println(" phys: " + inputDeviceDescriptor.getPhys()); 365 } 366 } 367 } 368 nativeOpenUinputKeyboard(String deviceName, int vendorId, int productId, String phys)369 private static native int nativeOpenUinputKeyboard(String deviceName, int vendorId, 370 int productId, String phys); nativeOpenUinputMouse(String deviceName, int vendorId, int productId, String phys)371 private static native int nativeOpenUinputMouse(String deviceName, int vendorId, int productId, 372 String phys); nativeOpenUinputTouchscreen(String deviceName, int vendorId, int productId, String phys, int height, int width)373 private static native int nativeOpenUinputTouchscreen(String deviceName, int vendorId, 374 int productId, String phys, int height, int width); nativeCloseUinput(int fd)375 private static native boolean nativeCloseUinput(int fd); nativeWriteKeyEvent(int fd, int androidKeyCode, int action)376 private static native boolean nativeWriteKeyEvent(int fd, int androidKeyCode, int action); nativeWriteButtonEvent(int fd, int buttonCode, int action)377 private static native boolean nativeWriteButtonEvent(int fd, int buttonCode, int action); nativeWriteTouchEvent(int fd, int pointerId, int toolType, int action, float locationX, float locationY, float pressure, float majorAxisSize)378 private static native boolean nativeWriteTouchEvent(int fd, int pointerId, int toolType, 379 int action, float locationX, float locationY, float pressure, float majorAxisSize); nativeWriteRelativeEvent(int fd, float relativeX, float relativeY)380 private static native boolean nativeWriteRelativeEvent(int fd, float relativeX, 381 float relativeY); nativeWriteScrollEvent(int fd, float xAxisMovement, float yAxisMovement)382 private static native boolean nativeWriteScrollEvent(int fd, float xAxisMovement, 383 float yAxisMovement); 384 385 /** Wrapper around the static native methods for tests. */ 386 @VisibleForTesting 387 protected static class NativeWrapper { openUinputKeyboard(String deviceName, int vendorId, int productId, String phys)388 public int openUinputKeyboard(String deviceName, int vendorId, int productId, String phys) { 389 return nativeOpenUinputKeyboard(deviceName, vendorId, productId, phys); 390 } 391 openUinputMouse(String deviceName, int vendorId, int productId, String phys)392 public int openUinputMouse(String deviceName, int vendorId, int productId, String phys) { 393 return nativeOpenUinputMouse(deviceName, vendorId, productId, phys); 394 } 395 openUinputTouchscreen(String deviceName, int vendorId, int productId, String phys, int height, int width)396 public int openUinputTouchscreen(String deviceName, int vendorId, 397 int productId, String phys, int height, int width) { 398 return nativeOpenUinputTouchscreen(deviceName, vendorId, productId, phys, height, 399 width); 400 } 401 closeUinput(int fd)402 public boolean closeUinput(int fd) { 403 return nativeCloseUinput(fd); 404 } 405 writeKeyEvent(int fd, int androidKeyCode, int action)406 public boolean writeKeyEvent(int fd, int androidKeyCode, int action) { 407 return nativeWriteKeyEvent(fd, androidKeyCode, action); 408 } 409 writeButtonEvent(int fd, int buttonCode, int action)410 public boolean writeButtonEvent(int fd, int buttonCode, int action) { 411 return nativeWriteButtonEvent(fd, buttonCode, action); 412 } 413 writeTouchEvent(int fd, int pointerId, int toolType, int action, float locationX, float locationY, float pressure, float majorAxisSize)414 public boolean writeTouchEvent(int fd, int pointerId, int toolType, int action, 415 float locationX, float locationY, float pressure, float majorAxisSize) { 416 return nativeWriteTouchEvent(fd, pointerId, toolType, 417 action, locationX, locationY, 418 pressure, majorAxisSize); 419 } 420 writeRelativeEvent(int fd, float relativeX, float relativeY)421 public boolean writeRelativeEvent(int fd, float relativeX, float relativeY) { 422 return nativeWriteRelativeEvent(fd, relativeX, relativeY); 423 } 424 writeScrollEvent(int fd, float xAxisMovement, float yAxisMovement)425 public boolean writeScrollEvent(int fd, float xAxisMovement, float yAxisMovement) { 426 return nativeWriteScrollEvent(fd, xAxisMovement, 427 yAxisMovement); 428 } 429 } 430 431 @VisibleForTesting static final class InputDeviceDescriptor { 432 433 static final int TYPE_KEYBOARD = 1; 434 static final int TYPE_MOUSE = 2; 435 static final int TYPE_TOUCHSCREEN = 3; 436 @IntDef(prefix = { "TYPE_" }, value = { 437 TYPE_KEYBOARD, 438 TYPE_MOUSE, 439 TYPE_TOUCHSCREEN, 440 }) 441 @Retention(RetentionPolicy.SOURCE) 442 @interface Type { 443 } 444 445 private static final AtomicLong sNextCreationOrderNumber = new AtomicLong(1); 446 447 private final int mFd; 448 private final IBinder.DeathRecipient mDeathRecipient; 449 private final @Type int mType; 450 private final int mDisplayId; 451 private final String mPhys; 452 // Monotonically increasing number; devices with lower numbers were created earlier. 453 private final long mCreationOrderNumber; 454 InputDeviceDescriptor(int fd, IBinder.DeathRecipient deathRecipient, @Type int type, int displayId, String phys)455 InputDeviceDescriptor(int fd, IBinder.DeathRecipient deathRecipient, @Type int type, 456 int displayId, String phys) { 457 mFd = fd; 458 mDeathRecipient = deathRecipient; 459 mType = type; 460 mDisplayId = displayId; 461 mPhys = phys; 462 mCreationOrderNumber = sNextCreationOrderNumber.getAndIncrement(); 463 } 464 getFileDescriptor()465 public int getFileDescriptor() { 466 return mFd; 467 } 468 getType()469 public int getType() { 470 return mType; 471 } 472 isMouse()473 public boolean isMouse() { 474 return mType == TYPE_MOUSE; 475 } 476 getDeathRecipient()477 public IBinder.DeathRecipient getDeathRecipient() { 478 return mDeathRecipient; 479 } 480 getDisplayId()481 public int getDisplayId() { 482 return mDisplayId; 483 } 484 getCreationOrderNumber()485 public long getCreationOrderNumber() { 486 return mCreationOrderNumber; 487 } 488 getPhys()489 public String getPhys() { 490 return mPhys; 491 } 492 } 493 494 private final class BinderDeathRecipient implements IBinder.DeathRecipient { 495 496 private final IBinder mDeviceToken; 497 BinderDeathRecipient(IBinder deviceToken)498 BinderDeathRecipient(IBinder deviceToken) { 499 mDeviceToken = deviceToken; 500 } 501 502 @Override binderDied()503 public void binderDied() { 504 // All callers are expected to call {@link VirtualDevice#unregisterInputDevice} before 505 // quitting, which removes this death recipient. If this is invoked, the remote end 506 // died, or they disposed of the object without properly unregistering. 507 Slog.e(TAG, "Virtual input controller binder died"); 508 unregisterInputDevice(mDeviceToken); 509 } 510 } 511 512 /** A helper class used to wait for an input device to be registered. */ 513 private class WaitForDevice implements AutoCloseable { 514 private final CountDownLatch mDeviceAddedLatch = new CountDownLatch(1); 515 private final InputManager.InputDeviceListener mListener; 516 WaitForDevice(String deviceName, int vendorId, int productId)517 WaitForDevice(String deviceName, int vendorId, int productId) { 518 mListener = new InputManager.InputDeviceListener() { 519 @Override 520 public void onInputDeviceAdded(int deviceId) { 521 final InputDevice device = InputManager.getInstance().getInputDevice( 522 deviceId); 523 Objects.requireNonNull(device, "Newly added input device was null."); 524 if (!device.getName().equals(deviceName)) { 525 return; 526 } 527 final InputDeviceIdentifier id = device.getIdentifier(); 528 if (id.getVendorId() != vendorId || id.getProductId() != productId) { 529 return; 530 } 531 mDeviceAddedLatch.countDown(); 532 } 533 534 @Override 535 public void onInputDeviceRemoved(int deviceId) { 536 537 } 538 539 @Override 540 public void onInputDeviceChanged(int deviceId) { 541 542 } 543 }; 544 InputManager.getInstance().registerInputDeviceListener(mListener, mHandler); 545 } 546 547 /** Note: This must not be called from {@link #mHandler}'s thread. */ waitForDeviceCreation()548 void waitForDeviceCreation() throws DeviceCreationException { 549 try { 550 if (!mDeviceAddedLatch.await(1, TimeUnit.MINUTES)) { 551 throw new DeviceCreationException( 552 "Timed out waiting for virtual device to be created."); 553 } 554 } catch (InterruptedException e) { 555 throw new DeviceCreationException( 556 "Interrupted while waiting for virtual device to be created.", e); 557 } 558 } 559 560 @Override close()561 public void close() { 562 InputManager.getInstance().unregisterInputDeviceListener(mListener); 563 } 564 } 565 566 /** An internal exception that is thrown to indicate an error when opening a virtual device. */ 567 private static class DeviceCreationException extends Exception { DeviceCreationException(String message)568 DeviceCreationException(String message) { 569 super(message); 570 } DeviceCreationException(String message, Exception cause)571 DeviceCreationException(String message, Exception cause) { 572 super(message, cause); 573 } 574 } 575 576 /** 577 * Creates a virtual input device synchronously, and waits for the notification that the device 578 * was added. 579 * 580 * Note: Input device creation is expected to happen on a binder thread, and the calling thread 581 * will be blocked until the input device creation is successful. This should not be called on 582 * the handler's thread. 583 * 584 * @throws DeviceCreationException Throws this exception if anything unexpected happens in the 585 * process of creating the device. This method will take care 586 * to restore the state of the system in the event of any 587 * unexpected behavior. 588 */ createDeviceInternal(@nputDeviceDescriptor.Type int type, String deviceName, int vendorId, int productId, IBinder deviceToken, int displayId, String phys, Supplier<Integer> deviceOpener)589 private void createDeviceInternal(@InputDeviceDescriptor.Type int type, String deviceName, 590 int vendorId, int productId, IBinder deviceToken, int displayId, String phys, 591 Supplier<Integer> deviceOpener) 592 throws DeviceCreationException { 593 if (!mThreadVerifier.isValidThread()) { 594 throw new IllegalStateException( 595 "Virtual device creation should happen on an auxiliary thread (e.g. binder " 596 + "thread) and not from the handler's thread."); 597 } 598 599 final int fd; 600 final BinderDeathRecipient binderDeathRecipient; 601 602 setUniqueIdAssociation(displayId, phys); 603 try (WaitForDevice waiter = new WaitForDevice(deviceName, vendorId, productId)) { 604 fd = deviceOpener.get(); 605 if (fd < 0) { 606 throw new DeviceCreationException( 607 "A native error occurred when creating touchscreen: " + -fd); 608 } 609 // The fd is valid from here, so ensure that all failures close the fd after this point. 610 try { 611 waiter.waitForDeviceCreation(); 612 613 binderDeathRecipient = new BinderDeathRecipient(deviceToken); 614 try { 615 deviceToken.linkToDeath(binderDeathRecipient, /* flags= */ 0); 616 } catch (RemoteException e) { 617 throw new DeviceCreationException( 618 "Client died before virtual device could be created.", e); 619 } 620 } catch (DeviceCreationException e) { 621 mNativeWrapper.closeUinput(fd); 622 throw e; 623 } 624 } catch (DeviceCreationException e) { 625 InputManager.getInstance().removeUniqueIdAssociation(phys); 626 throw e; 627 } 628 629 synchronized (mLock) { 630 mInputDeviceDescriptors.put(deviceToken, 631 new InputDeviceDescriptor(fd, binderDeathRecipient, type, displayId, phys)); 632 } 633 } 634 635 @VisibleForTesting 636 interface DeviceCreationThreadVerifier { 637 /** Returns true if the calling thread is a valid thread for device creation. */ isValidThread()638 boolean isValidThread(); 639 } 640 } 641