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