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