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