• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.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      * Gets information about all supported keyboard layouts.
524      * <p>
525      * The input manager consults the built-in keyboard layouts as well
526      * as all keyboard layouts advertised by applications using a
527      * {@link #ACTION_QUERY_KEYBOARD_LAYOUTS} broadcast receiver.
528      * </p>
529      *
530      * @return A list of all supported keyboard layouts.
531      *
532      * @hide
533      */
getKeyboardLayouts()534     public KeyboardLayout[] getKeyboardLayouts() {
535         try {
536             return mIm.getKeyboardLayouts();
537         } catch (RemoteException ex) {
538             throw ex.rethrowFromSystemServer();
539         }
540     }
541 
542     /**
543      * Gets information about all supported keyboard layouts appropriate
544      * for a specific input device.
545      * <p>
546      * The input manager consults the built-in keyboard layouts as well
547      * as all keyboard layouts advertised by applications using a
548      * {@link #ACTION_QUERY_KEYBOARD_LAYOUTS} broadcast receiver.
549      * </p>
550      *
551      * @return A list of all supported keyboard layouts for a specific
552      * input device.
553      *
554      * @hide
555      */
getKeyboardLayoutsForInputDevice(InputDeviceIdentifier identifier)556     public KeyboardLayout[] getKeyboardLayoutsForInputDevice(InputDeviceIdentifier identifier) {
557         try {
558             return mIm.getKeyboardLayoutsForInputDevice(identifier);
559         } catch (RemoteException ex) {
560             throw ex.rethrowFromSystemServer();
561         }
562     }
563 
564     /**
565      * Gets the keyboard layout with the specified descriptor.
566      *
567      * @param keyboardLayoutDescriptor The keyboard layout descriptor, as returned by
568      * {@link KeyboardLayout#getDescriptor()}.
569      * @return The keyboard layout, or null if it could not be loaded.
570      *
571      * @hide
572      */
getKeyboardLayout(String keyboardLayoutDescriptor)573     public KeyboardLayout getKeyboardLayout(String keyboardLayoutDescriptor) {
574         if (keyboardLayoutDescriptor == null) {
575             throw new IllegalArgumentException("keyboardLayoutDescriptor must not be null");
576         }
577 
578         try {
579             return mIm.getKeyboardLayout(keyboardLayoutDescriptor);
580         } catch (RemoteException ex) {
581             throw ex.rethrowFromSystemServer();
582         }
583     }
584 
585     /**
586      * Gets the current keyboard layout descriptor for the specified input
587      * device.
588      *
589      * @param identifier Identifier for the input device
590      * @return The keyboard layout descriptor, or null if no keyboard layout has
591      *         been set.
592      * @hide
593      */
getCurrentKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier)594     public String getCurrentKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier) {
595         try {
596             return mIm.getCurrentKeyboardLayoutForInputDevice(identifier);
597         } catch (RemoteException ex) {
598             throw ex.rethrowFromSystemServer();
599         }
600     }
601 
602     /**
603      * Sets the current keyboard layout descriptor for the specified input
604      * device.
605      * <p>
606      * This method may have the side-effect of causing the input device in
607      * question to be reconfigured.
608      * </p>
609      *
610      * @param identifier The identifier for the input device.
611      * @param keyboardLayoutDescriptor The keyboard layout descriptor to use,
612      *            must not be null.
613      * @hide
614      */
setCurrentKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier, String keyboardLayoutDescriptor)615     public void setCurrentKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier,
616             String keyboardLayoutDescriptor) {
617         if (identifier == null) {
618             throw new IllegalArgumentException("identifier must not be null");
619         }
620         if (keyboardLayoutDescriptor == null) {
621             throw new IllegalArgumentException("keyboardLayoutDescriptor must not be null");
622         }
623 
624         try {
625             mIm.setCurrentKeyboardLayoutForInputDevice(identifier,
626                     keyboardLayoutDescriptor);
627         } catch (RemoteException ex) {
628             throw ex.rethrowFromSystemServer();
629         }
630     }
631 
632     /**
633      * Gets all keyboard layout descriptors that are enabled for the specified
634      * input device.
635      *
636      * @param identifier The identifier for the input device.
637      * @return The keyboard layout descriptors.
638      * @hide
639      */
getEnabledKeyboardLayoutsForInputDevice(InputDeviceIdentifier identifier)640     public String[] getEnabledKeyboardLayoutsForInputDevice(InputDeviceIdentifier identifier) {
641         if (identifier == null) {
642             throw new IllegalArgumentException("inputDeviceDescriptor must not be null");
643         }
644 
645         try {
646             return mIm.getEnabledKeyboardLayoutsForInputDevice(identifier);
647         } catch (RemoteException ex) {
648             throw ex.rethrowFromSystemServer();
649         }
650     }
651 
652     /**
653      * Adds the keyboard layout descriptor for the specified input device.
654      * <p>
655      * This method may have the side-effect of causing the input device in
656      * question to be reconfigured.
657      * </p>
658      *
659      * @param identifier The identifier for the input device.
660      * @param keyboardLayoutDescriptor The descriptor of the keyboard layout to
661      *            add.
662      * @hide
663      */
addKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier, String keyboardLayoutDescriptor)664     public void addKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier,
665             String keyboardLayoutDescriptor) {
666         if (identifier == null) {
667             throw new IllegalArgumentException("inputDeviceDescriptor must not be null");
668         }
669         if (keyboardLayoutDescriptor == null) {
670             throw new IllegalArgumentException("keyboardLayoutDescriptor must not be null");
671         }
672 
673         try {
674             mIm.addKeyboardLayoutForInputDevice(identifier, keyboardLayoutDescriptor);
675         } catch (RemoteException ex) {
676             throw ex.rethrowFromSystemServer();
677         }
678     }
679 
680     /**
681      * Removes the keyboard layout descriptor for the specified input device.
682      * <p>
683      * This method may have the side-effect of causing the input device in
684      * question to be reconfigured.
685      * </p>
686      *
687      * @param identifier The identifier for the input device.
688      * @param keyboardLayoutDescriptor The descriptor of the keyboard layout to
689      *            remove.
690      * @hide
691      */
removeKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier, String keyboardLayoutDescriptor)692     public void removeKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier,
693             String keyboardLayoutDescriptor) {
694         if (identifier == null) {
695             throw new IllegalArgumentException("inputDeviceDescriptor must not be null");
696         }
697         if (keyboardLayoutDescriptor == null) {
698             throw new IllegalArgumentException("keyboardLayoutDescriptor must not be null");
699         }
700 
701         try {
702             mIm.removeKeyboardLayoutForInputDevice(identifier, keyboardLayoutDescriptor);
703         } catch (RemoteException ex) {
704             throw ex.rethrowFromSystemServer();
705         }
706     }
707 
708     /**
709      * Gets the TouchCalibration applied to the specified input device's coordinates.
710      *
711      * @param inputDeviceDescriptor The input device descriptor.
712      * @return The TouchCalibration currently assigned for use with the given
713      * input device. If none is set, an identity TouchCalibration is returned.
714      *
715      * @hide
716      */
getTouchCalibration(String inputDeviceDescriptor, int surfaceRotation)717     public TouchCalibration getTouchCalibration(String inputDeviceDescriptor, int surfaceRotation) {
718         try {
719             return mIm.getTouchCalibrationForInputDevice(inputDeviceDescriptor, surfaceRotation);
720         } catch (RemoteException ex) {
721             throw ex.rethrowFromSystemServer();
722         }
723     }
724 
725     /**
726      * Sets the TouchCalibration to apply to the specified input device's coordinates.
727      * <p>
728      * This method may have the side-effect of causing the input device in question
729      * to be reconfigured. Requires {@link android.Manifest.permissions.SET_INPUT_CALIBRATION}.
730      * </p>
731      *
732      * @param inputDeviceDescriptor The input device descriptor.
733      * @param calibration The calibration to be applied
734      *
735      * @hide
736      */
setTouchCalibration(String inputDeviceDescriptor, int surfaceRotation, TouchCalibration calibration)737     public void setTouchCalibration(String inputDeviceDescriptor, int surfaceRotation,
738             TouchCalibration calibration) {
739         try {
740             mIm.setTouchCalibrationForInputDevice(inputDeviceDescriptor, surfaceRotation, calibration);
741         } catch (RemoteException ex) {
742             throw ex.rethrowFromSystemServer();
743         }
744     }
745 
746     /**
747      * Gets the mouse pointer speed.
748      * <p>
749      * Only returns the permanent mouse pointer speed.  Ignores any temporary pointer
750      * speed set by {@link #tryPointerSpeed}.
751      * </p>
752      *
753      * @param context The application context.
754      * @return The pointer speed as a value between {@link #MIN_POINTER_SPEED} and
755      * {@link #MAX_POINTER_SPEED}, or the default value {@link #DEFAULT_POINTER_SPEED}.
756      *
757      * @hide
758      */
getPointerSpeed(Context context)759     public int getPointerSpeed(Context context) {
760         int speed = DEFAULT_POINTER_SPEED;
761         try {
762             speed = Settings.System.getInt(context.getContentResolver(),
763                     Settings.System.POINTER_SPEED);
764         } catch (SettingNotFoundException snfe) {
765         }
766         return speed;
767     }
768 
769     /**
770      * Sets the mouse pointer speed.
771      * <p>
772      * Requires {@link android.Manifest.permissions.WRITE_SETTINGS}.
773      * </p>
774      *
775      * @param context The application context.
776      * @param speed The pointer speed as a value between {@link #MIN_POINTER_SPEED} and
777      * {@link #MAX_POINTER_SPEED}, or the default value {@link #DEFAULT_POINTER_SPEED}.
778      *
779      * @hide
780      */
setPointerSpeed(Context context, int speed)781     public void setPointerSpeed(Context context, int speed) {
782         if (speed < MIN_POINTER_SPEED || speed > MAX_POINTER_SPEED) {
783             throw new IllegalArgumentException("speed out of range");
784         }
785 
786         Settings.System.putInt(context.getContentResolver(),
787                 Settings.System.POINTER_SPEED, speed);
788     }
789 
790     /**
791      * Changes the mouse pointer speed temporarily, but does not save the setting.
792      * <p>
793      * Requires {@link android.Manifest.permission.SET_POINTER_SPEED}.
794      * </p>
795      *
796      * @param speed The pointer speed as a value between {@link #MIN_POINTER_SPEED} and
797      * {@link #MAX_POINTER_SPEED}, or the default value {@link #DEFAULT_POINTER_SPEED}.
798      *
799      * @hide
800      */
tryPointerSpeed(int speed)801     public void tryPointerSpeed(int speed) {
802         if (speed < MIN_POINTER_SPEED || speed > MAX_POINTER_SPEED) {
803             throw new IllegalArgumentException("speed out of range");
804         }
805 
806         try {
807             mIm.tryPointerSpeed(speed);
808         } catch (RemoteException ex) {
809             throw ex.rethrowFromSystemServer();
810         }
811     }
812 
813     /**
814      * Queries the framework about whether any physical keys exist on the
815      * any keyboard attached to the device that are capable of producing the given
816      * array of key codes.
817      *
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
820      * are set to true if at least one attached keyboard supports the corresponding key code
821      * at the same index in the key codes array.
822      *
823      * @hide
824      */
deviceHasKeys(int[] keyCodes)825     public boolean[] deviceHasKeys(int[] keyCodes) {
826         return deviceHasKeys(-1, keyCodes);
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 id The id of the device to query.
835      * @param keyCodes The array of key codes to query.
836      * @return A new array of the same size as the key codes array whose elements are set to true
837      * if the given device could produce the corresponding key code at the same index in the key
838      * codes array.
839      *
840      * @hide
841      */
deviceHasKeys(int id, int[] keyCodes)842     public boolean[] deviceHasKeys(int id, int[] keyCodes) {
843         boolean[] ret = new boolean[keyCodes.length];
844         try {
845             mIm.hasKeys(id, InputDevice.SOURCE_ANY, keyCodes, ret);
846         } catch (RemoteException e) {
847             throw e.rethrowFromSystemServer();
848         }
849         return ret;
850     }
851 
852 
853     /**
854      * Injects an input event into the event system on behalf of an application.
855      * The synchronization mode determines whether the method blocks while waiting for
856      * input injection to proceed.
857      * <p>
858      * Requires {@link android.Manifest.permission.INJECT_EVENTS} to inject into
859      * windows that are owned by other applications.
860      * </p><p>
861      * Make sure you correctly set the event time and input source of the event
862      * before calling this method.
863      * </p>
864      *
865      * @param event The event to inject.
866      * @param mode The synchronization mode.  One of:
867      * {@link #INJECT_INPUT_EVENT_MODE_ASYNC},
868      * {@link #INJECT_INPUT_EVENT_MODE_WAIT_FOR_RESULT}, or
869      * {@link #INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH}.
870      * @return True if input event injection succeeded.
871      *
872      * @hide
873      */
874     @UnsupportedAppUsage
injectInputEvent(InputEvent event, int mode)875     public boolean injectInputEvent(InputEvent event, int mode) {
876         if (event == null) {
877             throw new IllegalArgumentException("event must not be null");
878         }
879         if (mode != INJECT_INPUT_EVENT_MODE_ASYNC
880                 && mode != INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH
881                 && mode != INJECT_INPUT_EVENT_MODE_WAIT_FOR_RESULT) {
882             throw new IllegalArgumentException("mode is invalid");
883         }
884 
885         try {
886             return mIm.injectInputEvent(event, mode);
887         } catch (RemoteException ex) {
888             throw ex.rethrowFromSystemServer();
889         }
890     }
891 
892     /**
893      * Changes the mouse pointer's icon shape into the specified id.
894      *
895      * @param iconId The id of the pointer graphic, as a value between
896      * {@link PointerIcon.TYPE_ARROW} and {@link PointerIcon.TYPE_GRABBING}.
897      *
898      * @hide
899      */
900     @UnsupportedAppUsage
setPointerIconType(int iconId)901     public void setPointerIconType(int iconId) {
902         try {
903             mIm.setPointerIconType(iconId);
904         } catch (RemoteException ex) {
905             throw ex.rethrowFromSystemServer();
906         }
907     }
908 
909     /** @hide */
setCustomPointerIcon(PointerIcon icon)910     public void setCustomPointerIcon(PointerIcon icon) {
911         try {
912             mIm.setCustomPointerIcon(icon);
913         } catch (RemoteException ex) {
914             throw ex.rethrowFromSystemServer();
915         }
916     }
917 
918     /**
919      * Request or release pointer capture.
920      * <p>
921      * When in capturing mode, the pointer icon disappears and all mouse events are dispatched to
922      * the window which has requested the capture. Relative position changes are available through
923      * {@link MotionEvent#getX} and {@link MotionEvent#getY}.
924      *
925      * @param enable true when requesting pointer capture, false when releasing.
926      *
927      * @hide
928      */
requestPointerCapture(IBinder windowToken, boolean enable)929     public void requestPointerCapture(IBinder windowToken, boolean enable) {
930         try {
931             mIm.requestPointerCapture(windowToken, enable);
932         } catch (RemoteException ex) {
933             throw ex.rethrowFromSystemServer();
934         }
935     }
936 
937     /**
938      * Monitor input on the specified display for gestures.
939      *
940      * @hide
941      */
monitorGestureInput(String name, int displayId)942     public InputMonitor monitorGestureInput(String name, int displayId) {
943         try {
944             return mIm.monitorGestureInput(name, displayId);
945         } catch (RemoteException ex) {
946             throw ex.rethrowFromSystemServer();
947         }
948     }
949 
populateInputDevicesLocked()950     private void populateInputDevicesLocked() {
951         if (mInputDevicesChangedListener == null) {
952             final InputDevicesChangedListener listener = new InputDevicesChangedListener();
953             try {
954                 mIm.registerInputDevicesChangedListener(listener);
955             } catch (RemoteException ex) {
956                 throw ex.rethrowFromSystemServer();
957             }
958             mInputDevicesChangedListener = listener;
959         }
960 
961         if (mInputDevices == null) {
962             final int[] ids;
963             try {
964                 ids = mIm.getInputDeviceIds();
965             } catch (RemoteException ex) {
966                 throw ex.rethrowFromSystemServer();
967             }
968 
969             mInputDevices = new SparseArray<InputDevice>();
970             for (int i = 0; i < ids.length; i++) {
971                 mInputDevices.put(ids[i], null);
972             }
973         }
974     }
975 
onInputDevicesChanged(int[] deviceIdAndGeneration)976     private void onInputDevicesChanged(int[] deviceIdAndGeneration) {
977         if (DEBUG) {
978             Log.d(TAG, "Received input devices changed.");
979         }
980 
981         synchronized (mInputDevicesLock) {
982             for (int i = mInputDevices.size(); --i > 0; ) {
983                 final int deviceId = mInputDevices.keyAt(i);
984                 if (!containsDeviceId(deviceIdAndGeneration, deviceId)) {
985                     if (DEBUG) {
986                         Log.d(TAG, "Device removed: " + deviceId);
987                     }
988                     mInputDevices.removeAt(i);
989                     sendMessageToInputDeviceListenersLocked(MSG_DEVICE_REMOVED, deviceId);
990                 }
991             }
992 
993             for (int i = 0; i < deviceIdAndGeneration.length; i += 2) {
994                 final int deviceId = deviceIdAndGeneration[i];
995                 int index = mInputDevices.indexOfKey(deviceId);
996                 if (index >= 0) {
997                     final InputDevice device = mInputDevices.valueAt(index);
998                     if (device != null) {
999                         final int generation = deviceIdAndGeneration[i + 1];
1000                         if (device.getGeneration() != generation) {
1001                             if (DEBUG) {
1002                                 Log.d(TAG, "Device changed: " + deviceId);
1003                             }
1004                             mInputDevices.setValueAt(index, null);
1005                             sendMessageToInputDeviceListenersLocked(MSG_DEVICE_CHANGED, deviceId);
1006                         }
1007                     }
1008                 } else {
1009                     if (DEBUG) {
1010                         Log.d(TAG, "Device added: " + deviceId);
1011                     }
1012                     mInputDevices.put(deviceId, null);
1013                     sendMessageToInputDeviceListenersLocked(MSG_DEVICE_ADDED, deviceId);
1014                 }
1015             }
1016         }
1017     }
1018 
sendMessageToInputDeviceListenersLocked(int what, int deviceId)1019     private void sendMessageToInputDeviceListenersLocked(int what, int deviceId) {
1020         final int numListeners = mInputDeviceListeners.size();
1021         for (int i = 0; i < numListeners; i++) {
1022             InputDeviceListenerDelegate listener = mInputDeviceListeners.get(i);
1023             listener.sendMessage(listener.obtainMessage(what, deviceId, 0));
1024         }
1025     }
1026 
containsDeviceId(int[] deviceIdAndGeneration, int deviceId)1027     private static boolean containsDeviceId(int[] deviceIdAndGeneration, int deviceId) {
1028         for (int i = 0; i < deviceIdAndGeneration.length; i += 2) {
1029             if (deviceIdAndGeneration[i] == deviceId) {
1030                 return true;
1031             }
1032         }
1033         return false;
1034     }
1035 
1036 
onTabletModeChanged(long whenNanos, boolean inTabletMode)1037     private void onTabletModeChanged(long whenNanos, boolean inTabletMode) {
1038         if (DEBUG) {
1039             Log.d(TAG, "Received tablet mode changed: "
1040                     + "whenNanos=" + whenNanos + ", inTabletMode=" + inTabletMode);
1041         }
1042         synchronized (mTabletModeLock) {
1043             final int N = mOnTabletModeChangedListeners.size();
1044             for (int i = 0; i < N; i++) {
1045                 OnTabletModeChangedListenerDelegate listener =
1046                         mOnTabletModeChangedListeners.get(i);
1047                 listener.sendTabletModeChanged(whenNanos, inTabletMode);
1048             }
1049         }
1050     }
1051 
1052     /**
1053      * Gets a vibrator service associated with an input device, assuming it has one.
1054      * @return The vibrator, never null.
1055      * @hide
1056      */
getInputDeviceVibrator(int deviceId)1057     public Vibrator getInputDeviceVibrator(int deviceId) {
1058         return new InputDeviceVibrator(deviceId);
1059     }
1060 
1061     /**
1062      * Listens for changes in input devices.
1063      */
1064     public interface InputDeviceListener {
1065         /**
1066          * Called whenever an input device has been added to the system.
1067          * Use {@link InputManager#getInputDevice} to get more information about the device.
1068          *
1069          * @param deviceId The id of the input device that was added.
1070          */
onInputDeviceAdded(int deviceId)1071         void onInputDeviceAdded(int deviceId);
1072 
1073         /**
1074          * Called whenever an input device has been removed from the system.
1075          *
1076          * @param deviceId The id of the input device that was removed.
1077          */
onInputDeviceRemoved(int deviceId)1078         void onInputDeviceRemoved(int deviceId);
1079 
1080         /**
1081          * Called whenever the properties of an input device have changed since they
1082          * were last queried.  Use {@link InputManager#getInputDevice} to get
1083          * a fresh {@link InputDevice} object with the new properties.
1084          *
1085          * @param deviceId The id of the input device that changed.
1086          */
onInputDeviceChanged(int deviceId)1087         void onInputDeviceChanged(int deviceId);
1088     }
1089 
1090     private final class InputDevicesChangedListener extends IInputDevicesChangedListener.Stub {
1091         @Override
onInputDevicesChanged(int[] deviceIdAndGeneration)1092         public void onInputDevicesChanged(int[] deviceIdAndGeneration) throws RemoteException {
1093             InputManager.this.onInputDevicesChanged(deviceIdAndGeneration);
1094         }
1095     }
1096 
1097     private static final class InputDeviceListenerDelegate extends Handler {
1098         public final InputDeviceListener mListener;
1099 
InputDeviceListenerDelegate(InputDeviceListener listener, Handler handler)1100         public InputDeviceListenerDelegate(InputDeviceListener listener, Handler handler) {
1101             super(handler != null ? handler.getLooper() : Looper.myLooper());
1102             mListener = listener;
1103         }
1104 
1105         @Override
handleMessage(Message msg)1106         public void handleMessage(Message msg) {
1107             switch (msg.what) {
1108                 case MSG_DEVICE_ADDED:
1109                     mListener.onInputDeviceAdded(msg.arg1);
1110                     break;
1111                 case MSG_DEVICE_REMOVED:
1112                     mListener.onInputDeviceRemoved(msg.arg1);
1113                     break;
1114                 case MSG_DEVICE_CHANGED:
1115                     mListener.onInputDeviceChanged(msg.arg1);
1116                     break;
1117             }
1118         }
1119     }
1120 
1121     /** @hide */
1122     public interface OnTabletModeChangedListener {
1123         /**
1124          * Called whenever the device goes into or comes out of tablet mode.
1125          *
1126          * @param whenNanos The time at which the device transitioned into or
1127          * out of tablet mode. This is given in nanoseconds in the
1128          * {@link SystemClock#uptimeMillis} time base.
1129          */
onTabletModeChanged(long whenNanos, boolean inTabletMode)1130         void onTabletModeChanged(long whenNanos, boolean inTabletMode);
1131     }
1132 
1133     private final class TabletModeChangedListener extends ITabletModeChangedListener.Stub {
1134         @Override
onTabletModeChanged(long whenNanos, boolean inTabletMode)1135         public void onTabletModeChanged(long whenNanos, boolean inTabletMode) {
1136             InputManager.this.onTabletModeChanged(whenNanos, inTabletMode);
1137         }
1138     }
1139 
1140     private static final class OnTabletModeChangedListenerDelegate extends Handler {
1141         private static final int MSG_TABLET_MODE_CHANGED = 0;
1142 
1143         public final OnTabletModeChangedListener mListener;
1144 
OnTabletModeChangedListenerDelegate( OnTabletModeChangedListener listener, Handler handler)1145         public OnTabletModeChangedListenerDelegate(
1146                 OnTabletModeChangedListener listener, Handler handler) {
1147             super(handler != null ? handler.getLooper() : Looper.myLooper());
1148             mListener = listener;
1149         }
1150 
sendTabletModeChanged(long whenNanos, boolean inTabletMode)1151         public void sendTabletModeChanged(long whenNanos, boolean inTabletMode) {
1152             SomeArgs args = SomeArgs.obtain();
1153             args.argi1 = (int) (whenNanos & 0xFFFFFFFF);
1154             args.argi2 = (int) (whenNanos >> 32);
1155             args.arg1 = (Boolean) inTabletMode;
1156             obtainMessage(MSG_TABLET_MODE_CHANGED, args).sendToTarget();
1157         }
1158 
1159         @Override
handleMessage(Message msg)1160         public void handleMessage(Message msg) {
1161             switch (msg.what) {
1162                 case MSG_TABLET_MODE_CHANGED:
1163                     SomeArgs args = (SomeArgs) msg.obj;
1164                     long whenNanos = (args.argi1 & 0xFFFFFFFFl) | ((long) args.argi2 << 32);
1165                     boolean inTabletMode = (boolean) args.arg1;
1166                     mListener.onTabletModeChanged(whenNanos, inTabletMode);
1167                     break;
1168             }
1169         }
1170     }
1171 
1172     private final class InputDeviceVibrator extends Vibrator {
1173         private final int mDeviceId;
1174         private final Binder mToken;
1175 
InputDeviceVibrator(int deviceId)1176         public InputDeviceVibrator(int deviceId) {
1177             mDeviceId = deviceId;
1178             mToken = new Binder();
1179         }
1180 
1181         @Override
hasVibrator()1182         public boolean hasVibrator() {
1183             return true;
1184         }
1185 
1186         @Override
hasAmplitudeControl()1187         public boolean hasAmplitudeControl() {
1188             return false;
1189         }
1190 
1191         /**
1192          * @hide
1193          */
1194         @Override
vibrate(int uid, String opPkg, VibrationEffect effect, String reason, AudioAttributes attributes)1195         public void vibrate(int uid, String opPkg, VibrationEffect effect,
1196                 String reason, AudioAttributes attributes) {
1197             long[] pattern;
1198             int repeat;
1199             if (effect instanceof VibrationEffect.OneShot) {
1200                 VibrationEffect.OneShot oneShot = (VibrationEffect.OneShot) effect;
1201                 pattern = new long[] { 0, oneShot.getDuration() };
1202                 repeat = -1;
1203             } else if (effect instanceof VibrationEffect.Waveform) {
1204                 VibrationEffect.Waveform waveform = (VibrationEffect.Waveform) effect;
1205                 pattern = waveform.getTimings();
1206                 repeat = waveform.getRepeatIndex();
1207             } else {
1208                 // TODO: Add support for prebaked effects
1209                 Log.w(TAG, "Pre-baked effects aren't supported on input devices");
1210                 return;
1211             }
1212 
1213             try {
1214                 mIm.vibrate(mDeviceId, pattern, repeat, mToken);
1215             } catch (RemoteException ex) {
1216                 throw ex.rethrowFromSystemServer();
1217             }
1218         }
1219 
1220         @Override
cancel()1221         public void cancel() {
1222             try {
1223                 mIm.cancelVibrate(mDeviceId, mToken);
1224             } catch (RemoteException ex) {
1225                 throw ex.rethrowFromSystemServer();
1226             }
1227         }
1228     }
1229 }
1230