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
17package android.hardware.input;
18
19import com.android.internal.util.ArrayUtils;
20
21import android.annotation.SdkConstant;
22import android.annotation.SdkConstant.SdkConstantType;
23import android.content.Context;
24import android.os.Binder;
25import android.os.Handler;
26import android.os.IBinder;
27import android.os.Looper;
28import android.os.Message;
29import android.os.RemoteException;
30import android.os.ServiceManager;
31import android.os.Vibrator;
32import android.provider.Settings;
33import android.provider.Settings.SettingNotFoundException;
34import android.util.Log;
35import android.util.SparseArray;
36import android.view.InputDevice;
37import android.view.InputEvent;
38
39import 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 */
50public 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
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     */
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     */
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     */
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     */
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     */
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     */
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
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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
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
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
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
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     */
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         */
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         */
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         */
769        void onInputDeviceChanged(int deviceId);
770    }
771
772    private final class InputDevicesChangedListener extends IInputDevicesChangedListener.Stub {
773        @Override
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
782        public InputDeviceListenerDelegate(InputDeviceListener listener, Handler handler) {
783            super(handler != null ? handler.getLooper() : Looper.myLooper());
784            mListener = listener;
785        }
786
787        @Override
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
807        public InputDeviceVibrator(int deviceId) {
808            mDeviceId = deviceId;
809            mToken = new Binder();
810        }
811
812        @Override
813        public boolean hasVibrator() {
814            return true;
815        }
816
817        @Override
818        public void vibrate(long milliseconds) {
819            vibrate(new long[] { 0, milliseconds}, -1);
820        }
821
822        @Override
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
838        public void vibrate(int owningUid, String owningPackage, long milliseconds) {
839            vibrate(milliseconds);
840        }
841
842        /**
843         * @hide
844         */
845        @Override
846        public void vibrate(int owningUid, String owningPackage, long[] pattern, int repeat) {
847            vibrate(pattern, repeat);
848        }
849
850        @Override
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