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