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
17package android.hardware.input;
18
19import android.annotation.IntDef;
20import android.annotation.Nullable;
21import android.annotation.SdkConstant;
22import android.annotation.SdkConstant.SdkConstantType;
23import android.annotation.SystemService;
24import android.app.IInputForwarder;
25import android.content.Context;
26import android.media.AudioAttributes;
27import android.os.Binder;
28import android.os.Handler;
29import android.os.IBinder;
30import android.os.Looper;
31import android.os.Message;
32import android.os.RemoteException;
33import android.os.ServiceManager;
34import android.os.ServiceManager.ServiceNotFoundException;
35import android.os.SystemClock;
36import android.os.VibrationEffect;
37import android.os.Vibrator;
38import android.provider.Settings;
39import android.provider.Settings.SettingNotFoundException;
40import android.util.Log;
41import android.util.SparseArray;
42import android.view.InputDevice;
43import android.view.InputEvent;
44import android.view.MotionEvent;
45import android.view.PointerIcon;
46import android.view.inputmethod.InputMethodInfo;
47import android.view.inputmethod.InputMethodSubtype;
48
49import com.android.internal.os.SomeArgs;
50
51import java.lang.annotation.Retention;
52import java.lang.annotation.RetentionPolicy;
53import java.util.ArrayList;
54import java.util.List;
55
56/**
57 * Provides information about input devices and available key layouts.
58 */
59@SystemService(Context.INPUT_SERVICE)
60public 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