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