blob: fbac58c039c6c94e8751b788731d454783c58277 [file] [log] [blame]
Jeff Brown9df6e7a2012-04-05 11:49:26 -07001/*
2 * Copyright (C) 2012 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.hardware.input;
18
Michael Wrightd5f7ed92016-01-19 11:23:51 -080019import com.android.internal.inputmethod.InputMethodSubtypeHandle;
Michael Wright39e5e942015-08-19 22:52:47 +010020import com.android.internal.os.SomeArgs;
Jeff Browncf39bdf2012-05-18 14:41:19 -070021import com.android.internal.util.ArrayUtils;
22
Michael Wright9209c9c2015-09-03 17:57:01 +010023import android.annotation.IntDef;
Jeff Brown9df6e7a2012-04-05 11:49:26 -070024import android.annotation.SdkConstant;
25import android.annotation.SdkConstant.SdkConstantType;
Jeff Brown9df6e7a2012-04-05 11:49:26 -070026import android.content.Context;
John Spurlock7b414672014-07-18 13:02:39 -040027import android.media.AudioAttributes;
Jeff Browna47425a2012-04-13 04:09:27 -070028import android.os.Binder;
Jeff Brownaf9e8d32012-04-12 17:32:48 -070029import android.os.Handler;
Jeff Brownac143512012-04-05 18:57:33 -070030import android.os.IBinder;
Jeff Brownaf9e8d32012-04-12 17:32:48 -070031import android.os.Looper;
32import android.os.Message;
Jeff Brownac143512012-04-05 18:57:33 -070033import android.os.RemoteException;
34import android.os.ServiceManager;
Michael Wright39e5e942015-08-19 22:52:47 +010035import android.os.SystemClock;
Jeff Browna47425a2012-04-13 04:09:27 -070036import android.os.Vibrator;
Jeff Brownac143512012-04-05 18:57:33 -070037import android.provider.Settings;
38import android.provider.Settings.SettingNotFoundException;
Jeff Brown9df6e7a2012-04-05 11:49:26 -070039import android.util.Log;
Jeff Brown9f25b7f2012-04-10 14:30:49 -070040import android.util.SparseArray;
Jeff Brownac143512012-04-05 18:57:33 -070041import android.view.InputDevice;
42import android.view.InputEvent;
Michael Wrightd5f7ed92016-01-19 11:23:51 -080043import android.view.PointerIcon;
44import android.view.inputmethod.InputMethodInfo;
45import android.view.inputmethod.InputMethodSubtype;
Jeff Brown9df6e7a2012-04-05 11:49:26 -070046
Michael Wright9209c9c2015-09-03 17:57:01 +010047import java.lang.annotation.Retention;
48import java.lang.annotation.RetentionPolicy;
Jeff Brownaf9e8d32012-04-12 17:32:48 -070049import java.util.ArrayList;
Michael Wright39e5e942015-08-19 22:52:47 +010050import java.util.List;
Jeff Brownaf9e8d32012-04-12 17:32:48 -070051
Jeff Brown9df6e7a2012-04-05 11:49:26 -070052/**
53 * Provides information about input devices and available key layouts.
54 * <p>
55 * Get an instance of this class by calling
56 * {@link android.content.Context#getSystemService(java.lang.String)
57 * Context.getSystemService()} with the argument
58 * {@link android.content.Context#INPUT_SERVICE}.
59 * </p>
60 */
61public final class InputManager {
62 private static final String TAG = "InputManager";
Jeff Brownaf9e8d32012-04-12 17:32:48 -070063 private static final boolean DEBUG = false;
64
65 private static final int MSG_DEVICE_ADDED = 1;
66 private static final int MSG_DEVICE_REMOVED = 2;
67 private static final int MSG_DEVICE_CHANGED = 3;
Jeff Brown9df6e7a2012-04-05 11:49:26 -070068
Jeff Brown9f25b7f2012-04-10 14:30:49 -070069 private static InputManager sInstance;
Jeff Brownac143512012-04-05 18:57:33 -070070
Jeff Brown9f25b7f2012-04-10 14:30:49 -070071 private final IInputManager mIm;
Jeff Brownaf9e8d32012-04-12 17:32:48 -070072
73 // Guarded by mInputDevicesLock
74 private final Object mInputDevicesLock = new Object();
75 private SparseArray<InputDevice> mInputDevices;
76 private InputDevicesChangedListener mInputDevicesChangedListener;
77 private final ArrayList<InputDeviceListenerDelegate> mInputDeviceListeners =
78 new ArrayList<InputDeviceListenerDelegate>();
Jeff Brown9df6e7a2012-04-05 11:49:26 -070079
Michael Wright39e5e942015-08-19 22:52:47 +010080 // Guarded by mTabletModeLock
81 private final Object mTabletModeLock = new Object();
82 private TabletModeChangedListener mTabletModeChangedListener;
83 private List<OnTabletModeChangedListenerDelegate> mOnTabletModeChangedListeners;
84
Jeff Brown9df6e7a2012-04-05 11:49:26 -070085 /**
86 * Broadcast Action: Query available keyboard layouts.
87 * <p>
88 * The input manager service locates available keyboard layouts
89 * by querying broadcast receivers that are registered for this action.
90 * An application can offer additional keyboard layouts to the user
91 * by declaring a suitable broadcast receiver in its manifest.
92 * </p><p>
93 * Here is an example broadcast receiver declaration that an application
94 * might include in its AndroidManifest.xml to advertise keyboard layouts.
95 * The meta-data specifies a resource that contains a description of each keyboard
96 * layout that is provided by the application.
97 * <pre><code>
Jeff Brownd9fec5d2012-05-17 16:01:54 -070098 * &lt;receiver android:name=".InputDeviceReceiver"
99 * android:label="@string/keyboard_layouts_label">
Jeff Brown9df6e7a2012-04-05 11:49:26 -0700100 * &lt;intent-filter>
101 * &lt;action android:name="android.hardware.input.action.QUERY_KEYBOARD_LAYOUTS" />
102 * &lt;/intent-filter>
103 * &lt;meta-data android:name="android.hardware.input.metadata.KEYBOARD_LAYOUTS"
104 * android:resource="@xml/keyboard_layouts" />
105 * &lt;/receiver>
106 * </code></pre>
107 * </p><p>
108 * In the above example, the <code>@xml/keyboard_layouts</code> resource refers to
109 * an XML resource whose root element is <code>&lt;keyboard-layouts></code> that
110 * contains zero or more <code>&lt;keyboard-layout></code> elements.
111 * Each <code>&lt;keyboard-layout></code> element specifies the name, label, and location
Jeff Brownd9fec5d2012-05-17 16:01:54 -0700112 * of a key character map for a particular keyboard layout. The label on the receiver
113 * is used to name the collection of keyboard layouts provided by this receiver in the
114 * keyboard layout settings.
Michael Wright112449c2015-10-22 17:23:12 +0100115 * <pre><code>
Jeff Brown9df6e7a2012-04-05 11:49:26 -0700116 * &lt;?xml version="1.0" encoding="utf-8"?>
117 * &lt;keyboard-layouts xmlns:android="http://schemas.android.com/apk/res/android">
118 * &lt;keyboard-layout android:name="keyboard_layout_english_us"
119 * android:label="@string/keyboard_layout_english_us_label"
Jeff Brown2f095762012-05-10 21:29:33 -0700120 * android:keyboardLayout="@raw/keyboard_layout_english_us" />
Jeff Brown9df6e7a2012-04-05 11:49:26 -0700121 * &lt;/keyboard-layouts>
Michael Wright112449c2015-10-22 17:23:12 +0100122 * </pre></code>
Jeff Brown9df6e7a2012-04-05 11:49:26 -0700123 * </p><p>
124 * The <code>android:name</code> attribute specifies an identifier by which
125 * the keyboard layout will be known in the package.
126 * The <code>android:label</code> attributes specifies a human-readable descriptive
127 * label to describe the keyboard layout in the user interface, such as "English (US)".
Jeff Brown2f095762012-05-10 21:29:33 -0700128 * The <code>android:keyboardLayout</code> attribute refers to a
Jeff Brown9df6e7a2012-04-05 11:49:26 -0700129 * <a href="http://source.android.com/tech/input/key-character-map-files.html">
130 * key character map</a> resource that defines the keyboard layout.
131 * </p>
132 */
133 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
134 public static final String ACTION_QUERY_KEYBOARD_LAYOUTS =
135 "android.hardware.input.action.QUERY_KEYBOARD_LAYOUTS";
136
137 /**
138 * Metadata Key: Keyboard layout metadata associated with
139 * {@link #ACTION_QUERY_KEYBOARD_LAYOUTS}.
140 * <p>
141 * Specifies the resource id of a XML resource that describes the keyboard
142 * layouts that are provided by the application.
143 * </p>
144 */
145 public static final String META_DATA_KEYBOARD_LAYOUTS =
146 "android.hardware.input.metadata.KEYBOARD_LAYOUTS";
147
Jeff Brownac143512012-04-05 18:57:33 -0700148 /**
149 * Pointer Speed: The minimum (slowest) pointer speed (-7).
150 * @hide
151 */
152 public static final int MIN_POINTER_SPEED = -7;
153
154 /**
155 * Pointer Speed: The maximum (fastest) pointer speed (7).
156 * @hide
157 */
158 public static final int MAX_POINTER_SPEED = 7;
159
160 /**
161 * Pointer Speed: The default pointer speed (0).
162 * @hide
163 */
164 public static final int DEFAULT_POINTER_SPEED = 0;
165
166 /**
167 * Input Event Injection Synchronization Mode: None.
168 * Never blocks. Injection is asynchronous and is assumed always to be successful.
169 * @hide
170 */
171 public static final int INJECT_INPUT_EVENT_MODE_ASYNC = 0; // see InputDispatcher.h
172
173 /**
174 * Input Event Injection Synchronization Mode: Wait for result.
175 * Waits for previous events to be dispatched so that the input dispatcher can
176 * determine whether input event injection will be permitted based on the current
177 * input focus. Does not wait for the input event to finish being handled
178 * by the application.
179 * @hide
180 */
181 public static final int INJECT_INPUT_EVENT_MODE_WAIT_FOR_RESULT = 1; // see InputDispatcher.h
182
183 /**
184 * Input Event Injection Synchronization Mode: Wait for finish.
185 * Waits for the event to be delivered to the application and handled.
186 * @hide
187 */
188 public static final int INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH = 2; // see InputDispatcher.h
189
Michael Wright9209c9c2015-09-03 17:57:01 +0100190 /** @hide */
191 @Retention(RetentionPolicy.SOURCE)
192 @IntDef({SWITCH_STATE_UNKNOWN, SWITCH_STATE_OFF, SWITCH_STATE_ON})
193 public @interface SwitchState {}
194
195 /**
196 * Switch State: Unknown.
197 *
198 * The system has yet to report a valid value for the switch.
199 * @hide
200 */
201 public static final int SWITCH_STATE_UNKNOWN = -1;
202
203 /**
204 * Switch State: Off.
205 * @hide
206 */
207 public static final int SWITCH_STATE_OFF = 0;
208
209 /**
210 * Switch State: On.
211 * @hide
212 */
213 public static final int SWITCH_STATE_ON = 1;
214
Jeff Brown9f25b7f2012-04-10 14:30:49 -0700215 private InputManager(IInputManager im) {
216 mIm = im;
Jeff Brownac143512012-04-05 18:57:33 -0700217 }
218
Jeff Brown9f25b7f2012-04-10 14:30:49 -0700219 /**
220 * Gets an instance of the input manager.
221 *
222 * @return The input manager instance.
223 *
224 * @hide
225 */
226 public static InputManager getInstance() {
227 synchronized (InputManager.class) {
228 if (sInstance == null) {
229 IBinder b = ServiceManager.getService(Context.INPUT_SERVICE);
230 sInstance = new InputManager(IInputManager.Stub.asInterface(b));
231 }
232 return sInstance;
233 }
Jeff Brown9df6e7a2012-04-05 11:49:26 -0700234 }
235
236 /**
Jeff Brownaf9e8d32012-04-12 17:32:48 -0700237 * Gets information about the input device with the specified id.
238 * @param id The device id.
239 * @return The input device or null if not found.
240 */
241 public InputDevice getInputDevice(int id) {
242 synchronized (mInputDevicesLock) {
243 populateInputDevicesLocked();
244
245 int index = mInputDevices.indexOfKey(id);
246 if (index < 0) {
247 return null;
248 }
249
250 InputDevice inputDevice = mInputDevices.valueAt(index);
251 if (inputDevice == null) {
252 try {
253 inputDevice = mIm.getInputDevice(id);
254 } catch (RemoteException ex) {
Jeff Sharkeyc53962d2016-03-01 19:27:23 -0700255 throw ex.rethrowFromSystemServer();
Jeff Brownaf9e8d32012-04-12 17:32:48 -0700256 }
Jeff Brown8f1248422013-01-30 16:23:40 -0800257 if (inputDevice != null) {
258 mInputDevices.setValueAt(index, inputDevice);
259 }
Jeff Brownaf9e8d32012-04-12 17:32:48 -0700260 }
Jeff Brownaf9e8d32012-04-12 17:32:48 -0700261 return inputDevice;
262 }
263 }
264
265 /**
Jeff Browncf39bdf2012-05-18 14:41:19 -0700266 * Gets information about the input device with the specified descriptor.
267 * @param descriptor The input device descriptor.
268 * @return The input device or null if not found.
269 * @hide
270 */
271 public InputDevice getInputDeviceByDescriptor(String descriptor) {
272 if (descriptor == null) {
273 throw new IllegalArgumentException("descriptor must not be null.");
274 }
275
276 synchronized (mInputDevicesLock) {
277 populateInputDevicesLocked();
278
279 int numDevices = mInputDevices.size();
280 for (int i = 0; i < numDevices; i++) {
281 InputDevice inputDevice = mInputDevices.valueAt(i);
282 if (inputDevice == null) {
283 int id = mInputDevices.keyAt(i);
284 try {
285 inputDevice = mIm.getInputDevice(id);
286 } catch (RemoteException ex) {
Jeff Sharkeyc53962d2016-03-01 19:27:23 -0700287 throw ex.rethrowFromSystemServer();
Jeff Brown8f1248422013-01-30 16:23:40 -0800288 }
289 if (inputDevice == null) {
Jeff Browncf39bdf2012-05-18 14:41:19 -0700290 continue;
291 }
292 mInputDevices.setValueAt(i, inputDevice);
293 }
294 if (descriptor.equals(inputDevice.getDescriptor())) {
295 return inputDevice;
296 }
297 }
298 return null;
299 }
300 }
301
302 /**
Jeff Brownaf9e8d32012-04-12 17:32:48 -0700303 * Gets the ids of all input devices in the system.
304 * @return The input device ids.
305 */
306 public int[] getInputDeviceIds() {
307 synchronized (mInputDevicesLock) {
308 populateInputDevicesLocked();
309
310 final int count = mInputDevices.size();
311 final int[] ids = new int[count];
312 for (int i = 0; i < count; i++) {
313 ids[i] = mInputDevices.keyAt(i);
314 }
315 return ids;
316 }
317 }
318
319 /**
320 * Registers an input device listener to receive notifications about when
321 * input devices are added, removed or changed.
322 *
323 * @param listener The listener to register.
324 * @param handler The handler on which the listener should be invoked, or null
325 * if the listener should be invoked on the calling thread's looper.
326 *
327 * @see #unregisterInputDeviceListener
328 */
329 public void registerInputDeviceListener(InputDeviceListener listener, Handler handler) {
330 if (listener == null) {
331 throw new IllegalArgumentException("listener must not be null");
332 }
333
334 synchronized (mInputDevicesLock) {
Tim Kilbourn9ddb8642015-09-01 13:03:53 -0700335 populateInputDevicesLocked();
Jeff Brownaf9e8d32012-04-12 17:32:48 -0700336 int index = findInputDeviceListenerLocked(listener);
337 if (index < 0) {
338 mInputDeviceListeners.add(new InputDeviceListenerDelegate(listener, handler));
339 }
340 }
341 }
342
343 /**
344 * Unregisters an input device listener.
345 *
346 * @param listener The listener to unregister.
347 *
348 * @see #registerInputDeviceListener
349 */
350 public void unregisterInputDeviceListener(InputDeviceListener listener) {
351 if (listener == null) {
352 throw new IllegalArgumentException("listener must not be null");
353 }
354
355 synchronized (mInputDevicesLock) {
356 int index = findInputDeviceListenerLocked(listener);
357 if (index >= 0) {
Jeff Brown9e6d4b02012-04-20 13:08:27 -0700358 InputDeviceListenerDelegate d = mInputDeviceListeners.get(index);
359 d.removeCallbacksAndMessages(null);
Jeff Brownaf9e8d32012-04-12 17:32:48 -0700360 mInputDeviceListeners.remove(index);
361 }
362 }
363 }
364
365 private int findInputDeviceListenerLocked(InputDeviceListener listener) {
366 final int numListeners = mInputDeviceListeners.size();
367 for (int i = 0; i < numListeners; i++) {
368 if (mInputDeviceListeners.get(i).mListener == listener) {
369 return i;
370 }
371 }
372 return -1;
373 }
374
375 /**
Michael Wright9209c9c2015-09-03 17:57:01 +0100376 * Queries whether the device is in tablet mode.
377 *
378 * @return The tablet switch state which is one of {@link #SWITCH_STATE_UNKNOWN},
379 * {@link #SWITCH_STATE_OFF} or {@link #SWITCH_STATE_ON}.
380 * @hide
381 */
382 @SwitchState
383 public int isInTabletMode() {
384 try {
385 return mIm.isInTabletMode();
386 } catch (RemoteException ex) {
Jeff Sharkeyc53962d2016-03-01 19:27:23 -0700387 throw ex.rethrowFromSystemServer();
Michael Wright9209c9c2015-09-03 17:57:01 +0100388 }
389 }
390
391 /**
Michael Wright39e5e942015-08-19 22:52:47 +0100392 * Register a tablet mode changed listener.
393 *
394 * @param listener The listener to register.
395 * @param handler The handler on which the listener should be invoked, or null
396 * if the listener should be invoked on the calling thread's looper.
397 * @hide
398 */
399 public void registerOnTabletModeChangedListener(
400 OnTabletModeChangedListener listener, Handler handler) {
401 if (listener == null) {
402 throw new IllegalArgumentException("listener must not be null");
403 }
404 synchronized (mTabletModeLock) {
405 if (mOnTabletModeChangedListeners == null) {
406 initializeTabletModeListenerLocked();
407 }
408 int idx = findOnTabletModeChangedListenerLocked(listener);
409 if (idx < 0) {
410 OnTabletModeChangedListenerDelegate d =
411 new OnTabletModeChangedListenerDelegate(listener, handler);
412 mOnTabletModeChangedListeners.add(d);
413 }
414 }
415 }
416
417 /**
418 * Unregister a tablet mode changed listener.
419 *
420 * @param listener The listener to unregister.
421 * @hide
422 */
423 public void unregisterOnTabletModeChangedListener(OnTabletModeChangedListener listener) {
424 if (listener == null) {
425 throw new IllegalArgumentException("listener must not be null");
426 }
427 synchronized (mTabletModeLock) {
428 int idx = findOnTabletModeChangedListenerLocked(listener);
429 if (idx >= 0) {
430 OnTabletModeChangedListenerDelegate d = mOnTabletModeChangedListeners.remove(idx);
431 d.removeCallbacksAndMessages(null);
432 }
433 }
434 }
435
436 private void initializeTabletModeListenerLocked() {
437 final TabletModeChangedListener listener = new TabletModeChangedListener();
438 try {
439 mIm.registerTabletModeChangedListener(listener);
440 } catch (RemoteException ex) {
Jeff Sharkeyc53962d2016-03-01 19:27:23 -0700441 throw ex.rethrowFromSystemServer();
Michael Wright39e5e942015-08-19 22:52:47 +0100442 }
443 mTabletModeChangedListener = listener;
444 mOnTabletModeChangedListeners = new ArrayList<>();
445 }
446
447 private int findOnTabletModeChangedListenerLocked(OnTabletModeChangedListener listener) {
448 final int N = mOnTabletModeChangedListeners.size();
449 for (int i = 0; i < N; i++) {
450 if (mOnTabletModeChangedListeners.get(i).mListener == listener) {
451 return i;
452 }
453 }
454 return -1;
455 }
456
457 /**
Jeff Brown9df6e7a2012-04-05 11:49:26 -0700458 * Gets information about all supported keyboard layouts.
459 * <p>
460 * The input manager consults the built-in keyboard layouts as well
461 * as all keyboard layouts advertised by applications using a
462 * {@link #ACTION_QUERY_KEYBOARD_LAYOUTS} broadcast receiver.
463 * </p>
464 *
465 * @return A list of all supported keyboard layouts.
Jeff Brown9f25b7f2012-04-10 14:30:49 -0700466 *
Jeff Brown9df6e7a2012-04-05 11:49:26 -0700467 * @hide
468 */
Jeff Brown9f25b7f2012-04-10 14:30:49 -0700469 public KeyboardLayout[] getKeyboardLayouts() {
470 try {
471 return mIm.getKeyboardLayouts();
472 } catch (RemoteException ex) {
Jeff Sharkeyc53962d2016-03-01 19:27:23 -0700473 throw ex.rethrowFromSystemServer();
Jeff Brown9df6e7a2012-04-05 11:49:26 -0700474 }
Jeff Brown9df6e7a2012-04-05 11:49:26 -0700475 }
476
477 /**
Michael Wright07483422015-10-30 16:20:13 +0000478 * Gets information about all supported keyboard layouts appropriate
479 * for a specific input device.
480 * <p>
481 * The input manager consults the built-in keyboard layouts as well
482 * as all keyboard layouts advertised by applications using a
483 * {@link #ACTION_QUERY_KEYBOARD_LAYOUTS} broadcast receiver.
484 * </p>
485 *
486 * @return A list of all supported keyboard layouts for a specific
487 * input device.
488 *
489 * @hide
490 */
491 public KeyboardLayout[] getKeyboardLayoutsForInputDevice(InputDeviceIdentifier identifier) {
492 try {
493 return mIm.getKeyboardLayoutsForInputDevice(identifier);
494 } catch (RemoteException ex) {
Jeff Sharkeyc53962d2016-03-01 19:27:23 -0700495 throw ex.rethrowFromSystemServer();
Michael Wright07483422015-10-30 16:20:13 +0000496 }
497 }
498
499 /**
Jeff Brown9df6e7a2012-04-05 11:49:26 -0700500 * Gets the keyboard layout with the specified descriptor.
501 *
502 * @param keyboardLayoutDescriptor The keyboard layout descriptor, as returned by
503 * {@link KeyboardLayout#getDescriptor()}.
504 * @return The keyboard layout, or null if it could not be loaded.
505 *
506 * @hide
507 */
508 public KeyboardLayout getKeyboardLayout(String keyboardLayoutDescriptor) {
509 if (keyboardLayoutDescriptor == null) {
510 throw new IllegalArgumentException("keyboardLayoutDescriptor must not be null");
511 }
512
Jeff Brown9df6e7a2012-04-05 11:49:26 -0700513 try {
Jeff Brown9f25b7f2012-04-10 14:30:49 -0700514 return mIm.getKeyboardLayout(keyboardLayoutDescriptor);
515 } catch (RemoteException ex) {
Jeff Sharkeyc53962d2016-03-01 19:27:23 -0700516 throw ex.rethrowFromSystemServer();
Jeff Brown9df6e7a2012-04-05 11:49:26 -0700517 }
518 }
519
520 /**
RoboErikfb290df2013-12-16 11:27:55 -0800521 * Gets the current keyboard layout descriptor for the specified input
522 * device.
Jeff Brown9df6e7a2012-04-05 11:49:26 -0700523 *
RoboErikfb290df2013-12-16 11:27:55 -0800524 * @param identifier Identifier for the input device
525 * @return The keyboard layout descriptor, or null if no keyboard layout has
526 * been set.
Jeff Brown9df6e7a2012-04-05 11:49:26 -0700527 * @hide
528 */
RoboErikfb290df2013-12-16 11:27:55 -0800529 public String getCurrentKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier) {
Jeff Brown9f25b7f2012-04-10 14:30:49 -0700530 try {
RoboErikfb290df2013-12-16 11:27:55 -0800531 return mIm.getCurrentKeyboardLayoutForInputDevice(identifier);
Jeff Brown9f25b7f2012-04-10 14:30:49 -0700532 } catch (RemoteException ex) {
Jeff Sharkeyc53962d2016-03-01 19:27:23 -0700533 throw ex.rethrowFromSystemServer();
Jeff Brown9f25b7f2012-04-10 14:30:49 -0700534 }
Jeff Brown9df6e7a2012-04-05 11:49:26 -0700535 }
536
537 /**
RoboErikfb290df2013-12-16 11:27:55 -0800538 * Sets the current keyboard layout descriptor for the specified input
539 * device.
Jeff Brown9df6e7a2012-04-05 11:49:26 -0700540 * <p>
RoboErikfb290df2013-12-16 11:27:55 -0800541 * This method may have the side-effect of causing the input device in
542 * question to be reconfigured.
Jeff Brown9df6e7a2012-04-05 11:49:26 -0700543 * </p>
544 *
RoboErikfb290df2013-12-16 11:27:55 -0800545 * @param identifier The identifier for the input device.
546 * @param keyboardLayoutDescriptor The keyboard layout descriptor to use,
547 * must not be null.
Jeff Brown9df6e7a2012-04-05 11:49:26 -0700548 * @hide
549 */
RoboErikfb290df2013-12-16 11:27:55 -0800550 public void setCurrentKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier,
Jeff Brown9df6e7a2012-04-05 11:49:26 -0700551 String keyboardLayoutDescriptor) {
RoboErikfb290df2013-12-16 11:27:55 -0800552 if (identifier == null) {
553 throw new IllegalArgumentException("identifier must not be null");
Jeff Brown9df6e7a2012-04-05 11:49:26 -0700554 }
Jeff Browncf39bdf2012-05-18 14:41:19 -0700555 if (keyboardLayoutDescriptor == null) {
556 throw new IllegalArgumentException("keyboardLayoutDescriptor must not be null");
557 }
558
559 try {
RoboErikfb290df2013-12-16 11:27:55 -0800560 mIm.setCurrentKeyboardLayoutForInputDevice(identifier,
Jeff Browncf39bdf2012-05-18 14:41:19 -0700561 keyboardLayoutDescriptor);
562 } catch (RemoteException ex) {
Jeff Sharkeyc53962d2016-03-01 19:27:23 -0700563 throw ex.rethrowFromSystemServer();
Jeff Browncf39bdf2012-05-18 14:41:19 -0700564 }
565 }
566
567 /**
RoboErikfb290df2013-12-16 11:27:55 -0800568 * Gets all keyboard layout descriptors that are enabled for the specified
569 * input device.
Jeff Browncf39bdf2012-05-18 14:41:19 -0700570 *
RoboErikfb290df2013-12-16 11:27:55 -0800571 * @param identifier The identifier for the input device.
Jeff Browncf39bdf2012-05-18 14:41:19 -0700572 * @return The keyboard layout descriptors.
Jeff Browncf39bdf2012-05-18 14:41:19 -0700573 * @hide
574 */
Michael Wright07483422015-10-30 16:20:13 +0000575 public String[] getEnabledKeyboardLayoutsForInputDevice(InputDeviceIdentifier identifier) {
RoboErikfb290df2013-12-16 11:27:55 -0800576 if (identifier == null) {
Jeff Browncf39bdf2012-05-18 14:41:19 -0700577 throw new IllegalArgumentException("inputDeviceDescriptor must not be null");
578 }
Jeff Brown9df6e7a2012-04-05 11:49:26 -0700579
Jeff Brown9df6e7a2012-04-05 11:49:26 -0700580 try {
Michael Wright07483422015-10-30 16:20:13 +0000581 return mIm.getEnabledKeyboardLayoutsForInputDevice(identifier);
Jeff Brown9f25b7f2012-04-10 14:30:49 -0700582 } catch (RemoteException ex) {
Jeff Sharkeyc53962d2016-03-01 19:27:23 -0700583 throw ex.rethrowFromSystemServer();
Jeff Browncf39bdf2012-05-18 14:41:19 -0700584 }
585 }
586
587 /**
588 * Adds the keyboard layout descriptor for the specified input device.
589 * <p>
RoboErikfb290df2013-12-16 11:27:55 -0800590 * This method may have the side-effect of causing the input device in
591 * question to be reconfigured.
Jeff Browncf39bdf2012-05-18 14:41:19 -0700592 * </p>
593 *
RoboErikfb290df2013-12-16 11:27:55 -0800594 * @param identifier The identifier for the input device.
595 * @param keyboardLayoutDescriptor The descriptor of the keyboard layout to
596 * add.
Jeff Browncf39bdf2012-05-18 14:41:19 -0700597 * @hide
598 */
RoboErikfb290df2013-12-16 11:27:55 -0800599 public void addKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier,
Jeff Browncf39bdf2012-05-18 14:41:19 -0700600 String keyboardLayoutDescriptor) {
RoboErikfb290df2013-12-16 11:27:55 -0800601 if (identifier == null) {
Jeff Browncf39bdf2012-05-18 14:41:19 -0700602 throw new IllegalArgumentException("inputDeviceDescriptor must not be null");
603 }
604 if (keyboardLayoutDescriptor == null) {
605 throw new IllegalArgumentException("keyboardLayoutDescriptor must not be null");
606 }
607
608 try {
RoboErikfb290df2013-12-16 11:27:55 -0800609 mIm.addKeyboardLayoutForInputDevice(identifier, keyboardLayoutDescriptor);
Jeff Browncf39bdf2012-05-18 14:41:19 -0700610 } catch (RemoteException ex) {
Jeff Sharkeyc53962d2016-03-01 19:27:23 -0700611 throw ex.rethrowFromSystemServer();
Jeff Browncf39bdf2012-05-18 14:41:19 -0700612 }
613 }
614
615 /**
616 * Removes the keyboard layout descriptor for the specified input device.
617 * <p>
RoboErikfb290df2013-12-16 11:27:55 -0800618 * This method may have the side-effect of causing the input device in
619 * question to be reconfigured.
Jeff Browncf39bdf2012-05-18 14:41:19 -0700620 * </p>
621 *
RoboErikfb290df2013-12-16 11:27:55 -0800622 * @param identifier The identifier for the input device.
623 * @param keyboardLayoutDescriptor The descriptor of the keyboard layout to
624 * remove.
Jeff Browncf39bdf2012-05-18 14:41:19 -0700625 * @hide
626 */
RoboErikfb290df2013-12-16 11:27:55 -0800627 public void removeKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier,
Jeff Browncf39bdf2012-05-18 14:41:19 -0700628 String keyboardLayoutDescriptor) {
RoboErikfb290df2013-12-16 11:27:55 -0800629 if (identifier == null) {
Jeff Browncf39bdf2012-05-18 14:41:19 -0700630 throw new IllegalArgumentException("inputDeviceDescriptor must not be null");
631 }
632 if (keyboardLayoutDescriptor == null) {
633 throw new IllegalArgumentException("keyboardLayoutDescriptor must not be null");
634 }
635
636 try {
RoboErikfb290df2013-12-16 11:27:55 -0800637 mIm.removeKeyboardLayoutForInputDevice(identifier, keyboardLayoutDescriptor);
Jeff Browncf39bdf2012-05-18 14:41:19 -0700638 } catch (RemoteException ex) {
Jeff Sharkeyc53962d2016-03-01 19:27:23 -0700639 throw ex.rethrowFromSystemServer();
Jeff Brown9df6e7a2012-04-05 11:49:26 -0700640 }
Jeff Brown9df6e7a2012-04-05 11:49:26 -0700641 }
642
Michael Wrightd5f7ed92016-01-19 11:23:51 -0800643
644 /**
645 * Gets the keyboard layout for the specified input device and IME subtype.
646 *
647 * @param identifier The identifier for the input device.
648 * @param inputMethodInfo The input method.
649 * @param inputMethodSubtype The input method subtype.
650 *
651 * @return The associated {@link KeyboardLayout}, or null if one has not been set.
652 *
653 * @hide
654 */
655 public KeyboardLayout getKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier,
656 InputMethodInfo inputMethodInfo, InputMethodSubtype inputMethodSubtype) {
657 try {
658 return mIm.getKeyboardLayoutForInputDevice(
659 identifier, inputMethodInfo, inputMethodSubtype);
660 } catch (RemoteException ex) {
Jeff Sharkeyc53962d2016-03-01 19:27:23 -0700661 throw ex.rethrowFromSystemServer();
Michael Wrightd5f7ed92016-01-19 11:23:51 -0800662 }
663 }
664
665 /**
666 * Sets the keyboard layout for the specified input device and IME subtype pair.
667 *
668 * @param identifier The identifier for the input device.
669 * @param inputMethodInfo The input method with which to associate the keyboard layout.
670 * @param inputMethodSubtype The input method subtype which which to associate the keyboard
671 * layout.
672 * @param keyboardLayoutDescriptor The descriptor of the keyboard layout to set
673 *
674 * @hide
675 */
676 public void setKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier,
677 InputMethodInfo inputMethodInfo, InputMethodSubtype inputMethodSubtype,
678 String keyboardLayoutDescriptor) {
679 try {
680 mIm.setKeyboardLayoutForInputDevice(identifier, inputMethodInfo,
681 inputMethodSubtype, keyboardLayoutDescriptor);
682 } catch (RemoteException ex) {
Jeff Sharkeyc53962d2016-03-01 19:27:23 -0700683 throw ex.rethrowFromSystemServer();
Michael Wrightd5f7ed92016-01-19 11:23:51 -0800684 }
685 }
686
Jeff Brownac143512012-04-05 18:57:33 -0700687 /**
Jason Gerecked6396d62014-01-27 18:30:37 -0800688 * Gets the TouchCalibration applied to the specified input device's coordinates.
689 *
690 * @param inputDeviceDescriptor The input device descriptor.
691 * @return The TouchCalibration currently assigned for use with the given
692 * input device. If none is set, an identity TouchCalibration is returned.
693 *
694 * @hide
695 */
Jason Gerecked5220742014-03-10 09:47:59 -0700696 public TouchCalibration getTouchCalibration(String inputDeviceDescriptor, int surfaceRotation) {
Jason Gerecked6396d62014-01-27 18:30:37 -0800697 try {
Jason Gerecked5220742014-03-10 09:47:59 -0700698 return mIm.getTouchCalibrationForInputDevice(inputDeviceDescriptor, surfaceRotation);
Jason Gerecked6396d62014-01-27 18:30:37 -0800699 } catch (RemoteException ex) {
Jeff Sharkeyc53962d2016-03-01 19:27:23 -0700700 throw ex.rethrowFromSystemServer();
Jason Gerecked6396d62014-01-27 18:30:37 -0800701 }
702 }
703
704 /**
705 * Sets the TouchCalibration to apply to the specified input device's coordinates.
706 * <p>
707 * This method may have the side-effect of causing the input device in question
708 * to be reconfigured. Requires {@link android.Manifest.permissions.SET_INPUT_CALIBRATION}.
709 * </p>
710 *
711 * @param inputDeviceDescriptor The input device descriptor.
712 * @param calibration The calibration to be applied
713 *
714 * @hide
715 */
Jason Gerecked5220742014-03-10 09:47:59 -0700716 public void setTouchCalibration(String inputDeviceDescriptor, int surfaceRotation,
717 TouchCalibration calibration) {
Jason Gerecked6396d62014-01-27 18:30:37 -0800718 try {
Jason Gerecked5220742014-03-10 09:47:59 -0700719 mIm.setTouchCalibrationForInputDevice(inputDeviceDescriptor, surfaceRotation, calibration);
Jason Gerecked6396d62014-01-27 18:30:37 -0800720 } catch (RemoteException ex) {
Jeff Sharkeyc53962d2016-03-01 19:27:23 -0700721 throw ex.rethrowFromSystemServer();
Jason Gerecked6396d62014-01-27 18:30:37 -0800722 }
723 }
724
725 /**
Jeff Brownac143512012-04-05 18:57:33 -0700726 * Gets the mouse pointer speed.
727 * <p>
728 * Only returns the permanent mouse pointer speed. Ignores any temporary pointer
729 * speed set by {@link #tryPointerSpeed}.
730 * </p>
731 *
Jeff Brown9f25b7f2012-04-10 14:30:49 -0700732 * @param context The application context.
Jeff Brownac143512012-04-05 18:57:33 -0700733 * @return The pointer speed as a value between {@link #MIN_POINTER_SPEED} and
734 * {@link #MAX_POINTER_SPEED}, or the default value {@link #DEFAULT_POINTER_SPEED}.
735 *
736 * @hide
737 */
Jeff Brown9f25b7f2012-04-10 14:30:49 -0700738 public int getPointerSpeed(Context context) {
Jeff Brownac143512012-04-05 18:57:33 -0700739 int speed = DEFAULT_POINTER_SPEED;
740 try {
Jeff Brown9f25b7f2012-04-10 14:30:49 -0700741 speed = Settings.System.getInt(context.getContentResolver(),
Jeff Brownac143512012-04-05 18:57:33 -0700742 Settings.System.POINTER_SPEED);
743 } catch (SettingNotFoundException snfe) {
744 }
745 return speed;
746 }
747
748 /**
749 * Sets the mouse pointer speed.
750 * <p>
751 * Requires {@link android.Manifest.permissions.WRITE_SETTINGS}.
752 * </p>
753 *
Jeff Brown9f25b7f2012-04-10 14:30:49 -0700754 * @param context The application context.
Jeff Brownac143512012-04-05 18:57:33 -0700755 * @param speed The pointer speed as a value between {@link #MIN_POINTER_SPEED} and
756 * {@link #MAX_POINTER_SPEED}, or the default value {@link #DEFAULT_POINTER_SPEED}.
757 *
758 * @hide
759 */
Jeff Brown9f25b7f2012-04-10 14:30:49 -0700760 public void setPointerSpeed(Context context, int speed) {
Jeff Brownac143512012-04-05 18:57:33 -0700761 if (speed < MIN_POINTER_SPEED || speed > MAX_POINTER_SPEED) {
762 throw new IllegalArgumentException("speed out of range");
763 }
764
Jeff Brown9f25b7f2012-04-10 14:30:49 -0700765 Settings.System.putInt(context.getContentResolver(),
Jeff Brownac143512012-04-05 18:57:33 -0700766 Settings.System.POINTER_SPEED, speed);
767 }
768
769 /**
770 * Changes the mouse pointer speed temporarily, but does not save the setting.
771 * <p>
772 * Requires {@link android.Manifest.permission.SET_POINTER_SPEED}.
773 * </p>
774 *
775 * @param speed 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 */
780 public void tryPointerSpeed(int speed) {
781 if (speed < MIN_POINTER_SPEED || speed > MAX_POINTER_SPEED) {
782 throw new IllegalArgumentException("speed out of range");
783 }
784
785 try {
Jeff Brown9f25b7f2012-04-10 14:30:49 -0700786 mIm.tryPointerSpeed(speed);
Jeff Brownac143512012-04-05 18:57:33 -0700787 } catch (RemoteException ex) {
Jeff Sharkeyc53962d2016-03-01 19:27:23 -0700788 throw ex.rethrowFromSystemServer();
Jeff Brownac143512012-04-05 18:57:33 -0700789 }
790 }
791
792 /**
Jeff Brownac143512012-04-05 18:57:33 -0700793 * Queries the framework about whether any physical keys exist on the
794 * any keyboard attached to the device that are capable of producing the given
795 * array of key codes.
796 *
797 * @param keyCodes The array of key codes to query.
798 * @return A new array of the same size as the key codes array whose elements
799 * are set to true if at least one attached keyboard supports the corresponding key code
800 * at the same index in the key codes array.
801 *
802 * @hide
803 */
Jeff Brown9f25b7f2012-04-10 14:30:49 -0700804 public boolean[] deviceHasKeys(int[] keyCodes) {
Michael Wrightb7b2d4b2013-08-19 15:55:38 -0700805 return deviceHasKeys(-1, keyCodes);
806 }
807
808 /**
809 * Queries the framework about whether any physical keys exist on the
810 * any keyboard attached to the device that are capable of producing the given
811 * array of key codes.
812 *
813 * @param id The id of the device to query.
814 * @param keyCodes The array of key codes to query.
815 * @return A new array of the same size as the key codes array whose elements are set to true
816 * if the given device could produce the corresponding key code at the same index in the key
817 * codes array.
818 *
819 * @hide
820 */
821 public boolean[] deviceHasKeys(int id, int[] keyCodes) {
Jeff Brownac143512012-04-05 18:57:33 -0700822 boolean[] ret = new boolean[keyCodes.length];
823 try {
Michael Wrightb7b2d4b2013-08-19 15:55:38 -0700824 mIm.hasKeys(id, InputDevice.SOURCE_ANY, keyCodes, ret);
Jeff Brownac143512012-04-05 18:57:33 -0700825 } catch (RemoteException e) {
Jeff Sharkeyc53962d2016-03-01 19:27:23 -0700826 throw e.rethrowFromSystemServer();
Jeff Brownac143512012-04-05 18:57:33 -0700827 }
828 return ret;
829 }
830
Michael Wrightb7b2d4b2013-08-19 15:55:38 -0700831
Jeff Brownac143512012-04-05 18:57:33 -0700832 /**
833 * Injects an input event into the event system on behalf of an application.
834 * The synchronization mode determines whether the method blocks while waiting for
835 * input injection to proceed.
836 * <p>
837 * Requires {@link android.Manifest.permission.INJECT_EVENTS} to inject into
838 * windows that are owned by other applications.
839 * </p><p>
840 * Make sure you correctly set the event time and input source of the event
841 * before calling this method.
842 * </p>
843 *
844 * @param event The event to inject.
845 * @param mode The synchronization mode. One of:
846 * {@link #INJECT_INPUT_EVENT_MODE_ASYNC},
847 * {@link #INJECT_INPUT_EVENT_MODE_WAIT_FOR_RESULT}, or
848 * {@link #INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH}.
849 * @return True if input event injection succeeded.
850 *
851 * @hide
852 */
Jeff Brown9f25b7f2012-04-10 14:30:49 -0700853 public boolean injectInputEvent(InputEvent event, int mode) {
Jeff Brownac143512012-04-05 18:57:33 -0700854 if (event == null) {
855 throw new IllegalArgumentException("event must not be null");
856 }
857 if (mode != INJECT_INPUT_EVENT_MODE_ASYNC
858 && mode != INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH
859 && mode != INJECT_INPUT_EVENT_MODE_WAIT_FOR_RESULT) {
860 throw new IllegalArgumentException("mode is invalid");
861 }
862
863 try {
Jeff Brown9f25b7f2012-04-10 14:30:49 -0700864 return mIm.injectInputEvent(event, mode);
Jeff Brownac143512012-04-05 18:57:33 -0700865 } catch (RemoteException ex) {
Jeff Sharkeyc53962d2016-03-01 19:27:23 -0700866 throw ex.rethrowFromSystemServer();
Jeff Brownac143512012-04-05 18:57:33 -0700867 }
868 }
Jeff Brownaf9e8d32012-04-12 17:32:48 -0700869
Jun Mukai1db53972015-09-11 18:08:31 -0700870 /**
871 * Changes the mouse pointer's icon shape into the specified id.
872 *
873 * @param iconId The id of the pointer graphic, as a value between
874 * {@link PointerIcon.STYLE_ARROW} and {@link PointerIcon.STYLE_GRABBING}.
875 *
876 * @hide
877 */
878 public void setPointerIconShape(int iconId) {
879 try {
880 mIm.setPointerIconShape(iconId);
881 } catch (RemoteException ex) {
Jeff Sharkeyc53962d2016-03-01 19:27:23 -0700882 throw ex.rethrowFromSystemServer();
Jun Mukai1db53972015-09-11 18:08:31 -0700883 }
884 }
885
Jun Mukaid4eaef72015-10-30 15:54:33 -0700886 /** @hide */
887 public void setCustomPointerIcon(PointerIcon icon) {
888 try {
889 mIm.setCustomPointerIcon(icon);
890 } catch (RemoteException ex) {
Jeff Sharkeyc53962d2016-03-01 19:27:23 -0700891 throw ex.rethrowFromSystemServer();
Jun Mukaid4eaef72015-10-30 15:54:33 -0700892 }
893 }
894
Jun Mukai347e5d42015-12-03 01:13:31 -0800895 /**
896 * Update the pointer icon status. When detached, the pointer icon disappears, and further
897 * mouse location will be stuck at the current point. Mouse movement events will still arrive,
898 * and movement should be handled through {@link MotionEvent.AXIS_RELATIVE_X} and
899 * {@link MotionEvent.AXIS_RELATIVE_Y}.
900 *
901 * @param detached true if the icon will be detached from the actual mouse movement.
902 *
903 * @hide
904 */
905 public void setPointerIconDetached(boolean detached) {
906 try {
907 mIm.setPointerIconDetached(detached);
908 } catch (RemoteException ex) {
Jeff Sharkeyc53962d2016-03-01 19:27:23 -0700909 throw ex.rethrowFromSystemServer();
Jun Mukai347e5d42015-12-03 01:13:31 -0800910 }
911 }
912
Jeff Brownaf9e8d32012-04-12 17:32:48 -0700913 private void populateInputDevicesLocked() {
914 if (mInputDevicesChangedListener == null) {
915 final InputDevicesChangedListener listener = new InputDevicesChangedListener();
916 try {
917 mIm.registerInputDevicesChangedListener(listener);
918 } catch (RemoteException ex) {
Jeff Sharkeyc53962d2016-03-01 19:27:23 -0700919 throw ex.rethrowFromSystemServer();
Jeff Brownaf9e8d32012-04-12 17:32:48 -0700920 }
921 mInputDevicesChangedListener = listener;
922 }
923
924 if (mInputDevices == null) {
925 final int[] ids;
926 try {
927 ids = mIm.getInputDeviceIds();
928 } catch (RemoteException ex) {
Jeff Sharkeyc53962d2016-03-01 19:27:23 -0700929 throw ex.rethrowFromSystemServer();
Jeff Brownaf9e8d32012-04-12 17:32:48 -0700930 }
931
932 mInputDevices = new SparseArray<InputDevice>();
933 for (int i = 0; i < ids.length; i++) {
934 mInputDevices.put(ids[i], null);
935 }
936 }
937 }
938
939 private void onInputDevicesChanged(int[] deviceIdAndGeneration) {
940 if (DEBUG) {
941 Log.d(TAG, "Received input devices changed.");
942 }
943
944 synchronized (mInputDevicesLock) {
945 for (int i = mInputDevices.size(); --i > 0; ) {
946 final int deviceId = mInputDevices.keyAt(i);
947 if (!containsDeviceId(deviceIdAndGeneration, deviceId)) {
948 if (DEBUG) {
949 Log.d(TAG, "Device removed: " + deviceId);
950 }
951 mInputDevices.removeAt(i);
952 sendMessageToInputDeviceListenersLocked(MSG_DEVICE_REMOVED, deviceId);
953 }
954 }
955
956 for (int i = 0; i < deviceIdAndGeneration.length; i += 2) {
957 final int deviceId = deviceIdAndGeneration[i];
958 int index = mInputDevices.indexOfKey(deviceId);
959 if (index >= 0) {
960 final InputDevice device = mInputDevices.valueAt(index);
961 if (device != null) {
962 final int generation = deviceIdAndGeneration[i + 1];
963 if (device.getGeneration() != generation) {
964 if (DEBUG) {
965 Log.d(TAG, "Device changed: " + deviceId);
966 }
967 mInputDevices.setValueAt(index, null);
968 sendMessageToInputDeviceListenersLocked(MSG_DEVICE_CHANGED, deviceId);
969 }
970 }
971 } else {
972 if (DEBUG) {
973 Log.d(TAG, "Device added: " + deviceId);
974 }
975 mInputDevices.put(deviceId, null);
976 sendMessageToInputDeviceListenersLocked(MSG_DEVICE_ADDED, deviceId);
977 }
978 }
979 }
980 }
981
982 private void sendMessageToInputDeviceListenersLocked(int what, int deviceId) {
983 final int numListeners = mInputDeviceListeners.size();
984 for (int i = 0; i < numListeners; i++) {
985 InputDeviceListenerDelegate listener = mInputDeviceListeners.get(i);
986 listener.sendMessage(listener.obtainMessage(what, deviceId, 0));
987 }
988 }
989
990 private static boolean containsDeviceId(int[] deviceIdAndGeneration, int deviceId) {
991 for (int i = 0; i < deviceIdAndGeneration.length; i += 2) {
992 if (deviceIdAndGeneration[i] == deviceId) {
993 return true;
994 }
995 }
996 return false;
997 }
998
Michael Wright39e5e942015-08-19 22:52:47 +0100999
1000 private void onTabletModeChanged(long whenNanos, boolean inTabletMode) {
1001 if (DEBUG) {
1002 Log.d(TAG, "Received tablet mode changed: "
1003 + "whenNanos=" + whenNanos + ", inTabletMode=" + inTabletMode);
1004 }
1005 synchronized (mTabletModeLock) {
1006 final int N = mOnTabletModeChangedListeners.size();
1007 for (int i = 0; i < N; i++) {
1008 OnTabletModeChangedListenerDelegate listener =
1009 mOnTabletModeChangedListeners.get(i);
1010 listener.sendTabletModeChanged(whenNanos, inTabletMode);
1011 }
1012 }
1013 }
1014
Jeff Brownaf9e8d32012-04-12 17:32:48 -07001015 /**
Jeff Browna47425a2012-04-13 04:09:27 -07001016 * Gets a vibrator service associated with an input device, assuming it has one.
1017 * @return The vibrator, never null.
1018 * @hide
1019 */
1020 public Vibrator getInputDeviceVibrator(int deviceId) {
1021 return new InputDeviceVibrator(deviceId);
1022 }
1023
1024 /**
Jeff Brownaf9e8d32012-04-12 17:32:48 -07001025 * Listens for changes in input devices.
1026 */
1027 public interface InputDeviceListener {
1028 /**
1029 * Called whenever an input device has been added to the system.
1030 * Use {@link InputManager#getInputDevice} to get more information about the device.
1031 *
1032 * @param deviceId The id of the input device that was added.
1033 */
1034 void onInputDeviceAdded(int deviceId);
1035
1036 /**
1037 * Called whenever an input device has been removed from the system.
1038 *
1039 * @param deviceId The id of the input device that was removed.
1040 */
1041 void onInputDeviceRemoved(int deviceId);
1042
1043 /**
1044 * Called whenever the properties of an input device have changed since they
1045 * were last queried. Use {@link InputManager#getInputDevice} to get
1046 * a fresh {@link InputDevice} object with the new properties.
1047 *
1048 * @param deviceId The id of the input device that changed.
1049 */
1050 void onInputDeviceChanged(int deviceId);
1051 }
1052
1053 private final class InputDevicesChangedListener extends IInputDevicesChangedListener.Stub {
1054 @Override
1055 public void onInputDevicesChanged(int[] deviceIdAndGeneration) throws RemoteException {
1056 InputManager.this.onInputDevicesChanged(deviceIdAndGeneration);
1057 }
1058 }
1059
1060 private static final class InputDeviceListenerDelegate extends Handler {
1061 public final InputDeviceListener mListener;
1062
1063 public InputDeviceListenerDelegate(InputDeviceListener listener, Handler handler) {
1064 super(handler != null ? handler.getLooper() : Looper.myLooper());
1065 mListener = listener;
1066 }
1067
1068 @Override
1069 public void handleMessage(Message msg) {
1070 switch (msg.what) {
1071 case MSG_DEVICE_ADDED:
1072 mListener.onInputDeviceAdded(msg.arg1);
1073 break;
1074 case MSG_DEVICE_REMOVED:
1075 mListener.onInputDeviceRemoved(msg.arg1);
1076 break;
1077 case MSG_DEVICE_CHANGED:
1078 mListener.onInputDeviceChanged(msg.arg1);
1079 break;
1080 }
1081 }
1082 }
Jeff Browna47425a2012-04-13 04:09:27 -07001083
Michael Wright39e5e942015-08-19 22:52:47 +01001084 /** @hide */
1085 public interface OnTabletModeChangedListener {
1086 /**
1087 * Called whenever the device goes into or comes out of tablet mode.
1088 *
1089 * @param whenNanos The time at which the device transitioned into or
1090 * out of tablet mode. This is given in nanoseconds in the
1091 * {@link SystemClock#uptimeMillis} time base.
1092 */
1093 void onTabletModeChanged(long whenNanos, boolean inTabletMode);
1094 }
1095
1096 private final class TabletModeChangedListener extends ITabletModeChangedListener.Stub {
1097 @Override
1098 public void onTabletModeChanged(long whenNanos, boolean inTabletMode) {
1099 InputManager.this.onTabletModeChanged(whenNanos, inTabletMode);
1100 }
1101 }
1102
1103 private static final class OnTabletModeChangedListenerDelegate extends Handler {
1104 private static final int MSG_TABLET_MODE_CHANGED = 0;
1105
1106 public final OnTabletModeChangedListener mListener;
1107
1108 public OnTabletModeChangedListenerDelegate(
1109 OnTabletModeChangedListener listener, Handler handler) {
1110 super(handler != null ? handler.getLooper() : Looper.myLooper());
1111 mListener = listener;
1112 }
1113
1114 public void sendTabletModeChanged(long whenNanos, boolean inTabletMode) {
1115 SomeArgs args = SomeArgs.obtain();
1116 args.argi1 = (int) (whenNanos & 0xFFFFFFFF);
1117 args.argi2 = (int) (whenNanos >> 32);
1118 args.arg1 = (Boolean) inTabletMode;
1119 obtainMessage(MSG_TABLET_MODE_CHANGED, args).sendToTarget();
1120 }
1121
1122 @Override
1123 public void handleMessage(Message msg) {
1124 switch (msg.what) {
1125 case MSG_TABLET_MODE_CHANGED:
1126 SomeArgs args = (SomeArgs) msg.obj;
1127 long whenNanos = (args.argi1 & 0xFFFFFFFFl) | ((long) args.argi2 << 32);
1128 boolean inTabletMode = (boolean) args.arg1;
1129 mListener.onTabletModeChanged(whenNanos, inTabletMode);
1130 break;
1131 }
1132 }
1133 }
1134
Jeff Browna47425a2012-04-13 04:09:27 -07001135 private final class InputDeviceVibrator extends Vibrator {
1136 private final int mDeviceId;
1137 private final Binder mToken;
1138
1139 public InputDeviceVibrator(int deviceId) {
1140 mDeviceId = deviceId;
1141 mToken = new Binder();
1142 }
1143
1144 @Override
1145 public boolean hasVibrator() {
1146 return true;
1147 }
1148
John Spurlock1af30c72014-03-10 08:33:35 -04001149 /**
1150 * @hide
1151 */
Jeff Browna47425a2012-04-13 04:09:27 -07001152 @Override
John Spurlock7b414672014-07-18 13:02:39 -04001153 public void vibrate(int uid, String opPkg, long milliseconds, AudioAttributes attributes) {
Jeff Browna47425a2012-04-13 04:09:27 -07001154 vibrate(new long[] { 0, milliseconds}, -1);
1155 }
1156
John Spurlock1af30c72014-03-10 08:33:35 -04001157 /**
1158 * @hide
1159 */
Jeff Browna47425a2012-04-13 04:09:27 -07001160 @Override
Christoph Studer8fd7f1e2014-04-11 17:35:05 -04001161 public void vibrate(int uid, String opPkg, long[] pattern, int repeat,
John Spurlock7b414672014-07-18 13:02:39 -04001162 AudioAttributes attributes) {
Jeff Browna47425a2012-04-13 04:09:27 -07001163 if (repeat >= pattern.length) {
1164 throw new ArrayIndexOutOfBoundsException();
1165 }
1166 try {
1167 mIm.vibrate(mDeviceId, pattern, repeat, mToken);
1168 } catch (RemoteException ex) {
Jeff Sharkeyc53962d2016-03-01 19:27:23 -07001169 throw ex.rethrowFromSystemServer();
Jeff Browna47425a2012-04-13 04:09:27 -07001170 }
1171 }
1172
1173 @Override
1174 public void cancel() {
1175 try {
1176 mIm.cancelVibrate(mDeviceId, mToken);
1177 } catch (RemoteException ex) {
Jeff Sharkeyc53962d2016-03-01 19:27:23 -07001178 throw ex.rethrowFromSystemServer();
Jeff Browna47425a2012-04-13 04:09:27 -07001179 }
1180 }
1181 }
Jeff Brown9df6e7a2012-04-05 11:49:26 -07001182}