blob: a754d6b1e0c75e61a21bb3eb0ff57d893dcfbb5b [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 Wright39e5e942015-08-19 22:52:47 +010019import com.android.internal.os.SomeArgs;
Jeff Browncf39bdf2012-05-18 14:41:19 -070020import com.android.internal.util.ArrayUtils;
21
Jeff Brown9df6e7a2012-04-05 11:49:26 -070022import android.annotation.SdkConstant;
23import android.annotation.SdkConstant.SdkConstantType;
Jeff Brown9df6e7a2012-04-05 11:49:26 -070024import android.content.Context;
John Spurlock7b414672014-07-18 13:02:39 -040025import android.media.AudioAttributes;
Jeff Browna47425a2012-04-13 04:09:27 -070026import android.os.Binder;
Jeff Brownaf9e8d32012-04-12 17:32:48 -070027import android.os.Handler;
Jeff Brownac143512012-04-05 18:57:33 -070028import android.os.IBinder;
Jeff Brownaf9e8d32012-04-12 17:32:48 -070029import android.os.Looper;
30import android.os.Message;
Jeff Brownac143512012-04-05 18:57:33 -070031import android.os.RemoteException;
32import android.os.ServiceManager;
Michael Wright39e5e942015-08-19 22:52:47 +010033import android.os.SystemClock;
Jeff Browna47425a2012-04-13 04:09:27 -070034import android.os.Vibrator;
Jeff Brownac143512012-04-05 18:57:33 -070035import android.provider.Settings;
36import android.provider.Settings.SettingNotFoundException;
Jeff Brown9df6e7a2012-04-05 11:49:26 -070037import android.util.Log;
Jeff Brown9f25b7f2012-04-10 14:30:49 -070038import android.util.SparseArray;
Jeff Brownac143512012-04-05 18:57:33 -070039import android.view.InputDevice;
40import android.view.InputEvent;
Jeff Brown9df6e7a2012-04-05 11:49:26 -070041
Jeff Brownaf9e8d32012-04-12 17:32:48 -070042import java.util.ArrayList;
Michael Wright39e5e942015-08-19 22:52:47 +010043import java.util.List;
Jeff Brownaf9e8d32012-04-12 17:32:48 -070044
Jeff Brown9df6e7a2012-04-05 11:49:26 -070045/**
46 * Provides information about input devices and available key layouts.
47 * <p>
48 * Get an instance of this class by calling
49 * {@link android.content.Context#getSystemService(java.lang.String)
50 * Context.getSystemService()} with the argument
51 * {@link android.content.Context#INPUT_SERVICE}.
52 * </p>
53 */
54public final class InputManager {
55 private static final String TAG = "InputManager";
Jeff Brownaf9e8d32012-04-12 17:32:48 -070056 private static final boolean DEBUG = false;
57
58 private static final int MSG_DEVICE_ADDED = 1;
59 private static final int MSG_DEVICE_REMOVED = 2;
60 private static final int MSG_DEVICE_CHANGED = 3;
Jeff Brown9df6e7a2012-04-05 11:49:26 -070061
Jeff Brown9f25b7f2012-04-10 14:30:49 -070062 private static InputManager sInstance;
Jeff Brownac143512012-04-05 18:57:33 -070063
Jeff Brown9f25b7f2012-04-10 14:30:49 -070064 private final IInputManager mIm;
Jeff Brownaf9e8d32012-04-12 17:32:48 -070065
66 // Guarded by mInputDevicesLock
67 private final Object mInputDevicesLock = new Object();
68 private SparseArray<InputDevice> mInputDevices;
69 private InputDevicesChangedListener mInputDevicesChangedListener;
70 private final ArrayList<InputDeviceListenerDelegate> mInputDeviceListeners =
71 new ArrayList<InputDeviceListenerDelegate>();
Jeff Brown9df6e7a2012-04-05 11:49:26 -070072
Michael Wright39e5e942015-08-19 22:52:47 +010073 // Guarded by mTabletModeLock
74 private final Object mTabletModeLock = new Object();
75 private TabletModeChangedListener mTabletModeChangedListener;
76 private List<OnTabletModeChangedListenerDelegate> mOnTabletModeChangedListeners;
77
Jeff Brown9df6e7a2012-04-05 11:49:26 -070078 /**
79 * Broadcast Action: Query available keyboard layouts.
80 * <p>
81 * The input manager service locates available keyboard layouts
82 * by querying broadcast receivers that are registered for this action.
83 * An application can offer additional keyboard layouts to the user
84 * by declaring a suitable broadcast receiver in its manifest.
85 * </p><p>
86 * Here is an example broadcast receiver declaration that an application
87 * might include in its AndroidManifest.xml to advertise keyboard layouts.
88 * The meta-data specifies a resource that contains a description of each keyboard
89 * layout that is provided by the application.
90 * <pre><code>
Jeff Brownd9fec5d2012-05-17 16:01:54 -070091 * &lt;receiver android:name=".InputDeviceReceiver"
92 * android:label="@string/keyboard_layouts_label">
Jeff Brown9df6e7a2012-04-05 11:49:26 -070093 * &lt;intent-filter>
94 * &lt;action android:name="android.hardware.input.action.QUERY_KEYBOARD_LAYOUTS" />
95 * &lt;/intent-filter>
96 * &lt;meta-data android:name="android.hardware.input.metadata.KEYBOARD_LAYOUTS"
97 * android:resource="@xml/keyboard_layouts" />
98 * &lt;/receiver>
99 * </code></pre>
100 * </p><p>
101 * In the above example, the <code>@xml/keyboard_layouts</code> resource refers to
102 * an XML resource whose root element is <code>&lt;keyboard-layouts></code> that
103 * contains zero or more <code>&lt;keyboard-layout></code> elements.
104 * Each <code>&lt;keyboard-layout></code> element specifies the name, label, and location
Jeff Brownd9fec5d2012-05-17 16:01:54 -0700105 * of a key character map for a particular keyboard layout. The label on the receiver
106 * is used to name the collection of keyboard layouts provided by this receiver in the
107 * keyboard layout settings.
Jeff Brown9df6e7a2012-04-05 11:49:26 -0700108 * <pre></code>
109 * &lt;?xml version="1.0" encoding="utf-8"?>
110 * &lt;keyboard-layouts xmlns:android="http://schemas.android.com/apk/res/android">
111 * &lt;keyboard-layout android:name="keyboard_layout_english_us"
112 * android:label="@string/keyboard_layout_english_us_label"
Jeff Brown2f095762012-05-10 21:29:33 -0700113 * android:keyboardLayout="@raw/keyboard_layout_english_us" />
Jeff Brown9df6e7a2012-04-05 11:49:26 -0700114 * &lt;/keyboard-layouts>
115 * </p><p>
116 * The <code>android:name</code> attribute specifies an identifier by which
117 * the keyboard layout will be known in the package.
118 * The <code>android:label</code> attributes specifies a human-readable descriptive
119 * label to describe the keyboard layout in the user interface, such as "English (US)".
Jeff Brown2f095762012-05-10 21:29:33 -0700120 * The <code>android:keyboardLayout</code> attribute refers to a
Jeff Brown9df6e7a2012-04-05 11:49:26 -0700121 * <a href="http://source.android.com/tech/input/key-character-map-files.html">
122 * key character map</a> resource that defines the keyboard layout.
123 * </p>
124 */
125 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
126 public static final String ACTION_QUERY_KEYBOARD_LAYOUTS =
127 "android.hardware.input.action.QUERY_KEYBOARD_LAYOUTS";
128
129 /**
130 * Metadata Key: Keyboard layout metadata associated with
131 * {@link #ACTION_QUERY_KEYBOARD_LAYOUTS}.
132 * <p>
133 * Specifies the resource id of a XML resource that describes the keyboard
134 * layouts that are provided by the application.
135 * </p>
136 */
137 public static final String META_DATA_KEYBOARD_LAYOUTS =
138 "android.hardware.input.metadata.KEYBOARD_LAYOUTS";
139
Jeff Brownac143512012-04-05 18:57:33 -0700140 /**
141 * Pointer Speed: The minimum (slowest) pointer speed (-7).
142 * @hide
143 */
144 public static final int MIN_POINTER_SPEED = -7;
145
146 /**
147 * Pointer Speed: The maximum (fastest) pointer speed (7).
148 * @hide
149 */
150 public static final int MAX_POINTER_SPEED = 7;
151
152 /**
153 * Pointer Speed: The default pointer speed (0).
154 * @hide
155 */
156 public static final int DEFAULT_POINTER_SPEED = 0;
157
158 /**
159 * Input Event Injection Synchronization Mode: None.
160 * Never blocks. Injection is asynchronous and is assumed always to be successful.
161 * @hide
162 */
163 public static final int INJECT_INPUT_EVENT_MODE_ASYNC = 0; // see InputDispatcher.h
164
165 /**
166 * Input Event Injection Synchronization Mode: Wait for result.
167 * Waits for previous events to be dispatched so that the input dispatcher can
168 * determine whether input event injection will be permitted based on the current
169 * input focus. Does not wait for the input event to finish being handled
170 * by the application.
171 * @hide
172 */
173 public static final int INJECT_INPUT_EVENT_MODE_WAIT_FOR_RESULT = 1; // see InputDispatcher.h
174
175 /**
176 * Input Event Injection Synchronization Mode: Wait for finish.
177 * Waits for the event to be delivered to the application and handled.
178 * @hide
179 */
180 public static final int INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH = 2; // see InputDispatcher.h
181
Jeff Brown9f25b7f2012-04-10 14:30:49 -0700182 private InputManager(IInputManager im) {
183 mIm = im;
Jeff Brownac143512012-04-05 18:57:33 -0700184 }
185
Jeff Brown9f25b7f2012-04-10 14:30:49 -0700186 /**
187 * Gets an instance of the input manager.
188 *
189 * @return The input manager instance.
190 *
191 * @hide
192 */
193 public static InputManager getInstance() {
194 synchronized (InputManager.class) {
195 if (sInstance == null) {
196 IBinder b = ServiceManager.getService(Context.INPUT_SERVICE);
197 sInstance = new InputManager(IInputManager.Stub.asInterface(b));
198 }
199 return sInstance;
200 }
Jeff Brown9df6e7a2012-04-05 11:49:26 -0700201 }
202
203 /**
Jeff Brownaf9e8d32012-04-12 17:32:48 -0700204 * Gets information about the input device with the specified id.
205 * @param id The device id.
206 * @return The input device or null if not found.
207 */
208 public InputDevice getInputDevice(int id) {
209 synchronized (mInputDevicesLock) {
210 populateInputDevicesLocked();
211
212 int index = mInputDevices.indexOfKey(id);
213 if (index < 0) {
214 return null;
215 }
216
217 InputDevice inputDevice = mInputDevices.valueAt(index);
218 if (inputDevice == null) {
219 try {
220 inputDevice = mIm.getInputDevice(id);
221 } catch (RemoteException ex) {
222 throw new RuntimeException("Could not get input device information.", ex);
223 }
Jeff Brown8f1248422013-01-30 16:23:40 -0800224 if (inputDevice != null) {
225 mInputDevices.setValueAt(index, inputDevice);
226 }
Jeff Brownaf9e8d32012-04-12 17:32:48 -0700227 }
Jeff Brownaf9e8d32012-04-12 17:32:48 -0700228 return inputDevice;
229 }
230 }
231
232 /**
Jeff Browncf39bdf2012-05-18 14:41:19 -0700233 * Gets information about the input device with the specified descriptor.
234 * @param descriptor The input device descriptor.
235 * @return The input device or null if not found.
236 * @hide
237 */
238 public InputDevice getInputDeviceByDescriptor(String descriptor) {
239 if (descriptor == null) {
240 throw new IllegalArgumentException("descriptor must not be null.");
241 }
242
243 synchronized (mInputDevicesLock) {
244 populateInputDevicesLocked();
245
246 int numDevices = mInputDevices.size();
247 for (int i = 0; i < numDevices; i++) {
248 InputDevice inputDevice = mInputDevices.valueAt(i);
249 if (inputDevice == null) {
250 int id = mInputDevices.keyAt(i);
251 try {
252 inputDevice = mIm.getInputDevice(id);
253 } catch (RemoteException ex) {
254 // Ignore the problem for the purposes of this method.
Jeff Brown8f1248422013-01-30 16:23:40 -0800255 }
256 if (inputDevice == null) {
Jeff Browncf39bdf2012-05-18 14:41:19 -0700257 continue;
258 }
259 mInputDevices.setValueAt(i, inputDevice);
260 }
261 if (descriptor.equals(inputDevice.getDescriptor())) {
262 return inputDevice;
263 }
264 }
265 return null;
266 }
267 }
268
269 /**
Jeff Brownaf9e8d32012-04-12 17:32:48 -0700270 * Gets the ids of all input devices in the system.
271 * @return The input device ids.
272 */
273 public int[] getInputDeviceIds() {
274 synchronized (mInputDevicesLock) {
275 populateInputDevicesLocked();
276
277 final int count = mInputDevices.size();
278 final int[] ids = new int[count];
279 for (int i = 0; i < count; i++) {
280 ids[i] = mInputDevices.keyAt(i);
281 }
282 return ids;
283 }
284 }
285
286 /**
287 * Registers an input device listener to receive notifications about when
288 * input devices are added, removed or changed.
289 *
290 * @param listener The listener to register.
291 * @param handler The handler on which the listener should be invoked, or null
292 * if the listener should be invoked on the calling thread's looper.
293 *
294 * @see #unregisterInputDeviceListener
295 */
296 public void registerInputDeviceListener(InputDeviceListener listener, Handler handler) {
297 if (listener == null) {
298 throw new IllegalArgumentException("listener must not be null");
299 }
300
301 synchronized (mInputDevicesLock) {
302 int index = findInputDeviceListenerLocked(listener);
303 if (index < 0) {
304 mInputDeviceListeners.add(new InputDeviceListenerDelegate(listener, handler));
305 }
306 }
307 }
308
309 /**
310 * Unregisters an input device listener.
311 *
312 * @param listener The listener to unregister.
313 *
314 * @see #registerInputDeviceListener
315 */
316 public void unregisterInputDeviceListener(InputDeviceListener listener) {
317 if (listener == null) {
318 throw new IllegalArgumentException("listener must not be null");
319 }
320
321 synchronized (mInputDevicesLock) {
322 int index = findInputDeviceListenerLocked(listener);
323 if (index >= 0) {
Jeff Brown9e6d4b02012-04-20 13:08:27 -0700324 InputDeviceListenerDelegate d = mInputDeviceListeners.get(index);
325 d.removeCallbacksAndMessages(null);
Jeff Brownaf9e8d32012-04-12 17:32:48 -0700326 mInputDeviceListeners.remove(index);
327 }
328 }
329 }
330
331 private int findInputDeviceListenerLocked(InputDeviceListener listener) {
332 final int numListeners = mInputDeviceListeners.size();
333 for (int i = 0; i < numListeners; i++) {
334 if (mInputDeviceListeners.get(i).mListener == listener) {
335 return i;
336 }
337 }
338 return -1;
339 }
340
341 /**
Michael Wright39e5e942015-08-19 22:52:47 +0100342 * Register a tablet mode changed listener.
343 *
344 * @param listener The listener to register.
345 * @param handler The handler on which the listener should be invoked, or null
346 * if the listener should be invoked on the calling thread's looper.
347 * @hide
348 */
349 public void registerOnTabletModeChangedListener(
350 OnTabletModeChangedListener listener, Handler handler) {
351 if (listener == null) {
352 throw new IllegalArgumentException("listener must not be null");
353 }
354 synchronized (mTabletModeLock) {
355 if (mOnTabletModeChangedListeners == null) {
356 initializeTabletModeListenerLocked();
357 }
358 int idx = findOnTabletModeChangedListenerLocked(listener);
359 if (idx < 0) {
360 OnTabletModeChangedListenerDelegate d =
361 new OnTabletModeChangedListenerDelegate(listener, handler);
362 mOnTabletModeChangedListeners.add(d);
363 }
364 }
365 }
366
367 /**
368 * Unregister a tablet mode changed listener.
369 *
370 * @param listener The listener to unregister.
371 * @hide
372 */
373 public void unregisterOnTabletModeChangedListener(OnTabletModeChangedListener listener) {
374 if (listener == null) {
375 throw new IllegalArgumentException("listener must not be null");
376 }
377 synchronized (mTabletModeLock) {
378 int idx = findOnTabletModeChangedListenerLocked(listener);
379 if (idx >= 0) {
380 OnTabletModeChangedListenerDelegate d = mOnTabletModeChangedListeners.remove(idx);
381 d.removeCallbacksAndMessages(null);
382 }
383 }
384 }
385
386 private void initializeTabletModeListenerLocked() {
387 final TabletModeChangedListener listener = new TabletModeChangedListener();
388 try {
389 mIm.registerTabletModeChangedListener(listener);
390 } catch (RemoteException ex) {
391 throw new RuntimeException("Could not register tablet mode changed listener", ex);
392 }
393 mTabletModeChangedListener = listener;
394 mOnTabletModeChangedListeners = new ArrayList<>();
395 }
396
397 private int findOnTabletModeChangedListenerLocked(OnTabletModeChangedListener listener) {
398 final int N = mOnTabletModeChangedListeners.size();
399 for (int i = 0; i < N; i++) {
400 if (mOnTabletModeChangedListeners.get(i).mListener == listener) {
401 return i;
402 }
403 }
404 return -1;
405 }
406
407 /**
Jeff Brown9df6e7a2012-04-05 11:49:26 -0700408 * Gets information about all supported keyboard layouts.
409 * <p>
410 * The input manager consults the built-in keyboard layouts as well
411 * as all keyboard layouts advertised by applications using a
412 * {@link #ACTION_QUERY_KEYBOARD_LAYOUTS} broadcast receiver.
413 * </p>
414 *
415 * @return A list of all supported keyboard layouts.
Jeff Brown9f25b7f2012-04-10 14:30:49 -0700416 *
Jeff Brown9df6e7a2012-04-05 11:49:26 -0700417 * @hide
418 */
Jeff Brown9f25b7f2012-04-10 14:30:49 -0700419 public KeyboardLayout[] getKeyboardLayouts() {
420 try {
421 return mIm.getKeyboardLayouts();
422 } catch (RemoteException ex) {
423 Log.w(TAG, "Could not get list of keyboard layout informations.", ex);
424 return new KeyboardLayout[0];
Jeff Brown9df6e7a2012-04-05 11:49:26 -0700425 }
Jeff Brown9df6e7a2012-04-05 11:49:26 -0700426 }
427
428 /**
429 * Gets the keyboard layout with the specified descriptor.
430 *
431 * @param keyboardLayoutDescriptor The keyboard layout descriptor, as returned by
432 * {@link KeyboardLayout#getDescriptor()}.
433 * @return The keyboard layout, or null if it could not be loaded.
434 *
435 * @hide
436 */
437 public KeyboardLayout getKeyboardLayout(String keyboardLayoutDescriptor) {
438 if (keyboardLayoutDescriptor == null) {
439 throw new IllegalArgumentException("keyboardLayoutDescriptor must not be null");
440 }
441
Jeff Brown9df6e7a2012-04-05 11:49:26 -0700442 try {
Jeff Brown9f25b7f2012-04-10 14:30:49 -0700443 return mIm.getKeyboardLayout(keyboardLayoutDescriptor);
444 } catch (RemoteException ex) {
445 Log.w(TAG, "Could not get keyboard layout information.", ex);
Jeff Brown9df6e7a2012-04-05 11:49:26 -0700446 return null;
447 }
448 }
449
450 /**
RoboErikfb290df2013-12-16 11:27:55 -0800451 * Gets the current keyboard layout descriptor for the specified input
452 * device.
Jeff Brown9df6e7a2012-04-05 11:49:26 -0700453 *
RoboErikfb290df2013-12-16 11:27:55 -0800454 * @param identifier Identifier for the input device
455 * @return The keyboard layout descriptor, or null if no keyboard layout has
456 * been set.
Jeff Brown9df6e7a2012-04-05 11:49:26 -0700457 * @hide
458 */
RoboErikfb290df2013-12-16 11:27:55 -0800459 public String getCurrentKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier) {
Jeff Brown9f25b7f2012-04-10 14:30:49 -0700460 try {
RoboErikfb290df2013-12-16 11:27:55 -0800461 return mIm.getCurrentKeyboardLayoutForInputDevice(identifier);
Jeff Brown9f25b7f2012-04-10 14:30:49 -0700462 } catch (RemoteException ex) {
Jeff Browncf39bdf2012-05-18 14:41:19 -0700463 Log.w(TAG, "Could not get current keyboard layout for input device.", ex);
Jeff Brown9f25b7f2012-04-10 14:30:49 -0700464 return null;
465 }
Jeff Brown9df6e7a2012-04-05 11:49:26 -0700466 }
467
468 /**
RoboErikfb290df2013-12-16 11:27:55 -0800469 * Sets the current keyboard layout descriptor for the specified input
470 * device.
Jeff Brown9df6e7a2012-04-05 11:49:26 -0700471 * <p>
RoboErikfb290df2013-12-16 11:27:55 -0800472 * This method may have the side-effect of causing the input device in
473 * question to be reconfigured.
Jeff Brown9df6e7a2012-04-05 11:49:26 -0700474 * </p>
475 *
RoboErikfb290df2013-12-16 11:27:55 -0800476 * @param identifier The identifier for the input device.
477 * @param keyboardLayoutDescriptor The keyboard layout descriptor to use,
478 * must not be null.
Jeff Brown9df6e7a2012-04-05 11:49:26 -0700479 * @hide
480 */
RoboErikfb290df2013-12-16 11:27:55 -0800481 public void setCurrentKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier,
Jeff Brown9df6e7a2012-04-05 11:49:26 -0700482 String keyboardLayoutDescriptor) {
RoboErikfb290df2013-12-16 11:27:55 -0800483 if (identifier == null) {
484 throw new IllegalArgumentException("identifier must not be null");
Jeff Brown9df6e7a2012-04-05 11:49:26 -0700485 }
Jeff Browncf39bdf2012-05-18 14:41:19 -0700486 if (keyboardLayoutDescriptor == null) {
487 throw new IllegalArgumentException("keyboardLayoutDescriptor must not be null");
488 }
489
490 try {
RoboErikfb290df2013-12-16 11:27:55 -0800491 mIm.setCurrentKeyboardLayoutForInputDevice(identifier,
Jeff Browncf39bdf2012-05-18 14:41:19 -0700492 keyboardLayoutDescriptor);
493 } catch (RemoteException ex) {
494 Log.w(TAG, "Could not set current keyboard layout for input device.", ex);
495 }
496 }
497
498 /**
RoboErikfb290df2013-12-16 11:27:55 -0800499 * Gets all keyboard layout descriptors that are enabled for the specified
500 * input device.
Jeff Browncf39bdf2012-05-18 14:41:19 -0700501 *
RoboErikfb290df2013-12-16 11:27:55 -0800502 * @param identifier The identifier for the input device.
Jeff Browncf39bdf2012-05-18 14:41:19 -0700503 * @return The keyboard layout descriptors.
Jeff Browncf39bdf2012-05-18 14:41:19 -0700504 * @hide
505 */
RoboErikfb290df2013-12-16 11:27:55 -0800506 public String[] getKeyboardLayoutsForInputDevice(InputDeviceIdentifier identifier) {
507 if (identifier == null) {
Jeff Browncf39bdf2012-05-18 14:41:19 -0700508 throw new IllegalArgumentException("inputDeviceDescriptor must not be null");
509 }
Jeff Brown9df6e7a2012-04-05 11:49:26 -0700510
Jeff Brown9df6e7a2012-04-05 11:49:26 -0700511 try {
RoboErikfb290df2013-12-16 11:27:55 -0800512 return mIm.getKeyboardLayoutsForInputDevice(identifier);
Jeff Brown9f25b7f2012-04-10 14:30:49 -0700513 } catch (RemoteException ex) {
Jeff Browncf39bdf2012-05-18 14:41:19 -0700514 Log.w(TAG, "Could not get keyboard layouts for input device.", ex);
515 return ArrayUtils.emptyArray(String.class);
516 }
517 }
518
519 /**
520 * Adds the keyboard layout descriptor for the specified input device.
521 * <p>
RoboErikfb290df2013-12-16 11:27:55 -0800522 * This method may have the side-effect of causing the input device in
523 * question to be reconfigured.
Jeff Browncf39bdf2012-05-18 14:41:19 -0700524 * </p>
525 *
RoboErikfb290df2013-12-16 11:27:55 -0800526 * @param identifier The identifier for the input device.
527 * @param keyboardLayoutDescriptor The descriptor of the keyboard layout to
528 * add.
Jeff Browncf39bdf2012-05-18 14:41:19 -0700529 * @hide
530 */
RoboErikfb290df2013-12-16 11:27:55 -0800531 public void addKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier,
Jeff Browncf39bdf2012-05-18 14:41:19 -0700532 String keyboardLayoutDescriptor) {
RoboErikfb290df2013-12-16 11:27:55 -0800533 if (identifier == null) {
Jeff Browncf39bdf2012-05-18 14:41:19 -0700534 throw new IllegalArgumentException("inputDeviceDescriptor must not be null");
535 }
536 if (keyboardLayoutDescriptor == null) {
537 throw new IllegalArgumentException("keyboardLayoutDescriptor must not be null");
538 }
539
540 try {
RoboErikfb290df2013-12-16 11:27:55 -0800541 mIm.addKeyboardLayoutForInputDevice(identifier, keyboardLayoutDescriptor);
Jeff Browncf39bdf2012-05-18 14:41:19 -0700542 } catch (RemoteException ex) {
543 Log.w(TAG, "Could not add keyboard layout for input device.", ex);
544 }
545 }
546
547 /**
548 * Removes the keyboard layout descriptor for the specified input device.
549 * <p>
RoboErikfb290df2013-12-16 11:27:55 -0800550 * This method may have the side-effect of causing the input device in
551 * question to be reconfigured.
Jeff Browncf39bdf2012-05-18 14:41:19 -0700552 * </p>
553 *
RoboErikfb290df2013-12-16 11:27:55 -0800554 * @param identifier The identifier for the input device.
555 * @param keyboardLayoutDescriptor The descriptor of the keyboard layout to
556 * remove.
Jeff Browncf39bdf2012-05-18 14:41:19 -0700557 * @hide
558 */
RoboErikfb290df2013-12-16 11:27:55 -0800559 public void removeKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier,
Jeff Browncf39bdf2012-05-18 14:41:19 -0700560 String keyboardLayoutDescriptor) {
RoboErikfb290df2013-12-16 11:27:55 -0800561 if (identifier == null) {
Jeff Browncf39bdf2012-05-18 14:41:19 -0700562 throw new IllegalArgumentException("inputDeviceDescriptor must not be null");
563 }
564 if (keyboardLayoutDescriptor == null) {
565 throw new IllegalArgumentException("keyboardLayoutDescriptor must not be null");
566 }
567
568 try {
RoboErikfb290df2013-12-16 11:27:55 -0800569 mIm.removeKeyboardLayoutForInputDevice(identifier, keyboardLayoutDescriptor);
Jeff Browncf39bdf2012-05-18 14:41:19 -0700570 } catch (RemoteException ex) {
571 Log.w(TAG, "Could not remove keyboard layout for input device.", ex);
Jeff Brown9df6e7a2012-04-05 11:49:26 -0700572 }
Jeff Brown9df6e7a2012-04-05 11:49:26 -0700573 }
574
Jeff Brownac143512012-04-05 18:57:33 -0700575 /**
Jason Gerecked6396d62014-01-27 18:30:37 -0800576 * Gets the TouchCalibration applied to the specified input device's coordinates.
577 *
578 * @param inputDeviceDescriptor The input device descriptor.
579 * @return The TouchCalibration currently assigned for use with the given
580 * input device. If none is set, an identity TouchCalibration is returned.
581 *
582 * @hide
583 */
Jason Gerecked5220742014-03-10 09:47:59 -0700584 public TouchCalibration getTouchCalibration(String inputDeviceDescriptor, int surfaceRotation) {
Jason Gerecked6396d62014-01-27 18:30:37 -0800585 try {
Jason Gerecked5220742014-03-10 09:47:59 -0700586 return mIm.getTouchCalibrationForInputDevice(inputDeviceDescriptor, surfaceRotation);
Jason Gerecked6396d62014-01-27 18:30:37 -0800587 } catch (RemoteException ex) {
588 Log.w(TAG, "Could not get calibration matrix for input device.", ex);
589 return TouchCalibration.IDENTITY;
590 }
591 }
592
593 /**
594 * Sets the TouchCalibration to apply to the specified input device's coordinates.
595 * <p>
596 * This method may have the side-effect of causing the input device in question
597 * to be reconfigured. Requires {@link android.Manifest.permissions.SET_INPUT_CALIBRATION}.
598 * </p>
599 *
600 * @param inputDeviceDescriptor The input device descriptor.
601 * @param calibration The calibration to be applied
602 *
603 * @hide
604 */
Jason Gerecked5220742014-03-10 09:47:59 -0700605 public void setTouchCalibration(String inputDeviceDescriptor, int surfaceRotation,
606 TouchCalibration calibration) {
Jason Gerecked6396d62014-01-27 18:30:37 -0800607 try {
Jason Gerecked5220742014-03-10 09:47:59 -0700608 mIm.setTouchCalibrationForInputDevice(inputDeviceDescriptor, surfaceRotation, calibration);
Jason Gerecked6396d62014-01-27 18:30:37 -0800609 } catch (RemoteException ex) {
610 Log.w(TAG, "Could not set calibration matrix for input device.", ex);
611 }
612 }
613
614 /**
Jeff Brownac143512012-04-05 18:57:33 -0700615 * Gets the mouse pointer speed.
616 * <p>
617 * Only returns the permanent mouse pointer speed. Ignores any temporary pointer
618 * speed set by {@link #tryPointerSpeed}.
619 * </p>
620 *
Jeff Brown9f25b7f2012-04-10 14:30:49 -0700621 * @param context The application context.
Jeff Brownac143512012-04-05 18:57:33 -0700622 * @return The pointer speed as a value between {@link #MIN_POINTER_SPEED} and
623 * {@link #MAX_POINTER_SPEED}, or the default value {@link #DEFAULT_POINTER_SPEED}.
624 *
625 * @hide
626 */
Jeff Brown9f25b7f2012-04-10 14:30:49 -0700627 public int getPointerSpeed(Context context) {
Jeff Brownac143512012-04-05 18:57:33 -0700628 int speed = DEFAULT_POINTER_SPEED;
629 try {
Jeff Brown9f25b7f2012-04-10 14:30:49 -0700630 speed = Settings.System.getInt(context.getContentResolver(),
Jeff Brownac143512012-04-05 18:57:33 -0700631 Settings.System.POINTER_SPEED);
632 } catch (SettingNotFoundException snfe) {
633 }
634 return speed;
635 }
636
637 /**
638 * Sets the mouse pointer speed.
639 * <p>
640 * Requires {@link android.Manifest.permissions.WRITE_SETTINGS}.
641 * </p>
642 *
Jeff Brown9f25b7f2012-04-10 14:30:49 -0700643 * @param context The application context.
Jeff Brownac143512012-04-05 18:57:33 -0700644 * @param speed The pointer speed as a value between {@link #MIN_POINTER_SPEED} and
645 * {@link #MAX_POINTER_SPEED}, or the default value {@link #DEFAULT_POINTER_SPEED}.
646 *
647 * @hide
648 */
Jeff Brown9f25b7f2012-04-10 14:30:49 -0700649 public void setPointerSpeed(Context context, int speed) {
Jeff Brownac143512012-04-05 18:57:33 -0700650 if (speed < MIN_POINTER_SPEED || speed > MAX_POINTER_SPEED) {
651 throw new IllegalArgumentException("speed out of range");
652 }
653
Jeff Brown9f25b7f2012-04-10 14:30:49 -0700654 Settings.System.putInt(context.getContentResolver(),
Jeff Brownac143512012-04-05 18:57:33 -0700655 Settings.System.POINTER_SPEED, speed);
656 }
657
658 /**
659 * Changes the mouse pointer speed temporarily, but does not save the setting.
660 * <p>
661 * Requires {@link android.Manifest.permission.SET_POINTER_SPEED}.
662 * </p>
663 *
664 * @param speed The pointer speed as a value between {@link #MIN_POINTER_SPEED} and
665 * {@link #MAX_POINTER_SPEED}, or the default value {@link #DEFAULT_POINTER_SPEED}.
666 *
667 * @hide
668 */
669 public void tryPointerSpeed(int speed) {
670 if (speed < MIN_POINTER_SPEED || speed > MAX_POINTER_SPEED) {
671 throw new IllegalArgumentException("speed out of range");
672 }
673
674 try {
Jeff Brown9f25b7f2012-04-10 14:30:49 -0700675 mIm.tryPointerSpeed(speed);
Jeff Brownac143512012-04-05 18:57:33 -0700676 } catch (RemoteException ex) {
677 Log.w(TAG, "Could not set temporary pointer speed.", ex);
678 }
679 }
680
681 /**
Jeff Brownac143512012-04-05 18:57:33 -0700682 * Queries the framework about whether any physical keys exist on the
683 * any keyboard attached to the device that are capable of producing the given
684 * array of key codes.
685 *
686 * @param keyCodes The array of key codes to query.
687 * @return A new array of the same size as the key codes array whose elements
688 * are set to true if at least one attached keyboard supports the corresponding key code
689 * at the same index in the key codes array.
690 *
691 * @hide
692 */
Jeff Brown9f25b7f2012-04-10 14:30:49 -0700693 public boolean[] deviceHasKeys(int[] keyCodes) {
Michael Wrightb7b2d4b2013-08-19 15:55:38 -0700694 return deviceHasKeys(-1, keyCodes);
695 }
696
697 /**
698 * Queries the framework about whether any physical keys exist on the
699 * any keyboard attached to the device that are capable of producing the given
700 * array of key codes.
701 *
702 * @param id The id of the device to query.
703 * @param keyCodes The array of key codes to query.
704 * @return A new array of the same size as the key codes array whose elements are set to true
705 * if the given device could produce the corresponding key code at the same index in the key
706 * codes array.
707 *
708 * @hide
709 */
710 public boolean[] deviceHasKeys(int id, int[] keyCodes) {
Jeff Brownac143512012-04-05 18:57:33 -0700711 boolean[] ret = new boolean[keyCodes.length];
712 try {
Michael Wrightb7b2d4b2013-08-19 15:55:38 -0700713 mIm.hasKeys(id, InputDevice.SOURCE_ANY, keyCodes, ret);
Jeff Brownac143512012-04-05 18:57:33 -0700714 } catch (RemoteException e) {
715 // no fallback; just return the empty array
716 }
717 return ret;
718 }
719
Michael Wrightb7b2d4b2013-08-19 15:55:38 -0700720
Jeff Brownac143512012-04-05 18:57:33 -0700721 /**
722 * Injects an input event into the event system on behalf of an application.
723 * The synchronization mode determines whether the method blocks while waiting for
724 * input injection to proceed.
725 * <p>
726 * Requires {@link android.Manifest.permission.INJECT_EVENTS} to inject into
727 * windows that are owned by other applications.
728 * </p><p>
729 * Make sure you correctly set the event time and input source of the event
730 * before calling this method.
731 * </p>
732 *
733 * @param event The event to inject.
734 * @param mode The synchronization mode. One of:
735 * {@link #INJECT_INPUT_EVENT_MODE_ASYNC},
736 * {@link #INJECT_INPUT_EVENT_MODE_WAIT_FOR_RESULT}, or
737 * {@link #INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH}.
738 * @return True if input event injection succeeded.
739 *
740 * @hide
741 */
Jeff Brown9f25b7f2012-04-10 14:30:49 -0700742 public boolean injectInputEvent(InputEvent event, int mode) {
Jeff Brownac143512012-04-05 18:57:33 -0700743 if (event == null) {
744 throw new IllegalArgumentException("event must not be null");
745 }
746 if (mode != INJECT_INPUT_EVENT_MODE_ASYNC
747 && mode != INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH
748 && mode != INJECT_INPUT_EVENT_MODE_WAIT_FOR_RESULT) {
749 throw new IllegalArgumentException("mode is invalid");
750 }
751
752 try {
Jeff Brown9f25b7f2012-04-10 14:30:49 -0700753 return mIm.injectInputEvent(event, mode);
Jeff Brownac143512012-04-05 18:57:33 -0700754 } catch (RemoteException ex) {
755 return false;
756 }
757 }
Jeff Brownaf9e8d32012-04-12 17:32:48 -0700758
759 private void populateInputDevicesLocked() {
760 if (mInputDevicesChangedListener == null) {
761 final InputDevicesChangedListener listener = new InputDevicesChangedListener();
762 try {
763 mIm.registerInputDevicesChangedListener(listener);
764 } catch (RemoteException ex) {
765 throw new RuntimeException(
766 "Could not get register input device changed listener", ex);
767 }
768 mInputDevicesChangedListener = listener;
769 }
770
771 if (mInputDevices == null) {
772 final int[] ids;
773 try {
774 ids = mIm.getInputDeviceIds();
775 } catch (RemoteException ex) {
776 throw new RuntimeException("Could not get input device ids.", ex);
777 }
778
779 mInputDevices = new SparseArray<InputDevice>();
780 for (int i = 0; i < ids.length; i++) {
781 mInputDevices.put(ids[i], null);
782 }
783 }
784 }
785
786 private void onInputDevicesChanged(int[] deviceIdAndGeneration) {
787 if (DEBUG) {
788 Log.d(TAG, "Received input devices changed.");
789 }
790
791 synchronized (mInputDevicesLock) {
792 for (int i = mInputDevices.size(); --i > 0; ) {
793 final int deviceId = mInputDevices.keyAt(i);
794 if (!containsDeviceId(deviceIdAndGeneration, deviceId)) {
795 if (DEBUG) {
796 Log.d(TAG, "Device removed: " + deviceId);
797 }
798 mInputDevices.removeAt(i);
799 sendMessageToInputDeviceListenersLocked(MSG_DEVICE_REMOVED, deviceId);
800 }
801 }
802
803 for (int i = 0; i < deviceIdAndGeneration.length; i += 2) {
804 final int deviceId = deviceIdAndGeneration[i];
805 int index = mInputDevices.indexOfKey(deviceId);
806 if (index >= 0) {
807 final InputDevice device = mInputDevices.valueAt(index);
808 if (device != null) {
809 final int generation = deviceIdAndGeneration[i + 1];
810 if (device.getGeneration() != generation) {
811 if (DEBUG) {
812 Log.d(TAG, "Device changed: " + deviceId);
813 }
814 mInputDevices.setValueAt(index, null);
815 sendMessageToInputDeviceListenersLocked(MSG_DEVICE_CHANGED, deviceId);
816 }
817 }
818 } else {
819 if (DEBUG) {
820 Log.d(TAG, "Device added: " + deviceId);
821 }
822 mInputDevices.put(deviceId, null);
823 sendMessageToInputDeviceListenersLocked(MSG_DEVICE_ADDED, deviceId);
824 }
825 }
826 }
827 }
828
829 private void sendMessageToInputDeviceListenersLocked(int what, int deviceId) {
830 final int numListeners = mInputDeviceListeners.size();
831 for (int i = 0; i < numListeners; i++) {
832 InputDeviceListenerDelegate listener = mInputDeviceListeners.get(i);
833 listener.sendMessage(listener.obtainMessage(what, deviceId, 0));
834 }
835 }
836
837 private static boolean containsDeviceId(int[] deviceIdAndGeneration, int deviceId) {
838 for (int i = 0; i < deviceIdAndGeneration.length; i += 2) {
839 if (deviceIdAndGeneration[i] == deviceId) {
840 return true;
841 }
842 }
843 return false;
844 }
845
Michael Wright39e5e942015-08-19 22:52:47 +0100846
847 private void onTabletModeChanged(long whenNanos, boolean inTabletMode) {
848 if (DEBUG) {
849 Log.d(TAG, "Received tablet mode changed: "
850 + "whenNanos=" + whenNanos + ", inTabletMode=" + inTabletMode);
851 }
852 synchronized (mTabletModeLock) {
853 final int N = mOnTabletModeChangedListeners.size();
854 for (int i = 0; i < N; i++) {
855 OnTabletModeChangedListenerDelegate listener =
856 mOnTabletModeChangedListeners.get(i);
857 listener.sendTabletModeChanged(whenNanos, inTabletMode);
858 }
859 }
860 }
861
Jeff Brownaf9e8d32012-04-12 17:32:48 -0700862 /**
Jeff Browna47425a2012-04-13 04:09:27 -0700863 * Gets a vibrator service associated with an input device, assuming it has one.
864 * @return The vibrator, never null.
865 * @hide
866 */
867 public Vibrator getInputDeviceVibrator(int deviceId) {
868 return new InputDeviceVibrator(deviceId);
869 }
870
871 /**
Jeff Brownaf9e8d32012-04-12 17:32:48 -0700872 * Listens for changes in input devices.
873 */
874 public interface InputDeviceListener {
875 /**
876 * Called whenever an input device has been added to the system.
877 * Use {@link InputManager#getInputDevice} to get more information about the device.
878 *
879 * @param deviceId The id of the input device that was added.
880 */
881 void onInputDeviceAdded(int deviceId);
882
883 /**
884 * Called whenever an input device has been removed from the system.
885 *
886 * @param deviceId The id of the input device that was removed.
887 */
888 void onInputDeviceRemoved(int deviceId);
889
890 /**
891 * Called whenever the properties of an input device have changed since they
892 * were last queried. Use {@link InputManager#getInputDevice} to get
893 * a fresh {@link InputDevice} object with the new properties.
894 *
895 * @param deviceId The id of the input device that changed.
896 */
897 void onInputDeviceChanged(int deviceId);
898 }
899
900 private final class InputDevicesChangedListener extends IInputDevicesChangedListener.Stub {
901 @Override
902 public void onInputDevicesChanged(int[] deviceIdAndGeneration) throws RemoteException {
903 InputManager.this.onInputDevicesChanged(deviceIdAndGeneration);
904 }
905 }
906
907 private static final class InputDeviceListenerDelegate extends Handler {
908 public final InputDeviceListener mListener;
909
910 public InputDeviceListenerDelegate(InputDeviceListener listener, Handler handler) {
911 super(handler != null ? handler.getLooper() : Looper.myLooper());
912 mListener = listener;
913 }
914
915 @Override
916 public void handleMessage(Message msg) {
917 switch (msg.what) {
918 case MSG_DEVICE_ADDED:
919 mListener.onInputDeviceAdded(msg.arg1);
920 break;
921 case MSG_DEVICE_REMOVED:
922 mListener.onInputDeviceRemoved(msg.arg1);
923 break;
924 case MSG_DEVICE_CHANGED:
925 mListener.onInputDeviceChanged(msg.arg1);
926 break;
927 }
928 }
929 }
Jeff Browna47425a2012-04-13 04:09:27 -0700930
Michael Wright39e5e942015-08-19 22:52:47 +0100931 /** @hide */
932 public interface OnTabletModeChangedListener {
933 /**
934 * Called whenever the device goes into or comes out of tablet mode.
935 *
936 * @param whenNanos The time at which the device transitioned into or
937 * out of tablet mode. This is given in nanoseconds in the
938 * {@link SystemClock#uptimeMillis} time base.
939 */
940 void onTabletModeChanged(long whenNanos, boolean inTabletMode);
941 }
942
943 private final class TabletModeChangedListener extends ITabletModeChangedListener.Stub {
944 @Override
945 public void onTabletModeChanged(long whenNanos, boolean inTabletMode) {
946 InputManager.this.onTabletModeChanged(whenNanos, inTabletMode);
947 }
948 }
949
950 private static final class OnTabletModeChangedListenerDelegate extends Handler {
951 private static final int MSG_TABLET_MODE_CHANGED = 0;
952
953 public final OnTabletModeChangedListener mListener;
954
955 public OnTabletModeChangedListenerDelegate(
956 OnTabletModeChangedListener listener, Handler handler) {
957 super(handler != null ? handler.getLooper() : Looper.myLooper());
958 mListener = listener;
959 }
960
961 public void sendTabletModeChanged(long whenNanos, boolean inTabletMode) {
962 SomeArgs args = SomeArgs.obtain();
963 args.argi1 = (int) (whenNanos & 0xFFFFFFFF);
964 args.argi2 = (int) (whenNanos >> 32);
965 args.arg1 = (Boolean) inTabletMode;
966 obtainMessage(MSG_TABLET_MODE_CHANGED, args).sendToTarget();
967 }
968
969 @Override
970 public void handleMessage(Message msg) {
971 switch (msg.what) {
972 case MSG_TABLET_MODE_CHANGED:
973 SomeArgs args = (SomeArgs) msg.obj;
974 long whenNanos = (args.argi1 & 0xFFFFFFFFl) | ((long) args.argi2 << 32);
975 boolean inTabletMode = (boolean) args.arg1;
976 mListener.onTabletModeChanged(whenNanos, inTabletMode);
977 break;
978 }
979 }
980 }
981
Jeff Browna47425a2012-04-13 04:09:27 -0700982 private final class InputDeviceVibrator extends Vibrator {
983 private final int mDeviceId;
984 private final Binder mToken;
985
986 public InputDeviceVibrator(int deviceId) {
987 mDeviceId = deviceId;
988 mToken = new Binder();
989 }
990
991 @Override
992 public boolean hasVibrator() {
993 return true;
994 }
995
John Spurlock1af30c72014-03-10 08:33:35 -0400996 /**
997 * @hide
998 */
Jeff Browna47425a2012-04-13 04:09:27 -0700999 @Override
John Spurlock7b414672014-07-18 13:02:39 -04001000 public void vibrate(int uid, String opPkg, long milliseconds, AudioAttributes attributes) {
Jeff Browna47425a2012-04-13 04:09:27 -07001001 vibrate(new long[] { 0, milliseconds}, -1);
1002 }
1003
John Spurlock1af30c72014-03-10 08:33:35 -04001004 /**
1005 * @hide
1006 */
Jeff Browna47425a2012-04-13 04:09:27 -07001007 @Override
Christoph Studer8fd7f1e2014-04-11 17:35:05 -04001008 public void vibrate(int uid, String opPkg, long[] pattern, int repeat,
John Spurlock7b414672014-07-18 13:02:39 -04001009 AudioAttributes attributes) {
Jeff Browna47425a2012-04-13 04:09:27 -07001010 if (repeat >= pattern.length) {
1011 throw new ArrayIndexOutOfBoundsException();
1012 }
1013 try {
1014 mIm.vibrate(mDeviceId, pattern, repeat, mToken);
1015 } catch (RemoteException ex) {
1016 Log.w(TAG, "Failed to vibrate.", ex);
1017 }
1018 }
1019
1020 @Override
1021 public void cancel() {
1022 try {
1023 mIm.cancelVibrate(mDeviceId, mToken);
1024 } catch (RemoteException ex) {
1025 Log.w(TAG, "Failed to cancel vibration.", ex);
1026 }
1027 }
1028 }
Jeff Brown9df6e7a2012-04-05 11:49:26 -07001029}