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 android.annotation.IntDef;
     20 import android.annotation.Nullable;
     21 import android.annotation.SdkConstant;
     22 import android.annotation.SdkConstant.SdkConstantType;
     23 import android.annotation.SystemService;
     24 import android.app.IInputForwarder;
     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.ServiceManager.ServiceNotFoundException;
     35 import android.os.SystemClock;
     36 import android.os.VibrationEffect;
     37 import android.os.Vibrator;
     38 import android.provider.Settings;
     39 import android.provider.Settings.SettingNotFoundException;
     40 import android.util.Log;
     41 import android.util.SparseArray;
     42 import android.view.InputDevice;
     43 import android.view.InputEvent;
     44 import android.view.MotionEvent;
     45 import android.view.PointerIcon;
     46 import android.view.inputmethod.InputMethodInfo;
     47 import android.view.inputmethod.InputMethodSubtype;
     48 
     49 import com.android.internal.os.SomeArgs;
     50 
     51 import java.lang.annotation.Retention;
     52 import java.lang.annotation.RetentionPolicy;
     53 import java.util.ArrayList;
     54 import java.util.List;
     55 
     56 /**
     57  * Provides information about input devices and available key layouts.
     58  */
     59 @SystemService(Context.INPUT_SERVICE)
     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                 try {
    229                     sInstance = new InputManager(IInputManager.Stub
    230                             .asInterface(ServiceManager.getServiceOrThrow(Context.INPUT_SERVICE)));
    231                 } catch (ServiceNotFoundException e) {
    232                     throw new IllegalStateException(e);
    233                 }
    234             }
    235             return sInstance;
    236         }
    237     }
    238 
    239     /**
    240      * Gets information about the input device with the specified id.
    241      * @param id The device id.
    242      * @return The input device or null if not found.
    243      */
    244     public InputDevice getInputDevice(int id) {
    245         synchronized (mInputDevicesLock) {
    246             populateInputDevicesLocked();
    247 
    248             int index = mInputDevices.indexOfKey(id);
    249             if (index < 0) {
    250                 return null;
    251             }
    252 
    253             InputDevice inputDevice = mInputDevices.valueAt(index);
    254             if (inputDevice == null) {
    255                 try {
    256                     inputDevice = mIm.getInputDevice(id);
    257                 } catch (RemoteException ex) {
    258                     throw ex.rethrowFromSystemServer();
    259                 }
    260                 if (inputDevice != null) {
    261                     mInputDevices.setValueAt(index, inputDevice);
    262                 }
    263             }
    264             return inputDevice;
    265         }
    266     }
    267 
    268     /**
    269      * Gets information about the input device with the specified descriptor.
    270      * @param descriptor The input device descriptor.
    271      * @return The input device or null if not found.
    272      * @hide
    273      */
    274     public InputDevice getInputDeviceByDescriptor(String descriptor) {
    275         if (descriptor == null) {
    276             throw new IllegalArgumentException("descriptor must not be null.");
    277         }
    278 
    279         synchronized (mInputDevicesLock) {
    280             populateInputDevicesLocked();
    281 
    282             int numDevices = mInputDevices.size();
    283             for (int i = 0; i < numDevices; i++) {
    284                 InputDevice inputDevice = mInputDevices.valueAt(i);
    285                 if (inputDevice == null) {
    286                     int id = mInputDevices.keyAt(i);
    287                     try {
    288                         inputDevice = mIm.getInputDevice(id);
    289                     } catch (RemoteException ex) {
    290                         throw ex.rethrowFromSystemServer();
    291                     }
    292                     if (inputDevice == null) {
    293                         continue;
    294                     }
    295                     mInputDevices.setValueAt(i, inputDevice);
    296                 }
    297                 if (descriptor.equals(inputDevice.getDescriptor())) {
    298                     return inputDevice;
    299                 }
    300             }
    301             return null;
    302         }
    303     }
    304 
    305     /**
    306      * Gets the ids of all input devices in the system.
    307      * @return The input device ids.
    308      */
    309     public int[] getInputDeviceIds() {
    310         synchronized (mInputDevicesLock) {
    311             populateInputDevicesLocked();
    312 
    313             final int count = mInputDevices.size();
    314             final int[] ids = new int[count];
    315             for (int i = 0; i < count; i++) {
    316                 ids[i] = mInputDevices.keyAt(i);
    317             }
    318             return ids;
    319         }
    320     }
    321 
    322     /**
    323      * Returns true if an input device is enabled. Should return true for most
    324      * situations. Some system apps may disable an input device, for
    325      * example to prevent unwanted touch events.
    326      *
    327      * @param id The input device Id.
    328      *
    329      * @hide
    330      */
    331     public boolean isInputDeviceEnabled(int id) {
    332         try {
    333             return mIm.isInputDeviceEnabled(id);
    334         } catch (RemoteException ex) {
    335             Log.w(TAG, "Could not check enabled status of input device with id = " + id);
    336             throw ex.rethrowFromSystemServer();
    337         }
    338     }
    339 
    340     /**
    341      * Enables an InputDevice.
    342      * <p>
    343      * Requires {@link android.Manifest.permissions.DISABLE_INPUT_DEVICE}.
    344      * </p>
    345      *
    346      * @param id The input device Id.
    347      *
    348      * @hide
    349      */
    350     public void enableInputDevice(int id) {
    351         try {
    352             mIm.enableInputDevice(id);
    353         } catch (RemoteException ex) {
    354             Log.w(TAG, "Could not enable input device with id = " + id);
    355             throw ex.rethrowFromSystemServer();
    356         }
    357     }
    358 
    359     /**
    360      * Disables an InputDevice.
    361      * <p>
    362      * Requires {@link android.Manifest.permissions.DISABLE_INPUT_DEVICE}.
    363      * </p>
    364      *
    365      * @param id The input device Id.
    366      *
    367      * @hide
    368      */
    369     public void disableInputDevice(int id) {
    370         try {
    371             mIm.disableInputDevice(id);
    372         } catch (RemoteException ex) {
    373             Log.w(TAG, "Could not disable input device with id = " + id);
    374             throw ex.rethrowFromSystemServer();
    375         }
    376     }
    377 
    378     /**
    379      * Registers an input device listener to receive notifications about when
    380      * input devices are added, removed or changed.
    381      *
    382      * @param listener The listener to register.
    383      * @param handler The handler on which the listener should be invoked, or null
    384      * if the listener should be invoked on the calling thread's looper.
    385      *
    386      * @see #unregisterInputDeviceListener
    387      */
    388     public void registerInputDeviceListener(InputDeviceListener listener, Handler handler) {
    389         if (listener == null) {
    390             throw new IllegalArgumentException("listener must not be null");
    391         }
    392 
    393         synchronized (mInputDevicesLock) {
    394             populateInputDevicesLocked();
    395             int index = findInputDeviceListenerLocked(listener);
    396             if (index < 0) {
    397                 mInputDeviceListeners.add(new InputDeviceListenerDelegate(listener, handler));
    398             }
    399         }
    400     }
    401 
    402     /**
    403      * Unregisters an input device listener.
    404      *
    405      * @param listener The listener to unregister.
    406      *
    407      * @see #registerInputDeviceListener
    408      */
    409     public void unregisterInputDeviceListener(InputDeviceListener listener) {
    410         if (listener == null) {
    411             throw new IllegalArgumentException("listener must not be null");
    412         }
    413 
    414         synchronized (mInputDevicesLock) {
    415             int index = findInputDeviceListenerLocked(listener);
    416             if (index >= 0) {
    417                 InputDeviceListenerDelegate d = mInputDeviceListeners.get(index);
    418                 d.removeCallbacksAndMessages(null);
    419                 mInputDeviceListeners.remove(index);
    420             }
    421         }
    422     }
    423 
    424     private int findInputDeviceListenerLocked(InputDeviceListener listener) {
    425         final int numListeners = mInputDeviceListeners.size();
    426         for (int i = 0; i < numListeners; i++) {
    427             if (mInputDeviceListeners.get(i).mListener == listener) {
    428                 return i;
    429             }
    430         }
    431         return -1;
    432     }
    433 
    434     /**
    435      * Queries whether the device is in tablet mode.
    436      *
    437      * @return The tablet switch state which is one of {@link #SWITCH_STATE_UNKNOWN},
    438      * {@link #SWITCH_STATE_OFF} or {@link #SWITCH_STATE_ON}.
    439      * @hide
    440      */
    441     @SwitchState
    442     public int isInTabletMode() {
    443         try {
    444             return mIm.isInTabletMode();
    445         } catch (RemoteException ex) {
    446             throw ex.rethrowFromSystemServer();
    447         }
    448     }
    449 
    450     /**
    451      * Register a tablet mode changed listener.
    452      *
    453      * @param listener The listener to register.
    454      * @param handler The handler on which the listener should be invoked, or null
    455      * if the listener should be invoked on the calling thread's looper.
    456      * @hide
    457      */
    458     public void registerOnTabletModeChangedListener(
    459             OnTabletModeChangedListener listener, Handler handler) {
    460         if (listener == null) {
    461             throw new IllegalArgumentException("listener must not be null");
    462         }
    463         synchronized (mTabletModeLock) {
    464             if (mOnTabletModeChangedListeners == null) {
    465                 initializeTabletModeListenerLocked();
    466             }
    467             int idx = findOnTabletModeChangedListenerLocked(listener);
    468             if (idx < 0) {
    469                 OnTabletModeChangedListenerDelegate d =
    470                     new OnTabletModeChangedListenerDelegate(listener, handler);
    471                 mOnTabletModeChangedListeners.add(d);
    472             }
    473         }
    474     }
    475 
    476     /**
    477      * Unregister a tablet mode changed listener.
    478      *
    479      * @param listener The listener to unregister.
    480      * @hide
    481      */
    482     public void unregisterOnTabletModeChangedListener(OnTabletModeChangedListener listener) {
    483         if (listener == null) {
    484             throw new IllegalArgumentException("listener must not be null");
    485         }
    486         synchronized (mTabletModeLock) {
    487             int idx = findOnTabletModeChangedListenerLocked(listener);
    488             if (idx >= 0) {
    489                 OnTabletModeChangedListenerDelegate d = mOnTabletModeChangedListeners.remove(idx);
    490                 d.removeCallbacksAndMessages(null);
    491             }
    492         }
    493     }
    494 
    495     private void initializeTabletModeListenerLocked() {
    496         final TabletModeChangedListener listener = new TabletModeChangedListener();
    497         try {
    498             mIm.registerTabletModeChangedListener(listener);
    499         } catch (RemoteException ex) {
    500             throw ex.rethrowFromSystemServer();
    501         }
    502         mTabletModeChangedListener = listener;
    503         mOnTabletModeChangedListeners = new ArrayList<>();
    504     }
    505 
    506     private int findOnTabletModeChangedListenerLocked(OnTabletModeChangedListener listener) {
    507         final int N = mOnTabletModeChangedListeners.size();
    508         for (int i = 0; i < N; i++) {
    509             if (mOnTabletModeChangedListeners.get(i).mListener == listener) {
    510                 return i;
    511             }
    512         }
    513         return -1;
    514     }
    515 
    516     /**
    517      * Gets information about all supported keyboard layouts.
    518      * <p>
    519      * The input manager consults the built-in keyboard layouts as well
    520      * as all keyboard layouts advertised by applications using a
    521      * {@link #ACTION_QUERY_KEYBOARD_LAYOUTS} broadcast receiver.
    522      * </p>
    523      *
    524      * @return A list of all supported keyboard layouts.
    525      *
    526      * @hide
    527      */
    528     public KeyboardLayout[] getKeyboardLayouts() {
    529         try {
    530             return mIm.getKeyboardLayouts();
    531         } catch (RemoteException ex) {
    532             throw ex.rethrowFromSystemServer();
    533         }
    534     }
    535 
    536     /**
    537      * Gets information about all supported keyboard layouts appropriate
    538      * for a specific input device.
    539      * <p>
    540      * The input manager consults the built-in keyboard layouts as well
    541      * as all keyboard layouts advertised by applications using a
    542      * {@link #ACTION_QUERY_KEYBOARD_LAYOUTS} broadcast receiver.
    543      * </p>
    544      *
    545      * @return A list of all supported keyboard layouts for a specific
    546      * input device.
    547      *
    548      * @hide
    549      */
    550     public KeyboardLayout[] getKeyboardLayoutsForInputDevice(InputDeviceIdentifier identifier) {
    551         try {
    552             return mIm.getKeyboardLayoutsForInputDevice(identifier);
    553         } catch (RemoteException ex) {
    554             throw ex.rethrowFromSystemServer();
    555         }
    556     }
    557 
    558     /**
    559      * Gets the keyboard layout with the specified descriptor.
    560      *
    561      * @param keyboardLayoutDescriptor The keyboard layout descriptor, as returned by
    562      * {@link KeyboardLayout#getDescriptor()}.
    563      * @return The keyboard layout, or null if it could not be loaded.
    564      *
    565      * @hide
    566      */
    567     public KeyboardLayout getKeyboardLayout(String keyboardLayoutDescriptor) {
    568         if (keyboardLayoutDescriptor == null) {
    569             throw new IllegalArgumentException("keyboardLayoutDescriptor must not be null");
    570         }
    571 
    572         try {
    573             return mIm.getKeyboardLayout(keyboardLayoutDescriptor);
    574         } catch (RemoteException ex) {
    575             throw ex.rethrowFromSystemServer();
    576         }
    577     }
    578 
    579     /**
    580      * Gets the current keyboard layout descriptor for the specified input
    581      * device.
    582      *
    583      * @param identifier Identifier for the input device
    584      * @return The keyboard layout descriptor, or null if no keyboard layout has
    585      *         been set.
    586      * @hide
    587      */
    588     public String getCurrentKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier) {
    589         try {
    590             return mIm.getCurrentKeyboardLayoutForInputDevice(identifier);
    591         } catch (RemoteException ex) {
    592             throw ex.rethrowFromSystemServer();
    593         }
    594     }
    595 
    596     /**
    597      * Sets the current keyboard layout descriptor for the specified input
    598      * device.
    599      * <p>
    600      * This method may have the side-effect of causing the input device in
    601      * question to be reconfigured.
    602      * </p>
    603      *
    604      * @param identifier The identifier for the input device.
    605      * @param keyboardLayoutDescriptor The keyboard layout descriptor to use,
    606      *            must not be null.
    607      * @hide
    608      */
    609     public void setCurrentKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier,
    610             String keyboardLayoutDescriptor) {
    611         if (identifier == null) {
    612             throw new IllegalArgumentException("identifier must not be null");
    613         }
    614         if (keyboardLayoutDescriptor == null) {
    615             throw new IllegalArgumentException("keyboardLayoutDescriptor must not be null");
    616         }
    617 
    618         try {
    619             mIm.setCurrentKeyboardLayoutForInputDevice(identifier,
    620                     keyboardLayoutDescriptor);
    621         } catch (RemoteException ex) {
    622             throw ex.rethrowFromSystemServer();
    623         }
    624     }
    625 
    626     /**
    627      * Gets all keyboard layout descriptors that are enabled for the specified
    628      * input device.
    629      *
    630      * @param identifier The identifier for the input device.
    631      * @return The keyboard layout descriptors.
    632      * @hide
    633      */
    634     public String[] getEnabledKeyboardLayoutsForInputDevice(InputDeviceIdentifier identifier) {
    635         if (identifier == null) {
    636             throw new IllegalArgumentException("inputDeviceDescriptor must not be null");
    637         }
    638 
    639         try {
    640             return mIm.getEnabledKeyboardLayoutsForInputDevice(identifier);
    641         } catch (RemoteException ex) {
    642             throw ex.rethrowFromSystemServer();
    643         }
    644     }
    645 
    646     /**
    647      * Adds the keyboard layout descriptor for the specified input device.
    648      * <p>
    649      * This method may have the side-effect of causing the input device in
    650      * question to be reconfigured.
    651      * </p>
    652      *
    653      * @param identifier The identifier for the input device.
    654      * @param keyboardLayoutDescriptor The descriptor of the keyboard layout to
    655      *            add.
    656      * @hide
    657      */
    658     public void addKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier,
    659             String keyboardLayoutDescriptor) {
    660         if (identifier == null) {
    661             throw new IllegalArgumentException("inputDeviceDescriptor must not be null");
    662         }
    663         if (keyboardLayoutDescriptor == null) {
    664             throw new IllegalArgumentException("keyboardLayoutDescriptor must not be null");
    665         }
    666 
    667         try {
    668             mIm.addKeyboardLayoutForInputDevice(identifier, keyboardLayoutDescriptor);
    669         } catch (RemoteException ex) {
    670             throw ex.rethrowFromSystemServer();
    671         }
    672     }
    673 
    674     /**
    675      * Removes the keyboard layout descriptor for the specified input device.
    676      * <p>
    677      * This method may have the side-effect of causing the input device in
    678      * question to be reconfigured.
    679      * </p>
    680      *
    681      * @param identifier The identifier for the input device.
    682      * @param keyboardLayoutDescriptor The descriptor of the keyboard layout to
    683      *            remove.
    684      * @hide
    685      */
    686     public void removeKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier,
    687             String keyboardLayoutDescriptor) {
    688         if (identifier == null) {
    689             throw new IllegalArgumentException("inputDeviceDescriptor must not be null");
    690         }
    691         if (keyboardLayoutDescriptor == null) {
    692             throw new IllegalArgumentException("keyboardLayoutDescriptor must not be null");
    693         }
    694 
    695         try {
    696             mIm.removeKeyboardLayoutForInputDevice(identifier, keyboardLayoutDescriptor);
    697         } catch (RemoteException ex) {
    698             throw ex.rethrowFromSystemServer();
    699         }
    700     }
    701 
    702 
    703     /**
    704      * Gets the keyboard layout for the specified input device and IME subtype.
    705      *
    706      * @param identifier The identifier for the input device.
    707      * @param inputMethodInfo The input method.
    708      * @param inputMethodSubtype The input method subtype. {@code null} if this input method does
    709      * not support any subtype.
    710      *
    711      * @return The associated {@link KeyboardLayout}, or null if one has not been set.
    712      *
    713      * @hide
    714      */
    715     @Nullable
    716     public KeyboardLayout getKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier,
    717             InputMethodInfo inputMethodInfo, @Nullable InputMethodSubtype inputMethodSubtype) {
    718         try {
    719             return mIm.getKeyboardLayoutForInputDevice(
    720                     identifier, inputMethodInfo, inputMethodSubtype);
    721         } catch (RemoteException ex) {
    722             throw ex.rethrowFromSystemServer();
    723         }
    724     }
    725 
    726     /**
    727      * Sets the keyboard layout for the specified input device and IME subtype pair.
    728      *
    729      * @param identifier The identifier for the input device.
    730      * @param inputMethodInfo The input method with which to associate the keyboard layout.
    731      * @param inputMethodSubtype The input method subtype which which to associate the keyboard
    732      * layout. {@code null} if this input method does not support any subtype.
    733      * @param keyboardLayoutDescriptor The descriptor of the keyboard layout to set
    734      *
    735      * @hide
    736      */
    737     public void setKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier,
    738             InputMethodInfo inputMethodInfo, @Nullable InputMethodSubtype inputMethodSubtype,
    739             String keyboardLayoutDescriptor) {
    740         try {
    741             mIm.setKeyboardLayoutForInputDevice(identifier, inputMethodInfo,
    742                     inputMethodSubtype, keyboardLayoutDescriptor);
    743         } catch (RemoteException ex) {
    744             throw ex.rethrowFromSystemServer();
    745         }
    746     }
    747 
    748     /**
    749      * Gets the TouchCalibration applied to the specified input device's coordinates.
    750      *
    751      * @param inputDeviceDescriptor The input device descriptor.
    752      * @return The TouchCalibration currently assigned for use with the given
    753      * input device. If none is set, an identity TouchCalibration is returned.
    754      *
    755      * @hide
    756      */
    757     public TouchCalibration getTouchCalibration(String inputDeviceDescriptor, int surfaceRotation) {
    758         try {
    759             return mIm.getTouchCalibrationForInputDevice(inputDeviceDescriptor, surfaceRotation);
    760         } catch (RemoteException ex) {
    761             throw ex.rethrowFromSystemServer();
    762         }
    763     }
    764 
    765     /**
    766      * Sets the TouchCalibration to apply to the specified input device's coordinates.
    767      * <p>
    768      * This method may have the side-effect of causing the input device in question
    769      * to be reconfigured. Requires {@link android.Manifest.permissions.SET_INPUT_CALIBRATION}.
    770      * </p>
    771      *
    772      * @param inputDeviceDescriptor The input device descriptor.
    773      * @param calibration The calibration to be applied
    774      *
    775      * @hide
    776      */
    777     public void setTouchCalibration(String inputDeviceDescriptor, int surfaceRotation,
    778             TouchCalibration calibration) {
    779         try {
    780             mIm.setTouchCalibrationForInputDevice(inputDeviceDescriptor, surfaceRotation, calibration);
    781         } catch (RemoteException ex) {
    782             throw ex.rethrowFromSystemServer();
    783         }
    784     }
    785 
    786     /**
    787      * Gets the mouse pointer speed.
    788      * <p>
    789      * Only returns the permanent mouse pointer speed.  Ignores any temporary pointer
    790      * speed set by {@link #tryPointerSpeed}.
    791      * </p>
    792      *
    793      * @param context The application context.
    794      * @return The pointer speed as a value between {@link #MIN_POINTER_SPEED} and
    795      * {@link #MAX_POINTER_SPEED}, or the default value {@link #DEFAULT_POINTER_SPEED}.
    796      *
    797      * @hide
    798      */
    799     public int getPointerSpeed(Context context) {
    800         int speed = DEFAULT_POINTER_SPEED;
    801         try {
    802             speed = Settings.System.getInt(context.getContentResolver(),
    803                     Settings.System.POINTER_SPEED);
    804         } catch (SettingNotFoundException snfe) {
    805         }
    806         return speed;
    807     }
    808 
    809     /**
    810      * Sets the mouse pointer speed.
    811      * <p>
    812      * Requires {@link android.Manifest.permissions.WRITE_SETTINGS}.
    813      * </p>
    814      *
    815      * @param context The application context.
    816      * @param speed The pointer speed as a value between {@link #MIN_POINTER_SPEED} and
    817      * {@link #MAX_POINTER_SPEED}, or the default value {@link #DEFAULT_POINTER_SPEED}.
    818      *
    819      * @hide
    820      */
    821     public void setPointerSpeed(Context context, int speed) {
    822         if (speed < MIN_POINTER_SPEED || speed > MAX_POINTER_SPEED) {
    823             throw new IllegalArgumentException("speed out of range");
    824         }
    825 
    826         Settings.System.putInt(context.getContentResolver(),
    827                 Settings.System.POINTER_SPEED, speed);
    828     }
    829 
    830     /**
    831      * Changes the mouse pointer speed temporarily, but does not save the setting.
    832      * <p>
    833      * Requires {@link android.Manifest.permission.SET_POINTER_SPEED}.
    834      * </p>
    835      *
    836      * @param speed The pointer speed as a value between {@link #MIN_POINTER_SPEED} and
    837      * {@link #MAX_POINTER_SPEED}, or the default value {@link #DEFAULT_POINTER_SPEED}.
    838      *
    839      * @hide
    840      */
    841     public void tryPointerSpeed(int speed) {
    842         if (speed < MIN_POINTER_SPEED || speed > MAX_POINTER_SPEED) {
    843             throw new IllegalArgumentException("speed out of range");
    844         }
    845 
    846         try {
    847             mIm.tryPointerSpeed(speed);
    848         } catch (RemoteException ex) {
    849             throw ex.rethrowFromSystemServer();
    850         }
    851     }
    852 
    853     /**
    854      * Queries the framework about whether any physical keys exist on the
    855      * any keyboard attached to the device that are capable of producing the given
    856      * array of key codes.
    857      *
    858      * @param keyCodes The array of key codes to query.
    859      * @return A new array of the same size as the key codes array whose elements
    860      * are set to true if at least one attached keyboard supports the corresponding key code
    861      * at the same index in the key codes array.
    862      *
    863      * @hide
    864      */
    865     public boolean[] deviceHasKeys(int[] keyCodes) {
    866         return deviceHasKeys(-1, keyCodes);
    867     }
    868 
    869     /**
    870      * Queries the framework about whether any physical keys exist on the
    871      * any keyboard attached to the device that are capable of producing the given
    872      * array of key codes.
    873      *
    874      * @param id The id of the device to query.
    875      * @param keyCodes The array of key codes to query.
    876      * @return A new array of the same size as the key codes array whose elements are set to true
    877      * if the given device could produce the corresponding key code at the same index in the key
    878      * codes array.
    879      *
    880      * @hide
    881      */
    882     public boolean[] deviceHasKeys(int id, int[] keyCodes) {
    883         boolean[] ret = new boolean[keyCodes.length];
    884         try {
    885             mIm.hasKeys(id, InputDevice.SOURCE_ANY, keyCodes, ret);
    886         } catch (RemoteException e) {
    887             throw e.rethrowFromSystemServer();
    888         }
    889         return ret;
    890     }
    891 
    892 
    893     /**
    894      * Injects an input event into the event system on behalf of an application.
    895      * The synchronization mode determines whether the method blocks while waiting for
    896      * input injection to proceed.
    897      * <p>
    898      * Requires {@link android.Manifest.permission.INJECT_EVENTS} to inject into
    899      * windows that are owned by other applications.
    900      * </p><p>
    901      * Make sure you correctly set the event time and input source of the event
    902      * before calling this method.
    903      * </p>
    904      *
    905      * @param event The event to inject.
    906      * @param mode The synchronization mode.  One of:
    907      * {@link #INJECT_INPUT_EVENT_MODE_ASYNC},
    908      * {@link #INJECT_INPUT_EVENT_MODE_WAIT_FOR_RESULT}, or
    909      * {@link #INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH}.
    910      * @return True if input event injection succeeded.
    911      *
    912      * @hide
    913      */
    914     public boolean injectInputEvent(InputEvent event, int mode) {
    915         if (event == null) {
    916             throw new IllegalArgumentException("event must not be null");
    917         }
    918         if (mode != INJECT_INPUT_EVENT_MODE_ASYNC
    919                 && mode != INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH
    920                 && mode != INJECT_INPUT_EVENT_MODE_WAIT_FOR_RESULT) {
    921             throw new IllegalArgumentException("mode is invalid");
    922         }
    923 
    924         try {
    925             return mIm.injectInputEvent(event, mode);
    926         } catch (RemoteException ex) {
    927             throw ex.rethrowFromSystemServer();
    928         }
    929     }
    930 
    931     /**
    932      * Changes the mouse pointer's icon shape into the specified id.
    933      *
    934      * @param iconId The id of the pointer graphic, as a value between
    935      * {@link PointerIcon.TYPE_ARROW} and {@link PointerIcon.TYPE_GRABBING}.
    936      *
    937      * @hide
    938      */
    939     public void setPointerIconType(int iconId) {
    940         try {
    941             mIm.setPointerIconType(iconId);
    942         } catch (RemoteException ex) {
    943             throw ex.rethrowFromSystemServer();
    944         }
    945     }
    946 
    947     /** @hide */
    948     public void setCustomPointerIcon(PointerIcon icon) {
    949         try {
    950             mIm.setCustomPointerIcon(icon);
    951         } catch (RemoteException ex) {
    952             throw ex.rethrowFromSystemServer();
    953         }
    954     }
    955 
    956     /**
    957      * Request or release pointer capture.
    958      * <p>
    959      * When in capturing mode, the pointer icon disappears and all mouse events are dispatched to
    960      * the window which has requested the capture. Relative position changes are available through
    961      * {@link MotionEvent#getX} and {@link MotionEvent#getY}.
    962      *
    963      * @param enable true when requesting pointer capture, false when releasing.
    964      *
    965      * @hide
    966      */
    967     public void requestPointerCapture(IBinder windowToken, boolean enable) {
    968         try {
    969             mIm.requestPointerCapture(windowToken, enable);
    970         } catch (RemoteException ex) {
    971             throw ex.rethrowFromSystemServer();
    972         }
    973     }
    974 
    975 
    976     /**
    977      * Create an {@link IInputForwarder} targeted to provided display.
    978      * {@link android.Manifest.permission.INJECT_EVENTS} permission is required to call this method.
    979      *
    980      * @param displayId Id of the target display where input events should be forwarded.
    981      *                  Display must exist and must be owned by the caller.
    982      * @return The forwarder instance.
    983      *
    984      * @hide
    985      */
    986     public IInputForwarder createInputForwarder(int displayId) {
    987         try {
    988             return mIm.createInputForwarder(displayId);
    989         } catch (RemoteException ex) {
    990             throw ex.rethrowFromSystemServer();
    991         }
    992     }
    993 
    994     private void populateInputDevicesLocked() {
    995         if (mInputDevicesChangedListener == null) {
    996             final InputDevicesChangedListener listener = new InputDevicesChangedListener();
    997             try {
    998                 mIm.registerInputDevicesChangedListener(listener);
    999             } catch (RemoteException ex) {
   1000                 throw ex.rethrowFromSystemServer();
   1001             }
   1002             mInputDevicesChangedListener = listener;
   1003         }
   1004 
   1005         if (mInputDevices == null) {
   1006             final int[] ids;
   1007             try {
   1008                 ids = mIm.getInputDeviceIds();
   1009             } catch (RemoteException ex) {
   1010                 throw ex.rethrowFromSystemServer();
   1011             }
   1012 
   1013             mInputDevices = new SparseArray<InputDevice>();
   1014             for (int i = 0; i < ids.length; i++) {
   1015                 mInputDevices.put(ids[i], null);
   1016             }
   1017         }
   1018     }
   1019 
   1020     private void onInputDevicesChanged(int[] deviceIdAndGeneration) {
   1021         if (DEBUG) {
   1022             Log.d(TAG, "Received input devices changed.");
   1023         }
   1024 
   1025         synchronized (mInputDevicesLock) {
   1026             for (int i = mInputDevices.size(); --i > 0; ) {
   1027                 final int deviceId = mInputDevices.keyAt(i);
   1028                 if (!containsDeviceId(deviceIdAndGeneration, deviceId)) {
   1029                     if (DEBUG) {
   1030                         Log.d(TAG, "Device removed: " + deviceId);
   1031                     }
   1032                     mInputDevices.removeAt(i);
   1033                     sendMessageToInputDeviceListenersLocked(MSG_DEVICE_REMOVED, deviceId);
   1034                 }
   1035             }
   1036 
   1037             for (int i = 0; i < deviceIdAndGeneration.length; i += 2) {
   1038                 final int deviceId = deviceIdAndGeneration[i];
   1039                 int index = mInputDevices.indexOfKey(deviceId);
   1040                 if (index >= 0) {
   1041                     final InputDevice device = mInputDevices.valueAt(index);
   1042                     if (device != null) {
   1043                         final int generation = deviceIdAndGeneration[i + 1];
   1044                         if (device.getGeneration() != generation) {
   1045                             if (DEBUG) {
   1046                                 Log.d(TAG, "Device changed: " + deviceId);
   1047                             }
   1048                             mInputDevices.setValueAt(index, null);
   1049                             sendMessageToInputDeviceListenersLocked(MSG_DEVICE_CHANGED, deviceId);
   1050                         }
   1051                     }
   1052                 } else {
   1053                     if (DEBUG) {
   1054                         Log.d(TAG, "Device added: " + deviceId);
   1055                     }
   1056                     mInputDevices.put(deviceId, null);
   1057                     sendMessageToInputDeviceListenersLocked(MSG_DEVICE_ADDED, deviceId);
   1058                 }
   1059             }
   1060         }
   1061     }
   1062 
   1063     private void sendMessageToInputDeviceListenersLocked(int what, int deviceId) {
   1064         final int numListeners = mInputDeviceListeners.size();
   1065         for (int i = 0; i < numListeners; i++) {
   1066             InputDeviceListenerDelegate listener = mInputDeviceListeners.get(i);
   1067             listener.sendMessage(listener.obtainMessage(what, deviceId, 0));
   1068         }
   1069     }
   1070 
   1071     private static boolean containsDeviceId(int[] deviceIdAndGeneration, int deviceId) {
   1072         for (int i = 0; i < deviceIdAndGeneration.length; i += 2) {
   1073             if (deviceIdAndGeneration[i] == deviceId) {
   1074                 return true;
   1075             }
   1076         }
   1077         return false;
   1078     }
   1079 
   1080 
   1081     private void onTabletModeChanged(long whenNanos, boolean inTabletMode) {
   1082         if (DEBUG) {
   1083             Log.d(TAG, "Received tablet mode changed: "
   1084                     + "whenNanos=" + whenNanos + ", inTabletMode=" + inTabletMode);
   1085         }
   1086         synchronized (mTabletModeLock) {
   1087             final int N = mOnTabletModeChangedListeners.size();
   1088             for (int i = 0; i < N; i++) {
   1089                 OnTabletModeChangedListenerDelegate listener =
   1090                         mOnTabletModeChangedListeners.get(i);
   1091                 listener.sendTabletModeChanged(whenNanos, inTabletMode);
   1092             }
   1093         }
   1094     }
   1095 
   1096     /**
   1097      * Gets a vibrator service associated with an input device, assuming it has one.
   1098      * @return The vibrator, never null.
   1099      * @hide
   1100      */
   1101     public Vibrator getInputDeviceVibrator(int deviceId) {
   1102         return new InputDeviceVibrator(deviceId);
   1103     }
   1104 
   1105     /**
   1106      * Listens for changes in input devices.
   1107      */
   1108     public interface InputDeviceListener {
   1109         /**
   1110          * Called whenever an input device has been added to the system.
   1111          * Use {@link InputManager#getInputDevice} to get more information about the device.
   1112          *
   1113          * @param deviceId The id of the input device that was added.
   1114          */
   1115         void onInputDeviceAdded(int deviceId);
   1116 
   1117         /**
   1118          * Called whenever an input device has been removed from the system.
   1119          *
   1120          * @param deviceId The id of the input device that was removed.
   1121          */
   1122         void onInputDeviceRemoved(int deviceId);
   1123 
   1124         /**
   1125          * Called whenever the properties of an input device have changed since they
   1126          * were last queried.  Use {@link InputManager#getInputDevice} to get
   1127          * a fresh {@link InputDevice} object with the new properties.
   1128          *
   1129          * @param deviceId The id of the input device that changed.
   1130          */
   1131         void onInputDeviceChanged(int deviceId);
   1132     }
   1133 
   1134     private final class InputDevicesChangedListener extends IInputDevicesChangedListener.Stub {
   1135         @Override
   1136         public void onInputDevicesChanged(int[] deviceIdAndGeneration) throws RemoteException {
   1137             InputManager.this.onInputDevicesChanged(deviceIdAndGeneration);
   1138         }
   1139     }
   1140 
   1141     private static final class InputDeviceListenerDelegate extends Handler {
   1142         public final InputDeviceListener mListener;
   1143 
   1144         public InputDeviceListenerDelegate(InputDeviceListener listener, Handler handler) {
   1145             super(handler != null ? handler.getLooper() : Looper.myLooper());
   1146             mListener = listener;
   1147         }
   1148 
   1149         @Override
   1150         public void handleMessage(Message msg) {
   1151             switch (msg.what) {
   1152                 case MSG_DEVICE_ADDED:
   1153                     mListener.onInputDeviceAdded(msg.arg1);
   1154                     break;
   1155                 case MSG_DEVICE_REMOVED:
   1156                     mListener.onInputDeviceRemoved(msg.arg1);
   1157                     break;
   1158                 case MSG_DEVICE_CHANGED:
   1159                     mListener.onInputDeviceChanged(msg.arg1);
   1160                     break;
   1161             }
   1162         }
   1163     }
   1164 
   1165     /** @hide */
   1166     public interface OnTabletModeChangedListener {
   1167         /**
   1168          * Called whenever the device goes into or comes out of tablet mode.
   1169          *
   1170          * @param whenNanos The time at which the device transitioned into or
   1171          * out of tablet mode. This is given in nanoseconds in the
   1172          * {@link SystemClock#uptimeMillis} time base.
   1173          */
   1174         void onTabletModeChanged(long whenNanos, boolean inTabletMode);
   1175     }
   1176 
   1177     private final class TabletModeChangedListener extends ITabletModeChangedListener.Stub {
   1178         @Override
   1179         public void onTabletModeChanged(long whenNanos, boolean inTabletMode) {
   1180             InputManager.this.onTabletModeChanged(whenNanos, inTabletMode);
   1181         }
   1182     }
   1183 
   1184     private static final class OnTabletModeChangedListenerDelegate extends Handler {
   1185         private static final int MSG_TABLET_MODE_CHANGED = 0;
   1186 
   1187         public final OnTabletModeChangedListener mListener;
   1188 
   1189         public OnTabletModeChangedListenerDelegate(
   1190                 OnTabletModeChangedListener listener, Handler handler) {
   1191             super(handler != null ? handler.getLooper() : Looper.myLooper());
   1192             mListener = listener;
   1193         }
   1194 
   1195         public void sendTabletModeChanged(long whenNanos, boolean inTabletMode) {
   1196             SomeArgs args = SomeArgs.obtain();
   1197             args.argi1 = (int) (whenNanos & 0xFFFFFFFF);
   1198             args.argi2 = (int) (whenNanos >> 32);
   1199             args.arg1 = (Boolean) inTabletMode;
   1200             obtainMessage(MSG_TABLET_MODE_CHANGED, args).sendToTarget();
   1201         }
   1202 
   1203         @Override
   1204         public void handleMessage(Message msg) {
   1205             switch (msg.what) {
   1206                 case MSG_TABLET_MODE_CHANGED:
   1207                     SomeArgs args = (SomeArgs) msg.obj;
   1208                     long whenNanos = (args.argi1 & 0xFFFFFFFFl) | ((long) args.argi2 << 32);
   1209                     boolean inTabletMode = (boolean) args.arg1;
   1210                     mListener.onTabletModeChanged(whenNanos, inTabletMode);
   1211                     break;
   1212             }
   1213         }
   1214     }
   1215 
   1216     private final class InputDeviceVibrator extends Vibrator {
   1217         private final int mDeviceId;
   1218         private final Binder mToken;
   1219 
   1220         public InputDeviceVibrator(int deviceId) {
   1221             mDeviceId = deviceId;
   1222             mToken = new Binder();
   1223         }
   1224 
   1225         @Override
   1226         public boolean hasVibrator() {
   1227             return true;
   1228         }
   1229 
   1230         @Override
   1231         public boolean hasAmplitudeControl() {
   1232             return false;
   1233         }
   1234 
   1235         /**
   1236          * @hide
   1237          */
   1238         @Override
   1239         public void vibrate(int uid, String opPkg,
   1240                 VibrationEffect effect, AudioAttributes attributes) {
   1241             long[] pattern;
   1242             int repeat;
   1243             if (effect instanceof VibrationEffect.OneShot) {
   1244                 VibrationEffect.OneShot oneShot = (VibrationEffect.OneShot) effect;
   1245                 pattern = new long[] { 0, oneShot.getTiming() };
   1246                 repeat = -1;
   1247             } else if (effect instanceof VibrationEffect.Waveform) {
   1248                 VibrationEffect.Waveform waveform = (VibrationEffect.Waveform) effect;
   1249                 pattern = waveform.getTimings();
   1250                 repeat = waveform.getRepeatIndex();
   1251             } else {
   1252                 // TODO: Add support for prebaked effects
   1253                 Log.w(TAG, "Pre-baked effects aren't supported on input devices");
   1254                 return;
   1255             }
   1256 
   1257             try {
   1258                 mIm.vibrate(mDeviceId, pattern, repeat, mToken);
   1259             } catch (RemoteException ex) {
   1260                 throw ex.rethrowFromSystemServer();
   1261             }
   1262         }
   1263 
   1264         @Override
   1265         public void cancel() {
   1266             try {
   1267                 mIm.cancelVibrate(mDeviceId, mToken);
   1268             } catch (RemoteException ex) {
   1269                 throw ex.rethrowFromSystemServer();
   1270             }
   1271         }
   1272     }
   1273 }
   1274