1 /*
2  * Copyright (C) 2012 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.hardware.input;
18 
19 import com.android.internal.os.SomeArgs;
20 
21 import android.annotation.IntDef;
22 import android.annotation.Nullable;
23 import android.annotation.SdkConstant;
24 import android.annotation.SdkConstant.SdkConstantType;
25 import android.content.Context;
26 import android.media.AudioAttributes;
27 import android.os.Binder;
28 import android.os.Handler;
29 import android.os.IBinder;
30 import android.os.Looper;
31 import android.os.Message;
32 import android.os.RemoteException;
33 import android.os.ServiceManager;
34 import android.os.SystemClock;
35 import android.os.Vibrator;
36 import android.provider.Settings;
37 import android.provider.Settings.SettingNotFoundException;
38 import android.util.Log;
39 import android.util.SparseArray;
40 import android.view.InputDevice;
41 import android.view.InputEvent;
42 import android.view.PointerIcon;
43 import android.view.inputmethod.InputMethodInfo;
44 import android.view.inputmethod.InputMethodSubtype;
45 
46 import java.lang.annotation.Retention;
47 import java.lang.annotation.RetentionPolicy;
48 import java.util.ArrayList;
49 import java.util.List;
50 
51 /**
52  * Provides information about input devices and available key layouts.
53  * <p>
54  * Get an instance of this class by calling
55  * {@link android.content.Context#getSystemService(java.lang.String)
56  * Context.getSystemService()} with the argument
57  * {@link android.content.Context#INPUT_SERVICE}.
58  * </p>
59  */
60 public final class InputManager {
61     private static final String TAG = "InputManager";
62     private static final boolean DEBUG = false;
63 
64     private static final int MSG_DEVICE_ADDED = 1;
65     private static final int MSG_DEVICE_REMOVED = 2;
66     private static final int MSG_DEVICE_CHANGED = 3;
67 
68     private static InputManager sInstance;
69 
70     private final IInputManager mIm;
71 
72     // Guarded by mInputDevicesLock
73     private final Object mInputDevicesLock = new Object();
74     private SparseArray<InputDevice> mInputDevices;
75     private InputDevicesChangedListener mInputDevicesChangedListener;
76     private final ArrayList<InputDeviceListenerDelegate> mInputDeviceListeners =
77             new ArrayList<InputDeviceListenerDelegate>();
78 
79     // Guarded by mTabletModeLock
80     private final Object mTabletModeLock = new Object();
81     private TabletModeChangedListener mTabletModeChangedListener;
82     private List<OnTabletModeChangedListenerDelegate> mOnTabletModeChangedListeners;
83 
84     /**
85      * Broadcast Action: Query available keyboard layouts.
86      * <p>
87      * The input manager service locates available keyboard layouts
88      * by querying broadcast receivers that are registered for this action.
89      * An application can offer additional keyboard layouts to the user
90      * by declaring a suitable broadcast receiver in its manifest.
91      * </p><p>
92      * Here is an example broadcast receiver declaration that an application
93      * might include in its AndroidManifest.xml to advertise keyboard layouts.
94      * The meta-data specifies a resource that contains a description of each keyboard
95      * layout that is provided by the application.
96      * <pre><code>
97      * &lt;receiver android:name=".InputDeviceReceiver"
98      *         android:label="@string/keyboard_layouts_label">
99      *     &lt;intent-filter>
100      *         &lt;action android:name="android.hardware.input.action.QUERY_KEYBOARD_LAYOUTS" />
101      *     &lt;/intent-filter>
102      *     &lt;meta-data android:name="android.hardware.input.metadata.KEYBOARD_LAYOUTS"
103      *             android:resource="@xml/keyboard_layouts" />
104      * &lt;/receiver>
105      * </code></pre>
106      * </p><p>
107      * In the above example, the <code>@xml/keyboard_layouts</code> resource refers to
108      * an XML resource whose root element is <code>&lt;keyboard-layouts></code> that
109      * contains zero or more <code>&lt;keyboard-layout></code> elements.
110      * Each <code>&lt;keyboard-layout></code> element specifies the name, label, and location
111      * of a key character map for a particular keyboard layout.  The label on the receiver
112      * is used to name the collection of keyboard layouts provided by this receiver in the
113      * keyboard layout settings.
114      * <pre><code>
115      * &lt;?xml version="1.0" encoding="utf-8"?>
116      * &lt;keyboard-layouts xmlns:android="http://schemas.android.com/apk/res/android">
117      *     &lt;keyboard-layout android:name="keyboard_layout_english_us"
118      *             android:label="@string/keyboard_layout_english_us_label"
119      *             android:keyboardLayout="@raw/keyboard_layout_english_us" />
120      * &lt;/keyboard-layouts>
121      * </pre></code>
122      * </p><p>
123      * The <code>android:name</code> attribute specifies an identifier by which
124      * the keyboard layout will be known in the package.
125      * The <code>android:label</code> attribute specifies a human-readable descriptive
126      * label to describe the keyboard layout in the user interface, such as "English (US)".
127      * The <code>android:keyboardLayout</code> attribute refers to a
128      * <a href="http://source.android.com/tech/input/key-character-map-files.html">
129      * key character map</a> resource that defines the keyboard layout.
130      * </p>
131      */
132     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
133     public static final String ACTION_QUERY_KEYBOARD_LAYOUTS =
134             "android.hardware.input.action.QUERY_KEYBOARD_LAYOUTS";
135 
136     /**
137      * Metadata Key: Keyboard layout metadata associated with
138      * {@link #ACTION_QUERY_KEYBOARD_LAYOUTS}.
139      * <p>
140      * Specifies the resource id of a XML resource that describes the keyboard
141      * layouts that are provided by the application.
142      * </p>
143      */
144     public static final String META_DATA_KEYBOARD_LAYOUTS =
145             "android.hardware.input.metadata.KEYBOARD_LAYOUTS";
146 
147     /**
148      * Pointer Speed: The minimum (slowest) pointer speed (-7).
149      * @hide
150      */
151     public static final int MIN_POINTER_SPEED = -7;
152 
153     /**
154      * Pointer Speed: The maximum (fastest) pointer speed (7).
155      * @hide
156      */
157     public static final int MAX_POINTER_SPEED = 7;
158 
159     /**
160      * Pointer Speed: The default pointer speed (0).
161      * @hide
162      */
163     public static final int DEFAULT_POINTER_SPEED = 0;
164 
165     /**
166      * Input Event Injection Synchronization Mode: None.
167      * Never blocks.  Injection is asynchronous and is assumed always to be successful.
168      * @hide
169      */
170     public static final int INJECT_INPUT_EVENT_MODE_ASYNC = 0; // see InputDispatcher.h
171 
172     /**
173      * Input Event Injection Synchronization Mode: Wait for result.
174      * Waits for previous events to be dispatched so that the input dispatcher can
175      * determine whether input event injection will be permitted based on the current
176      * input focus.  Does not wait for the input event to finish being handled
177      * by the application.
178      * @hide
179      */
180     public static final int INJECT_INPUT_EVENT_MODE_WAIT_FOR_RESULT = 1;  // see InputDispatcher.h
181 
182     /**
183      * Input Event Injection Synchronization Mode: Wait for finish.
184      * Waits for the event to be delivered to the application and handled.
185      * @hide
186      */
187     public static final int INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH = 2;  // see InputDispatcher.h
188 
189     /** @hide */
190     @Retention(RetentionPolicy.SOURCE)
191     @IntDef({SWITCH_STATE_UNKNOWN, SWITCH_STATE_OFF, SWITCH_STATE_ON})
192     public @interface SwitchState {}
193 
194     /**
195      * Switch State: Unknown.
196      *
197      * The system has yet to report a valid value for the switch.
198      * @hide
199      */
200     public static final int SWITCH_STATE_UNKNOWN = -1;
201 
202     /**
203      * Switch State: Off.
204      * @hide
205      */
206     public static final int SWITCH_STATE_OFF = 0;
207 
208     /**
209      * Switch State: On.
210      * @hide
211      */
212     public static final int SWITCH_STATE_ON = 1;
213 
InputManager(IInputManager im)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      */
getInstance()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      */
getInputDevice(int id)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      */
getInputDeviceByDescriptor(String descriptor)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      */
getInputDeviceIds()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      */
registerInputDeviceListener(InputDeviceListener listener, Handler handler)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      */
unregisterInputDeviceListener(InputDeviceListener listener)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 
findInputDeviceListenerLocked(InputDeviceListener listener)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
isInTabletMode()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      */
registerOnTabletModeChangedListener( OnTabletModeChangedListener listener, Handler handler)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      */
unregisterOnTabletModeChangedListener(OnTabletModeChangedListener listener)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 
initializeTabletModeListenerLocked()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 
findOnTabletModeChangedListenerLocked(OnTabletModeChangedListener listener)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      */
getKeyboardLayouts()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      */
getKeyboardLayoutsForInputDevice(InputDeviceIdentifier identifier)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      */
getKeyboardLayout(String keyboardLayoutDescriptor)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      */
getCurrentKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier)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      */
setCurrentKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier, String keyboardLayoutDescriptor)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      */
getEnabledKeyboardLayoutsForInputDevice(InputDeviceIdentifier identifier)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      */
addKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier, String keyboardLayoutDescriptor)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      */
removeKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier, String keyboardLayoutDescriptor)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
getKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier, InputMethodInfo inputMethodInfo, @Nullable InputMethodSubtype inputMethodSubtype)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      */
setKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier, InputMethodInfo inputMethodInfo, @Nullable InputMethodSubtype inputMethodSubtype, String keyboardLayoutDescriptor)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      */
getTouchCalibration(String inputDeviceDescriptor, int surfaceRotation)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      */
setTouchCalibration(String inputDeviceDescriptor, int surfaceRotation, TouchCalibration calibration)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      */
getPointerSpeed(Context context)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      */
setPointerSpeed(Context context, int speed)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      */
tryPointerSpeed(int speed)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      */
deviceHasKeys(int[] keyCodes)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      */
deviceHasKeys(int id, int[] keyCodes)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      */
injectInputEvent(InputEvent event, int mode)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      */
setPointerIconType(int iconId)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 */
setCustomPointerIcon(PointerIcon icon)888     public void setCustomPointerIcon(PointerIcon icon) {
889         try {
890             mIm.setCustomPointerIcon(icon);
891         } catch (RemoteException ex) {
892             throw ex.rethrowFromSystemServer();
893         }
894     }
895 
populateInputDevicesLocked()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 
onInputDevicesChanged(int[] deviceIdAndGeneration)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 
sendMessageToInputDeviceListenersLocked(int what, int deviceId)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 
containsDeviceId(int[] deviceIdAndGeneration, int deviceId)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 
onTabletModeChanged(long whenNanos, boolean inTabletMode)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      */
getInputDeviceVibrator(int deviceId)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          */
onInputDeviceAdded(int deviceId)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          */
onInputDeviceRemoved(int deviceId)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          */
onInputDeviceChanged(int deviceId)1033         void onInputDeviceChanged(int deviceId);
1034     }
1035 
1036     private final class InputDevicesChangedListener extends IInputDevicesChangedListener.Stub {
1037         @Override
onInputDevicesChanged(int[] deviceIdAndGeneration)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 
InputDeviceListenerDelegate(InputDeviceListener listener, Handler handler)1046         public InputDeviceListenerDelegate(InputDeviceListener listener, Handler handler) {
1047             super(handler != null ? handler.getLooper() : Looper.myLooper());
1048             mListener = listener;
1049         }
1050 
1051         @Override
handleMessage(Message msg)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          */
onTabletModeChanged(long whenNanos, boolean inTabletMode)1076         void onTabletModeChanged(long whenNanos, boolean inTabletMode);
1077     }
1078 
1079     private final class TabletModeChangedListener extends ITabletModeChangedListener.Stub {
1080         @Override
onTabletModeChanged(long whenNanos, boolean inTabletMode)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 
OnTabletModeChangedListenerDelegate( OnTabletModeChangedListener listener, Handler handler)1091         public OnTabletModeChangedListenerDelegate(
1092                 OnTabletModeChangedListener listener, Handler handler) {
1093             super(handler != null ? handler.getLooper() : Looper.myLooper());
1094             mListener = listener;
1095         }
1096 
sendTabletModeChanged(long whenNanos, boolean inTabletMode)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
handleMessage(Message msg)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 
InputDeviceVibrator(int deviceId)1122         public InputDeviceVibrator(int deviceId) {
1123             mDeviceId = deviceId;
1124             mToken = new Binder();
1125         }
1126 
1127         @Override
hasVibrator()1128         public boolean hasVibrator() {
1129             return true;
1130         }
1131 
1132         /**
1133          * @hide
1134          */
1135         @Override
vibrate(int uid, String opPkg, long milliseconds, AudioAttributes attributes)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
vibrate(int uid, String opPkg, long[] pattern, int repeat, AudioAttributes attributes)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
cancel()1157         public void cancel() {
1158             try {
1159                 mIm.cancelVibrate(mDeviceId, mToken);
1160             } catch (RemoteException ex) {
1161                 throw ex.rethrowFromSystemServer();
1162             }
1163         }
1164     }
1165 }
1166