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