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