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