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