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 com.android.internal.os.SomeArgs; 20 21 import android.annotation.IntDef; 22 import android.annotation.Nullable; 23 import android.annotation.SdkConstant; 24 import android.annotation.SdkConstant.SdkConstantType; 25 import android.content.Context; 26 import android.media.AudioAttributes; 27 import android.os.Binder; 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.SystemClock; 35 import android.os.Vibrator; 36 import android.provider.Settings; 37 import android.provider.Settings.SettingNotFoundException; 38 import android.util.Log; 39 import android.util.SparseArray; 40 import android.view.InputDevice; 41 import android.view.InputEvent; 42 import android.view.PointerIcon; 43 import android.view.inputmethod.InputMethodInfo; 44 import android.view.inputmethod.InputMethodSubtype; 45 46 import java.lang.annotation.Retention; 47 import java.lang.annotation.RetentionPolicy; 48 import java.util.ArrayList; 49 import java.util.List; 50 51 /** 52 * Provides information about input devices and available key layouts. 53 * <p> 54 * Get an instance of this class by calling 55 * {@link android.content.Context#getSystemService(java.lang.String) 56 * Context.getSystemService()} with the argument 57 * {@link android.content.Context#INPUT_SERVICE}. 58 * </p> 59 */ 60 public final class InputManager { 61 private static final String TAG = "InputManager"; 62 private static final boolean DEBUG = false; 63 64 private static final int MSG_DEVICE_ADDED = 1; 65 private static final int MSG_DEVICE_REMOVED = 2; 66 private static final int MSG_DEVICE_CHANGED = 3; 67 68 private static InputManager sInstance; 69 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 public static final int INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH = 2; // see InputDispatcher.h 188 189 /** @hide */ 190 @Retention(RetentionPolicy.SOURCE) 191 @IntDef({SWITCH_STATE_UNKNOWN, SWITCH_STATE_OFF, SWITCH_STATE_ON}) 192 public @interface SwitchState {} 193 194 /** 195 * Switch State: Unknown. 196 * 197 * The system has yet to report a valid value for the switch. 198 * @hide 199 */ 200 public static final int SWITCH_STATE_UNKNOWN = -1; 201 202 /** 203 * Switch State: Off. 204 * @hide 205 */ 206 public static final int SWITCH_STATE_OFF = 0; 207 208 /** 209 * Switch State: On. 210 * @hide 211 */ 212 public static final int SWITCH_STATE_ON = 1; 213 214 private InputManager(IInputManager im) { 215 mIm = im; 216 } 217 218 /** 219 * Gets an instance of the input manager. 220 * 221 * @return The input manager instance. 222 * 223 * @hide 224 */ 225 public static InputManager getInstance() { 226 synchronized (InputManager.class) { 227 if (sInstance == null) { 228 IBinder b = ServiceManager.getService(Context.INPUT_SERVICE); 229 sInstance = new InputManager(IInputManager.Stub.asInterface(b)); 230 } 231 return sInstance; 232 } 233 } 234 235 /** 236 * Gets information about the input device with the specified id. 237 * @param id The device id. 238 * @return The input device or null if not found. 239 */ 240 public InputDevice getInputDevice(int id) { 241 synchronized (mInputDevicesLock) { 242 populateInputDevicesLocked(); 243 244 int index = mInputDevices.indexOfKey(id); 245 if (index < 0) { 246 return null; 247 } 248 249 InputDevice inputDevice = mInputDevices.valueAt(index); 250 if (inputDevice == null) { 251 try { 252 inputDevice = mIm.getInputDevice(id); 253 } catch (RemoteException ex) { 254 throw ex.rethrowFromSystemServer(); 255 } 256 if (inputDevice != null) { 257 mInputDevices.setValueAt(index, inputDevice); 258 } 259 } 260 return inputDevice; 261 } 262 } 263 264 /** 265 * Gets information about the input device with the specified descriptor. 266 * @param descriptor The input device descriptor. 267 * @return The input device or null if not found. 268 * @hide 269 */ 270 public InputDevice getInputDeviceByDescriptor(String descriptor) { 271 if (descriptor == null) { 272 throw new IllegalArgumentException("descriptor must not be null."); 273 } 274 275 synchronized (mInputDevicesLock) { 276 populateInputDevicesLocked(); 277 278 int numDevices = mInputDevices.size(); 279 for (int i = 0; i < numDevices; i++) { 280 InputDevice inputDevice = mInputDevices.valueAt(i); 281 if (inputDevice == null) { 282 int id = mInputDevices.keyAt(i); 283 try { 284 inputDevice = mIm.getInputDevice(id); 285 } catch (RemoteException ex) { 286 throw ex.rethrowFromSystemServer(); 287 } 288 if (inputDevice == null) { 289 continue; 290 } 291 mInputDevices.setValueAt(i, inputDevice); 292 } 293 if (descriptor.equals(inputDevice.getDescriptor())) { 294 return inputDevice; 295 } 296 } 297 return null; 298 } 299 } 300 301 /** 302 * Gets the ids of all input devices in the system. 303 * @return The input device ids. 304 */ 305 public int[] getInputDeviceIds() { 306 synchronized (mInputDevicesLock) { 307 populateInputDevicesLocked(); 308 309 final int count = mInputDevices.size(); 310 final int[] ids = new int[count]; 311 for (int i = 0; i < count; i++) { 312 ids[i] = mInputDevices.keyAt(i); 313 } 314 return ids; 315 } 316 } 317 318 /** 319 * Registers an input device listener to receive notifications about when 320 * input devices are added, removed or changed. 321 * 322 * @param listener The listener to register. 323 * @param handler The handler on which the listener should be invoked, or null 324 * if the listener should be invoked on the calling thread's looper. 325 * 326 * @see #unregisterInputDeviceListener 327 */ 328 public void registerInputDeviceListener(InputDeviceListener listener, Handler handler) { 329 if (listener == null) { 330 throw new IllegalArgumentException("listener must not be null"); 331 } 332 333 synchronized (mInputDevicesLock) { 334 populateInputDevicesLocked(); 335 int index = findInputDeviceListenerLocked(listener); 336 if (index < 0) { 337 mInputDeviceListeners.add(new InputDeviceListenerDelegate(listener, handler)); 338 } 339 } 340 } 341 342 /** 343 * Unregisters an input device listener. 344 * 345 * @param listener The listener to unregister. 346 * 347 * @see #registerInputDeviceListener 348 */ 349 public void unregisterInputDeviceListener(InputDeviceListener listener) { 350 if (listener == null) { 351 throw new IllegalArgumentException("listener must not be null"); 352 } 353 354 synchronized (mInputDevicesLock) { 355 int index = findInputDeviceListenerLocked(listener); 356 if (index >= 0) { 357 InputDeviceListenerDelegate d = mInputDeviceListeners.get(index); 358 d.removeCallbacksAndMessages(null); 359 mInputDeviceListeners.remove(index); 360 } 361 } 362 } 363 364 private int findInputDeviceListenerLocked(InputDeviceListener listener) { 365 final int numListeners = mInputDeviceListeners.size(); 366 for (int i = 0; i < numListeners; i++) { 367 if (mInputDeviceListeners.get(i).mListener == listener) { 368 return i; 369 } 370 } 371 return -1; 372 } 373 374 /** 375 * Queries whether the device is in tablet mode. 376 * 377 * @return The tablet switch state which is one of {@link #SWITCH_STATE_UNKNOWN}, 378 * {@link #SWITCH_STATE_OFF} or {@link #SWITCH_STATE_ON}. 379 * @hide 380 */ 381 @SwitchState 382 public int isInTabletMode() { 383 try { 384 return mIm.isInTabletMode(); 385 } catch (RemoteException ex) { 386 throw ex.rethrowFromSystemServer(); 387 } 388 } 389 390 /** 391 * Register a tablet mode changed listener. 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 * @hide 397 */ 398 public void registerOnTabletModeChangedListener( 399 OnTabletModeChangedListener listener, Handler handler) { 400 if (listener == null) { 401 throw new IllegalArgumentException("listener must not be null"); 402 } 403 synchronized (mTabletModeLock) { 404 if (mOnTabletModeChangedListeners == null) { 405 initializeTabletModeListenerLocked(); 406 } 407 int idx = findOnTabletModeChangedListenerLocked(listener); 408 if (idx < 0) { 409 OnTabletModeChangedListenerDelegate d = 410 new OnTabletModeChangedListenerDelegate(listener, handler); 411 mOnTabletModeChangedListeners.add(d); 412 } 413 } 414 } 415 416 /** 417 * Unregister a tablet mode changed listener. 418 * 419 * @param listener The listener to unregister. 420 * @hide 421 */ 422 public void unregisterOnTabletModeChangedListener(OnTabletModeChangedListener listener) { 423 if (listener == null) { 424 throw new IllegalArgumentException("listener must not be null"); 425 } 426 synchronized (mTabletModeLock) { 427 int idx = findOnTabletModeChangedListenerLocked(listener); 428 if (idx >= 0) { 429 OnTabletModeChangedListenerDelegate d = mOnTabletModeChangedListeners.remove(idx); 430 d.removeCallbacksAndMessages(null); 431 } 432 } 433 } 434 435 private void initializeTabletModeListenerLocked() { 436 final TabletModeChangedListener listener = new TabletModeChangedListener(); 437 try { 438 mIm.registerTabletModeChangedListener(listener); 439 } catch (RemoteException ex) { 440 throw ex.rethrowFromSystemServer(); 441 } 442 mTabletModeChangedListener = listener; 443 mOnTabletModeChangedListeners = new ArrayList<>(); 444 } 445 446 private int findOnTabletModeChangedListenerLocked(OnTabletModeChangedListener listener) { 447 final int N = mOnTabletModeChangedListeners.size(); 448 for (int i = 0; i < N; i++) { 449 if (mOnTabletModeChangedListeners.get(i).mListener == listener) { 450 return i; 451 } 452 } 453 return -1; 454 } 455 456 /** 457 * Gets information about all supported keyboard layouts. 458 * <p> 459 * The input manager consults the built-in keyboard layouts as well 460 * as all keyboard layouts advertised by applications using a 461 * {@link #ACTION_QUERY_KEYBOARD_LAYOUTS} broadcast receiver. 462 * </p> 463 * 464 * @return A list of all supported keyboard layouts. 465 * 466 * @hide 467 */ 468 public KeyboardLayout[] getKeyboardLayouts() { 469 try { 470 return mIm.getKeyboardLayouts(); 471 } catch (RemoteException ex) { 472 throw ex.rethrowFromSystemServer(); 473 } 474 } 475 476 /** 477 * Gets information about all supported keyboard layouts appropriate 478 * for a specific input device. 479 * <p> 480 * The input manager consults the built-in keyboard layouts as well 481 * as all keyboard layouts advertised by applications using a 482 * {@link #ACTION_QUERY_KEYBOARD_LAYOUTS} broadcast receiver. 483 * </p> 484 * 485 * @return A list of all supported keyboard layouts for a specific 486 * input device. 487 * 488 * @hide 489 */ 490 public KeyboardLayout[] getKeyboardLayoutsForInputDevice(InputDeviceIdentifier identifier) { 491 try { 492 return mIm.getKeyboardLayoutsForInputDevice(identifier); 493 } catch (RemoteException ex) { 494 throw ex.rethrowFromSystemServer(); 495 } 496 } 497 498 /** 499 * Gets the keyboard layout with the specified descriptor. 500 * 501 * @param keyboardLayoutDescriptor The keyboard layout descriptor, as returned by 502 * {@link KeyboardLayout#getDescriptor()}. 503 * @return The keyboard layout, or null if it could not be loaded. 504 * 505 * @hide 506 */ 507 public KeyboardLayout getKeyboardLayout(String keyboardLayoutDescriptor) { 508 if (keyboardLayoutDescriptor == null) { 509 throw new IllegalArgumentException("keyboardLayoutDescriptor must not be null"); 510 } 511 512 try { 513 return mIm.getKeyboardLayout(keyboardLayoutDescriptor); 514 } catch (RemoteException ex) { 515 throw ex.rethrowFromSystemServer(); 516 } 517 } 518 519 /** 520 * Gets the current keyboard layout descriptor for the specified input 521 * device. 522 * 523 * @param identifier Identifier for the input device 524 * @return The keyboard layout descriptor, or null if no keyboard layout has 525 * been set. 526 * @hide 527 */ 528 public String getCurrentKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier) { 529 try { 530 return mIm.getCurrentKeyboardLayoutForInputDevice(identifier); 531 } catch (RemoteException ex) { 532 throw ex.rethrowFromSystemServer(); 533 } 534 } 535 536 /** 537 * Sets the current keyboard layout descriptor for the specified input 538 * device. 539 * <p> 540 * This method may have the side-effect of causing the input device in 541 * question to be reconfigured. 542 * </p> 543 * 544 * @param identifier The identifier for the input device. 545 * @param keyboardLayoutDescriptor The keyboard layout descriptor to use, 546 * must not be null. 547 * @hide 548 */ 549 public void setCurrentKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier, 550 String keyboardLayoutDescriptor) { 551 if (identifier == null) { 552 throw new IllegalArgumentException("identifier must not be null"); 553 } 554 if (keyboardLayoutDescriptor == null) { 555 throw new IllegalArgumentException("keyboardLayoutDescriptor must not be null"); 556 } 557 558 try { 559 mIm.setCurrentKeyboardLayoutForInputDevice(identifier, 560 keyboardLayoutDescriptor); 561 } catch (RemoteException ex) { 562 throw ex.rethrowFromSystemServer(); 563 } 564 } 565 566 /** 567 * Gets all keyboard layout descriptors that are enabled for the specified 568 * input device. 569 * 570 * @param identifier The identifier for the input device. 571 * @return The keyboard layout descriptors. 572 * @hide 573 */ 574 public String[] getEnabledKeyboardLayoutsForInputDevice(InputDeviceIdentifier identifier) { 575 if (identifier == null) { 576 throw new IllegalArgumentException("inputDeviceDescriptor must not be null"); 577 } 578 579 try { 580 return mIm.getEnabledKeyboardLayoutsForInputDevice(identifier); 581 } catch (RemoteException ex) { 582 throw ex.rethrowFromSystemServer(); 583 } 584 } 585 586 /** 587 * Adds the keyboard layout descriptor for the specified input device. 588 * <p> 589 * This method may have the side-effect of causing the input device in 590 * question to be reconfigured. 591 * </p> 592 * 593 * @param identifier The identifier for the input device. 594 * @param keyboardLayoutDescriptor The descriptor of the keyboard layout to 595 * add. 596 * @hide 597 */ 598 public void addKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier, 599 String keyboardLayoutDescriptor) { 600 if (identifier == null) { 601 throw new IllegalArgumentException("inputDeviceDescriptor must not be null"); 602 } 603 if (keyboardLayoutDescriptor == null) { 604 throw new IllegalArgumentException("keyboardLayoutDescriptor must not be null"); 605 } 606 607 try { 608 mIm.addKeyboardLayoutForInputDevice(identifier, keyboardLayoutDescriptor); 609 } catch (RemoteException ex) { 610 throw ex.rethrowFromSystemServer(); 611 } 612 } 613 614 /** 615 * Removes the keyboard layout descriptor for the specified input device. 616 * <p> 617 * This method may have the side-effect of causing the input device in 618 * question to be reconfigured. 619 * </p> 620 * 621 * @param identifier The identifier for the input device. 622 * @param keyboardLayoutDescriptor The descriptor of the keyboard layout to 623 * remove. 624 * @hide 625 */ 626 public void removeKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier, 627 String keyboardLayoutDescriptor) { 628 if (identifier == null) { 629 throw new IllegalArgumentException("inputDeviceDescriptor must not be null"); 630 } 631 if (keyboardLayoutDescriptor == null) { 632 throw new IllegalArgumentException("keyboardLayoutDescriptor must not be null"); 633 } 634 635 try { 636 mIm.removeKeyboardLayoutForInputDevice(identifier, keyboardLayoutDescriptor); 637 } catch (RemoteException ex) { 638 throw ex.rethrowFromSystemServer(); 639 } 640 } 641 642 643 /** 644 * Gets the keyboard layout for the specified input device and IME subtype. 645 * 646 * @param identifier The identifier for the input device. 647 * @param inputMethodInfo The input method. 648 * @param inputMethodSubtype The input method subtype. {@code null} if this input method does 649 * not support any subtype. 650 * 651 * @return The associated {@link KeyboardLayout}, or null if one has not been set. 652 * 653 * @hide 654 */ 655 @Nullable 656 public KeyboardLayout getKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier, 657 InputMethodInfo inputMethodInfo, @Nullable InputMethodSubtype inputMethodSubtype) { 658 try { 659 return mIm.getKeyboardLayoutForInputDevice( 660 identifier, inputMethodInfo, inputMethodSubtype); 661 } catch (RemoteException ex) { 662 throw ex.rethrowFromSystemServer(); 663 } 664 } 665 666 /** 667 * Sets the keyboard layout for the specified input device and IME subtype pair. 668 * 669 * @param identifier The identifier for the input device. 670 * @param inputMethodInfo The input method with which to associate the keyboard layout. 671 * @param inputMethodSubtype The input method subtype which which to associate the keyboard 672 * layout. {@code null} if this input method does not support any subtype. 673 * @param keyboardLayoutDescriptor The descriptor of the keyboard layout to set 674 * 675 * @hide 676 */ 677 public void setKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier, 678 InputMethodInfo inputMethodInfo, @Nullable InputMethodSubtype inputMethodSubtype, 679 String keyboardLayoutDescriptor) { 680 try { 681 mIm.setKeyboardLayoutForInputDevice(identifier, inputMethodInfo, 682 inputMethodSubtype, keyboardLayoutDescriptor); 683 } catch (RemoteException ex) { 684 throw ex.rethrowFromSystemServer(); 685 } 686 } 687 688 /** 689 * Gets the TouchCalibration applied to the specified input device's coordinates. 690 * 691 * @param inputDeviceDescriptor The input device descriptor. 692 * @return The TouchCalibration currently assigned for use with the given 693 * input device. If none is set, an identity TouchCalibration is returned. 694 * 695 * @hide 696 */ 697 public TouchCalibration getTouchCalibration(String inputDeviceDescriptor, int surfaceRotation) { 698 try { 699 return mIm.getTouchCalibrationForInputDevice(inputDeviceDescriptor, surfaceRotation); 700 } catch (RemoteException ex) { 701 throw ex.rethrowFromSystemServer(); 702 } 703 } 704 705 /** 706 * Sets the TouchCalibration to apply to the specified input device's coordinates. 707 * <p> 708 * This method may have the side-effect of causing the input device in question 709 * to be reconfigured. Requires {@link android.Manifest.permissions.SET_INPUT_CALIBRATION}. 710 * </p> 711 * 712 * @param inputDeviceDescriptor The input device descriptor. 713 * @param calibration The calibration to be applied 714 * 715 * @hide 716 */ 717 public void setTouchCalibration(String inputDeviceDescriptor, int surfaceRotation, 718 TouchCalibration calibration) { 719 try { 720 mIm.setTouchCalibrationForInputDevice(inputDeviceDescriptor, surfaceRotation, calibration); 721 } catch (RemoteException ex) { 722 throw ex.rethrowFromSystemServer(); 723 } 724 } 725 726 /** 727 * Gets the mouse pointer speed. 728 * <p> 729 * Only returns the permanent mouse pointer speed. Ignores any temporary pointer 730 * speed set by {@link #tryPointerSpeed}. 731 * </p> 732 * 733 * @param context The application context. 734 * @return The pointer speed as a value between {@link #MIN_POINTER_SPEED} and 735 * {@link #MAX_POINTER_SPEED}, or the default value {@link #DEFAULT_POINTER_SPEED}. 736 * 737 * @hide 738 */ 739 public int getPointerSpeed(Context context) { 740 int speed = DEFAULT_POINTER_SPEED; 741 try { 742 speed = Settings.System.getInt(context.getContentResolver(), 743 Settings.System.POINTER_SPEED); 744 } catch (SettingNotFoundException snfe) { 745 } 746 return speed; 747 } 748 749 /** 750 * Sets the mouse pointer speed. 751 * <p> 752 * Requires {@link android.Manifest.permissions.WRITE_SETTINGS}. 753 * </p> 754 * 755 * @param context The application context. 756 * @param speed The pointer speed as a value between {@link #MIN_POINTER_SPEED} and 757 * {@link #MAX_POINTER_SPEED}, or the default value {@link #DEFAULT_POINTER_SPEED}. 758 * 759 * @hide 760 */ 761 public void setPointerSpeed(Context context, int speed) { 762 if (speed < MIN_POINTER_SPEED || speed > MAX_POINTER_SPEED) { 763 throw new IllegalArgumentException("speed out of range"); 764 } 765 766 Settings.System.putInt(context.getContentResolver(), 767 Settings.System.POINTER_SPEED, speed); 768 } 769 770 /** 771 * Changes the mouse pointer speed temporarily, but does not save the setting. 772 * <p> 773 * Requires {@link android.Manifest.permission.SET_POINTER_SPEED}. 774 * </p> 775 * 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 */ 781 public void tryPointerSpeed(int speed) { 782 if (speed < MIN_POINTER_SPEED || speed > MAX_POINTER_SPEED) { 783 throw new IllegalArgumentException("speed out of range"); 784 } 785 786 try { 787 mIm.tryPointerSpeed(speed); 788 } catch (RemoteException ex) { 789 throw ex.rethrowFromSystemServer(); 790 } 791 } 792 793 /** 794 * Queries the framework about whether any physical keys exist on the 795 * any keyboard attached to the device that are capable of producing the given 796 * array of key codes. 797 * 798 * @param keyCodes The array of key codes to query. 799 * @return A new array of the same size as the key codes array whose elements 800 * are set to true if at least one attached keyboard supports the corresponding key code 801 * at the same index in the key codes array. 802 * 803 * @hide 804 */ 805 public boolean[] deviceHasKeys(int[] keyCodes) { 806 return deviceHasKeys(-1, keyCodes); 807 } 808 809 /** 810 * Queries the framework about whether any physical keys exist on the 811 * any keyboard attached to the device that are capable of producing the given 812 * array of key codes. 813 * 814 * @param id The id of the device to query. 815 * @param keyCodes The array of key codes to query. 816 * @return A new array of the same size as the key codes array whose elements are set to true 817 * if the given device could produce the corresponding key code at the same index in the key 818 * codes array. 819 * 820 * @hide 821 */ 822 public boolean[] deviceHasKeys(int id, int[] keyCodes) { 823 boolean[] ret = new boolean[keyCodes.length]; 824 try { 825 mIm.hasKeys(id, InputDevice.SOURCE_ANY, keyCodes, ret); 826 } catch (RemoteException e) { 827 throw e.rethrowFromSystemServer(); 828 } 829 return ret; 830 } 831 832 833 /** 834 * Injects an input event into the event system on behalf of an application. 835 * The synchronization mode determines whether the method blocks while waiting for 836 * input injection to proceed. 837 * <p> 838 * Requires {@link android.Manifest.permission.INJECT_EVENTS} to inject into 839 * windows that are owned by other applications. 840 * </p><p> 841 * Make sure you correctly set the event time and input source of the event 842 * before calling this method. 843 * </p> 844 * 845 * @param event The event to inject. 846 * @param mode The synchronization mode. One of: 847 * {@link #INJECT_INPUT_EVENT_MODE_ASYNC}, 848 * {@link #INJECT_INPUT_EVENT_MODE_WAIT_FOR_RESULT}, or 849 * {@link #INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH}. 850 * @return True if input event injection succeeded. 851 * 852 * @hide 853 */ 854 public boolean injectInputEvent(InputEvent event, int mode) { 855 if (event == null) { 856 throw new IllegalArgumentException("event must not be null"); 857 } 858 if (mode != INJECT_INPUT_EVENT_MODE_ASYNC 859 && mode != INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH 860 && mode != INJECT_INPUT_EVENT_MODE_WAIT_FOR_RESULT) { 861 throw new IllegalArgumentException("mode is invalid"); 862 } 863 864 try { 865 return mIm.injectInputEvent(event, mode); 866 } catch (RemoteException ex) { 867 throw ex.rethrowFromSystemServer(); 868 } 869 } 870 871 /** 872 * Changes the mouse pointer's icon shape into the specified id. 873 * 874 * @param iconId The id of the pointer graphic, as a value between 875 * {@link PointerIcon.TYPE_ARROW} and {@link PointerIcon.TYPE_GRABBING}. 876 * 877 * @hide 878 */ 879 public void setPointerIconType(int iconId) { 880 try { 881 mIm.setPointerIconType(iconId); 882 } catch (RemoteException ex) { 883 throw ex.rethrowFromSystemServer(); 884 } 885 } 886 887 /** @hide */ 888 public void setCustomPointerIcon(PointerIcon icon) { 889 try { 890 mIm.setCustomPointerIcon(icon); 891 } catch (RemoteException ex) { 892 throw ex.rethrowFromSystemServer(); 893 } 894 } 895 896 private void populateInputDevicesLocked() { 897 if (mInputDevicesChangedListener == null) { 898 final InputDevicesChangedListener listener = new InputDevicesChangedListener(); 899 try { 900 mIm.registerInputDevicesChangedListener(listener); 901 } catch (RemoteException ex) { 902 throw ex.rethrowFromSystemServer(); 903 } 904 mInputDevicesChangedListener = listener; 905 } 906 907 if (mInputDevices == null) { 908 final int[] ids; 909 try { 910 ids = mIm.getInputDeviceIds(); 911 } catch (RemoteException ex) { 912 throw ex.rethrowFromSystemServer(); 913 } 914 915 mInputDevices = new SparseArray<InputDevice>(); 916 for (int i = 0; i < ids.length; i++) { 917 mInputDevices.put(ids[i], null); 918 } 919 } 920 } 921 922 private void onInputDevicesChanged(int[] deviceIdAndGeneration) { 923 if (DEBUG) { 924 Log.d(TAG, "Received input devices changed."); 925 } 926 927 synchronized (mInputDevicesLock) { 928 for (int i = mInputDevices.size(); --i > 0; ) { 929 final int deviceId = mInputDevices.keyAt(i); 930 if (!containsDeviceId(deviceIdAndGeneration, deviceId)) { 931 if (DEBUG) { 932 Log.d(TAG, "Device removed: " + deviceId); 933 } 934 mInputDevices.removeAt(i); 935 sendMessageToInputDeviceListenersLocked(MSG_DEVICE_REMOVED, deviceId); 936 } 937 } 938 939 for (int i = 0; i < deviceIdAndGeneration.length; i += 2) { 940 final int deviceId = deviceIdAndGeneration[i]; 941 int index = mInputDevices.indexOfKey(deviceId); 942 if (index >= 0) { 943 final InputDevice device = mInputDevices.valueAt(index); 944 if (device != null) { 945 final int generation = deviceIdAndGeneration[i + 1]; 946 if (device.getGeneration() != generation) { 947 if (DEBUG) { 948 Log.d(TAG, "Device changed: " + deviceId); 949 } 950 mInputDevices.setValueAt(index, null); 951 sendMessageToInputDeviceListenersLocked(MSG_DEVICE_CHANGED, deviceId); 952 } 953 } 954 } else { 955 if (DEBUG) { 956 Log.d(TAG, "Device added: " + deviceId); 957 } 958 mInputDevices.put(deviceId, null); 959 sendMessageToInputDeviceListenersLocked(MSG_DEVICE_ADDED, deviceId); 960 } 961 } 962 } 963 } 964 965 private void sendMessageToInputDeviceListenersLocked(int what, int deviceId) { 966 final int numListeners = mInputDeviceListeners.size(); 967 for (int i = 0; i < numListeners; i++) { 968 InputDeviceListenerDelegate listener = mInputDeviceListeners.get(i); 969 listener.sendMessage(listener.obtainMessage(what, deviceId, 0)); 970 } 971 } 972 973 private static boolean containsDeviceId(int[] deviceIdAndGeneration, int deviceId) { 974 for (int i = 0; i < deviceIdAndGeneration.length; i += 2) { 975 if (deviceIdAndGeneration[i] == deviceId) { 976 return true; 977 } 978 } 979 return false; 980 } 981 982 983 private void onTabletModeChanged(long whenNanos, boolean inTabletMode) { 984 if (DEBUG) { 985 Log.d(TAG, "Received tablet mode changed: " 986 + "whenNanos=" + whenNanos + ", inTabletMode=" + inTabletMode); 987 } 988 synchronized (mTabletModeLock) { 989 final int N = mOnTabletModeChangedListeners.size(); 990 for (int i = 0; i < N; i++) { 991 OnTabletModeChangedListenerDelegate listener = 992 mOnTabletModeChangedListeners.get(i); 993 listener.sendTabletModeChanged(whenNanos, inTabletMode); 994 } 995 } 996 } 997 998 /** 999 * Gets a vibrator service associated with an input device, assuming it has one. 1000 * @return The vibrator, never null. 1001 * @hide 1002 */ 1003 public Vibrator getInputDeviceVibrator(int deviceId) { 1004 return new InputDeviceVibrator(deviceId); 1005 } 1006 1007 /** 1008 * Listens for changes in input devices. 1009 */ 1010 public interface InputDeviceListener { 1011 /** 1012 * Called whenever an input device has been added to the system. 1013 * Use {@link InputManager#getInputDevice} to get more information about the device. 1014 * 1015 * @param deviceId The id of the input device that was added. 1016 */ 1017 void onInputDeviceAdded(int deviceId); 1018 1019 /** 1020 * Called whenever an input device has been removed from the system. 1021 * 1022 * @param deviceId The id of the input device that was removed. 1023 */ 1024 void onInputDeviceRemoved(int deviceId); 1025 1026 /** 1027 * Called whenever the properties of an input device have changed since they 1028 * were last queried. Use {@link InputManager#getInputDevice} to get 1029 * a fresh {@link InputDevice} object with the new properties. 1030 * 1031 * @param deviceId The id of the input device that changed. 1032 */ 1033 void onInputDeviceChanged(int deviceId); 1034 } 1035 1036 private final class InputDevicesChangedListener extends IInputDevicesChangedListener.Stub { 1037 @Override 1038 public void onInputDevicesChanged(int[] deviceIdAndGeneration) throws RemoteException { 1039 InputManager.this.onInputDevicesChanged(deviceIdAndGeneration); 1040 } 1041 } 1042 1043 private static final class InputDeviceListenerDelegate extends Handler { 1044 public final InputDeviceListener mListener; 1045 1046 public InputDeviceListenerDelegate(InputDeviceListener listener, Handler handler) { 1047 super(handler != null ? handler.getLooper() : Looper.myLooper()); 1048 mListener = listener; 1049 } 1050 1051 @Override 1052 public void handleMessage(Message msg) { 1053 switch (msg.what) { 1054 case MSG_DEVICE_ADDED: 1055 mListener.onInputDeviceAdded(msg.arg1); 1056 break; 1057 case MSG_DEVICE_REMOVED: 1058 mListener.onInputDeviceRemoved(msg.arg1); 1059 break; 1060 case MSG_DEVICE_CHANGED: 1061 mListener.onInputDeviceChanged(msg.arg1); 1062 break; 1063 } 1064 } 1065 } 1066 1067 /** @hide */ 1068 public interface OnTabletModeChangedListener { 1069 /** 1070 * Called whenever the device goes into or comes out of tablet mode. 1071 * 1072 * @param whenNanos The time at which the device transitioned into or 1073 * out of tablet mode. This is given in nanoseconds in the 1074 * {@link SystemClock#uptimeMillis} time base. 1075 */ 1076 void onTabletModeChanged(long whenNanos, boolean inTabletMode); 1077 } 1078 1079 private final class TabletModeChangedListener extends ITabletModeChangedListener.Stub { 1080 @Override 1081 public void onTabletModeChanged(long whenNanos, boolean inTabletMode) { 1082 InputManager.this.onTabletModeChanged(whenNanos, inTabletMode); 1083 } 1084 } 1085 1086 private static final class OnTabletModeChangedListenerDelegate extends Handler { 1087 private static final int MSG_TABLET_MODE_CHANGED = 0; 1088 1089 public final OnTabletModeChangedListener mListener; 1090 1091 public OnTabletModeChangedListenerDelegate( 1092 OnTabletModeChangedListener listener, Handler handler) { 1093 super(handler != null ? handler.getLooper() : Looper.myLooper()); 1094 mListener = listener; 1095 } 1096 1097 public void sendTabletModeChanged(long whenNanos, boolean inTabletMode) { 1098 SomeArgs args = SomeArgs.obtain(); 1099 args.argi1 = (int) (whenNanos & 0xFFFFFFFF); 1100 args.argi2 = (int) (whenNanos >> 32); 1101 args.arg1 = (Boolean) inTabletMode; 1102 obtainMessage(MSG_TABLET_MODE_CHANGED, args).sendToTarget(); 1103 } 1104 1105 @Override 1106 public void handleMessage(Message msg) { 1107 switch (msg.what) { 1108 case MSG_TABLET_MODE_CHANGED: 1109 SomeArgs args = (SomeArgs) msg.obj; 1110 long whenNanos = (args.argi1 & 0xFFFFFFFFl) | ((long) args.argi2 << 32); 1111 boolean inTabletMode = (boolean) args.arg1; 1112 mListener.onTabletModeChanged(whenNanos, inTabletMode); 1113 break; 1114 } 1115 } 1116 } 1117 1118 private final class InputDeviceVibrator extends Vibrator { 1119 private final int mDeviceId; 1120 private final Binder mToken; 1121 1122 public InputDeviceVibrator(int deviceId) { 1123 mDeviceId = deviceId; 1124 mToken = new Binder(); 1125 } 1126 1127 @Override 1128 public boolean hasVibrator() { 1129 return true; 1130 } 1131 1132 /** 1133 * @hide 1134 */ 1135 @Override 1136 public void vibrate(int uid, String opPkg, long milliseconds, AudioAttributes attributes) { 1137 vibrate(new long[] { 0, milliseconds}, -1); 1138 } 1139 1140 /** 1141 * @hide 1142 */ 1143 @Override 1144 public void vibrate(int uid, String opPkg, long[] pattern, int repeat, 1145 AudioAttributes attributes) { 1146 if (repeat >= pattern.length) { 1147 throw new ArrayIndexOutOfBoundsException(); 1148 } 1149 try { 1150 mIm.vibrate(mDeviceId, pattern, repeat, mToken); 1151 } catch (RemoteException ex) { 1152 throw ex.rethrowFromSystemServer(); 1153 } 1154 } 1155 1156 @Override 1157 public void cancel() { 1158 try { 1159 mIm.cancelVibrate(mDeviceId, mToken); 1160 } catch (RemoteException ex) { 1161 throw ex.rethrowFromSystemServer(); 1162 } 1163 } 1164 } 1165 } 1166