1 /*
2  * Copyright (C) 2012 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.hardware.input;
18 
19 import com.android.internal.util.ArrayUtils;
20 
21 import android.annotation.SdkConstant;
22 import android.annotation.SdkConstant.SdkConstantType;
23 import android.content.Context;
24 import android.media.AudioAttributes;
25 import android.os.Binder;
26 import android.os.Handler;
27 import android.os.IBinder;
28 import android.os.Looper;
29 import android.os.Message;
30 import android.os.RemoteException;
31 import android.os.ServiceManager;
32 import android.os.Vibrator;
33 import android.provider.Settings;
34 import android.provider.Settings.SettingNotFoundException;
35 import android.util.Log;
36 import android.util.SparseArray;
37 import android.view.InputDevice;
38 import android.view.InputEvent;
39 
40 import java.util.ArrayList;
41 
42 /**
43  * Provides information about input devices and available key layouts.
44  * <p>
45  * Get an instance of this class by calling
46  * {@link android.content.Context#getSystemService(java.lang.String)
47  * Context.getSystemService()} with the argument
48  * {@link android.content.Context#INPUT_SERVICE}.
49  * </p>
50  */
51 public final class InputManager {
52     private static final String TAG = "InputManager";
53     private static final boolean DEBUG = false;
54 
55     private static final int MSG_DEVICE_ADDED = 1;
56     private static final int MSG_DEVICE_REMOVED = 2;
57     private static final int MSG_DEVICE_CHANGED = 3;
58 
59     private static InputManager sInstance;
60 
61     private final IInputManager mIm;
62 
63     // Guarded by mInputDevicesLock
64     private final Object mInputDevicesLock = new Object();
65     private SparseArray<InputDevice> mInputDevices;
66     private InputDevicesChangedListener mInputDevicesChangedListener;
67     private final ArrayList<InputDeviceListenerDelegate> mInputDeviceListeners =
68             new ArrayList<InputDeviceListenerDelegate>();
69 
70     /**
71      * Broadcast Action: Query available keyboard layouts.
72      * <p>
73      * The input manager service locates available keyboard layouts
74      * by querying broadcast receivers that are registered for this action.
75      * An application can offer additional keyboard layouts to the user
76      * by declaring a suitable broadcast receiver in its manifest.
77      * </p><p>
78      * Here is an example broadcast receiver declaration that an application
79      * might include in its AndroidManifest.xml to advertise keyboard layouts.
80      * The meta-data specifies a resource that contains a description of each keyboard
81      * layout that is provided by the application.
82      * <pre><code>
83      * &lt;receiver android:name=".InputDeviceReceiver"
84      *         android:label="@string/keyboard_layouts_label">
85      *     &lt;intent-filter>
86      *         &lt;action android:name="android.hardware.input.action.QUERY_KEYBOARD_LAYOUTS" />
87      *     &lt;/intent-filter>
88      *     &lt;meta-data android:name="android.hardware.input.metadata.KEYBOARD_LAYOUTS"
89      *             android:resource="@xml/keyboard_layouts" />
90      * &lt;/receiver>
91      * </code></pre>
92      * </p><p>
93      * In the above example, the <code>@xml/keyboard_layouts</code> resource refers to
94      * an XML resource whose root element is <code>&lt;keyboard-layouts></code> that
95      * contains zero or more <code>&lt;keyboard-layout></code> elements.
96      * Each <code>&lt;keyboard-layout></code> element specifies the name, label, and location
97      * of a key character map for a particular keyboard layout.  The label on the receiver
98      * is used to name the collection of keyboard layouts provided by this receiver in the
99      * keyboard layout settings.
100      * <pre></code>
101      * &lt;?xml version="1.0" encoding="utf-8"?>
102      * &lt;keyboard-layouts xmlns:android="http://schemas.android.com/apk/res/android">
103      *     &lt;keyboard-layout android:name="keyboard_layout_english_us"
104      *             android:label="@string/keyboard_layout_english_us_label"
105      *             android:keyboardLayout="@raw/keyboard_layout_english_us" />
106      * &lt;/keyboard-layouts>
107      * </p><p>
108      * The <code>android:name</code> attribute specifies an identifier by which
109      * the keyboard layout will be known in the package.
110      * The <code>android:label</code> attributes specifies a human-readable descriptive
111      * label to describe the keyboard layout in the user interface, such as "English (US)".
112      * The <code>android:keyboardLayout</code> attribute refers to a
113      * <a href="http://source.android.com/tech/input/key-character-map-files.html">
114      * key character map</a> resource that defines the keyboard layout.
115      * </p>
116      */
117     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
118     public static final String ACTION_QUERY_KEYBOARD_LAYOUTS =
119             "android.hardware.input.action.QUERY_KEYBOARD_LAYOUTS";
120 
121     /**
122      * Metadata Key: Keyboard layout metadata associated with
123      * {@link #ACTION_QUERY_KEYBOARD_LAYOUTS}.
124      * <p>
125      * Specifies the resource id of a XML resource that describes the keyboard
126      * layouts that are provided by the application.
127      * </p>
128      */
129     public static final String META_DATA_KEYBOARD_LAYOUTS =
130             "android.hardware.input.metadata.KEYBOARD_LAYOUTS";
131 
132     /**
133      * Pointer Speed: The minimum (slowest) pointer speed (-7).
134      * @hide
135      */
136     public static final int MIN_POINTER_SPEED = -7;
137 
138     /**
139      * Pointer Speed: The maximum (fastest) pointer speed (7).
140      * @hide
141      */
142     public static final int MAX_POINTER_SPEED = 7;
143 
144     /**
145      * Pointer Speed: The default pointer speed (0).
146      * @hide
147      */
148     public static final int DEFAULT_POINTER_SPEED = 0;
149 
150     /**
151      * Input Event Injection Synchronization Mode: None.
152      * Never blocks.  Injection is asynchronous and is assumed always to be successful.
153      * @hide
154      */
155     public static final int INJECT_INPUT_EVENT_MODE_ASYNC = 0; // see InputDispatcher.h
156 
157     /**
158      * Input Event Injection Synchronization Mode: Wait for result.
159      * Waits for previous events to be dispatched so that the input dispatcher can
160      * determine whether input event injection will be permitted based on the current
161      * input focus.  Does not wait for the input event to finish being handled
162      * by the application.
163      * @hide
164      */
165     public static final int INJECT_INPUT_EVENT_MODE_WAIT_FOR_RESULT = 1;  // see InputDispatcher.h
166 
167     /**
168      * Input Event Injection Synchronization Mode: Wait for finish.
169      * Waits for the event to be delivered to the application and handled.
170      * @hide
171      */
172     public static final int INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH = 2;  // see InputDispatcher.h
173 
InputManager(IInputManager im)174     private InputManager(IInputManager im) {
175         mIm = im;
176     }
177 
178     /**
179      * Gets an instance of the input manager.
180      *
181      * @return The input manager instance.
182      *
183      * @hide
184      */
getInstance()185     public static InputManager getInstance() {
186         synchronized (InputManager.class) {
187             if (sInstance == null) {
188                 IBinder b = ServiceManager.getService(Context.INPUT_SERVICE);
189                 sInstance = new InputManager(IInputManager.Stub.asInterface(b));
190             }
191             return sInstance;
192         }
193     }
194 
195     /**
196      * Gets information about the input device with the specified id.
197      * @param id The device id.
198      * @return The input device or null if not found.
199      */
getInputDevice(int id)200     public InputDevice getInputDevice(int id) {
201         synchronized (mInputDevicesLock) {
202             populateInputDevicesLocked();
203 
204             int index = mInputDevices.indexOfKey(id);
205             if (index < 0) {
206                 return null;
207             }
208 
209             InputDevice inputDevice = mInputDevices.valueAt(index);
210             if (inputDevice == null) {
211                 try {
212                     inputDevice = mIm.getInputDevice(id);
213                 } catch (RemoteException ex) {
214                     throw new RuntimeException("Could not get input device information.", ex);
215                 }
216                 if (inputDevice != null) {
217                     mInputDevices.setValueAt(index, inputDevice);
218                 }
219             }
220             return inputDevice;
221         }
222     }
223 
224     /**
225      * Gets information about the input device with the specified descriptor.
226      * @param descriptor The input device descriptor.
227      * @return The input device or null if not found.
228      * @hide
229      */
getInputDeviceByDescriptor(String descriptor)230     public InputDevice getInputDeviceByDescriptor(String descriptor) {
231         if (descriptor == null) {
232             throw new IllegalArgumentException("descriptor must not be null.");
233         }
234 
235         synchronized (mInputDevicesLock) {
236             populateInputDevicesLocked();
237 
238             int numDevices = mInputDevices.size();
239             for (int i = 0; i < numDevices; i++) {
240                 InputDevice inputDevice = mInputDevices.valueAt(i);
241                 if (inputDevice == null) {
242                     int id = mInputDevices.keyAt(i);
243                     try {
244                         inputDevice = mIm.getInputDevice(id);
245                     } catch (RemoteException ex) {
246                         // Ignore the problem for the purposes of this method.
247                     }
248                     if (inputDevice == null) {
249                         continue;
250                     }
251                     mInputDevices.setValueAt(i, inputDevice);
252                 }
253                 if (descriptor.equals(inputDevice.getDescriptor())) {
254                     return inputDevice;
255                 }
256             }
257             return null;
258         }
259     }
260 
261     /**
262      * Gets the ids of all input devices in the system.
263      * @return The input device ids.
264      */
getInputDeviceIds()265     public int[] getInputDeviceIds() {
266         synchronized (mInputDevicesLock) {
267             populateInputDevicesLocked();
268 
269             final int count = mInputDevices.size();
270             final int[] ids = new int[count];
271             for (int i = 0; i < count; i++) {
272                 ids[i] = mInputDevices.keyAt(i);
273             }
274             return ids;
275         }
276     }
277 
278     /**
279      * Registers an input device listener to receive notifications about when
280      * input devices are added, removed or changed.
281      *
282      * @param listener The listener to register.
283      * @param handler The handler on which the listener should be invoked, or null
284      * if the listener should be invoked on the calling thread's looper.
285      *
286      * @see #unregisterInputDeviceListener
287      */
registerInputDeviceListener(InputDeviceListener listener, Handler handler)288     public void registerInputDeviceListener(InputDeviceListener listener, Handler handler) {
289         if (listener == null) {
290             throw new IllegalArgumentException("listener must not be null");
291         }
292 
293         synchronized (mInputDevicesLock) {
294             int index = findInputDeviceListenerLocked(listener);
295             if (index < 0) {
296                 mInputDeviceListeners.add(new InputDeviceListenerDelegate(listener, handler));
297             }
298         }
299     }
300 
301     /**
302      * Unregisters an input device listener.
303      *
304      * @param listener The listener to unregister.
305      *
306      * @see #registerInputDeviceListener
307      */
unregisterInputDeviceListener(InputDeviceListener listener)308     public void unregisterInputDeviceListener(InputDeviceListener listener) {
309         if (listener == null) {
310             throw new IllegalArgumentException("listener must not be null");
311         }
312 
313         synchronized (mInputDevicesLock) {
314             int index = findInputDeviceListenerLocked(listener);
315             if (index >= 0) {
316                 InputDeviceListenerDelegate d = mInputDeviceListeners.get(index);
317                 d.removeCallbacksAndMessages(null);
318                 mInputDeviceListeners.remove(index);
319             }
320         }
321     }
322 
findInputDeviceListenerLocked(InputDeviceListener listener)323     private int findInputDeviceListenerLocked(InputDeviceListener listener) {
324         final int numListeners = mInputDeviceListeners.size();
325         for (int i = 0; i < numListeners; i++) {
326             if (mInputDeviceListeners.get(i).mListener == listener) {
327                 return i;
328             }
329         }
330         return -1;
331     }
332 
333     /**
334      * Gets information about all supported keyboard layouts.
335      * <p>
336      * The input manager consults the built-in keyboard layouts as well
337      * as all keyboard layouts advertised by applications using a
338      * {@link #ACTION_QUERY_KEYBOARD_LAYOUTS} broadcast receiver.
339      * </p>
340      *
341      * @return A list of all supported keyboard layouts.
342      *
343      * @hide
344      */
getKeyboardLayouts()345     public KeyboardLayout[] getKeyboardLayouts() {
346         try {
347             return mIm.getKeyboardLayouts();
348         } catch (RemoteException ex) {
349             Log.w(TAG, "Could not get list of keyboard layout informations.", ex);
350             return new KeyboardLayout[0];
351         }
352     }
353 
354     /**
355      * Gets the keyboard layout with the specified descriptor.
356      *
357      * @param keyboardLayoutDescriptor The keyboard layout descriptor, as returned by
358      * {@link KeyboardLayout#getDescriptor()}.
359      * @return The keyboard layout, or null if it could not be loaded.
360      *
361      * @hide
362      */
getKeyboardLayout(String keyboardLayoutDescriptor)363     public KeyboardLayout getKeyboardLayout(String keyboardLayoutDescriptor) {
364         if (keyboardLayoutDescriptor == null) {
365             throw new IllegalArgumentException("keyboardLayoutDescriptor must not be null");
366         }
367 
368         try {
369             return mIm.getKeyboardLayout(keyboardLayoutDescriptor);
370         } catch (RemoteException ex) {
371             Log.w(TAG, "Could not get keyboard layout information.", ex);
372             return null;
373         }
374     }
375 
376     /**
377      * Gets the current keyboard layout descriptor for the specified input
378      * device.
379      *
380      * @param identifier Identifier for the input device
381      * @return The keyboard layout descriptor, or null if no keyboard layout has
382      *         been set.
383      * @hide
384      */
getCurrentKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier)385     public String getCurrentKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier) {
386         try {
387             return mIm.getCurrentKeyboardLayoutForInputDevice(identifier);
388         } catch (RemoteException ex) {
389             Log.w(TAG, "Could not get current keyboard layout for input device.", ex);
390             return null;
391         }
392     }
393 
394     /**
395      * Sets the current keyboard layout descriptor for the specified input
396      * device.
397      * <p>
398      * This method may have the side-effect of causing the input device in
399      * question to be reconfigured.
400      * </p>
401      *
402      * @param identifier The identifier for the input device.
403      * @param keyboardLayoutDescriptor The keyboard layout descriptor to use,
404      *            must not be null.
405      * @hide
406      */
setCurrentKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier, String keyboardLayoutDescriptor)407     public void setCurrentKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier,
408             String keyboardLayoutDescriptor) {
409         if (identifier == null) {
410             throw new IllegalArgumentException("identifier must not be null");
411         }
412         if (keyboardLayoutDescriptor == null) {
413             throw new IllegalArgumentException("keyboardLayoutDescriptor must not be null");
414         }
415 
416         try {
417             mIm.setCurrentKeyboardLayoutForInputDevice(identifier,
418                     keyboardLayoutDescriptor);
419         } catch (RemoteException ex) {
420             Log.w(TAG, "Could not set current keyboard layout for input device.", ex);
421         }
422     }
423 
424     /**
425      * Gets all keyboard layout descriptors that are enabled for the specified
426      * input device.
427      *
428      * @param identifier The identifier for the input device.
429      * @return The keyboard layout descriptors.
430      * @hide
431      */
getKeyboardLayoutsForInputDevice(InputDeviceIdentifier identifier)432     public String[] getKeyboardLayoutsForInputDevice(InputDeviceIdentifier identifier) {
433         if (identifier == null) {
434             throw new IllegalArgumentException("inputDeviceDescriptor must not be null");
435         }
436 
437         try {
438             return mIm.getKeyboardLayoutsForInputDevice(identifier);
439         } catch (RemoteException ex) {
440             Log.w(TAG, "Could not get keyboard layouts for input device.", ex);
441             return ArrayUtils.emptyArray(String.class);
442         }
443     }
444 
445     /**
446      * Adds the keyboard layout descriptor for the specified input device.
447      * <p>
448      * This method may have the side-effect of causing the input device in
449      * question to be reconfigured.
450      * </p>
451      *
452      * @param identifier The identifier for the input device.
453      * @param keyboardLayoutDescriptor The descriptor of the keyboard layout to
454      *            add.
455      * @hide
456      */
addKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier, String keyboardLayoutDescriptor)457     public void addKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier,
458             String keyboardLayoutDescriptor) {
459         if (identifier == null) {
460             throw new IllegalArgumentException("inputDeviceDescriptor must not be null");
461         }
462         if (keyboardLayoutDescriptor == null) {
463             throw new IllegalArgumentException("keyboardLayoutDescriptor must not be null");
464         }
465 
466         try {
467             mIm.addKeyboardLayoutForInputDevice(identifier, keyboardLayoutDescriptor);
468         } catch (RemoteException ex) {
469             Log.w(TAG, "Could not add keyboard layout for input device.", ex);
470         }
471     }
472 
473     /**
474      * Removes the keyboard layout descriptor for the specified input device.
475      * <p>
476      * This method may have the side-effect of causing the input device in
477      * question to be reconfigured.
478      * </p>
479      *
480      * @param identifier The identifier for the input device.
481      * @param keyboardLayoutDescriptor The descriptor of the keyboard layout to
482      *            remove.
483      * @hide
484      */
removeKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier, String keyboardLayoutDescriptor)485     public void removeKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier,
486             String keyboardLayoutDescriptor) {
487         if (identifier == null) {
488             throw new IllegalArgumentException("inputDeviceDescriptor must not be null");
489         }
490         if (keyboardLayoutDescriptor == null) {
491             throw new IllegalArgumentException("keyboardLayoutDescriptor must not be null");
492         }
493 
494         try {
495             mIm.removeKeyboardLayoutForInputDevice(identifier, keyboardLayoutDescriptor);
496         } catch (RemoteException ex) {
497             Log.w(TAG, "Could not remove keyboard layout for input device.", ex);
498         }
499     }
500 
501     /**
502      * Gets the TouchCalibration applied to the specified input device's coordinates.
503      *
504      * @param inputDeviceDescriptor The input device descriptor.
505      * @return The TouchCalibration currently assigned for use with the given
506      * input device. If none is set, an identity TouchCalibration is returned.
507      *
508      * @hide
509      */
getTouchCalibration(String inputDeviceDescriptor, int surfaceRotation)510     public TouchCalibration getTouchCalibration(String inputDeviceDescriptor, int surfaceRotation) {
511         try {
512             return mIm.getTouchCalibrationForInputDevice(inputDeviceDescriptor, surfaceRotation);
513         } catch (RemoteException ex) {
514             Log.w(TAG, "Could not get calibration matrix for input device.", ex);
515             return TouchCalibration.IDENTITY;
516         }
517     }
518 
519     /**
520      * Sets the TouchCalibration to apply to the specified input device's coordinates.
521      * <p>
522      * This method may have the side-effect of causing the input device in question
523      * to be reconfigured. Requires {@link android.Manifest.permissions.SET_INPUT_CALIBRATION}.
524      * </p>
525      *
526      * @param inputDeviceDescriptor The input device descriptor.
527      * @param calibration The calibration to be applied
528      *
529      * @hide
530      */
setTouchCalibration(String inputDeviceDescriptor, int surfaceRotation, TouchCalibration calibration)531     public void setTouchCalibration(String inputDeviceDescriptor, int surfaceRotation,
532             TouchCalibration calibration) {
533         try {
534             mIm.setTouchCalibrationForInputDevice(inputDeviceDescriptor, surfaceRotation, calibration);
535         } catch (RemoteException ex) {
536             Log.w(TAG, "Could not set calibration matrix for input device.", ex);
537         }
538     }
539 
540     /**
541      * Gets the mouse pointer speed.
542      * <p>
543      * Only returns the permanent mouse pointer speed.  Ignores any temporary pointer
544      * speed set by {@link #tryPointerSpeed}.
545      * </p>
546      *
547      * @param context The application context.
548      * @return The pointer speed as a value between {@link #MIN_POINTER_SPEED} and
549      * {@link #MAX_POINTER_SPEED}, or the default value {@link #DEFAULT_POINTER_SPEED}.
550      *
551      * @hide
552      */
getPointerSpeed(Context context)553     public int getPointerSpeed(Context context) {
554         int speed = DEFAULT_POINTER_SPEED;
555         try {
556             speed = Settings.System.getInt(context.getContentResolver(),
557                     Settings.System.POINTER_SPEED);
558         } catch (SettingNotFoundException snfe) {
559         }
560         return speed;
561     }
562 
563     /**
564      * Sets the mouse pointer speed.
565      * <p>
566      * Requires {@link android.Manifest.permissions.WRITE_SETTINGS}.
567      * </p>
568      *
569      * @param context The application context.
570      * @param speed The pointer speed as a value between {@link #MIN_POINTER_SPEED} and
571      * {@link #MAX_POINTER_SPEED}, or the default value {@link #DEFAULT_POINTER_SPEED}.
572      *
573      * @hide
574      */
setPointerSpeed(Context context, int speed)575     public void setPointerSpeed(Context context, int speed) {
576         if (speed < MIN_POINTER_SPEED || speed > MAX_POINTER_SPEED) {
577             throw new IllegalArgumentException("speed out of range");
578         }
579 
580         Settings.System.putInt(context.getContentResolver(),
581                 Settings.System.POINTER_SPEED, speed);
582     }
583 
584     /**
585      * Changes the mouse pointer speed temporarily, but does not save the setting.
586      * <p>
587      * Requires {@link android.Manifest.permission.SET_POINTER_SPEED}.
588      * </p>
589      *
590      * @param speed The pointer speed as a value between {@link #MIN_POINTER_SPEED} and
591      * {@link #MAX_POINTER_SPEED}, or the default value {@link #DEFAULT_POINTER_SPEED}.
592      *
593      * @hide
594      */
tryPointerSpeed(int speed)595     public void tryPointerSpeed(int speed) {
596         if (speed < MIN_POINTER_SPEED || speed > MAX_POINTER_SPEED) {
597             throw new IllegalArgumentException("speed out of range");
598         }
599 
600         try {
601             mIm.tryPointerSpeed(speed);
602         } catch (RemoteException ex) {
603             Log.w(TAG, "Could not set temporary pointer speed.", ex);
604         }
605     }
606 
607     /**
608      * Queries the framework about whether any physical keys exist on the
609      * any keyboard attached to the device that are capable of producing the given
610      * array of key codes.
611      *
612      * @param keyCodes The array of key codes to query.
613      * @return A new array of the same size as the key codes array whose elements
614      * are set to true if at least one attached keyboard supports the corresponding key code
615      * at the same index in the key codes array.
616      *
617      * @hide
618      */
deviceHasKeys(int[] keyCodes)619     public boolean[] deviceHasKeys(int[] keyCodes) {
620         return deviceHasKeys(-1, keyCodes);
621     }
622 
623     /**
624      * Queries the framework about whether any physical keys exist on the
625      * any keyboard attached to the device that are capable of producing the given
626      * array of key codes.
627      *
628      * @param id The id of the device to query.
629      * @param keyCodes The array of key codes to query.
630      * @return A new array of the same size as the key codes array whose elements are set to true
631      * if the given device could produce the corresponding key code at the same index in the key
632      * codes array.
633      *
634      * @hide
635      */
deviceHasKeys(int id, int[] keyCodes)636     public boolean[] deviceHasKeys(int id, int[] keyCodes) {
637         boolean[] ret = new boolean[keyCodes.length];
638         try {
639             mIm.hasKeys(id, InputDevice.SOURCE_ANY, keyCodes, ret);
640         } catch (RemoteException e) {
641             // no fallback; just return the empty array
642         }
643         return ret;
644     }
645 
646 
647     /**
648      * Injects an input event into the event system on behalf of an application.
649      * The synchronization mode determines whether the method blocks while waiting for
650      * input injection to proceed.
651      * <p>
652      * Requires {@link android.Manifest.permission.INJECT_EVENTS} to inject into
653      * windows that are owned by other applications.
654      * </p><p>
655      * Make sure you correctly set the event time and input source of the event
656      * before calling this method.
657      * </p>
658      *
659      * @param event The event to inject.
660      * @param mode The synchronization mode.  One of:
661      * {@link #INJECT_INPUT_EVENT_MODE_ASYNC},
662      * {@link #INJECT_INPUT_EVENT_MODE_WAIT_FOR_RESULT}, or
663      * {@link #INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH}.
664      * @return True if input event injection succeeded.
665      *
666      * @hide
667      */
injectInputEvent(InputEvent event, int mode)668     public boolean injectInputEvent(InputEvent event, int mode) {
669         if (event == null) {
670             throw new IllegalArgumentException("event must not be null");
671         }
672         if (mode != INJECT_INPUT_EVENT_MODE_ASYNC
673                 && mode != INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH
674                 && mode != INJECT_INPUT_EVENT_MODE_WAIT_FOR_RESULT) {
675             throw new IllegalArgumentException("mode is invalid");
676         }
677 
678         try {
679             return mIm.injectInputEvent(event, mode);
680         } catch (RemoteException ex) {
681             return false;
682         }
683     }
684 
populateInputDevicesLocked()685     private void populateInputDevicesLocked() {
686         if (mInputDevicesChangedListener == null) {
687             final InputDevicesChangedListener listener = new InputDevicesChangedListener();
688             try {
689                 mIm.registerInputDevicesChangedListener(listener);
690             } catch (RemoteException ex) {
691                 throw new RuntimeException(
692                         "Could not get register input device changed listener", ex);
693             }
694             mInputDevicesChangedListener = listener;
695         }
696 
697         if (mInputDevices == null) {
698             final int[] ids;
699             try {
700                 ids = mIm.getInputDeviceIds();
701             } catch (RemoteException ex) {
702                 throw new RuntimeException("Could not get input device ids.", ex);
703             }
704 
705             mInputDevices = new SparseArray<InputDevice>();
706             for (int i = 0; i < ids.length; i++) {
707                 mInputDevices.put(ids[i], null);
708             }
709         }
710     }
711 
onInputDevicesChanged(int[] deviceIdAndGeneration)712     private void onInputDevicesChanged(int[] deviceIdAndGeneration) {
713         if (DEBUG) {
714             Log.d(TAG, "Received input devices changed.");
715         }
716 
717         synchronized (mInputDevicesLock) {
718             for (int i = mInputDevices.size(); --i > 0; ) {
719                 final int deviceId = mInputDevices.keyAt(i);
720                 if (!containsDeviceId(deviceIdAndGeneration, deviceId)) {
721                     if (DEBUG) {
722                         Log.d(TAG, "Device removed: " + deviceId);
723                     }
724                     mInputDevices.removeAt(i);
725                     sendMessageToInputDeviceListenersLocked(MSG_DEVICE_REMOVED, deviceId);
726                 }
727             }
728 
729             for (int i = 0; i < deviceIdAndGeneration.length; i += 2) {
730                 final int deviceId = deviceIdAndGeneration[i];
731                 int index = mInputDevices.indexOfKey(deviceId);
732                 if (index >= 0) {
733                     final InputDevice device = mInputDevices.valueAt(index);
734                     if (device != null) {
735                         final int generation = deviceIdAndGeneration[i + 1];
736                         if (device.getGeneration() != generation) {
737                             if (DEBUG) {
738                                 Log.d(TAG, "Device changed: " + deviceId);
739                             }
740                             mInputDevices.setValueAt(index, null);
741                             sendMessageToInputDeviceListenersLocked(MSG_DEVICE_CHANGED, deviceId);
742                         }
743                     }
744                 } else {
745                     if (DEBUG) {
746                         Log.d(TAG, "Device added: " + deviceId);
747                     }
748                     mInputDevices.put(deviceId, null);
749                     sendMessageToInputDeviceListenersLocked(MSG_DEVICE_ADDED, deviceId);
750                 }
751             }
752         }
753     }
754 
sendMessageToInputDeviceListenersLocked(int what, int deviceId)755     private void sendMessageToInputDeviceListenersLocked(int what, int deviceId) {
756         final int numListeners = mInputDeviceListeners.size();
757         for (int i = 0; i < numListeners; i++) {
758             InputDeviceListenerDelegate listener = mInputDeviceListeners.get(i);
759             listener.sendMessage(listener.obtainMessage(what, deviceId, 0));
760         }
761     }
762 
containsDeviceId(int[] deviceIdAndGeneration, int deviceId)763     private static boolean containsDeviceId(int[] deviceIdAndGeneration, int deviceId) {
764         for (int i = 0; i < deviceIdAndGeneration.length; i += 2) {
765             if (deviceIdAndGeneration[i] == deviceId) {
766                 return true;
767             }
768         }
769         return false;
770     }
771 
772     /**
773      * Gets a vibrator service associated with an input device, assuming it has one.
774      * @return The vibrator, never null.
775      * @hide
776      */
getInputDeviceVibrator(int deviceId)777     public Vibrator getInputDeviceVibrator(int deviceId) {
778         return new InputDeviceVibrator(deviceId);
779     }
780 
781     /**
782      * Listens for changes in input devices.
783      */
784     public interface InputDeviceListener {
785         /**
786          * Called whenever an input device has been added to the system.
787          * Use {@link InputManager#getInputDevice} to get more information about the device.
788          *
789          * @param deviceId The id of the input device that was added.
790          */
onInputDeviceAdded(int deviceId)791         void onInputDeviceAdded(int deviceId);
792 
793         /**
794          * Called whenever an input device has been removed from the system.
795          *
796          * @param deviceId The id of the input device that was removed.
797          */
onInputDeviceRemoved(int deviceId)798         void onInputDeviceRemoved(int deviceId);
799 
800         /**
801          * Called whenever the properties of an input device have changed since they
802          * were last queried.  Use {@link InputManager#getInputDevice} to get
803          * a fresh {@link InputDevice} object with the new properties.
804          *
805          * @param deviceId The id of the input device that changed.
806          */
onInputDeviceChanged(int deviceId)807         void onInputDeviceChanged(int deviceId);
808     }
809 
810     private final class InputDevicesChangedListener extends IInputDevicesChangedListener.Stub {
811         @Override
onInputDevicesChanged(int[] deviceIdAndGeneration)812         public void onInputDevicesChanged(int[] deviceIdAndGeneration) throws RemoteException {
813             InputManager.this.onInputDevicesChanged(deviceIdAndGeneration);
814         }
815     }
816 
817     private static final class InputDeviceListenerDelegate extends Handler {
818         public final InputDeviceListener mListener;
819 
InputDeviceListenerDelegate(InputDeviceListener listener, Handler handler)820         public InputDeviceListenerDelegate(InputDeviceListener listener, Handler handler) {
821             super(handler != null ? handler.getLooper() : Looper.myLooper());
822             mListener = listener;
823         }
824 
825         @Override
handleMessage(Message msg)826         public void handleMessage(Message msg) {
827             switch (msg.what) {
828                 case MSG_DEVICE_ADDED:
829                     mListener.onInputDeviceAdded(msg.arg1);
830                     break;
831                 case MSG_DEVICE_REMOVED:
832                     mListener.onInputDeviceRemoved(msg.arg1);
833                     break;
834                 case MSG_DEVICE_CHANGED:
835                     mListener.onInputDeviceChanged(msg.arg1);
836                     break;
837             }
838         }
839     }
840 
841     private final class InputDeviceVibrator extends Vibrator {
842         private final int mDeviceId;
843         private final Binder mToken;
844 
InputDeviceVibrator(int deviceId)845         public InputDeviceVibrator(int deviceId) {
846             mDeviceId = deviceId;
847             mToken = new Binder();
848         }
849 
850         @Override
hasVibrator()851         public boolean hasVibrator() {
852             return true;
853         }
854 
855         /**
856          * @hide
857          */
858         @Override
vibrate(int uid, String opPkg, long milliseconds, AudioAttributes attributes)859         public void vibrate(int uid, String opPkg, long milliseconds, AudioAttributes attributes) {
860             vibrate(new long[] { 0, milliseconds}, -1);
861         }
862 
863         /**
864          * @hide
865          */
866         @Override
vibrate(int uid, String opPkg, long[] pattern, int repeat, AudioAttributes attributes)867         public void vibrate(int uid, String opPkg, long[] pattern, int repeat,
868                 AudioAttributes attributes) {
869             if (repeat >= pattern.length) {
870                 throw new ArrayIndexOutOfBoundsException();
871             }
872             try {
873                 mIm.vibrate(mDeviceId, pattern, repeat, mToken);
874             } catch (RemoteException ex) {
875                 Log.w(TAG, "Failed to vibrate.", ex);
876             }
877         }
878 
879         @Override
cancel()880         public void cancel() {
881             try {
882                 mIm.cancelVibrate(mDeviceId, mToken);
883             } catch (RemoteException ex) {
884                 Log.w(TAG, "Failed to cancel vibration.", ex);
885             }
886         }
887     }
888 }
889