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