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 InputManager(IInputManager im)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 */ getInstance()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 */ getInputDevice(int id)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 } 216 mInputDevices.setValueAt(index, inputDevice); 217 return inputDevice; 218 } 219 } 220 221 /** 222 * Gets information about the input device with the specified descriptor. 223 * @param descriptor The input device descriptor. 224 * @return The input device or null if not found. 225 * @hide 226 */ getInputDeviceByDescriptor(String descriptor)227 public InputDevice getInputDeviceByDescriptor(String descriptor) { 228 if (descriptor == null) { 229 throw new IllegalArgumentException("descriptor must not be null."); 230 } 231 232 synchronized (mInputDevicesLock) { 233 populateInputDevicesLocked(); 234 235 int numDevices = mInputDevices.size(); 236 for (int i = 0; i < numDevices; i++) { 237 InputDevice inputDevice = mInputDevices.valueAt(i); 238 if (inputDevice == null) { 239 int id = mInputDevices.keyAt(i); 240 try { 241 inputDevice = mIm.getInputDevice(id); 242 } catch (RemoteException ex) { 243 // Ignore the problem for the purposes of this method. 244 continue; 245 } 246 mInputDevices.setValueAt(i, inputDevice); 247 } 248 if (descriptor.equals(inputDevice.getDescriptor())) { 249 return inputDevice; 250 } 251 } 252 return null; 253 } 254 } 255 256 /** 257 * Gets the ids of all input devices in the system. 258 * @return The input device ids. 259 */ getInputDeviceIds()260 public int[] getInputDeviceIds() { 261 synchronized (mInputDevicesLock) { 262 populateInputDevicesLocked(); 263 264 final int count = mInputDevices.size(); 265 final int[] ids = new int[count]; 266 for (int i = 0; i < count; i++) { 267 ids[i] = mInputDevices.keyAt(i); 268 } 269 return ids; 270 } 271 } 272 273 /** 274 * Registers an input device listener to receive notifications about when 275 * input devices are added, removed or changed. 276 * 277 * @param listener The listener to register. 278 * @param handler The handler on which the listener should be invoked, or null 279 * if the listener should be invoked on the calling thread's looper. 280 * 281 * @see #unregisterInputDeviceListener 282 */ registerInputDeviceListener(InputDeviceListener listener, Handler handler)283 public void registerInputDeviceListener(InputDeviceListener listener, Handler handler) { 284 if (listener == null) { 285 throw new IllegalArgumentException("listener must not be null"); 286 } 287 288 synchronized (mInputDevicesLock) { 289 int index = findInputDeviceListenerLocked(listener); 290 if (index < 0) { 291 mInputDeviceListeners.add(new InputDeviceListenerDelegate(listener, handler)); 292 } 293 } 294 } 295 296 /** 297 * Unregisters an input device listener. 298 * 299 * @param listener The listener to unregister. 300 * 301 * @see #registerInputDeviceListener 302 */ unregisterInputDeviceListener(InputDeviceListener listener)303 public void unregisterInputDeviceListener(InputDeviceListener listener) { 304 if (listener == null) { 305 throw new IllegalArgumentException("listener must not be null"); 306 } 307 308 synchronized (mInputDevicesLock) { 309 int index = findInputDeviceListenerLocked(listener); 310 if (index >= 0) { 311 InputDeviceListenerDelegate d = mInputDeviceListeners.get(index); 312 d.removeCallbacksAndMessages(null); 313 mInputDeviceListeners.remove(index); 314 } 315 } 316 } 317 findInputDeviceListenerLocked(InputDeviceListener listener)318 private int findInputDeviceListenerLocked(InputDeviceListener listener) { 319 final int numListeners = mInputDeviceListeners.size(); 320 for (int i = 0; i < numListeners; i++) { 321 if (mInputDeviceListeners.get(i).mListener == listener) { 322 return i; 323 } 324 } 325 return -1; 326 } 327 328 /** 329 * Gets information about all supported keyboard layouts. 330 * <p> 331 * The input manager consults the built-in keyboard layouts as well 332 * as all keyboard layouts advertised by applications using a 333 * {@link #ACTION_QUERY_KEYBOARD_LAYOUTS} broadcast receiver. 334 * </p> 335 * 336 * @return A list of all supported keyboard layouts. 337 * 338 * @hide 339 */ getKeyboardLayouts()340 public KeyboardLayout[] getKeyboardLayouts() { 341 try { 342 return mIm.getKeyboardLayouts(); 343 } catch (RemoteException ex) { 344 Log.w(TAG, "Could not get list of keyboard layout informations.", ex); 345 return new KeyboardLayout[0]; 346 } 347 } 348 349 /** 350 * Gets the keyboard layout with the specified descriptor. 351 * 352 * @param keyboardLayoutDescriptor The keyboard layout descriptor, as returned by 353 * {@link KeyboardLayout#getDescriptor()}. 354 * @return The keyboard layout, or null if it could not be loaded. 355 * 356 * @hide 357 */ getKeyboardLayout(String keyboardLayoutDescriptor)358 public KeyboardLayout getKeyboardLayout(String keyboardLayoutDescriptor) { 359 if (keyboardLayoutDescriptor == null) { 360 throw new IllegalArgumentException("keyboardLayoutDescriptor must not be null"); 361 } 362 363 try { 364 return mIm.getKeyboardLayout(keyboardLayoutDescriptor); 365 } catch (RemoteException ex) { 366 Log.w(TAG, "Could not get keyboard layout information.", ex); 367 return null; 368 } 369 } 370 371 /** 372 * Gets the current keyboard layout descriptor for the specified input device. 373 * 374 * @param inputDeviceDescriptor The input device descriptor. 375 * @return The keyboard layout descriptor, or null if no keyboard layout has been set. 376 * 377 * @hide 378 */ getCurrentKeyboardLayoutForInputDevice(String inputDeviceDescriptor)379 public String getCurrentKeyboardLayoutForInputDevice(String inputDeviceDescriptor) { 380 if (inputDeviceDescriptor == null) { 381 throw new IllegalArgumentException("inputDeviceDescriptor must not be null"); 382 } 383 384 try { 385 return mIm.getCurrentKeyboardLayoutForInputDevice(inputDeviceDescriptor); 386 } catch (RemoteException ex) { 387 Log.w(TAG, "Could not get current keyboard layout for input device.", ex); 388 return null; 389 } 390 } 391 392 /** 393 * Sets the current keyboard layout descriptor for the specified input device. 394 * <p> 395 * This method may have the side-effect of causing the input device in question 396 * to be reconfigured. 397 * </p> 398 * 399 * @param inputDeviceDescriptor The input device descriptor. 400 * @param keyboardLayoutDescriptor The keyboard layout descriptor to use, must not be null. 401 * 402 * @hide 403 */ setCurrentKeyboardLayoutForInputDevice(String inputDeviceDescriptor, String keyboardLayoutDescriptor)404 public void setCurrentKeyboardLayoutForInputDevice(String inputDeviceDescriptor, 405 String keyboardLayoutDescriptor) { 406 if (inputDeviceDescriptor == null) { 407 throw new IllegalArgumentException("inputDeviceDescriptor must not be null"); 408 } 409 if (keyboardLayoutDescriptor == null) { 410 throw new IllegalArgumentException("keyboardLayoutDescriptor must not be null"); 411 } 412 413 try { 414 mIm.setCurrentKeyboardLayoutForInputDevice(inputDeviceDescriptor, 415 keyboardLayoutDescriptor); 416 } catch (RemoteException ex) { 417 Log.w(TAG, "Could not set current keyboard layout for input device.", ex); 418 } 419 } 420 421 /** 422 * Gets all keyboard layout descriptors that are enabled for the specified input device. 423 * 424 * @param inputDeviceDescriptor The input device descriptor. 425 * @return The keyboard layout descriptors. 426 * 427 * @hide 428 */ getKeyboardLayoutsForInputDevice(String inputDeviceDescriptor)429 public String[] getKeyboardLayoutsForInputDevice(String inputDeviceDescriptor) { 430 if (inputDeviceDescriptor == null) { 431 throw new IllegalArgumentException("inputDeviceDescriptor must not be null"); 432 } 433 434 try { 435 return mIm.getKeyboardLayoutsForInputDevice(inputDeviceDescriptor); 436 } catch (RemoteException ex) { 437 Log.w(TAG, "Could not get keyboard layouts for input device.", ex); 438 return ArrayUtils.emptyArray(String.class); 439 } 440 } 441 442 /** 443 * Adds the keyboard layout descriptor for the specified input device. 444 * <p> 445 * This method may have the side-effect of causing the input device in question 446 * to be reconfigured. 447 * </p> 448 * 449 * @param inputDeviceDescriptor The input device descriptor. 450 * @param keyboardLayoutDescriptor The descriptor of the keyboard layout to add. 451 * 452 * @hide 453 */ addKeyboardLayoutForInputDevice(String inputDeviceDescriptor, String keyboardLayoutDescriptor)454 public void addKeyboardLayoutForInputDevice(String inputDeviceDescriptor, 455 String keyboardLayoutDescriptor) { 456 if (inputDeviceDescriptor == null) { 457 throw new IllegalArgumentException("inputDeviceDescriptor must not be null"); 458 } 459 if (keyboardLayoutDescriptor == null) { 460 throw new IllegalArgumentException("keyboardLayoutDescriptor must not be null"); 461 } 462 463 try { 464 mIm.addKeyboardLayoutForInputDevice(inputDeviceDescriptor, keyboardLayoutDescriptor); 465 } catch (RemoteException ex) { 466 Log.w(TAG, "Could not add keyboard layout for input device.", ex); 467 } 468 } 469 470 /** 471 * Removes the keyboard layout descriptor for the specified input device. 472 * <p> 473 * This method may have the side-effect of causing the input device in question 474 * to be reconfigured. 475 * </p> 476 * 477 * @param inputDeviceDescriptor The input device descriptor. 478 * @param keyboardLayoutDescriptor The descriptor of the keyboard layout to remove. 479 * 480 * @hide 481 */ removeKeyboardLayoutForInputDevice(String inputDeviceDescriptor, String keyboardLayoutDescriptor)482 public void removeKeyboardLayoutForInputDevice(String inputDeviceDescriptor, 483 String keyboardLayoutDescriptor) { 484 if (inputDeviceDescriptor == null) { 485 throw new IllegalArgumentException("inputDeviceDescriptor must not be null"); 486 } 487 if (keyboardLayoutDescriptor == null) { 488 throw new IllegalArgumentException("keyboardLayoutDescriptor must not be null"); 489 } 490 491 try { 492 mIm.removeKeyboardLayoutForInputDevice(inputDeviceDescriptor, keyboardLayoutDescriptor); 493 } catch (RemoteException ex) { 494 Log.w(TAG, "Could not remove keyboard layout for input device.", ex); 495 } 496 } 497 498 /** 499 * Gets the mouse pointer speed. 500 * <p> 501 * Only returns the permanent mouse pointer speed. Ignores any temporary pointer 502 * speed set by {@link #tryPointerSpeed}. 503 * </p> 504 * 505 * @param context The application context. 506 * @return The pointer speed as a value between {@link #MIN_POINTER_SPEED} and 507 * {@link #MAX_POINTER_SPEED}, or the default value {@link #DEFAULT_POINTER_SPEED}. 508 * 509 * @hide 510 */ getPointerSpeed(Context context)511 public int getPointerSpeed(Context context) { 512 int speed = DEFAULT_POINTER_SPEED; 513 try { 514 speed = Settings.System.getInt(context.getContentResolver(), 515 Settings.System.POINTER_SPEED); 516 } catch (SettingNotFoundException snfe) { 517 } 518 return speed; 519 } 520 521 /** 522 * Sets the mouse pointer speed. 523 * <p> 524 * Requires {@link android.Manifest.permissions.WRITE_SETTINGS}. 525 * </p> 526 * 527 * @param context The application context. 528 * @param speed The pointer speed as a value between {@link #MIN_POINTER_SPEED} and 529 * {@link #MAX_POINTER_SPEED}, or the default value {@link #DEFAULT_POINTER_SPEED}. 530 * 531 * @hide 532 */ setPointerSpeed(Context context, int speed)533 public void setPointerSpeed(Context context, int speed) { 534 if (speed < MIN_POINTER_SPEED || speed > MAX_POINTER_SPEED) { 535 throw new IllegalArgumentException("speed out of range"); 536 } 537 538 Settings.System.putInt(context.getContentResolver(), 539 Settings.System.POINTER_SPEED, speed); 540 } 541 542 /** 543 * Changes the mouse pointer speed temporarily, but does not save the setting. 544 * <p> 545 * Requires {@link android.Manifest.permission.SET_POINTER_SPEED}. 546 * </p> 547 * 548 * @param speed 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 */ tryPointerSpeed(int speed)553 public void tryPointerSpeed(int speed) { 554 if (speed < MIN_POINTER_SPEED || speed > MAX_POINTER_SPEED) { 555 throw new IllegalArgumentException("speed out of range"); 556 } 557 558 try { 559 mIm.tryPointerSpeed(speed); 560 } catch (RemoteException ex) { 561 Log.w(TAG, "Could not set temporary pointer speed.", ex); 562 } 563 } 564 565 /** 566 * Queries the framework about whether any physical keys exist on the 567 * any keyboard attached to the device that are capable of producing the given 568 * array of key codes. 569 * 570 * @param keyCodes The array of key codes to query. 571 * @return A new array of the same size as the key codes array whose elements 572 * are set to true if at least one attached keyboard supports the corresponding key code 573 * at the same index in the key codes array. 574 * 575 * @hide 576 */ deviceHasKeys(int[] keyCodes)577 public boolean[] deviceHasKeys(int[] keyCodes) { 578 boolean[] ret = new boolean[keyCodes.length]; 579 try { 580 mIm.hasKeys(-1, InputDevice.SOURCE_ANY, keyCodes, ret); 581 } catch (RemoteException e) { 582 // no fallback; just return the empty array 583 } 584 return ret; 585 } 586 587 /** 588 * Injects an input event into the event system on behalf of an application. 589 * The synchronization mode determines whether the method blocks while waiting for 590 * input injection to proceed. 591 * <p> 592 * Requires {@link android.Manifest.permission.INJECT_EVENTS} to inject into 593 * windows that are owned by other applications. 594 * </p><p> 595 * Make sure you correctly set the event time and input source of the event 596 * before calling this method. 597 * </p> 598 * 599 * @param event The event to inject. 600 * @param mode The synchronization mode. One of: 601 * {@link #INJECT_INPUT_EVENT_MODE_ASYNC}, 602 * {@link #INJECT_INPUT_EVENT_MODE_WAIT_FOR_RESULT}, or 603 * {@link #INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH}. 604 * @return True if input event injection succeeded. 605 * 606 * @hide 607 */ injectInputEvent(InputEvent event, int mode)608 public boolean injectInputEvent(InputEvent event, int mode) { 609 if (event == null) { 610 throw new IllegalArgumentException("event must not be null"); 611 } 612 if (mode != INJECT_INPUT_EVENT_MODE_ASYNC 613 && mode != INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH 614 && mode != INJECT_INPUT_EVENT_MODE_WAIT_FOR_RESULT) { 615 throw new IllegalArgumentException("mode is invalid"); 616 } 617 618 try { 619 return mIm.injectInputEvent(event, mode); 620 } catch (RemoteException ex) { 621 return false; 622 } 623 } 624 populateInputDevicesLocked()625 private void populateInputDevicesLocked() { 626 if (mInputDevicesChangedListener == null) { 627 final InputDevicesChangedListener listener = new InputDevicesChangedListener(); 628 try { 629 mIm.registerInputDevicesChangedListener(listener); 630 } catch (RemoteException ex) { 631 throw new RuntimeException( 632 "Could not get register input device changed listener", ex); 633 } 634 mInputDevicesChangedListener = listener; 635 } 636 637 if (mInputDevices == null) { 638 final int[] ids; 639 try { 640 ids = mIm.getInputDeviceIds(); 641 } catch (RemoteException ex) { 642 throw new RuntimeException("Could not get input device ids.", ex); 643 } 644 645 mInputDevices = new SparseArray<InputDevice>(); 646 for (int i = 0; i < ids.length; i++) { 647 mInputDevices.put(ids[i], null); 648 } 649 } 650 } 651 onInputDevicesChanged(int[] deviceIdAndGeneration)652 private void onInputDevicesChanged(int[] deviceIdAndGeneration) { 653 if (DEBUG) { 654 Log.d(TAG, "Received input devices changed."); 655 } 656 657 synchronized (mInputDevicesLock) { 658 for (int i = mInputDevices.size(); --i > 0; ) { 659 final int deviceId = mInputDevices.keyAt(i); 660 if (!containsDeviceId(deviceIdAndGeneration, deviceId)) { 661 if (DEBUG) { 662 Log.d(TAG, "Device removed: " + deviceId); 663 } 664 mInputDevices.removeAt(i); 665 sendMessageToInputDeviceListenersLocked(MSG_DEVICE_REMOVED, deviceId); 666 } 667 } 668 669 for (int i = 0; i < deviceIdAndGeneration.length; i += 2) { 670 final int deviceId = deviceIdAndGeneration[i]; 671 int index = mInputDevices.indexOfKey(deviceId); 672 if (index >= 0) { 673 final InputDevice device = mInputDevices.valueAt(index); 674 if (device != null) { 675 final int generation = deviceIdAndGeneration[i + 1]; 676 if (device.getGeneration() != generation) { 677 if (DEBUG) { 678 Log.d(TAG, "Device changed: " + deviceId); 679 } 680 mInputDevices.setValueAt(index, null); 681 sendMessageToInputDeviceListenersLocked(MSG_DEVICE_CHANGED, deviceId); 682 } 683 } 684 } else { 685 if (DEBUG) { 686 Log.d(TAG, "Device added: " + deviceId); 687 } 688 mInputDevices.put(deviceId, null); 689 sendMessageToInputDeviceListenersLocked(MSG_DEVICE_ADDED, deviceId); 690 } 691 } 692 } 693 } 694 sendMessageToInputDeviceListenersLocked(int what, int deviceId)695 private void sendMessageToInputDeviceListenersLocked(int what, int deviceId) { 696 final int numListeners = mInputDeviceListeners.size(); 697 for (int i = 0; i < numListeners; i++) { 698 InputDeviceListenerDelegate listener = mInputDeviceListeners.get(i); 699 listener.sendMessage(listener.obtainMessage(what, deviceId, 0)); 700 } 701 } 702 containsDeviceId(int[] deviceIdAndGeneration, int deviceId)703 private static boolean containsDeviceId(int[] deviceIdAndGeneration, int deviceId) { 704 for (int i = 0; i < deviceIdAndGeneration.length; i += 2) { 705 if (deviceIdAndGeneration[i] == deviceId) { 706 return true; 707 } 708 } 709 return false; 710 } 711 712 /** 713 * Gets a vibrator service associated with an input device, assuming it has one. 714 * @return The vibrator, never null. 715 * @hide 716 */ getInputDeviceVibrator(int deviceId)717 public Vibrator getInputDeviceVibrator(int deviceId) { 718 return new InputDeviceVibrator(deviceId); 719 } 720 721 /** 722 * Listens for changes in input devices. 723 */ 724 public interface InputDeviceListener { 725 /** 726 * Called whenever an input device has been added to the system. 727 * Use {@link InputManager#getInputDevice} to get more information about the device. 728 * 729 * @param deviceId The id of the input device that was added. 730 */ onInputDeviceAdded(int deviceId)731 void onInputDeviceAdded(int deviceId); 732 733 /** 734 * Called whenever an input device has been removed from the system. 735 * 736 * @param deviceId The id of the input device that was removed. 737 */ onInputDeviceRemoved(int deviceId)738 void onInputDeviceRemoved(int deviceId); 739 740 /** 741 * Called whenever the properties of an input device have changed since they 742 * were last queried. Use {@link InputManager#getInputDevice} to get 743 * a fresh {@link InputDevice} object with the new properties. 744 * 745 * @param deviceId The id of the input device that changed. 746 */ onInputDeviceChanged(int deviceId)747 void onInputDeviceChanged(int deviceId); 748 } 749 750 private final class InputDevicesChangedListener extends IInputDevicesChangedListener.Stub { 751 @Override onInputDevicesChanged(int[] deviceIdAndGeneration)752 public void onInputDevicesChanged(int[] deviceIdAndGeneration) throws RemoteException { 753 InputManager.this.onInputDevicesChanged(deviceIdAndGeneration); 754 } 755 } 756 757 private static final class InputDeviceListenerDelegate extends Handler { 758 public final InputDeviceListener mListener; 759 InputDeviceListenerDelegate(InputDeviceListener listener, Handler handler)760 public InputDeviceListenerDelegate(InputDeviceListener listener, Handler handler) { 761 super(handler != null ? handler.getLooper() : Looper.myLooper()); 762 mListener = listener; 763 } 764 765 @Override handleMessage(Message msg)766 public void handleMessage(Message msg) { 767 switch (msg.what) { 768 case MSG_DEVICE_ADDED: 769 mListener.onInputDeviceAdded(msg.arg1); 770 break; 771 case MSG_DEVICE_REMOVED: 772 mListener.onInputDeviceRemoved(msg.arg1); 773 break; 774 case MSG_DEVICE_CHANGED: 775 mListener.onInputDeviceChanged(msg.arg1); 776 break; 777 } 778 } 779 } 780 781 private final class InputDeviceVibrator extends Vibrator { 782 private final int mDeviceId; 783 private final Binder mToken; 784 InputDeviceVibrator(int deviceId)785 public InputDeviceVibrator(int deviceId) { 786 mDeviceId = deviceId; 787 mToken = new Binder(); 788 } 789 790 @Override hasVibrator()791 public boolean hasVibrator() { 792 return true; 793 } 794 795 @Override vibrate(long milliseconds)796 public void vibrate(long milliseconds) { 797 vibrate(new long[] { 0, milliseconds}, -1); 798 } 799 800 @Override vibrate(long[] pattern, int repeat)801 public void vibrate(long[] pattern, int repeat) { 802 if (repeat >= pattern.length) { 803 throw new ArrayIndexOutOfBoundsException(); 804 } 805 try { 806 mIm.vibrate(mDeviceId, pattern, repeat, mToken); 807 } catch (RemoteException ex) { 808 Log.w(TAG, "Failed to vibrate.", ex); 809 } 810 } 811 812 @Override cancel()813 public void cancel() { 814 try { 815 mIm.cancelVibrate(mDeviceId, mToken); 816 } catch (RemoteException ex) { 817 Log.w(TAG, "Failed to cancel vibration.", ex); 818 } 819 } 820 } 821 } 822