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.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 * Gets information about all supported keyboard layouts. 524 * <p> 525 * The input manager consults the built-in keyboard layouts as well 526 * as all keyboard layouts advertised by applications using a 527 * {@link #ACTION_QUERY_KEYBOARD_LAYOUTS} broadcast receiver. 528 * </p> 529 * 530 * @return A list of all supported keyboard layouts. 531 * 532 * @hide 533 */ getKeyboardLayouts()534 public KeyboardLayout[] getKeyboardLayouts() { 535 try { 536 return mIm.getKeyboardLayouts(); 537 } catch (RemoteException ex) { 538 throw ex.rethrowFromSystemServer(); 539 } 540 } 541 542 /** 543 * Gets information about all supported keyboard layouts appropriate 544 * for a specific input device. 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 for a specific 552 * input device. 553 * 554 * @hide 555 */ getKeyboardLayoutsForInputDevice(InputDeviceIdentifier identifier)556 public KeyboardLayout[] getKeyboardLayoutsForInputDevice(InputDeviceIdentifier identifier) { 557 try { 558 return mIm.getKeyboardLayoutsForInputDevice(identifier); 559 } catch (RemoteException ex) { 560 throw ex.rethrowFromSystemServer(); 561 } 562 } 563 564 /** 565 * Gets the keyboard layout with the specified descriptor. 566 * 567 * @param keyboardLayoutDescriptor The keyboard layout descriptor, as returned by 568 * {@link KeyboardLayout#getDescriptor()}. 569 * @return The keyboard layout, or null if it could not be loaded. 570 * 571 * @hide 572 */ getKeyboardLayout(String keyboardLayoutDescriptor)573 public KeyboardLayout getKeyboardLayout(String keyboardLayoutDescriptor) { 574 if (keyboardLayoutDescriptor == null) { 575 throw new IllegalArgumentException("keyboardLayoutDescriptor must not be null"); 576 } 577 578 try { 579 return mIm.getKeyboardLayout(keyboardLayoutDescriptor); 580 } catch (RemoteException ex) { 581 throw ex.rethrowFromSystemServer(); 582 } 583 } 584 585 /** 586 * Gets the current keyboard layout descriptor for the specified input 587 * device. 588 * 589 * @param identifier Identifier for the input device 590 * @return The keyboard layout descriptor, or null if no keyboard layout has 591 * been set. 592 * @hide 593 */ getCurrentKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier)594 public String getCurrentKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier) { 595 try { 596 return mIm.getCurrentKeyboardLayoutForInputDevice(identifier); 597 } catch (RemoteException ex) { 598 throw ex.rethrowFromSystemServer(); 599 } 600 } 601 602 /** 603 * Sets the current keyboard layout descriptor for the specified input 604 * device. 605 * <p> 606 * This method may have the side-effect of causing the input device in 607 * question to be reconfigured. 608 * </p> 609 * 610 * @param identifier The identifier for the input device. 611 * @param keyboardLayoutDescriptor The keyboard layout descriptor to use, 612 * must not be null. 613 * @hide 614 */ setCurrentKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier, String keyboardLayoutDescriptor)615 public void setCurrentKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier, 616 String keyboardLayoutDescriptor) { 617 if (identifier == null) { 618 throw new IllegalArgumentException("identifier must not be null"); 619 } 620 if (keyboardLayoutDescriptor == null) { 621 throw new IllegalArgumentException("keyboardLayoutDescriptor must not be null"); 622 } 623 624 try { 625 mIm.setCurrentKeyboardLayoutForInputDevice(identifier, 626 keyboardLayoutDescriptor); 627 } catch (RemoteException ex) { 628 throw ex.rethrowFromSystemServer(); 629 } 630 } 631 632 /** 633 * Gets all keyboard layout descriptors that are enabled for the specified 634 * input device. 635 * 636 * @param identifier The identifier for the input device. 637 * @return The keyboard layout descriptors. 638 * @hide 639 */ getEnabledKeyboardLayoutsForInputDevice(InputDeviceIdentifier identifier)640 public String[] getEnabledKeyboardLayoutsForInputDevice(InputDeviceIdentifier identifier) { 641 if (identifier == null) { 642 throw new IllegalArgumentException("inputDeviceDescriptor must not be null"); 643 } 644 645 try { 646 return mIm.getEnabledKeyboardLayoutsForInputDevice(identifier); 647 } catch (RemoteException ex) { 648 throw ex.rethrowFromSystemServer(); 649 } 650 } 651 652 /** 653 * Adds the keyboard layout descriptor for the specified input device. 654 * <p> 655 * This method may have the side-effect of causing the input device in 656 * question to be reconfigured. 657 * </p> 658 * 659 * @param identifier The identifier for the input device. 660 * @param keyboardLayoutDescriptor The descriptor of the keyboard layout to 661 * add. 662 * @hide 663 */ addKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier, String keyboardLayoutDescriptor)664 public void addKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier, 665 String keyboardLayoutDescriptor) { 666 if (identifier == null) { 667 throw new IllegalArgumentException("inputDeviceDescriptor must not be null"); 668 } 669 if (keyboardLayoutDescriptor == null) { 670 throw new IllegalArgumentException("keyboardLayoutDescriptor must not be null"); 671 } 672 673 try { 674 mIm.addKeyboardLayoutForInputDevice(identifier, keyboardLayoutDescriptor); 675 } catch (RemoteException ex) { 676 throw ex.rethrowFromSystemServer(); 677 } 678 } 679 680 /** 681 * Removes the keyboard layout descriptor for the specified input device. 682 * <p> 683 * This method may have the side-effect of causing the input device in 684 * question to be reconfigured. 685 * </p> 686 * 687 * @param identifier The identifier for the input device. 688 * @param keyboardLayoutDescriptor The descriptor of the keyboard layout to 689 * remove. 690 * @hide 691 */ removeKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier, String keyboardLayoutDescriptor)692 public void removeKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier, 693 String keyboardLayoutDescriptor) { 694 if (identifier == null) { 695 throw new IllegalArgumentException("inputDeviceDescriptor must not be null"); 696 } 697 if (keyboardLayoutDescriptor == null) { 698 throw new IllegalArgumentException("keyboardLayoutDescriptor must not be null"); 699 } 700 701 try { 702 mIm.removeKeyboardLayoutForInputDevice(identifier, keyboardLayoutDescriptor); 703 } catch (RemoteException ex) { 704 throw ex.rethrowFromSystemServer(); 705 } 706 } 707 708 /** 709 * Gets the TouchCalibration applied to the specified input device's coordinates. 710 * 711 * @param inputDeviceDescriptor The input device descriptor. 712 * @return The TouchCalibration currently assigned for use with the given 713 * input device. If none is set, an identity TouchCalibration is returned. 714 * 715 * @hide 716 */ getTouchCalibration(String inputDeviceDescriptor, int surfaceRotation)717 public TouchCalibration getTouchCalibration(String inputDeviceDescriptor, int surfaceRotation) { 718 try { 719 return mIm.getTouchCalibrationForInputDevice(inputDeviceDescriptor, surfaceRotation); 720 } catch (RemoteException ex) { 721 throw ex.rethrowFromSystemServer(); 722 } 723 } 724 725 /** 726 * Sets the TouchCalibration to apply to the specified input device's coordinates. 727 * <p> 728 * This method may have the side-effect of causing the input device in question 729 * to be reconfigured. Requires {@link android.Manifest.permissions.SET_INPUT_CALIBRATION}. 730 * </p> 731 * 732 * @param inputDeviceDescriptor The input device descriptor. 733 * @param calibration The calibration to be applied 734 * 735 * @hide 736 */ setTouchCalibration(String inputDeviceDescriptor, int surfaceRotation, TouchCalibration calibration)737 public void setTouchCalibration(String inputDeviceDescriptor, int surfaceRotation, 738 TouchCalibration calibration) { 739 try { 740 mIm.setTouchCalibrationForInputDevice(inputDeviceDescriptor, surfaceRotation, calibration); 741 } catch (RemoteException ex) { 742 throw ex.rethrowFromSystemServer(); 743 } 744 } 745 746 /** 747 * Gets the mouse pointer speed. 748 * <p> 749 * Only returns the permanent mouse pointer speed. Ignores any temporary pointer 750 * speed set by {@link #tryPointerSpeed}. 751 * </p> 752 * 753 * @param context The application context. 754 * @return The pointer speed as a value between {@link #MIN_POINTER_SPEED} and 755 * {@link #MAX_POINTER_SPEED}, or the default value {@link #DEFAULT_POINTER_SPEED}. 756 * 757 * @hide 758 */ getPointerSpeed(Context context)759 public int getPointerSpeed(Context context) { 760 int speed = DEFAULT_POINTER_SPEED; 761 try { 762 speed = Settings.System.getInt(context.getContentResolver(), 763 Settings.System.POINTER_SPEED); 764 } catch (SettingNotFoundException snfe) { 765 } 766 return speed; 767 } 768 769 /** 770 * Sets the mouse pointer speed. 771 * <p> 772 * Requires {@link android.Manifest.permissions.WRITE_SETTINGS}. 773 * </p> 774 * 775 * @param context The application context. 776 * @param speed The pointer speed as a value between {@link #MIN_POINTER_SPEED} and 777 * {@link #MAX_POINTER_SPEED}, or the default value {@link #DEFAULT_POINTER_SPEED}. 778 * 779 * @hide 780 */ setPointerSpeed(Context context, int speed)781 public void setPointerSpeed(Context context, int speed) { 782 if (speed < MIN_POINTER_SPEED || speed > MAX_POINTER_SPEED) { 783 throw new IllegalArgumentException("speed out of range"); 784 } 785 786 Settings.System.putInt(context.getContentResolver(), 787 Settings.System.POINTER_SPEED, speed); 788 } 789 790 /** 791 * Changes the mouse pointer speed temporarily, but does not save the setting. 792 * <p> 793 * Requires {@link android.Manifest.permission.SET_POINTER_SPEED}. 794 * </p> 795 * 796 * @param speed The pointer speed as a value between {@link #MIN_POINTER_SPEED} and 797 * {@link #MAX_POINTER_SPEED}, or the default value {@link #DEFAULT_POINTER_SPEED}. 798 * 799 * @hide 800 */ tryPointerSpeed(int speed)801 public void tryPointerSpeed(int speed) { 802 if (speed < MIN_POINTER_SPEED || speed > MAX_POINTER_SPEED) { 803 throw new IllegalArgumentException("speed out of range"); 804 } 805 806 try { 807 mIm.tryPointerSpeed(speed); 808 } catch (RemoteException ex) { 809 throw ex.rethrowFromSystemServer(); 810 } 811 } 812 813 /** 814 * Queries the framework about whether any physical keys exist on the 815 * any keyboard attached to the device that are capable of producing the given 816 * array of key codes. 817 * 818 * @param keyCodes The array of key codes to query. 819 * @return A new array of the same size as the key codes array whose elements 820 * are set to true if at least one attached keyboard supports the corresponding key code 821 * at the same index in the key codes array. 822 * 823 * @hide 824 */ deviceHasKeys(int[] keyCodes)825 public boolean[] deviceHasKeys(int[] keyCodes) { 826 return deviceHasKeys(-1, keyCodes); 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 id The id of the device to query. 835 * @param keyCodes The array of key codes to query. 836 * @return A new array of the same size as the key codes array whose elements are set to true 837 * if the given device could produce the corresponding key code at the same index in the key 838 * codes array. 839 * 840 * @hide 841 */ deviceHasKeys(int id, int[] keyCodes)842 public boolean[] deviceHasKeys(int id, int[] keyCodes) { 843 boolean[] ret = new boolean[keyCodes.length]; 844 try { 845 mIm.hasKeys(id, InputDevice.SOURCE_ANY, keyCodes, ret); 846 } catch (RemoteException e) { 847 throw e.rethrowFromSystemServer(); 848 } 849 return ret; 850 } 851 852 853 /** 854 * Injects an input event into the event system on behalf of an application. 855 * The synchronization mode determines whether the method blocks while waiting for 856 * input injection to proceed. 857 * <p> 858 * Requires {@link android.Manifest.permission.INJECT_EVENTS} to inject into 859 * windows that are owned by other applications. 860 * </p><p> 861 * Make sure you correctly set the event time and input source of the event 862 * before calling this method. 863 * </p> 864 * 865 * @param event The event to inject. 866 * @param mode The synchronization mode. One of: 867 * {@link #INJECT_INPUT_EVENT_MODE_ASYNC}, 868 * {@link #INJECT_INPUT_EVENT_MODE_WAIT_FOR_RESULT}, or 869 * {@link #INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH}. 870 * @return True if input event injection succeeded. 871 * 872 * @hide 873 */ 874 @UnsupportedAppUsage injectInputEvent(InputEvent event, int mode)875 public boolean injectInputEvent(InputEvent event, int mode) { 876 if (event == null) { 877 throw new IllegalArgumentException("event must not be null"); 878 } 879 if (mode != INJECT_INPUT_EVENT_MODE_ASYNC 880 && mode != INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH 881 && mode != INJECT_INPUT_EVENT_MODE_WAIT_FOR_RESULT) { 882 throw new IllegalArgumentException("mode is invalid"); 883 } 884 885 try { 886 return mIm.injectInputEvent(event, mode); 887 } catch (RemoteException ex) { 888 throw ex.rethrowFromSystemServer(); 889 } 890 } 891 892 /** 893 * Changes the mouse pointer's icon shape into the specified id. 894 * 895 * @param iconId The id of the pointer graphic, as a value between 896 * {@link PointerIcon.TYPE_ARROW} and {@link PointerIcon.TYPE_GRABBING}. 897 * 898 * @hide 899 */ 900 @UnsupportedAppUsage setPointerIconType(int iconId)901 public void setPointerIconType(int iconId) { 902 try { 903 mIm.setPointerIconType(iconId); 904 } catch (RemoteException ex) { 905 throw ex.rethrowFromSystemServer(); 906 } 907 } 908 909 /** @hide */ setCustomPointerIcon(PointerIcon icon)910 public void setCustomPointerIcon(PointerIcon icon) { 911 try { 912 mIm.setCustomPointerIcon(icon); 913 } catch (RemoteException ex) { 914 throw ex.rethrowFromSystemServer(); 915 } 916 } 917 918 /** 919 * Request or release pointer capture. 920 * <p> 921 * When in capturing mode, the pointer icon disappears and all mouse events are dispatched to 922 * the window which has requested the capture. Relative position changes are available through 923 * {@link MotionEvent#getX} and {@link MotionEvent#getY}. 924 * 925 * @param enable true when requesting pointer capture, false when releasing. 926 * 927 * @hide 928 */ requestPointerCapture(IBinder windowToken, boolean enable)929 public void requestPointerCapture(IBinder windowToken, boolean enable) { 930 try { 931 mIm.requestPointerCapture(windowToken, enable); 932 } catch (RemoteException ex) { 933 throw ex.rethrowFromSystemServer(); 934 } 935 } 936 937 /** 938 * Monitor input on the specified display for gestures. 939 * 940 * @hide 941 */ monitorGestureInput(String name, int displayId)942 public InputMonitor monitorGestureInput(String name, int displayId) { 943 try { 944 return mIm.monitorGestureInput(name, displayId); 945 } catch (RemoteException ex) { 946 throw ex.rethrowFromSystemServer(); 947 } 948 } 949 populateInputDevicesLocked()950 private void populateInputDevicesLocked() { 951 if (mInputDevicesChangedListener == null) { 952 final InputDevicesChangedListener listener = new InputDevicesChangedListener(); 953 try { 954 mIm.registerInputDevicesChangedListener(listener); 955 } catch (RemoteException ex) { 956 throw ex.rethrowFromSystemServer(); 957 } 958 mInputDevicesChangedListener = listener; 959 } 960 961 if (mInputDevices == null) { 962 final int[] ids; 963 try { 964 ids = mIm.getInputDeviceIds(); 965 } catch (RemoteException ex) { 966 throw ex.rethrowFromSystemServer(); 967 } 968 969 mInputDevices = new SparseArray<InputDevice>(); 970 for (int i = 0; i < ids.length; i++) { 971 mInputDevices.put(ids[i], null); 972 } 973 } 974 } 975 onInputDevicesChanged(int[] deviceIdAndGeneration)976 private void onInputDevicesChanged(int[] deviceIdAndGeneration) { 977 if (DEBUG) { 978 Log.d(TAG, "Received input devices changed."); 979 } 980 981 synchronized (mInputDevicesLock) { 982 for (int i = mInputDevices.size(); --i > 0; ) { 983 final int deviceId = mInputDevices.keyAt(i); 984 if (!containsDeviceId(deviceIdAndGeneration, deviceId)) { 985 if (DEBUG) { 986 Log.d(TAG, "Device removed: " + deviceId); 987 } 988 mInputDevices.removeAt(i); 989 sendMessageToInputDeviceListenersLocked(MSG_DEVICE_REMOVED, deviceId); 990 } 991 } 992 993 for (int i = 0; i < deviceIdAndGeneration.length; i += 2) { 994 final int deviceId = deviceIdAndGeneration[i]; 995 int index = mInputDevices.indexOfKey(deviceId); 996 if (index >= 0) { 997 final InputDevice device = mInputDevices.valueAt(index); 998 if (device != null) { 999 final int generation = deviceIdAndGeneration[i + 1]; 1000 if (device.getGeneration() != generation) { 1001 if (DEBUG) { 1002 Log.d(TAG, "Device changed: " + deviceId); 1003 } 1004 mInputDevices.setValueAt(index, null); 1005 sendMessageToInputDeviceListenersLocked(MSG_DEVICE_CHANGED, deviceId); 1006 } 1007 } 1008 } else { 1009 if (DEBUG) { 1010 Log.d(TAG, "Device added: " + deviceId); 1011 } 1012 mInputDevices.put(deviceId, null); 1013 sendMessageToInputDeviceListenersLocked(MSG_DEVICE_ADDED, deviceId); 1014 } 1015 } 1016 } 1017 } 1018 sendMessageToInputDeviceListenersLocked(int what, int deviceId)1019 private void sendMessageToInputDeviceListenersLocked(int what, int deviceId) { 1020 final int numListeners = mInputDeviceListeners.size(); 1021 for (int i = 0; i < numListeners; i++) { 1022 InputDeviceListenerDelegate listener = mInputDeviceListeners.get(i); 1023 listener.sendMessage(listener.obtainMessage(what, deviceId, 0)); 1024 } 1025 } 1026 containsDeviceId(int[] deviceIdAndGeneration, int deviceId)1027 private static boolean containsDeviceId(int[] deviceIdAndGeneration, int deviceId) { 1028 for (int i = 0; i < deviceIdAndGeneration.length; i += 2) { 1029 if (deviceIdAndGeneration[i] == deviceId) { 1030 return true; 1031 } 1032 } 1033 return false; 1034 } 1035 1036 onTabletModeChanged(long whenNanos, boolean inTabletMode)1037 private void onTabletModeChanged(long whenNanos, boolean inTabletMode) { 1038 if (DEBUG) { 1039 Log.d(TAG, "Received tablet mode changed: " 1040 + "whenNanos=" + whenNanos + ", inTabletMode=" + inTabletMode); 1041 } 1042 synchronized (mTabletModeLock) { 1043 final int N = mOnTabletModeChangedListeners.size(); 1044 for (int i = 0; i < N; i++) { 1045 OnTabletModeChangedListenerDelegate listener = 1046 mOnTabletModeChangedListeners.get(i); 1047 listener.sendTabletModeChanged(whenNanos, inTabletMode); 1048 } 1049 } 1050 } 1051 1052 /** 1053 * Gets a vibrator service associated with an input device, assuming it has one. 1054 * @return The vibrator, never null. 1055 * @hide 1056 */ getInputDeviceVibrator(int deviceId)1057 public Vibrator getInputDeviceVibrator(int deviceId) { 1058 return new InputDeviceVibrator(deviceId); 1059 } 1060 1061 /** 1062 * Listens for changes in input devices. 1063 */ 1064 public interface InputDeviceListener { 1065 /** 1066 * Called whenever an input device has been added to the system. 1067 * Use {@link InputManager#getInputDevice} to get more information about the device. 1068 * 1069 * @param deviceId The id of the input device that was added. 1070 */ onInputDeviceAdded(int deviceId)1071 void onInputDeviceAdded(int deviceId); 1072 1073 /** 1074 * Called whenever an input device has been removed from the system. 1075 * 1076 * @param deviceId The id of the input device that was removed. 1077 */ onInputDeviceRemoved(int deviceId)1078 void onInputDeviceRemoved(int deviceId); 1079 1080 /** 1081 * Called whenever the properties of an input device have changed since they 1082 * were last queried. Use {@link InputManager#getInputDevice} to get 1083 * a fresh {@link InputDevice} object with the new properties. 1084 * 1085 * @param deviceId The id of the input device that changed. 1086 */ onInputDeviceChanged(int deviceId)1087 void onInputDeviceChanged(int deviceId); 1088 } 1089 1090 private final class InputDevicesChangedListener extends IInputDevicesChangedListener.Stub { 1091 @Override onInputDevicesChanged(int[] deviceIdAndGeneration)1092 public void onInputDevicesChanged(int[] deviceIdAndGeneration) throws RemoteException { 1093 InputManager.this.onInputDevicesChanged(deviceIdAndGeneration); 1094 } 1095 } 1096 1097 private static final class InputDeviceListenerDelegate extends Handler { 1098 public final InputDeviceListener mListener; 1099 InputDeviceListenerDelegate(InputDeviceListener listener, Handler handler)1100 public InputDeviceListenerDelegate(InputDeviceListener listener, Handler handler) { 1101 super(handler != null ? handler.getLooper() : Looper.myLooper()); 1102 mListener = listener; 1103 } 1104 1105 @Override handleMessage(Message msg)1106 public void handleMessage(Message msg) { 1107 switch (msg.what) { 1108 case MSG_DEVICE_ADDED: 1109 mListener.onInputDeviceAdded(msg.arg1); 1110 break; 1111 case MSG_DEVICE_REMOVED: 1112 mListener.onInputDeviceRemoved(msg.arg1); 1113 break; 1114 case MSG_DEVICE_CHANGED: 1115 mListener.onInputDeviceChanged(msg.arg1); 1116 break; 1117 } 1118 } 1119 } 1120 1121 /** @hide */ 1122 public interface OnTabletModeChangedListener { 1123 /** 1124 * Called whenever the device goes into or comes out of tablet mode. 1125 * 1126 * @param whenNanos The time at which the device transitioned into or 1127 * out of tablet mode. This is given in nanoseconds in the 1128 * {@link SystemClock#uptimeMillis} time base. 1129 */ onTabletModeChanged(long whenNanos, boolean inTabletMode)1130 void onTabletModeChanged(long whenNanos, boolean inTabletMode); 1131 } 1132 1133 private final class TabletModeChangedListener extends ITabletModeChangedListener.Stub { 1134 @Override onTabletModeChanged(long whenNanos, boolean inTabletMode)1135 public void onTabletModeChanged(long whenNanos, boolean inTabletMode) { 1136 InputManager.this.onTabletModeChanged(whenNanos, inTabletMode); 1137 } 1138 } 1139 1140 private static final class OnTabletModeChangedListenerDelegate extends Handler { 1141 private static final int MSG_TABLET_MODE_CHANGED = 0; 1142 1143 public final OnTabletModeChangedListener mListener; 1144 OnTabletModeChangedListenerDelegate( OnTabletModeChangedListener listener, Handler handler)1145 public OnTabletModeChangedListenerDelegate( 1146 OnTabletModeChangedListener listener, Handler handler) { 1147 super(handler != null ? handler.getLooper() : Looper.myLooper()); 1148 mListener = listener; 1149 } 1150 sendTabletModeChanged(long whenNanos, boolean inTabletMode)1151 public void sendTabletModeChanged(long whenNanos, boolean inTabletMode) { 1152 SomeArgs args = SomeArgs.obtain(); 1153 args.argi1 = (int) (whenNanos & 0xFFFFFFFF); 1154 args.argi2 = (int) (whenNanos >> 32); 1155 args.arg1 = (Boolean) inTabletMode; 1156 obtainMessage(MSG_TABLET_MODE_CHANGED, args).sendToTarget(); 1157 } 1158 1159 @Override handleMessage(Message msg)1160 public void handleMessage(Message msg) { 1161 switch (msg.what) { 1162 case MSG_TABLET_MODE_CHANGED: 1163 SomeArgs args = (SomeArgs) msg.obj; 1164 long whenNanos = (args.argi1 & 0xFFFFFFFFl) | ((long) args.argi2 << 32); 1165 boolean inTabletMode = (boolean) args.arg1; 1166 mListener.onTabletModeChanged(whenNanos, inTabletMode); 1167 break; 1168 } 1169 } 1170 } 1171 1172 private final class InputDeviceVibrator extends Vibrator { 1173 private final int mDeviceId; 1174 private final Binder mToken; 1175 InputDeviceVibrator(int deviceId)1176 public InputDeviceVibrator(int deviceId) { 1177 mDeviceId = deviceId; 1178 mToken = new Binder(); 1179 } 1180 1181 @Override hasVibrator()1182 public boolean hasVibrator() { 1183 return true; 1184 } 1185 1186 @Override hasAmplitudeControl()1187 public boolean hasAmplitudeControl() { 1188 return false; 1189 } 1190 1191 /** 1192 * @hide 1193 */ 1194 @Override vibrate(int uid, String opPkg, VibrationEffect effect, String reason, AudioAttributes attributes)1195 public void vibrate(int uid, String opPkg, VibrationEffect effect, 1196 String reason, AudioAttributes attributes) { 1197 long[] pattern; 1198 int repeat; 1199 if (effect instanceof VibrationEffect.OneShot) { 1200 VibrationEffect.OneShot oneShot = (VibrationEffect.OneShot) effect; 1201 pattern = new long[] { 0, oneShot.getDuration() }; 1202 repeat = -1; 1203 } else if (effect instanceof VibrationEffect.Waveform) { 1204 VibrationEffect.Waveform waveform = (VibrationEffect.Waveform) effect; 1205 pattern = waveform.getTimings(); 1206 repeat = waveform.getRepeatIndex(); 1207 } else { 1208 // TODO: Add support for prebaked effects 1209 Log.w(TAG, "Pre-baked effects aren't supported on input devices"); 1210 return; 1211 } 1212 1213 try { 1214 mIm.vibrate(mDeviceId, pattern, repeat, mToken); 1215 } catch (RemoteException ex) { 1216 throw ex.rethrowFromSystemServer(); 1217 } 1218 } 1219 1220 @Override cancel()1221 public void cancel() { 1222 try { 1223 mIm.cancelVibrate(mDeviceId, mToken); 1224 } catch (RemoteException ex) { 1225 throw ex.rethrowFromSystemServer(); 1226 } 1227 } 1228 } 1229 } 1230