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