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