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