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