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