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