1 /* 2 * Copyright (C) 2012 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 android.hardware.input; 18 19 import android.annotation.CallbackExecutor; 20 import android.annotation.IntDef; 21 import android.annotation.NonNull; 22 import android.annotation.Nullable; 23 import android.annotation.SdkConstant; 24 import android.annotation.SdkConstant.SdkConstantType; 25 import android.annotation.SystemService; 26 import android.compat.annotation.UnsupportedAppUsage; 27 import android.content.Context; 28 import android.media.AudioAttributes; 29 import android.os.Binder; 30 import android.os.Build; 31 import android.os.Handler; 32 import android.os.IBinder; 33 import android.os.Looper; 34 import android.os.Message; 35 import android.os.RemoteException; 36 import android.os.ServiceManager; 37 import android.os.ServiceManager.ServiceNotFoundException; 38 import android.os.SystemClock; 39 import android.os.VibrationEffect; 40 import android.os.Vibrator; 41 import android.provider.Settings; 42 import android.provider.Settings.SettingNotFoundException; 43 import android.util.Log; 44 import android.util.SparseArray; 45 import android.view.InputDevice; 46 import android.view.InputEvent; 47 import android.view.InputMonitor; 48 import android.view.MotionEvent; 49 import android.view.PointerIcon; 50 import android.view.VerifiedInputEvent; 51 52 import com.android.internal.os.SomeArgs; 53 54 import java.lang.annotation.Retention; 55 import java.lang.annotation.RetentionPolicy; 56 import java.util.ArrayList; 57 import java.util.concurrent.Executor; 58 import java.util.List; 59 60 /** 61 * Provides information about input devices and available key layouts. 62 */ 63 @SystemService(Context.INPUT_SERVICE) 64 public final class InputManager { 65 private static final String TAG = "InputManager"; 66 private static final boolean DEBUG = false; 67 68 private static final int MSG_DEVICE_ADDED = 1; 69 private static final int MSG_DEVICE_REMOVED = 2; 70 private static final int MSG_DEVICE_CHANGED = 3; 71 72 private static InputManager sInstance; 73 74 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) 75 private final IInputManager mIm; 76 77 // Guarded by mInputDevicesLock 78 private final Object mInputDevicesLock = new Object(); 79 private SparseArray<InputDevice> mInputDevices; 80 private InputDevicesChangedListener mInputDevicesChangedListener; 81 private final ArrayList<InputDeviceListenerDelegate> mInputDeviceListeners = 82 new ArrayList<InputDeviceListenerDelegate>(); 83 84 // Guarded by mTabletModeLock 85 private final Object mTabletModeLock = new Object(); 86 private TabletModeChangedListener mTabletModeChangedListener; 87 private List<OnTabletModeChangedListenerDelegate> mOnTabletModeChangedListeners; 88 89 /** 90 * Broadcast Action: Query available keyboard layouts. 91 * <p> 92 * The input manager service locates available keyboard layouts 93 * by querying broadcast receivers that are registered for this action. 94 * An application can offer additional keyboard layouts to the user 95 * by declaring a suitable broadcast receiver in its manifest. 96 * </p><p> 97 * Here is an example broadcast receiver declaration that an application 98 * might include in its AndroidManifest.xml to advertise keyboard layouts. 99 * The meta-data specifies a resource that contains a description of each keyboard 100 * layout that is provided by the application. 101 * <pre><code> 102 * <receiver android:name=".InputDeviceReceiver" 103 * android:label="@string/keyboard_layouts_label"> 104 * <intent-filter> 105 * <action android:name="android.hardware.input.action.QUERY_KEYBOARD_LAYOUTS" /> 106 * </intent-filter> 107 * <meta-data android:name="android.hardware.input.metadata.KEYBOARD_LAYOUTS" 108 * android:resource="@xml/keyboard_layouts" /> 109 * </receiver> 110 * </code></pre> 111 * </p><p> 112 * In the above example, the <code>@xml/keyboard_layouts</code> resource refers to 113 * an XML resource whose root element is <code><keyboard-layouts></code> that 114 * contains zero or more <code><keyboard-layout></code> elements. 115 * Each <code><keyboard-layout></code> element specifies the name, label, and location 116 * of a key character map for a particular keyboard layout. The label on the receiver 117 * is used to name the collection of keyboard layouts provided by this receiver in the 118 * keyboard layout settings. 119 * <pre><code> 120 * <?xml version="1.0" encoding="utf-8"?> 121 * <keyboard-layouts xmlns:android="http://schemas.android.com/apk/res/android"> 122 * <keyboard-layout android:name="keyboard_layout_english_us" 123 * android:label="@string/keyboard_layout_english_us_label" 124 * android:keyboardLayout="@raw/keyboard_layout_english_us" /> 125 * </keyboard-layouts> 126 * </pre></code> 127 * </p><p> 128 * The <code>android:name</code> attribute specifies an identifier by which 129 * the keyboard layout will be known in the package. 130 * The <code>android:label</code> attribute specifies a human-readable descriptive 131 * label to describe the keyboard layout in the user interface, such as "English (US)". 132 * The <code>android:keyboardLayout</code> attribute refers to a 133 * <a href="http://source.android.com/tech/input/key-character-map-files.html"> 134 * key character map</a> resource that defines the keyboard layout. 135 * </p> 136 */ 137 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) 138 public static final String ACTION_QUERY_KEYBOARD_LAYOUTS = 139 "android.hardware.input.action.QUERY_KEYBOARD_LAYOUTS"; 140 141 /** 142 * Metadata Key: Keyboard layout metadata associated with 143 * {@link #ACTION_QUERY_KEYBOARD_LAYOUTS}. 144 * <p> 145 * Specifies the resource id of a XML resource that describes the keyboard 146 * layouts that are provided by the application. 147 * </p> 148 */ 149 public static final String META_DATA_KEYBOARD_LAYOUTS = 150 "android.hardware.input.metadata.KEYBOARD_LAYOUTS"; 151 152 /** 153 * Pointer Speed: The minimum (slowest) pointer speed (-7). 154 * @hide 155 */ 156 public static final int MIN_POINTER_SPEED = -7; 157 158 /** 159 * Pointer Speed: The maximum (fastest) pointer speed (7). 160 * @hide 161 */ 162 public static final int MAX_POINTER_SPEED = 7; 163 164 /** 165 * Pointer Speed: The default pointer speed (0). 166 * @hide 167 */ 168 public static final int DEFAULT_POINTER_SPEED = 0; 169 170 /** 171 * Input Event Injection Synchronization Mode: None. 172 * Never blocks. Injection is asynchronous and is assumed always to be successful. 173 * @hide 174 */ 175 public static final int INJECT_INPUT_EVENT_MODE_ASYNC = 0; // see InputDispatcher.h 176 177 /** 178 * Input Event Injection Synchronization Mode: Wait for result. 179 * Waits for previous events to be dispatched so that the input dispatcher can 180 * determine whether input event injection will be permitted based on the current 181 * input focus. Does not wait for the input event to finish being handled 182 * by the application. 183 * @hide 184 */ 185 public static final int INJECT_INPUT_EVENT_MODE_WAIT_FOR_RESULT = 1; // see InputDispatcher.h 186 187 /** 188 * Input Event Injection Synchronization Mode: Wait for finish. 189 * Waits for the event to be delivered to the application and handled. 190 * @hide 191 */ 192 @UnsupportedAppUsage 193 public static final int INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH = 2; // see InputDispatcher.h 194 195 /** @hide */ 196 @Retention(RetentionPolicy.SOURCE) 197 @IntDef(prefix = { "SWITCH_STATE_" }, value = { 198 SWITCH_STATE_UNKNOWN, 199 SWITCH_STATE_OFF, 200 SWITCH_STATE_ON 201 }) 202 public @interface SwitchState {} 203 204 /** 205 * Switch State: Unknown. 206 * 207 * The system has yet to report a valid value for the switch. 208 * @hide 209 */ 210 public static final int SWITCH_STATE_UNKNOWN = -1; 211 212 /** 213 * Switch State: Off. 214 * @hide 215 */ 216 public static final int SWITCH_STATE_OFF = 0; 217 218 /** 219 * Switch State: On. 220 * @hide 221 */ 222 public static final int SWITCH_STATE_ON = 1; 223 InputManager(IInputManager im)224 private InputManager(IInputManager im) { 225 mIm = im; 226 } 227 228 /** 229 * Gets an instance of the input manager. 230 * 231 * @return The input manager instance. 232 * 233 * @hide 234 */ 235 @UnsupportedAppUsage getInstance()236 public static InputManager getInstance() { 237 synchronized (InputManager.class) { 238 if (sInstance == null) { 239 try { 240 sInstance = new InputManager(IInputManager.Stub 241 .asInterface(ServiceManager.getServiceOrThrow(Context.INPUT_SERVICE))); 242 } catch (ServiceNotFoundException e) { 243 throw new IllegalStateException(e); 244 } 245 } 246 return sInstance; 247 } 248 } 249 250 /** 251 * Gets information about the input device with the specified id. 252 * @param id The device id. 253 * @return The input device or null if not found. 254 */ getInputDevice(int id)255 public InputDevice getInputDevice(int id) { 256 synchronized (mInputDevicesLock) { 257 populateInputDevicesLocked(); 258 259 int index = mInputDevices.indexOfKey(id); 260 if (index < 0) { 261 return null; 262 } 263 264 InputDevice inputDevice = mInputDevices.valueAt(index); 265 if (inputDevice == null) { 266 try { 267 inputDevice = mIm.getInputDevice(id); 268 } catch (RemoteException ex) { 269 throw ex.rethrowFromSystemServer(); 270 } 271 if (inputDevice != null) { 272 mInputDevices.setValueAt(index, inputDevice); 273 } 274 } 275 return inputDevice; 276 } 277 } 278 279 /** 280 * Gets information about the input device with the specified descriptor. 281 * @param descriptor The input device descriptor. 282 * @return The input device or null if not found. 283 * @hide 284 */ getInputDeviceByDescriptor(String descriptor)285 public InputDevice getInputDeviceByDescriptor(String descriptor) { 286 if (descriptor == null) { 287 throw new IllegalArgumentException("descriptor must not be null."); 288 } 289 290 synchronized (mInputDevicesLock) { 291 populateInputDevicesLocked(); 292 293 int numDevices = mInputDevices.size(); 294 for (int i = 0; i < numDevices; i++) { 295 InputDevice inputDevice = mInputDevices.valueAt(i); 296 if (inputDevice == null) { 297 int id = mInputDevices.keyAt(i); 298 try { 299 inputDevice = mIm.getInputDevice(id); 300 } catch (RemoteException ex) { 301 throw ex.rethrowFromSystemServer(); 302 } 303 if (inputDevice == null) { 304 continue; 305 } 306 mInputDevices.setValueAt(i, inputDevice); 307 } 308 if (descriptor.equals(inputDevice.getDescriptor())) { 309 return inputDevice; 310 } 311 } 312 return null; 313 } 314 } 315 316 /** 317 * Gets the ids of all input devices in the system. 318 * @return The input device ids. 319 */ getInputDeviceIds()320 public int[] getInputDeviceIds() { 321 synchronized (mInputDevicesLock) { 322 populateInputDevicesLocked(); 323 324 final int count = mInputDevices.size(); 325 final int[] ids = new int[count]; 326 for (int i = 0; i < count; i++) { 327 ids[i] = mInputDevices.keyAt(i); 328 } 329 return ids; 330 } 331 } 332 333 /** 334 * Returns true if an input device is enabled. Should return true for most 335 * situations. Some system apps may disable an input device, for 336 * example to prevent unwanted touch events. 337 * 338 * @param id The input device Id. 339 * 340 * @hide 341 */ isInputDeviceEnabled(int id)342 public boolean isInputDeviceEnabled(int id) { 343 try { 344 return mIm.isInputDeviceEnabled(id); 345 } catch (RemoteException ex) { 346 Log.w(TAG, "Could not check enabled status of input device with id = " + id); 347 throw ex.rethrowFromSystemServer(); 348 } 349 } 350 351 /** 352 * Enables an InputDevice. 353 * <p> 354 * Requires {@link android.Manifest.permissions.DISABLE_INPUT_DEVICE}. 355 * </p> 356 * 357 * @param id The input device Id. 358 * 359 * @hide 360 */ enableInputDevice(int id)361 public void enableInputDevice(int id) { 362 try { 363 mIm.enableInputDevice(id); 364 } catch (RemoteException ex) { 365 Log.w(TAG, "Could not enable input device with id = " + id); 366 throw ex.rethrowFromSystemServer(); 367 } 368 } 369 370 /** 371 * Disables an InputDevice. 372 * <p> 373 * Requires {@link android.Manifest.permissions.DISABLE_INPUT_DEVICE}. 374 * </p> 375 * 376 * @param id The input device Id. 377 * 378 * @hide 379 */ disableInputDevice(int id)380 public void disableInputDevice(int id) { 381 try { 382 mIm.disableInputDevice(id); 383 } catch (RemoteException ex) { 384 Log.w(TAG, "Could not disable input device with id = " + id); 385 throw ex.rethrowFromSystemServer(); 386 } 387 } 388 389 /** 390 * Registers an input device listener to receive notifications about when 391 * input devices are added, removed or changed. 392 * 393 * @param listener The listener to register. 394 * @param handler The handler on which the listener should be invoked, or null 395 * if the listener should be invoked on the calling thread's looper. 396 * 397 * @see #unregisterInputDeviceListener 398 */ registerInputDeviceListener(InputDeviceListener listener, Handler handler)399 public void registerInputDeviceListener(InputDeviceListener listener, Handler handler) { 400 if (listener == null) { 401 throw new IllegalArgumentException("listener must not be null"); 402 } 403 404 synchronized (mInputDevicesLock) { 405 populateInputDevicesLocked(); 406 int index = findInputDeviceListenerLocked(listener); 407 if (index < 0) { 408 mInputDeviceListeners.add(new InputDeviceListenerDelegate(listener, handler)); 409 } 410 } 411 } 412 413 /** 414 * Unregisters an input device listener. 415 * 416 * @param listener The listener to unregister. 417 * 418 * @see #registerInputDeviceListener 419 */ unregisterInputDeviceListener(InputDeviceListener listener)420 public void unregisterInputDeviceListener(InputDeviceListener listener) { 421 if (listener == null) { 422 throw new IllegalArgumentException("listener must not be null"); 423 } 424 425 synchronized (mInputDevicesLock) { 426 int index = findInputDeviceListenerLocked(listener); 427 if (index >= 0) { 428 InputDeviceListenerDelegate d = mInputDeviceListeners.get(index); 429 d.removeCallbacksAndMessages(null); 430 mInputDeviceListeners.remove(index); 431 } 432 } 433 } 434 findInputDeviceListenerLocked(InputDeviceListener listener)435 private int findInputDeviceListenerLocked(InputDeviceListener listener) { 436 final int numListeners = mInputDeviceListeners.size(); 437 for (int i = 0; i < numListeners; i++) { 438 if (mInputDeviceListeners.get(i).mListener == listener) { 439 return i; 440 } 441 } 442 return -1; 443 } 444 445 /** 446 * Queries whether the device is in tablet mode. 447 * 448 * @return The tablet switch state which is one of {@link #SWITCH_STATE_UNKNOWN}, 449 * {@link #SWITCH_STATE_OFF} or {@link #SWITCH_STATE_ON}. 450 * @hide 451 */ 452 @SwitchState isInTabletMode()453 public int isInTabletMode() { 454 try { 455 return mIm.isInTabletMode(); 456 } catch (RemoteException ex) { 457 throw ex.rethrowFromSystemServer(); 458 } 459 } 460 461 /** 462 * Register a tablet mode changed listener. 463 * 464 * @param listener The listener to register. 465 * @param handler The handler on which the listener should be invoked, or null 466 * if the listener should be invoked on the calling thread's looper. 467 * @hide 468 */ registerOnTabletModeChangedListener( OnTabletModeChangedListener listener, Handler handler)469 public void registerOnTabletModeChangedListener( 470 OnTabletModeChangedListener listener, Handler handler) { 471 if (listener == null) { 472 throw new IllegalArgumentException("listener must not be null"); 473 } 474 synchronized (mTabletModeLock) { 475 if (mOnTabletModeChangedListeners == null) { 476 initializeTabletModeListenerLocked(); 477 } 478 int idx = findOnTabletModeChangedListenerLocked(listener); 479 if (idx < 0) { 480 OnTabletModeChangedListenerDelegate d = 481 new OnTabletModeChangedListenerDelegate(listener, handler); 482 mOnTabletModeChangedListeners.add(d); 483 } 484 } 485 } 486 487 /** 488 * Unregister a tablet mode changed listener. 489 * 490 * @param listener The listener to unregister. 491 * @hide 492 */ unregisterOnTabletModeChangedListener(OnTabletModeChangedListener listener)493 public void unregisterOnTabletModeChangedListener(OnTabletModeChangedListener listener) { 494 if (listener == null) { 495 throw new IllegalArgumentException("listener must not be null"); 496 } 497 synchronized (mTabletModeLock) { 498 int idx = findOnTabletModeChangedListenerLocked(listener); 499 if (idx >= 0) { 500 OnTabletModeChangedListenerDelegate d = mOnTabletModeChangedListeners.remove(idx); 501 d.removeCallbacksAndMessages(null); 502 } 503 } 504 } 505 initializeTabletModeListenerLocked()506 private void initializeTabletModeListenerLocked() { 507 final TabletModeChangedListener listener = new TabletModeChangedListener(); 508 try { 509 mIm.registerTabletModeChangedListener(listener); 510 } catch (RemoteException ex) { 511 throw ex.rethrowFromSystemServer(); 512 } 513 mTabletModeChangedListener = listener; 514 mOnTabletModeChangedListeners = new ArrayList<>(); 515 } 516 findOnTabletModeChangedListenerLocked(OnTabletModeChangedListener listener)517 private int findOnTabletModeChangedListenerLocked(OnTabletModeChangedListener listener) { 518 final int N = mOnTabletModeChangedListeners.size(); 519 for (int i = 0; i < N; i++) { 520 if (mOnTabletModeChangedListeners.get(i).mListener == listener) { 521 return i; 522 } 523 } 524 return -1; 525 } 526 527 /** 528 * Queries whether the device's microphone is muted 529 * 530 * @return The mic mute switch state which is one of {@link #SWITCH_STATE_UNKNOWN}, 531 * {@link #SWITCH_STATE_OFF} or {@link #SWITCH_STATE_ON}. 532 * @hide 533 */ 534 @SwitchState isMicMuted()535 public int isMicMuted() { 536 try { 537 return mIm.isMicMuted(); 538 } catch (RemoteException ex) { 539 throw ex.rethrowFromSystemServer(); 540 } 541 } 542 543 /** 544 * Gets information about all supported keyboard layouts. 545 * <p> 546 * The input manager consults the built-in keyboard layouts as well 547 * as all keyboard layouts advertised by applications using a 548 * {@link #ACTION_QUERY_KEYBOARD_LAYOUTS} broadcast receiver. 549 * </p> 550 * 551 * @return A list of all supported keyboard layouts. 552 * 553 * @hide 554 */ getKeyboardLayouts()555 public KeyboardLayout[] getKeyboardLayouts() { 556 try { 557 return mIm.getKeyboardLayouts(); 558 } catch (RemoteException ex) { 559 throw ex.rethrowFromSystemServer(); 560 } 561 } 562 563 /** 564 * Gets information about all supported keyboard layouts appropriate 565 * for a specific input device. 566 * <p> 567 * The input manager consults the built-in keyboard layouts as well 568 * as all keyboard layouts advertised by applications using a 569 * {@link #ACTION_QUERY_KEYBOARD_LAYOUTS} broadcast receiver. 570 * </p> 571 * 572 * @return A list of all supported keyboard layouts for a specific 573 * input device. 574 * 575 * @hide 576 */ getKeyboardLayoutsForInputDevice(InputDeviceIdentifier identifier)577 public KeyboardLayout[] getKeyboardLayoutsForInputDevice(InputDeviceIdentifier identifier) { 578 try { 579 return mIm.getKeyboardLayoutsForInputDevice(identifier); 580 } catch (RemoteException ex) { 581 throw ex.rethrowFromSystemServer(); 582 } 583 } 584 585 /** 586 * Gets the keyboard layout with the specified descriptor. 587 * 588 * @param keyboardLayoutDescriptor The keyboard layout descriptor, as returned by 589 * {@link KeyboardLayout#getDescriptor()}. 590 * @return The keyboard layout, or null if it could not be loaded. 591 * 592 * @hide 593 */ getKeyboardLayout(String keyboardLayoutDescriptor)594 public KeyboardLayout getKeyboardLayout(String keyboardLayoutDescriptor) { 595 if (keyboardLayoutDescriptor == null) { 596 throw new IllegalArgumentException("keyboardLayoutDescriptor must not be null"); 597 } 598 599 try { 600 return mIm.getKeyboardLayout(keyboardLayoutDescriptor); 601 } catch (RemoteException ex) { 602 throw ex.rethrowFromSystemServer(); 603 } 604 } 605 606 /** 607 * Gets the current keyboard layout descriptor for the specified input 608 * device. 609 * 610 * @param identifier Identifier for the input device 611 * @return The keyboard layout descriptor, or null if no keyboard layout has 612 * been set. 613 * @hide 614 */ getCurrentKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier)615 public String getCurrentKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier) { 616 try { 617 return mIm.getCurrentKeyboardLayoutForInputDevice(identifier); 618 } catch (RemoteException ex) { 619 throw ex.rethrowFromSystemServer(); 620 } 621 } 622 623 /** 624 * Sets the current keyboard layout descriptor for the specified input 625 * device. 626 * <p> 627 * This method may have the side-effect of causing the input device in 628 * question to be reconfigured. 629 * </p> 630 * 631 * @param identifier The identifier for the input device. 632 * @param keyboardLayoutDescriptor The keyboard layout descriptor to use, 633 * must not be null. 634 * @hide 635 */ setCurrentKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier, String keyboardLayoutDescriptor)636 public void setCurrentKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier, 637 String keyboardLayoutDescriptor) { 638 if (identifier == null) { 639 throw new IllegalArgumentException("identifier must not be null"); 640 } 641 if (keyboardLayoutDescriptor == null) { 642 throw new IllegalArgumentException("keyboardLayoutDescriptor must not be null"); 643 } 644 645 try { 646 mIm.setCurrentKeyboardLayoutForInputDevice(identifier, 647 keyboardLayoutDescriptor); 648 } catch (RemoteException ex) { 649 throw ex.rethrowFromSystemServer(); 650 } 651 } 652 653 /** 654 * Gets all keyboard layout descriptors that are enabled for the specified 655 * input device. 656 * 657 * @param identifier The identifier for the input device. 658 * @return The keyboard layout descriptors. 659 * @hide 660 */ getEnabledKeyboardLayoutsForInputDevice(InputDeviceIdentifier identifier)661 public String[] getEnabledKeyboardLayoutsForInputDevice(InputDeviceIdentifier identifier) { 662 if (identifier == null) { 663 throw new IllegalArgumentException("inputDeviceDescriptor must not be null"); 664 } 665 666 try { 667 return mIm.getEnabledKeyboardLayoutsForInputDevice(identifier); 668 } catch (RemoteException ex) { 669 throw ex.rethrowFromSystemServer(); 670 } 671 } 672 673 /** 674 * Adds the keyboard layout descriptor for the specified input device. 675 * <p> 676 * This method may have the side-effect of causing the input device in 677 * question to be reconfigured. 678 * </p> 679 * 680 * @param identifier The identifier for the input device. 681 * @param keyboardLayoutDescriptor The descriptor of the keyboard layout to 682 * add. 683 * @hide 684 */ addKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier, String keyboardLayoutDescriptor)685 public void addKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier, 686 String keyboardLayoutDescriptor) { 687 if (identifier == null) { 688 throw new IllegalArgumentException("inputDeviceDescriptor must not be null"); 689 } 690 if (keyboardLayoutDescriptor == null) { 691 throw new IllegalArgumentException("keyboardLayoutDescriptor must not be null"); 692 } 693 694 try { 695 mIm.addKeyboardLayoutForInputDevice(identifier, keyboardLayoutDescriptor); 696 } catch (RemoteException ex) { 697 throw ex.rethrowFromSystemServer(); 698 } 699 } 700 701 /** 702 * Removes the keyboard layout descriptor for the specified input device. 703 * <p> 704 * This method may have the side-effect of causing the input device in 705 * question to be reconfigured. 706 * </p> 707 * 708 * @param identifier The identifier for the input device. 709 * @param keyboardLayoutDescriptor The descriptor of the keyboard layout to 710 * remove. 711 * @hide 712 */ removeKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier, String keyboardLayoutDescriptor)713 public void removeKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier, 714 String keyboardLayoutDescriptor) { 715 if (identifier == null) { 716 throw new IllegalArgumentException("inputDeviceDescriptor must not be null"); 717 } 718 if (keyboardLayoutDescriptor == null) { 719 throw new IllegalArgumentException("keyboardLayoutDescriptor must not be null"); 720 } 721 722 try { 723 mIm.removeKeyboardLayoutForInputDevice(identifier, keyboardLayoutDescriptor); 724 } catch (RemoteException ex) { 725 throw ex.rethrowFromSystemServer(); 726 } 727 } 728 729 /** 730 * Gets the TouchCalibration applied to the specified input device's coordinates. 731 * 732 * @param inputDeviceDescriptor The input device descriptor. 733 * @return The TouchCalibration currently assigned for use with the given 734 * input device. If none is set, an identity TouchCalibration is returned. 735 * 736 * @hide 737 */ getTouchCalibration(String inputDeviceDescriptor, int surfaceRotation)738 public TouchCalibration getTouchCalibration(String inputDeviceDescriptor, int surfaceRotation) { 739 try { 740 return mIm.getTouchCalibrationForInputDevice(inputDeviceDescriptor, surfaceRotation); 741 } catch (RemoteException ex) { 742 throw ex.rethrowFromSystemServer(); 743 } 744 } 745 746 /** 747 * Sets the TouchCalibration to apply to the specified input device's coordinates. 748 * <p> 749 * This method may have the side-effect of causing the input device in question 750 * to be reconfigured. Requires {@link android.Manifest.permissions.SET_INPUT_CALIBRATION}. 751 * </p> 752 * 753 * @param inputDeviceDescriptor The input device descriptor. 754 * @param calibration The calibration to be applied 755 * 756 * @hide 757 */ setTouchCalibration(String inputDeviceDescriptor, int surfaceRotation, TouchCalibration calibration)758 public void setTouchCalibration(String inputDeviceDescriptor, int surfaceRotation, 759 TouchCalibration calibration) { 760 try { 761 mIm.setTouchCalibrationForInputDevice(inputDeviceDescriptor, surfaceRotation, calibration); 762 } catch (RemoteException ex) { 763 throw ex.rethrowFromSystemServer(); 764 } 765 } 766 767 /** 768 * Gets the mouse pointer speed. 769 * <p> 770 * Only returns the permanent mouse pointer speed. Ignores any temporary pointer 771 * speed set by {@link #tryPointerSpeed}. 772 * </p> 773 * 774 * @param context The application context. 775 * @return The pointer speed as a value between {@link #MIN_POINTER_SPEED} and 776 * {@link #MAX_POINTER_SPEED}, or the default value {@link #DEFAULT_POINTER_SPEED}. 777 * 778 * @hide 779 */ getPointerSpeed(Context context)780 public int getPointerSpeed(Context context) { 781 int speed = DEFAULT_POINTER_SPEED; 782 try { 783 speed = Settings.System.getInt(context.getContentResolver(), 784 Settings.System.POINTER_SPEED); 785 } catch (SettingNotFoundException snfe) { 786 } 787 return speed; 788 } 789 790 /** 791 * Sets the mouse pointer speed. 792 * <p> 793 * Requires {@link android.Manifest.permissions.WRITE_SETTINGS}. 794 * </p> 795 * 796 * @param context The application context. 797 * @param speed The pointer speed as a value between {@link #MIN_POINTER_SPEED} and 798 * {@link #MAX_POINTER_SPEED}, or the default value {@link #DEFAULT_POINTER_SPEED}. 799 * 800 * @hide 801 */ setPointerSpeed(Context context, int speed)802 public void setPointerSpeed(Context context, int speed) { 803 if (speed < MIN_POINTER_SPEED || speed > MAX_POINTER_SPEED) { 804 throw new IllegalArgumentException("speed out of range"); 805 } 806 807 Settings.System.putInt(context.getContentResolver(), 808 Settings.System.POINTER_SPEED, speed); 809 } 810 811 /** 812 * Changes the mouse pointer speed temporarily, but does not save the setting. 813 * <p> 814 * Requires {@link android.Manifest.permission.SET_POINTER_SPEED}. 815 * </p> 816 * 817 * @param speed The pointer speed as a value between {@link #MIN_POINTER_SPEED} and 818 * {@link #MAX_POINTER_SPEED}, or the default value {@link #DEFAULT_POINTER_SPEED}. 819 * 820 * @hide 821 */ tryPointerSpeed(int speed)822 public void tryPointerSpeed(int speed) { 823 if (speed < MIN_POINTER_SPEED || speed > MAX_POINTER_SPEED) { 824 throw new IllegalArgumentException("speed out of range"); 825 } 826 827 try { 828 mIm.tryPointerSpeed(speed); 829 } catch (RemoteException ex) { 830 throw ex.rethrowFromSystemServer(); 831 } 832 } 833 834 /** 835 * Queries the framework about whether any physical keys exist on the 836 * any keyboard attached to the device that are capable of producing the given 837 * array of key codes. 838 * 839 * @param keyCodes The array of key codes to query. 840 * @return A new array of the same size as the key codes array whose elements 841 * are set to true if at least one attached keyboard supports the corresponding key code 842 * at the same index in the key codes array. 843 * 844 * @hide 845 */ deviceHasKeys(int[] keyCodes)846 public boolean[] deviceHasKeys(int[] keyCodes) { 847 return deviceHasKeys(-1, keyCodes); 848 } 849 850 /** 851 * Queries the framework about whether any physical keys exist on the 852 * any keyboard attached to the device that are capable of producing the given 853 * array of key codes. 854 * 855 * @param id The id of the device to query. 856 * @param keyCodes The array of key codes to query. 857 * @return A new array of the same size as the key codes array whose elements are set to true 858 * if the given device could produce the corresponding key code at the same index in the key 859 * codes array. 860 * 861 * @hide 862 */ deviceHasKeys(int id, int[] keyCodes)863 public boolean[] deviceHasKeys(int id, int[] keyCodes) { 864 boolean[] ret = new boolean[keyCodes.length]; 865 try { 866 mIm.hasKeys(id, InputDevice.SOURCE_ANY, keyCodes, ret); 867 } catch (RemoteException e) { 868 throw e.rethrowFromSystemServer(); 869 } 870 return ret; 871 } 872 873 874 /** 875 * Injects an input event into the event system on behalf of an application. 876 * The synchronization mode determines whether the method blocks while waiting for 877 * input injection to proceed. 878 * <p> 879 * Requires {@link android.Manifest.permission.INJECT_EVENTS} to inject into 880 * windows that are owned by other applications. 881 * </p><p> 882 * Make sure you correctly set the event time and input source of the event 883 * before calling this method. 884 * </p> 885 * 886 * @param event The event to inject. 887 * @param mode The synchronization mode. One of: 888 * {@link #INJECT_INPUT_EVENT_MODE_ASYNC}, 889 * {@link #INJECT_INPUT_EVENT_MODE_WAIT_FOR_RESULT}, or 890 * {@link #INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH}. 891 * @return True if input event injection succeeded. 892 * 893 * @hide 894 */ 895 @UnsupportedAppUsage injectInputEvent(InputEvent event, int mode)896 public boolean injectInputEvent(InputEvent event, int mode) { 897 if (event == null) { 898 throw new IllegalArgumentException("event must not be null"); 899 } 900 if (mode != INJECT_INPUT_EVENT_MODE_ASYNC 901 && mode != INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH 902 && mode != INJECT_INPUT_EVENT_MODE_WAIT_FOR_RESULT) { 903 throw new IllegalArgumentException("mode is invalid"); 904 } 905 906 try { 907 return mIm.injectInputEvent(event, mode); 908 } catch (RemoteException ex) { 909 throw ex.rethrowFromSystemServer(); 910 } 911 } 912 913 /** 914 * Verify the details of an {@link android.view.InputEvent} that came from the system. 915 * If the event did not come from the system, or its details could not be verified, then this 916 * will return {@code null}. Receiving {@code null} does not mean that the event did not 917 * originate from the system, just that we were unable to verify it. This can 918 * happen for a number of reasons during normal operation. 919 * 920 * @param event The {@link android.view.InputEvent} to check 921 * 922 * @return {@link android.view.VerifiedInputEvent}, which is a subset of the provided 923 * {@link android.view.InputEvent} 924 * {@code null} if the event could not be verified. 925 */ verifyInputEvent(@onNull InputEvent event)926 public @Nullable VerifiedInputEvent verifyInputEvent(@NonNull InputEvent event) { 927 try { 928 return mIm.verifyInputEvent(event); 929 } catch (RemoteException ex) { 930 throw ex.rethrowFromSystemServer(); 931 } 932 } 933 934 /** 935 * Changes the mouse pointer's icon shape into the specified id. 936 * 937 * @param iconId The id of the pointer graphic, as a value between 938 * {@link PointerIcon.TYPE_ARROW} and {@link PointerIcon.TYPE_GRABBING}. 939 * 940 * @hide 941 */ 942 @UnsupportedAppUsage setPointerIconType(int iconId)943 public void setPointerIconType(int iconId) { 944 try { 945 mIm.setPointerIconType(iconId); 946 } catch (RemoteException ex) { 947 throw ex.rethrowFromSystemServer(); 948 } 949 } 950 951 /** @hide */ setCustomPointerIcon(PointerIcon icon)952 public void setCustomPointerIcon(PointerIcon icon) { 953 try { 954 mIm.setCustomPointerIcon(icon); 955 } catch (RemoteException ex) { 956 throw ex.rethrowFromSystemServer(); 957 } 958 } 959 960 /** 961 * Request or release pointer capture. 962 * <p> 963 * When in capturing mode, the pointer icon disappears and all mouse events are dispatched to 964 * the window which has requested the capture. Relative position changes are available through 965 * {@link MotionEvent#getX} and {@link MotionEvent#getY}. 966 * 967 * @param enable true when requesting pointer capture, false when releasing. 968 * 969 * @hide 970 */ requestPointerCapture(IBinder windowToken, boolean enable)971 public void requestPointerCapture(IBinder windowToken, boolean enable) { 972 try { 973 mIm.requestPointerCapture(windowToken, enable); 974 } catch (RemoteException ex) { 975 throw ex.rethrowFromSystemServer(); 976 } 977 } 978 979 /** 980 * Monitor input on the specified display for gestures. 981 * 982 * @hide 983 */ monitorGestureInput(String name, int displayId)984 public InputMonitor monitorGestureInput(String name, int displayId) { 985 try { 986 return mIm.monitorGestureInput(name, displayId); 987 } catch (RemoteException ex) { 988 throw ex.rethrowFromSystemServer(); 989 } 990 } 991 992 /** 993 * Add a runtime association between the input port and the display port. This overrides any 994 * static associations. 995 * @param inputPort The port of the input device. 996 * @param displayPort The physical port of the associated display. 997 * <p> 998 * Requires {@link android.Manifest.permissions.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY_BY_PORT}. 999 * </p> 1000 * @hide 1001 */ addPortAssociation(@onNull String inputPort, int displayPort)1002 public void addPortAssociation(@NonNull String inputPort, int displayPort) { 1003 try { 1004 mIm.addPortAssociation(inputPort, displayPort); 1005 } catch (RemoteException ex) { 1006 throw ex.rethrowFromSystemServer(); 1007 } 1008 } 1009 1010 /** 1011 * Remove the runtime association between the input port and the display port. Any existing 1012 * static association for the cleared input port will be restored. 1013 * @param inputPort The port of the input device to be cleared. 1014 * <p> 1015 * Requires {@link android.Manifest.permissions.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY_BY_PORT}. 1016 * </p> 1017 * @hide 1018 */ removePortAssociation(@onNull String inputPort)1019 public void removePortAssociation(@NonNull String inputPort) { 1020 try { 1021 mIm.removePortAssociation(inputPort); 1022 } catch (RemoteException ex) { 1023 throw ex.rethrowFromSystemServer(); 1024 } 1025 } 1026 populateInputDevicesLocked()1027 private void populateInputDevicesLocked() { 1028 if (mInputDevicesChangedListener == null) { 1029 final InputDevicesChangedListener listener = new InputDevicesChangedListener(); 1030 try { 1031 mIm.registerInputDevicesChangedListener(listener); 1032 } catch (RemoteException ex) { 1033 throw ex.rethrowFromSystemServer(); 1034 } 1035 mInputDevicesChangedListener = listener; 1036 } 1037 1038 if (mInputDevices == null) { 1039 final int[] ids; 1040 try { 1041 ids = mIm.getInputDeviceIds(); 1042 } catch (RemoteException ex) { 1043 throw ex.rethrowFromSystemServer(); 1044 } 1045 1046 mInputDevices = new SparseArray<InputDevice>(); 1047 for (int i = 0; i < ids.length; i++) { 1048 mInputDevices.put(ids[i], null); 1049 } 1050 } 1051 } 1052 onInputDevicesChanged(int[] deviceIdAndGeneration)1053 private void onInputDevicesChanged(int[] deviceIdAndGeneration) { 1054 if (DEBUG) { 1055 Log.d(TAG, "Received input devices changed."); 1056 } 1057 1058 synchronized (mInputDevicesLock) { 1059 for (int i = mInputDevices.size(); --i > 0; ) { 1060 final int deviceId = mInputDevices.keyAt(i); 1061 if (!containsDeviceId(deviceIdAndGeneration, deviceId)) { 1062 if (DEBUG) { 1063 Log.d(TAG, "Device removed: " + deviceId); 1064 } 1065 mInputDevices.removeAt(i); 1066 sendMessageToInputDeviceListenersLocked(MSG_DEVICE_REMOVED, deviceId); 1067 } 1068 } 1069 1070 for (int i = 0; i < deviceIdAndGeneration.length; i += 2) { 1071 final int deviceId = deviceIdAndGeneration[i]; 1072 int index = mInputDevices.indexOfKey(deviceId); 1073 if (index >= 0) { 1074 final InputDevice device = mInputDevices.valueAt(index); 1075 if (device != null) { 1076 final int generation = deviceIdAndGeneration[i + 1]; 1077 if (device.getGeneration() != generation) { 1078 if (DEBUG) { 1079 Log.d(TAG, "Device changed: " + deviceId); 1080 } 1081 mInputDevices.setValueAt(index, null); 1082 sendMessageToInputDeviceListenersLocked(MSG_DEVICE_CHANGED, deviceId); 1083 } 1084 } 1085 } else { 1086 if (DEBUG) { 1087 Log.d(TAG, "Device added: " + deviceId); 1088 } 1089 mInputDevices.put(deviceId, null); 1090 sendMessageToInputDeviceListenersLocked(MSG_DEVICE_ADDED, deviceId); 1091 } 1092 } 1093 } 1094 } 1095 sendMessageToInputDeviceListenersLocked(int what, int deviceId)1096 private void sendMessageToInputDeviceListenersLocked(int what, int deviceId) { 1097 final int numListeners = mInputDeviceListeners.size(); 1098 for (int i = 0; i < numListeners; i++) { 1099 InputDeviceListenerDelegate listener = mInputDeviceListeners.get(i); 1100 listener.sendMessage(listener.obtainMessage(what, deviceId, 0)); 1101 } 1102 } 1103 containsDeviceId(int[] deviceIdAndGeneration, int deviceId)1104 private static boolean containsDeviceId(int[] deviceIdAndGeneration, int deviceId) { 1105 for (int i = 0; i < deviceIdAndGeneration.length; i += 2) { 1106 if (deviceIdAndGeneration[i] == deviceId) { 1107 return true; 1108 } 1109 } 1110 return false; 1111 } 1112 1113 onTabletModeChanged(long whenNanos, boolean inTabletMode)1114 private void onTabletModeChanged(long whenNanos, boolean inTabletMode) { 1115 if (DEBUG) { 1116 Log.d(TAG, "Received tablet mode changed: " 1117 + "whenNanos=" + whenNanos + ", inTabletMode=" + inTabletMode); 1118 } 1119 synchronized (mTabletModeLock) { 1120 final int N = mOnTabletModeChangedListeners.size(); 1121 for (int i = 0; i < N; i++) { 1122 OnTabletModeChangedListenerDelegate listener = 1123 mOnTabletModeChangedListeners.get(i); 1124 listener.sendTabletModeChanged(whenNanos, inTabletMode); 1125 } 1126 } 1127 } 1128 1129 /** 1130 * Gets a vibrator service associated with an input device, assuming it has one. 1131 * @return The vibrator, never null. 1132 * @hide 1133 */ getInputDeviceVibrator(int deviceId)1134 public Vibrator getInputDeviceVibrator(int deviceId) { 1135 return new InputDeviceVibrator(deviceId); 1136 } 1137 1138 /** 1139 * Listens for changes in input devices. 1140 */ 1141 public interface InputDeviceListener { 1142 /** 1143 * Called whenever an input device has been added to the system. 1144 * Use {@link InputManager#getInputDevice} to get more information about the device. 1145 * 1146 * @param deviceId The id of the input device that was added. 1147 */ onInputDeviceAdded(int deviceId)1148 void onInputDeviceAdded(int deviceId); 1149 1150 /** 1151 * Called whenever an input device has been removed from the system. 1152 * 1153 * @param deviceId The id of the input device that was removed. 1154 */ onInputDeviceRemoved(int deviceId)1155 void onInputDeviceRemoved(int deviceId); 1156 1157 /** 1158 * Called whenever the properties of an input device have changed since they 1159 * were last queried. Use {@link InputManager#getInputDevice} to get 1160 * a fresh {@link InputDevice} object with the new properties. 1161 * 1162 * @param deviceId The id of the input device that changed. 1163 */ onInputDeviceChanged(int deviceId)1164 void onInputDeviceChanged(int deviceId); 1165 } 1166 1167 private final class InputDevicesChangedListener extends IInputDevicesChangedListener.Stub { 1168 @Override onInputDevicesChanged(int[] deviceIdAndGeneration)1169 public void onInputDevicesChanged(int[] deviceIdAndGeneration) throws RemoteException { 1170 InputManager.this.onInputDevicesChanged(deviceIdAndGeneration); 1171 } 1172 } 1173 1174 private static final class InputDeviceListenerDelegate extends Handler { 1175 public final InputDeviceListener mListener; 1176 InputDeviceListenerDelegate(InputDeviceListener listener, Handler handler)1177 public InputDeviceListenerDelegate(InputDeviceListener listener, Handler handler) { 1178 super(handler != null ? handler.getLooper() : Looper.myLooper()); 1179 mListener = listener; 1180 } 1181 1182 @Override handleMessage(Message msg)1183 public void handleMessage(Message msg) { 1184 switch (msg.what) { 1185 case MSG_DEVICE_ADDED: 1186 mListener.onInputDeviceAdded(msg.arg1); 1187 break; 1188 case MSG_DEVICE_REMOVED: 1189 mListener.onInputDeviceRemoved(msg.arg1); 1190 break; 1191 case MSG_DEVICE_CHANGED: 1192 mListener.onInputDeviceChanged(msg.arg1); 1193 break; 1194 } 1195 } 1196 } 1197 1198 /** @hide */ 1199 public interface OnTabletModeChangedListener { 1200 /** 1201 * Called whenever the device goes into or comes out of tablet mode. 1202 * 1203 * @param whenNanos The time at which the device transitioned into or 1204 * out of tablet mode. This is given in nanoseconds in the 1205 * {@link SystemClock#uptimeMillis} time base. 1206 */ onTabletModeChanged(long whenNanos, boolean inTabletMode)1207 void onTabletModeChanged(long whenNanos, boolean inTabletMode); 1208 } 1209 1210 private final class TabletModeChangedListener extends ITabletModeChangedListener.Stub { 1211 @Override onTabletModeChanged(long whenNanos, boolean inTabletMode)1212 public void onTabletModeChanged(long whenNanos, boolean inTabletMode) { 1213 InputManager.this.onTabletModeChanged(whenNanos, inTabletMode); 1214 } 1215 } 1216 1217 private static final class OnTabletModeChangedListenerDelegate extends Handler { 1218 private static final int MSG_TABLET_MODE_CHANGED = 0; 1219 1220 public final OnTabletModeChangedListener mListener; 1221 OnTabletModeChangedListenerDelegate( OnTabletModeChangedListener listener, Handler handler)1222 public OnTabletModeChangedListenerDelegate( 1223 OnTabletModeChangedListener listener, Handler handler) { 1224 super(handler != null ? handler.getLooper() : Looper.myLooper()); 1225 mListener = listener; 1226 } 1227 sendTabletModeChanged(long whenNanos, boolean inTabletMode)1228 public void sendTabletModeChanged(long whenNanos, boolean inTabletMode) { 1229 SomeArgs args = SomeArgs.obtain(); 1230 args.argi1 = (int) (whenNanos & 0xFFFFFFFF); 1231 args.argi2 = (int) (whenNanos >> 32); 1232 args.arg1 = (Boolean) inTabletMode; 1233 obtainMessage(MSG_TABLET_MODE_CHANGED, args).sendToTarget(); 1234 } 1235 1236 @Override handleMessage(Message msg)1237 public void handleMessage(Message msg) { 1238 switch (msg.what) { 1239 case MSG_TABLET_MODE_CHANGED: 1240 SomeArgs args = (SomeArgs) msg.obj; 1241 long whenNanos = (args.argi1 & 0xFFFFFFFFl) | ((long) args.argi2 << 32); 1242 boolean inTabletMode = (boolean) args.arg1; 1243 mListener.onTabletModeChanged(whenNanos, inTabletMode); 1244 break; 1245 } 1246 } 1247 } 1248 1249 private final class InputDeviceVibrator extends Vibrator { 1250 private final int mDeviceId; 1251 private final Binder mToken; 1252 InputDeviceVibrator(int deviceId)1253 public InputDeviceVibrator(int deviceId) { 1254 mDeviceId = deviceId; 1255 mToken = new Binder(); 1256 } 1257 1258 @Override hasVibrator()1259 public boolean hasVibrator() { 1260 return true; 1261 } 1262 1263 @Override isVibrating()1264 public boolean isVibrating() { 1265 throw new UnsupportedOperationException( 1266 "isVibrating not supported in InputDeviceVibrator"); 1267 } 1268 1269 @Override addVibratorStateListener(@onNull OnVibratorStateChangedListener listener)1270 public void addVibratorStateListener(@NonNull OnVibratorStateChangedListener listener) { 1271 throw new UnsupportedOperationException( 1272 "addVibratorStateListener not supported in InputDeviceVibrator"); 1273 } 1274 1275 @Override addVibratorStateListener( @onNull @allbackExecutor Executor executor, @NonNull OnVibratorStateChangedListener listener)1276 public void addVibratorStateListener( 1277 @NonNull @CallbackExecutor Executor executor, 1278 @NonNull OnVibratorStateChangedListener listener) { 1279 throw new UnsupportedOperationException( 1280 "addVibratorStateListener not supported in InputDeviceVibrator"); 1281 } 1282 1283 @Override removeVibratorStateListener(@onNull OnVibratorStateChangedListener listener)1284 public void removeVibratorStateListener(@NonNull OnVibratorStateChangedListener listener) { 1285 throw new UnsupportedOperationException( 1286 "removeVibratorStateListener not supported in InputDeviceVibrator"); 1287 } 1288 1289 @Override hasAmplitudeControl()1290 public boolean hasAmplitudeControl() { 1291 return false; 1292 } 1293 1294 /** 1295 * @hide 1296 */ 1297 @Override vibrate(int uid, String opPkg, VibrationEffect effect, String reason, AudioAttributes attributes)1298 public void vibrate(int uid, String opPkg, VibrationEffect effect, 1299 String reason, AudioAttributes attributes) { 1300 long[] pattern; 1301 int repeat; 1302 if (effect instanceof VibrationEffect.OneShot) { 1303 VibrationEffect.OneShot oneShot = (VibrationEffect.OneShot) effect; 1304 pattern = new long[] { 0, oneShot.getDuration() }; 1305 repeat = -1; 1306 } else if (effect instanceof VibrationEffect.Waveform) { 1307 VibrationEffect.Waveform waveform = (VibrationEffect.Waveform) effect; 1308 pattern = waveform.getTimings(); 1309 repeat = waveform.getRepeatIndex(); 1310 } else { 1311 // TODO: Add support for prebaked effects 1312 Log.w(TAG, "Pre-baked effects aren't supported on input devices"); 1313 return; 1314 } 1315 1316 try { 1317 mIm.vibrate(mDeviceId, pattern, repeat, mToken); 1318 } catch (RemoteException ex) { 1319 throw ex.rethrowFromSystemServer(); 1320 } 1321 } 1322 1323 @Override cancel()1324 public void cancel() { 1325 try { 1326 mIm.cancelVibrate(mDeviceId, mToken); 1327 } catch (RemoteException ex) { 1328 throw ex.rethrowFromSystemServer(); 1329 } 1330 } 1331 } 1332 } 1333