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