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