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