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.util.ArrayUtils; 20 21 import android.annotation.SdkConstant; 22 import android.annotation.SdkConstant.SdkConstantType; 23 import android.content.Context; 24 import android.media.AudioAttributes; 25 import android.os.Binder; 26 import android.os.Handler; 27 import android.os.IBinder; 28 import android.os.Looper; 29 import android.os.Message; 30 import android.os.Parcel; 31 import android.os.Parcelable; 32 import android.os.RemoteException; 33 import android.os.ServiceManager; 34 import android.os.Vibrator; 35 import android.provider.Settings; 36 import android.provider.Settings.SettingNotFoundException; 37 import android.util.Log; 38 import android.util.SparseArray; 39 import android.view.InputDevice; 40 import android.view.InputEvent; 41 42 import java.util.ArrayList; 43 44 /** 45 * Provides information about input devices and available key layouts. 46 * <p> 47 * Get an instance of this class by calling 48 * {@link android.content.Context#getSystemService(java.lang.String) 49 * Context.getSystemService()} with the argument 50 * {@link android.content.Context#INPUT_SERVICE}. 51 * </p> 52 */ 53 public final class InputManager { 54 private static final String TAG = "InputManager"; 55 private static final boolean DEBUG = false; 56 57 private static final int MSG_DEVICE_ADDED = 1; 58 private static final int MSG_DEVICE_REMOVED = 2; 59 private static final int MSG_DEVICE_CHANGED = 3; 60 61 private static InputManager sInstance; 62 63 private final IInputManager mIm; 64 65 // Guarded by mInputDevicesLock 66 private final Object mInputDevicesLock = new Object(); 67 private SparseArray<InputDevice> mInputDevices; 68 private InputDevicesChangedListener mInputDevicesChangedListener; 69 private final ArrayList<InputDeviceListenerDelegate> mInputDeviceListeners = 70 new ArrayList<InputDeviceListenerDelegate>(); 71 72 /** 73 * Broadcast Action: Query available keyboard layouts. 74 * <p> 75 * The input manager service locates available keyboard layouts 76 * by querying broadcast receivers that are registered for this action. 77 * An application can offer additional keyboard layouts to the user 78 * by declaring a suitable broadcast receiver in its manifest. 79 * </p><p> 80 * Here is an example broadcast receiver declaration that an application 81 * might include in its AndroidManifest.xml to advertise keyboard layouts. 82 * The meta-data specifies a resource that contains a description of each keyboard 83 * layout that is provided by the application. 84 * <pre><code> 85 * <receiver android:name=".InputDeviceReceiver" 86 * android:label="@string/keyboard_layouts_label"> 87 * <intent-filter> 88 * <action android:name="android.hardware.input.action.QUERY_KEYBOARD_LAYOUTS" /> 89 * </intent-filter> 90 * <meta-data android:name="android.hardware.input.metadata.KEYBOARD_LAYOUTS" 91 * android:resource="@xml/keyboard_layouts" /> 92 * </receiver> 93 * </code></pre> 94 * </p><p> 95 * In the above example, the <code>@xml/keyboard_layouts</code> resource refers to 96 * an XML resource whose root element is <code><keyboard-layouts></code> that 97 * contains zero or more <code><keyboard-layout></code> elements. 98 * Each <code><keyboard-layout></code> element specifies the name, label, and location 99 * of a key character map for a particular keyboard layout. The label on the receiver 100 * is used to name the collection of keyboard layouts provided by this receiver in the 101 * keyboard layout settings. 102 * <pre></code> 103 * <?xml version="1.0" encoding="utf-8"?> 104 * <keyboard-layouts xmlns:android="http://schemas.android.com/apk/res/android"> 105 * <keyboard-layout android:name="keyboard_layout_english_us" 106 * android:label="@string/keyboard_layout_english_us_label" 107 * android:keyboardLayout="@raw/keyboard_layout_english_us" /> 108 * </keyboard-layouts> 109 * </p><p> 110 * The <code>android:name</code> attribute specifies an identifier by which 111 * the keyboard layout will be known in the package. 112 * The <code>android:label</code> attributes specifies a human-readable descriptive 113 * label to describe the keyboard layout in the user interface, such as "English (US)". 114 * The <code>android:keyboardLayout</code> attribute refers to a 115 * <a href="http://source.android.com/tech/input/key-character-map-files.html"> 116 * key character map</a> resource that defines the keyboard layout. 117 * </p> 118 */ 119 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) 120 public static final String ACTION_QUERY_KEYBOARD_LAYOUTS = 121 "android.hardware.input.action.QUERY_KEYBOARD_LAYOUTS"; 122 123 /** 124 * Metadata Key: Keyboard layout metadata associated with 125 * {@link #ACTION_QUERY_KEYBOARD_LAYOUTS}. 126 * <p> 127 * Specifies the resource id of a XML resource that describes the keyboard 128 * layouts that are provided by the application. 129 * </p> 130 */ 131 public static final String META_DATA_KEYBOARD_LAYOUTS = 132 "android.hardware.input.metadata.KEYBOARD_LAYOUTS"; 133 134 /** 135 * Pointer Speed: The minimum (slowest) pointer speed (-7). 136 * @hide 137 */ 138 public static final int MIN_POINTER_SPEED = -7; 139 140 /** 141 * Pointer Speed: The maximum (fastest) pointer speed (7). 142 * @hide 143 */ 144 public static final int MAX_POINTER_SPEED = 7; 145 146 /** 147 * Pointer Speed: The default pointer speed (0). 148 * @hide 149 */ 150 public static final int DEFAULT_POINTER_SPEED = 0; 151 152 /** 153 * Input Event Injection Synchronization Mode: None. 154 * Never blocks. Injection is asynchronous and is assumed always to be successful. 155 * @hide 156 */ 157 public static final int INJECT_INPUT_EVENT_MODE_ASYNC = 0; // see InputDispatcher.h 158 159 /** 160 * Input Event Injection Synchronization Mode: Wait for result. 161 * Waits for previous events to be dispatched so that the input dispatcher can 162 * determine whether input event injection will be permitted based on the current 163 * input focus. Does not wait for the input event to finish being handled 164 * by the application. 165 * @hide 166 */ 167 public static final int INJECT_INPUT_EVENT_MODE_WAIT_FOR_RESULT = 1; // see InputDispatcher.h 168 169 /** 170 * Input Event Injection Synchronization Mode: Wait for finish. 171 * Waits for the event to be delivered to the application and handled. 172 * @hide 173 */ 174 public static final int INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH = 2; // see InputDispatcher.h 175 InputManager(IInputManager im)176 private InputManager(IInputManager im) { 177 mIm = im; 178 } 179 180 /** 181 * Gets an instance of the input manager. 182 * 183 * @return The input manager instance. 184 * 185 * @hide 186 */ getInstance()187 public static InputManager getInstance() { 188 synchronized (InputManager.class) { 189 if (sInstance == null) { 190 IBinder b = ServiceManager.getService(Context.INPUT_SERVICE); 191 sInstance = new InputManager(IInputManager.Stub.asInterface(b)); 192 } 193 return sInstance; 194 } 195 } 196 197 /** 198 * Gets information about the input device with the specified id. 199 * @param id The device id. 200 * @return The input device or null if not found. 201 */ getInputDevice(int id)202 public InputDevice getInputDevice(int id) { 203 synchronized (mInputDevicesLock) { 204 populateInputDevicesLocked(); 205 206 int index = mInputDevices.indexOfKey(id); 207 if (index < 0) { 208 return null; 209 } 210 211 InputDevice inputDevice = mInputDevices.valueAt(index); 212 if (inputDevice == null) { 213 try { 214 inputDevice = mIm.getInputDevice(id); 215 } catch (RemoteException ex) { 216 throw new RuntimeException("Could not get input device information.", ex); 217 } 218 if (inputDevice != null) { 219 mInputDevices.setValueAt(index, inputDevice); 220 } 221 } 222 return inputDevice; 223 } 224 } 225 226 /** 227 * Gets information about the input device with the specified descriptor. 228 * @param descriptor The input device descriptor. 229 * @return The input device or null if not found. 230 * @hide 231 */ getInputDeviceByDescriptor(String descriptor)232 public InputDevice getInputDeviceByDescriptor(String descriptor) { 233 if (descriptor == null) { 234 throw new IllegalArgumentException("descriptor must not be null."); 235 } 236 237 synchronized (mInputDevicesLock) { 238 populateInputDevicesLocked(); 239 240 int numDevices = mInputDevices.size(); 241 for (int i = 0; i < numDevices; i++) { 242 InputDevice inputDevice = mInputDevices.valueAt(i); 243 if (inputDevice == null) { 244 int id = mInputDevices.keyAt(i); 245 try { 246 inputDevice = mIm.getInputDevice(id); 247 } catch (RemoteException ex) { 248 // Ignore the problem for the purposes of this method. 249 } 250 if (inputDevice == null) { 251 continue; 252 } 253 mInputDevices.setValueAt(i, inputDevice); 254 } 255 if (descriptor.equals(inputDevice.getDescriptor())) { 256 return inputDevice; 257 } 258 } 259 return null; 260 } 261 } 262 263 /** 264 * Gets the ids of all input devices in the system. 265 * @return The input device ids. 266 */ getInputDeviceIds()267 public int[] getInputDeviceIds() { 268 synchronized (mInputDevicesLock) { 269 populateInputDevicesLocked(); 270 271 final int count = mInputDevices.size(); 272 final int[] ids = new int[count]; 273 for (int i = 0; i < count; i++) { 274 ids[i] = mInputDevices.keyAt(i); 275 } 276 return ids; 277 } 278 } 279 280 /** 281 * Registers an input device listener to receive notifications about when 282 * input devices are added, removed or changed. 283 * 284 * @param listener The listener to register. 285 * @param handler The handler on which the listener should be invoked, or null 286 * if the listener should be invoked on the calling thread's looper. 287 * 288 * @see #unregisterInputDeviceListener 289 */ registerInputDeviceListener(InputDeviceListener listener, Handler handler)290 public void registerInputDeviceListener(InputDeviceListener listener, Handler handler) { 291 if (listener == null) { 292 throw new IllegalArgumentException("listener must not be null"); 293 } 294 295 synchronized (mInputDevicesLock) { 296 int index = findInputDeviceListenerLocked(listener); 297 if (index < 0) { 298 mInputDeviceListeners.add(new InputDeviceListenerDelegate(listener, handler)); 299 } 300 } 301 } 302 303 /** 304 * Unregisters an input device listener. 305 * 306 * @param listener The listener to unregister. 307 * 308 * @see #registerInputDeviceListener 309 */ unregisterInputDeviceListener(InputDeviceListener listener)310 public void unregisterInputDeviceListener(InputDeviceListener listener) { 311 if (listener == null) { 312 throw new IllegalArgumentException("listener must not be null"); 313 } 314 315 synchronized (mInputDevicesLock) { 316 int index = findInputDeviceListenerLocked(listener); 317 if (index >= 0) { 318 InputDeviceListenerDelegate d = mInputDeviceListeners.get(index); 319 d.removeCallbacksAndMessages(null); 320 mInputDeviceListeners.remove(index); 321 } 322 } 323 } 324 findInputDeviceListenerLocked(InputDeviceListener listener)325 private int findInputDeviceListenerLocked(InputDeviceListener listener) { 326 final int numListeners = mInputDeviceListeners.size(); 327 for (int i = 0; i < numListeners; i++) { 328 if (mInputDeviceListeners.get(i).mListener == listener) { 329 return i; 330 } 331 } 332 return -1; 333 } 334 335 /** 336 * Gets information about all supported keyboard layouts. 337 * <p> 338 * The input manager consults the built-in keyboard layouts as well 339 * as all keyboard layouts advertised by applications using a 340 * {@link #ACTION_QUERY_KEYBOARD_LAYOUTS} broadcast receiver. 341 * </p> 342 * 343 * @return A list of all supported keyboard layouts. 344 * 345 * @hide 346 */ getKeyboardLayouts()347 public KeyboardLayout[] getKeyboardLayouts() { 348 try { 349 return mIm.getKeyboardLayouts(); 350 } catch (RemoteException ex) { 351 Log.w(TAG, "Could not get list of keyboard layout informations.", ex); 352 return new KeyboardLayout[0]; 353 } 354 } 355 356 /** 357 * Gets the keyboard layout with the specified descriptor. 358 * 359 * @param keyboardLayoutDescriptor The keyboard layout descriptor, as returned by 360 * {@link KeyboardLayout#getDescriptor()}. 361 * @return The keyboard layout, or null if it could not be loaded. 362 * 363 * @hide 364 */ getKeyboardLayout(String keyboardLayoutDescriptor)365 public KeyboardLayout getKeyboardLayout(String keyboardLayoutDescriptor) { 366 if (keyboardLayoutDescriptor == null) { 367 throw new IllegalArgumentException("keyboardLayoutDescriptor must not be null"); 368 } 369 370 try { 371 return mIm.getKeyboardLayout(keyboardLayoutDescriptor); 372 } catch (RemoteException ex) { 373 Log.w(TAG, "Could not get keyboard layout information.", ex); 374 return null; 375 } 376 } 377 378 /** 379 * Gets the current keyboard layout descriptor for the specified input 380 * device. 381 * 382 * @param identifier Identifier for the input device 383 * @return The keyboard layout descriptor, or null if no keyboard layout has 384 * been set. 385 * @hide 386 */ getCurrentKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier)387 public String getCurrentKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier) { 388 try { 389 return mIm.getCurrentKeyboardLayoutForInputDevice(identifier); 390 } catch (RemoteException ex) { 391 Log.w(TAG, "Could not get current keyboard layout for input device.", ex); 392 return null; 393 } 394 } 395 396 /** 397 * Sets the current keyboard layout descriptor for the specified input 398 * device. 399 * <p> 400 * This method may have the side-effect of causing the input device in 401 * question to be reconfigured. 402 * </p> 403 * 404 * @param identifier The identifier for the input device. 405 * @param keyboardLayoutDescriptor The keyboard layout descriptor to use, 406 * must not be null. 407 * @hide 408 */ setCurrentKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier, String keyboardLayoutDescriptor)409 public void setCurrentKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier, 410 String keyboardLayoutDescriptor) { 411 if (identifier == null) { 412 throw new IllegalArgumentException("identifier must not be null"); 413 } 414 if (keyboardLayoutDescriptor == null) { 415 throw new IllegalArgumentException("keyboardLayoutDescriptor must not be null"); 416 } 417 418 try { 419 mIm.setCurrentKeyboardLayoutForInputDevice(identifier, 420 keyboardLayoutDescriptor); 421 } catch (RemoteException ex) { 422 Log.w(TAG, "Could not set current keyboard layout for input device.", ex); 423 } 424 } 425 426 /** 427 * Gets all keyboard layout descriptors that are enabled for the specified 428 * input device. 429 * 430 * @param identifier The identifier for the input device. 431 * @return The keyboard layout descriptors. 432 * @hide 433 */ getKeyboardLayoutsForInputDevice(InputDeviceIdentifier identifier)434 public String[] getKeyboardLayoutsForInputDevice(InputDeviceIdentifier identifier) { 435 if (identifier == null) { 436 throw new IllegalArgumentException("inputDeviceDescriptor must not be null"); 437 } 438 439 try { 440 return mIm.getKeyboardLayoutsForInputDevice(identifier); 441 } catch (RemoteException ex) { 442 Log.w(TAG, "Could not get keyboard layouts for input device.", ex); 443 return ArrayUtils.emptyArray(String.class); 444 } 445 } 446 447 /** 448 * Adds the keyboard layout descriptor for the specified input device. 449 * <p> 450 * This method may have the side-effect of causing the input device in 451 * question to be reconfigured. 452 * </p> 453 * 454 * @param identifier The identifier for the input device. 455 * @param keyboardLayoutDescriptor The descriptor of the keyboard layout to 456 * add. 457 * @hide 458 */ addKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier, String keyboardLayoutDescriptor)459 public void addKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier, 460 String keyboardLayoutDescriptor) { 461 if (identifier == null) { 462 throw new IllegalArgumentException("inputDeviceDescriptor must not be null"); 463 } 464 if (keyboardLayoutDescriptor == null) { 465 throw new IllegalArgumentException("keyboardLayoutDescriptor must not be null"); 466 } 467 468 try { 469 mIm.addKeyboardLayoutForInputDevice(identifier, keyboardLayoutDescriptor); 470 } catch (RemoteException ex) { 471 Log.w(TAG, "Could not add keyboard layout for input device.", ex); 472 } 473 } 474 475 /** 476 * Removes the keyboard layout descriptor for the specified input device. 477 * <p> 478 * This method may have the side-effect of causing the input device in 479 * question to be reconfigured. 480 * </p> 481 * 482 * @param identifier The identifier for the input device. 483 * @param keyboardLayoutDescriptor The descriptor of the keyboard layout to 484 * remove. 485 * @hide 486 */ removeKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier, String keyboardLayoutDescriptor)487 public void removeKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier, 488 String keyboardLayoutDescriptor) { 489 if (identifier == null) { 490 throw new IllegalArgumentException("inputDeviceDescriptor must not be null"); 491 } 492 if (keyboardLayoutDescriptor == null) { 493 throw new IllegalArgumentException("keyboardLayoutDescriptor must not be null"); 494 } 495 496 try { 497 mIm.removeKeyboardLayoutForInputDevice(identifier, keyboardLayoutDescriptor); 498 } catch (RemoteException ex) { 499 Log.w(TAG, "Could not remove keyboard layout for input device.", ex); 500 } 501 } 502 503 /** 504 * Gets the TouchCalibration applied to the specified input device's coordinates. 505 * 506 * @param inputDeviceDescriptor The input device descriptor. 507 * @return The TouchCalibration currently assigned for use with the given 508 * input device. If none is set, an identity TouchCalibration is returned. 509 * 510 * @hide 511 */ getTouchCalibration(String inputDeviceDescriptor, int surfaceRotation)512 public TouchCalibration getTouchCalibration(String inputDeviceDescriptor, int surfaceRotation) { 513 try { 514 return mIm.getTouchCalibrationForInputDevice(inputDeviceDescriptor, surfaceRotation); 515 } catch (RemoteException ex) { 516 Log.w(TAG, "Could not get calibration matrix for input device.", ex); 517 return TouchCalibration.IDENTITY; 518 } 519 } 520 521 /** 522 * Sets the TouchCalibration to apply to the specified input device's coordinates. 523 * <p> 524 * This method may have the side-effect of causing the input device in question 525 * to be reconfigured. Requires {@link android.Manifest.permissions.SET_INPUT_CALIBRATION}. 526 * </p> 527 * 528 * @param inputDeviceDescriptor The input device descriptor. 529 * @param calibration The calibration to be applied 530 * 531 * @hide 532 */ setTouchCalibration(String inputDeviceDescriptor, int surfaceRotation, TouchCalibration calibration)533 public void setTouchCalibration(String inputDeviceDescriptor, int surfaceRotation, 534 TouchCalibration calibration) { 535 try { 536 mIm.setTouchCalibrationForInputDevice(inputDeviceDescriptor, surfaceRotation, calibration); 537 } catch (RemoteException ex) { 538 Log.w(TAG, "Could not set calibration matrix for input device.", ex); 539 } 540 } 541 542 /** 543 * Gets the mouse pointer speed. 544 * <p> 545 * Only returns the permanent mouse pointer speed. Ignores any temporary pointer 546 * speed set by {@link #tryPointerSpeed}. 547 * </p> 548 * 549 * @param context The application context. 550 * @return The pointer speed as a value between {@link #MIN_POINTER_SPEED} and 551 * {@link #MAX_POINTER_SPEED}, or the default value {@link #DEFAULT_POINTER_SPEED}. 552 * 553 * @hide 554 */ getPointerSpeed(Context context)555 public int getPointerSpeed(Context context) { 556 int speed = DEFAULT_POINTER_SPEED; 557 try { 558 speed = Settings.System.getInt(context.getContentResolver(), 559 Settings.System.POINTER_SPEED); 560 } catch (SettingNotFoundException snfe) { 561 } 562 return speed; 563 } 564 565 /** 566 * Sets the mouse pointer speed. 567 * <p> 568 * Requires {@link android.Manifest.permissions.WRITE_SETTINGS}. 569 * </p> 570 * 571 * @param context The application context. 572 * @param speed The pointer speed as a value between {@link #MIN_POINTER_SPEED} and 573 * {@link #MAX_POINTER_SPEED}, or the default value {@link #DEFAULT_POINTER_SPEED}. 574 * 575 * @hide 576 */ setPointerSpeed(Context context, int speed)577 public void setPointerSpeed(Context context, int speed) { 578 if (speed < MIN_POINTER_SPEED || speed > MAX_POINTER_SPEED) { 579 throw new IllegalArgumentException("speed out of range"); 580 } 581 582 Settings.System.putInt(context.getContentResolver(), 583 Settings.System.POINTER_SPEED, speed); 584 } 585 586 /** 587 * Changes the mouse pointer speed temporarily, but does not save the setting. 588 * <p> 589 * Requires {@link android.Manifest.permission.SET_POINTER_SPEED}. 590 * </p> 591 * 592 * @param speed The pointer speed as a value between {@link #MIN_POINTER_SPEED} and 593 * {@link #MAX_POINTER_SPEED}, or the default value {@link #DEFAULT_POINTER_SPEED}. 594 * 595 * @hide 596 */ tryPointerSpeed(int speed)597 public void tryPointerSpeed(int speed) { 598 if (speed < MIN_POINTER_SPEED || speed > MAX_POINTER_SPEED) { 599 throw new IllegalArgumentException("speed out of range"); 600 } 601 602 try { 603 mIm.tryPointerSpeed(speed); 604 } catch (RemoteException ex) { 605 Log.w(TAG, "Could not set temporary pointer speed.", ex); 606 } 607 } 608 609 /** 610 * Queries the framework about whether any physical keys exist on the 611 * any keyboard attached to the device that are capable of producing the given 612 * array of key codes. 613 * 614 * @param keyCodes The array of key codes to query. 615 * @return A new array of the same size as the key codes array whose elements 616 * are set to true if at least one attached keyboard supports the corresponding key code 617 * at the same index in the key codes array. 618 * 619 * @hide 620 */ deviceHasKeys(int[] keyCodes)621 public boolean[] deviceHasKeys(int[] keyCodes) { 622 return deviceHasKeys(-1, keyCodes); 623 } 624 625 /** 626 * Queries the framework about whether any physical keys exist on the 627 * any keyboard attached to the device that are capable of producing the given 628 * array of key codes. 629 * 630 * @param id The id of the device to query. 631 * @param keyCodes The array of key codes to query. 632 * @return A new array of the same size as the key codes array whose elements are set to true 633 * if the given device could produce the corresponding key code at the same index in the key 634 * codes array. 635 * 636 * @hide 637 */ deviceHasKeys(int id, int[] keyCodes)638 public boolean[] deviceHasKeys(int id, int[] keyCodes) { 639 boolean[] ret = new boolean[keyCodes.length]; 640 try { 641 mIm.hasKeys(id, InputDevice.SOURCE_ANY, keyCodes, ret); 642 } catch (RemoteException e) { 643 // no fallback; just return the empty array 644 } 645 return ret; 646 } 647 648 649 /** 650 * Injects an input event into the event system on behalf of an application. 651 * The synchronization mode determines whether the method blocks while waiting for 652 * input injection to proceed. 653 * <p> 654 * Requires {@link android.Manifest.permission.INJECT_EVENTS} to inject into 655 * windows that are owned by other applications. 656 * </p><p> 657 * Make sure you correctly set the event time and input source of the event 658 * before calling this method. 659 * </p> 660 * 661 * @param event The event to inject. 662 * @param mode The synchronization mode. One of: 663 * {@link #INJECT_INPUT_EVENT_MODE_ASYNC}, 664 * {@link #INJECT_INPUT_EVENT_MODE_WAIT_FOR_RESULT}, or 665 * {@link #INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH}. 666 * @return True if input event injection succeeded. 667 * 668 * @hide 669 */ injectInputEvent(InputEvent event, int mode)670 public boolean injectInputEvent(InputEvent event, int mode) { 671 if (event == null) { 672 throw new IllegalArgumentException("event must not be null"); 673 } 674 if (mode != INJECT_INPUT_EVENT_MODE_ASYNC 675 && mode != INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH 676 && mode != INJECT_INPUT_EVENT_MODE_WAIT_FOR_RESULT) { 677 throw new IllegalArgumentException("mode is invalid"); 678 } 679 680 try { 681 return mIm.injectInputEvent(event, mode); 682 } catch (RemoteException ex) { 683 return false; 684 } 685 } 686 populateInputDevicesLocked()687 private void populateInputDevicesLocked() { 688 if (mInputDevicesChangedListener == null) { 689 final InputDevicesChangedListener listener = new InputDevicesChangedListener(); 690 try { 691 mIm.registerInputDevicesChangedListener(listener); 692 } catch (RemoteException ex) { 693 throw new RuntimeException( 694 "Could not get register input device changed listener", ex); 695 } 696 mInputDevicesChangedListener = listener; 697 } 698 699 if (mInputDevices == null) { 700 final int[] ids; 701 try { 702 ids = mIm.getInputDeviceIds(); 703 } catch (RemoteException ex) { 704 throw new RuntimeException("Could not get input device ids.", ex); 705 } 706 707 mInputDevices = new SparseArray<InputDevice>(); 708 for (int i = 0; i < ids.length; i++) { 709 mInputDevices.put(ids[i], null); 710 } 711 } 712 } 713 onInputDevicesChanged(int[] deviceIdAndGeneration)714 private void onInputDevicesChanged(int[] deviceIdAndGeneration) { 715 if (DEBUG) { 716 Log.d(TAG, "Received input devices changed."); 717 } 718 719 synchronized (mInputDevicesLock) { 720 for (int i = mInputDevices.size(); --i > 0; ) { 721 final int deviceId = mInputDevices.keyAt(i); 722 if (!containsDeviceId(deviceIdAndGeneration, deviceId)) { 723 if (DEBUG) { 724 Log.d(TAG, "Device removed: " + deviceId); 725 } 726 mInputDevices.removeAt(i); 727 sendMessageToInputDeviceListenersLocked(MSG_DEVICE_REMOVED, deviceId); 728 } 729 } 730 731 for (int i = 0; i < deviceIdAndGeneration.length; i += 2) { 732 final int deviceId = deviceIdAndGeneration[i]; 733 int index = mInputDevices.indexOfKey(deviceId); 734 if (index >= 0) { 735 final InputDevice device = mInputDevices.valueAt(index); 736 if (device != null) { 737 final int generation = deviceIdAndGeneration[i + 1]; 738 if (device.getGeneration() != generation) { 739 if (DEBUG) { 740 Log.d(TAG, "Device changed: " + deviceId); 741 } 742 mInputDevices.setValueAt(index, null); 743 sendMessageToInputDeviceListenersLocked(MSG_DEVICE_CHANGED, deviceId); 744 } 745 } 746 } else { 747 if (DEBUG) { 748 Log.d(TAG, "Device added: " + deviceId); 749 } 750 mInputDevices.put(deviceId, null); 751 sendMessageToInputDeviceListenersLocked(MSG_DEVICE_ADDED, deviceId); 752 } 753 } 754 } 755 } 756 sendMessageToInputDeviceListenersLocked(int what, int deviceId)757 private void sendMessageToInputDeviceListenersLocked(int what, int deviceId) { 758 final int numListeners = mInputDeviceListeners.size(); 759 for (int i = 0; i < numListeners; i++) { 760 InputDeviceListenerDelegate listener = mInputDeviceListeners.get(i); 761 listener.sendMessage(listener.obtainMessage(what, deviceId, 0)); 762 } 763 } 764 containsDeviceId(int[] deviceIdAndGeneration, int deviceId)765 private static boolean containsDeviceId(int[] deviceIdAndGeneration, int deviceId) { 766 for (int i = 0; i < deviceIdAndGeneration.length; i += 2) { 767 if (deviceIdAndGeneration[i] == deviceId) { 768 return true; 769 } 770 } 771 return false; 772 } 773 774 /** 775 * Gets a vibrator service associated with an input device, assuming it has one. 776 * @return The vibrator, never null. 777 * @hide 778 */ getInputDeviceVibrator(int deviceId)779 public Vibrator getInputDeviceVibrator(int deviceId) { 780 return new InputDeviceVibrator(deviceId); 781 } 782 783 /** 784 * Listens for changes in input devices. 785 */ 786 public interface InputDeviceListener { 787 /** 788 * Called whenever an input device has been added to the system. 789 * Use {@link InputManager#getInputDevice} to get more information about the device. 790 * 791 * @param deviceId The id of the input device that was added. 792 */ onInputDeviceAdded(int deviceId)793 void onInputDeviceAdded(int deviceId); 794 795 /** 796 * Called whenever an input device has been removed from the system. 797 * 798 * @param deviceId The id of the input device that was removed. 799 */ onInputDeviceRemoved(int deviceId)800 void onInputDeviceRemoved(int deviceId); 801 802 /** 803 * Called whenever the properties of an input device have changed since they 804 * were last queried. Use {@link InputManager#getInputDevice} to get 805 * a fresh {@link InputDevice} object with the new properties. 806 * 807 * @param deviceId The id of the input device that changed. 808 */ onInputDeviceChanged(int deviceId)809 void onInputDeviceChanged(int deviceId); 810 } 811 812 private final class InputDevicesChangedListener extends IInputDevicesChangedListener.Stub { 813 @Override onInputDevicesChanged(int[] deviceIdAndGeneration)814 public void onInputDevicesChanged(int[] deviceIdAndGeneration) throws RemoteException { 815 InputManager.this.onInputDevicesChanged(deviceIdAndGeneration); 816 } 817 } 818 819 private static final class InputDeviceListenerDelegate extends Handler { 820 public final InputDeviceListener mListener; 821 InputDeviceListenerDelegate(InputDeviceListener listener, Handler handler)822 public InputDeviceListenerDelegate(InputDeviceListener listener, Handler handler) { 823 super(handler != null ? handler.getLooper() : Looper.myLooper()); 824 mListener = listener; 825 } 826 827 @Override handleMessage(Message msg)828 public void handleMessage(Message msg) { 829 switch (msg.what) { 830 case MSG_DEVICE_ADDED: 831 mListener.onInputDeviceAdded(msg.arg1); 832 break; 833 case MSG_DEVICE_REMOVED: 834 mListener.onInputDeviceRemoved(msg.arg1); 835 break; 836 case MSG_DEVICE_CHANGED: 837 mListener.onInputDeviceChanged(msg.arg1); 838 break; 839 } 840 } 841 } 842 843 private final class InputDeviceVibrator extends Vibrator { 844 private final int mDeviceId; 845 private final Binder mToken; 846 InputDeviceVibrator(int deviceId)847 public InputDeviceVibrator(int deviceId) { 848 mDeviceId = deviceId; 849 mToken = new Binder(); 850 } 851 852 @Override hasVibrator()853 public boolean hasVibrator() { 854 return true; 855 } 856 857 /** 858 * @hide 859 */ 860 @Override vibrate(int uid, String opPkg, long milliseconds, AudioAttributes attributes)861 public void vibrate(int uid, String opPkg, long milliseconds, AudioAttributes attributes) { 862 vibrate(new long[] { 0, milliseconds}, -1); 863 } 864 865 /** 866 * @hide 867 */ 868 @Override vibrate(int uid, String opPkg, long[] pattern, int repeat, AudioAttributes attributes)869 public void vibrate(int uid, String opPkg, long[] pattern, int repeat, 870 AudioAttributes attributes) { 871 if (repeat >= pattern.length) { 872 throw new ArrayIndexOutOfBoundsException(); 873 } 874 try { 875 mIm.vibrate(mDeviceId, pattern, repeat, mToken); 876 } catch (RemoteException ex) { 877 Log.w(TAG, "Failed to vibrate.", ex); 878 } 879 } 880 881 @Override cancel()882 public void cancel() { 883 try { 884 mIm.cancelVibrate(mDeviceId, mToken); 885 } catch (RemoteException ex) { 886 Log.w(TAG, "Failed to cancel vibration.", ex); 887 } 888 } 889 } 890 } 891