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