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