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