• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2010 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 com.android.server.input;
18 
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.os.Build;
22 import android.os.LocaleList;
23 import android.util.Log;
24 import android.view.Display;
25 import com.android.internal.inputmethod.InputMethodSubtypeHandle;
26 import com.android.internal.os.SomeArgs;
27 import com.android.internal.R;
28 import com.android.internal.util.Preconditions;
29 import com.android.internal.util.XmlUtils;
30 import com.android.server.DisplayThread;
31 import com.android.server.LocalServices;
32 import com.android.server.Watchdog;
33 
34 import org.xmlpull.v1.XmlPullParser;
35 
36 import android.Manifest;
37 import android.app.Notification;
38 import android.app.NotificationManager;
39 import android.app.PendingIntent;
40 import android.bluetooth.BluetoothAdapter;
41 import android.bluetooth.BluetoothDevice;
42 import android.content.BroadcastReceiver;
43 import android.content.ComponentName;
44 import android.content.Context;
45 import android.content.Intent;
46 import android.content.IntentFilter;
47 import android.content.pm.ActivityInfo;
48 import android.content.pm.ApplicationInfo;
49 import android.content.pm.PackageManager;
50 import android.content.pm.ResolveInfo;
51 import android.content.pm.PackageManager.NameNotFoundException;
52 import android.content.res.Resources;
53 import android.content.res.Resources.NotFoundException;
54 import android.content.res.TypedArray;
55 import android.content.res.XmlResourceParser;
56 import android.database.ContentObserver;
57 import android.hardware.display.DisplayViewport;
58 import android.hardware.input.IInputDevicesChangedListener;
59 import android.hardware.input.IInputManager;
60 import android.hardware.input.InputDeviceIdentifier;
61 import android.hardware.input.InputManager;
62 import android.hardware.input.InputManagerInternal;
63 import android.hardware.input.ITabletModeChangedListener;
64 import android.hardware.input.KeyboardLayout;
65 import android.hardware.input.TouchCalibration;
66 import android.os.Binder;
67 import android.os.Bundle;
68 import android.os.Environment;
69 import android.os.Handler;
70 import android.os.IBinder;
71 import android.os.Looper;
72 import android.os.Message;
73 import android.os.MessageQueue;
74 import android.os.Process;
75 import android.os.RemoteException;
76 import android.os.ResultReceiver;
77 import android.os.ShellCommand;
78 import android.os.UserHandle;
79 import android.provider.Settings;
80 import android.provider.Settings.SettingNotFoundException;
81 import android.text.TextUtils;
82 import android.util.Slog;
83 import android.util.SparseArray;
84 import android.util.Xml;
85 import android.view.IInputFilter;
86 import android.view.IInputFilterHost;
87 import android.view.InputChannel;
88 import android.view.InputDevice;
89 import android.view.InputEvent;
90 import android.view.KeyEvent;
91 import android.view.PointerIcon;
92 import android.view.Surface;
93 import android.view.ViewConfiguration;
94 import android.view.WindowManagerPolicy;
95 import android.view.inputmethod.InputMethod;
96 import android.view.inputmethod.InputMethodInfo;
97 import android.view.inputmethod.InputMethodSubtype;
98 import android.widget.Toast;
99 
100 import java.io.File;
101 import java.io.FileDescriptor;
102 import java.io.FileNotFoundException;
103 import java.io.FileReader;
104 import java.io.FileWriter;
105 import java.io.IOException;
106 import java.io.InputStreamReader;
107 import java.io.OutputStream;
108 import java.io.OutputStreamWriter;
109 import java.io.PrintWriter;
110 import java.util.ArrayList;
111 import java.util.Arrays;
112 import java.util.Collections;
113 import java.util.HashMap;
114 import java.util.HashSet;
115 import java.util.List;
116 import java.util.Locale;
117 
118 import libcore.io.IoUtils;
119 import libcore.io.Streams;
120 import libcore.util.Objects;
121 
122 /*
123  * Wraps the C++ InputManager and provides its callbacks.
124  */
125 public class InputManagerService extends IInputManager.Stub
126         implements Watchdog.Monitor {
127     static final String TAG = "InputManager";
128     static final boolean DEBUG = false;
129 
130     private static final String EXCLUDED_DEVICES_PATH = "etc/excluded-input-devices.xml";
131 
132     private static final int MSG_DELIVER_INPUT_DEVICES_CHANGED = 1;
133     private static final int MSG_SWITCH_KEYBOARD_LAYOUT = 2;
134     private static final int MSG_RELOAD_KEYBOARD_LAYOUTS = 3;
135     private static final int MSG_UPDATE_KEYBOARD_LAYOUTS = 4;
136     private static final int MSG_RELOAD_DEVICE_ALIASES = 5;
137     private static final int MSG_DELIVER_TABLET_MODE_CHANGED = 6;
138     private static final int MSG_INPUT_METHOD_SUBTYPE_CHANGED = 7;
139 
140     // Pointer to native input manager service object.
141     private final long mPtr;
142 
143     private final Context mContext;
144     private final InputManagerHandler mHandler;
145 
146     private final File mDoubleTouchGestureEnableFile;
147 
148     private WindowManagerCallbacks mWindowManagerCallbacks;
149     private WiredAccessoryCallbacks mWiredAccessoryCallbacks;
150     private boolean mSystemReady;
151     private NotificationManager mNotificationManager;
152 
153     private final Object mTabletModeLock = new Object();
154     // List of currently registered tablet mode changed listeners by process id
155     private final SparseArray<TabletModeChangedListenerRecord> mTabletModeChangedListeners =
156             new SparseArray<>(); // guarded by mTabletModeLock
157     private final List<TabletModeChangedListenerRecord> mTempTabletModeChangedListenersToNotify =
158             new ArrayList<>();
159 
160     // Persistent data store.  Must be locked each time during use.
161     private final PersistentDataStore mDataStore = new PersistentDataStore();
162 
163     // List of currently registered input devices changed listeners by process id.
164     private Object mInputDevicesLock = new Object();
165     private boolean mInputDevicesChangedPending; // guarded by mInputDevicesLock
166     private InputDevice[] mInputDevices = new InputDevice[0];
167     private final SparseArray<InputDevicesChangedListenerRecord> mInputDevicesChangedListeners =
168             new SparseArray<InputDevicesChangedListenerRecord>(); // guarded by mInputDevicesLock
169     private final ArrayList<InputDevicesChangedListenerRecord>
170             mTempInputDevicesChangedListenersToNotify =
171                     new ArrayList<InputDevicesChangedListenerRecord>(); // handler thread only
172     private final ArrayList<InputDevice>
173             mTempFullKeyboards = new ArrayList<InputDevice>(); // handler thread only
174     private boolean mKeyboardLayoutNotificationShown;
175     private InputMethodSubtypeHandle mCurrentImeHandle;
176 
177     // State for vibrator tokens.
178     private Object mVibratorLock = new Object();
179     private HashMap<IBinder, VibratorToken> mVibratorTokens =
180             new HashMap<IBinder, VibratorToken>();
181     private int mNextVibratorTokenValue;
182 
183     // State for the currently installed input filter.
184     final Object mInputFilterLock = new Object();
185     IInputFilter mInputFilter; // guarded by mInputFilterLock
186     InputFilterHost mInputFilterHost; // guarded by mInputFilterLock
187 
nativeInit(InputManagerService service, Context context, MessageQueue messageQueue)188     private static native long nativeInit(InputManagerService service,
189             Context context, MessageQueue messageQueue);
nativeStart(long ptr)190     private static native void nativeStart(long ptr);
nativeSetDisplayViewport(long ptr, boolean external, int displayId, int rotation, int logicalLeft, int logicalTop, int logicalRight, int logicalBottom, int physicalLeft, int physicalTop, int physicalRight, int physicalBottom, int deviceWidth, int deviceHeight)191     private static native void nativeSetDisplayViewport(long ptr, boolean external,
192             int displayId, int rotation,
193             int logicalLeft, int logicalTop, int logicalRight, int logicalBottom,
194             int physicalLeft, int physicalTop, int physicalRight, int physicalBottom,
195             int deviceWidth, int deviceHeight);
196 
nativeGetScanCodeState(long ptr, int deviceId, int sourceMask, int scanCode)197     private static native int nativeGetScanCodeState(long ptr,
198             int deviceId, int sourceMask, int scanCode);
nativeGetKeyCodeState(long ptr, int deviceId, int sourceMask, int keyCode)199     private static native int nativeGetKeyCodeState(long ptr,
200             int deviceId, int sourceMask, int keyCode);
nativeGetSwitchState(long ptr, int deviceId, int sourceMask, int sw)201     private static native int nativeGetSwitchState(long ptr,
202             int deviceId, int sourceMask, int sw);
nativeHasKeys(long ptr, int deviceId, int sourceMask, int[] keyCodes, boolean[] keyExists)203     private static native boolean nativeHasKeys(long ptr,
204             int deviceId, int sourceMask, int[] keyCodes, boolean[] keyExists);
nativeRegisterInputChannel(long ptr, InputChannel inputChannel, InputWindowHandle inputWindowHandle, boolean monitor)205     private static native void nativeRegisterInputChannel(long ptr, InputChannel inputChannel,
206             InputWindowHandle inputWindowHandle, boolean monitor);
nativeUnregisterInputChannel(long ptr, InputChannel inputChannel)207     private static native void nativeUnregisterInputChannel(long ptr, InputChannel inputChannel);
nativeSetInputFilterEnabled(long ptr, boolean enable)208     private static native void nativeSetInputFilterEnabled(long ptr, boolean enable);
nativeInjectInputEvent(long ptr, InputEvent event, int displayId, int injectorPid, int injectorUid, int syncMode, int timeoutMillis, int policyFlags)209     private static native int nativeInjectInputEvent(long ptr, InputEvent event, int displayId,
210             int injectorPid, int injectorUid, int syncMode, int timeoutMillis,
211             int policyFlags);
nativeToggleCapsLock(long ptr, int deviceId)212     private static native void nativeToggleCapsLock(long ptr, int deviceId);
nativeSetInputWindows(long ptr, InputWindowHandle[] windowHandles)213     private static native void nativeSetInputWindows(long ptr, InputWindowHandle[] windowHandles);
nativeSetInputDispatchMode(long ptr, boolean enabled, boolean frozen)214     private static native void nativeSetInputDispatchMode(long ptr, boolean enabled, boolean frozen);
nativeSetSystemUiVisibility(long ptr, int visibility)215     private static native void nativeSetSystemUiVisibility(long ptr, int visibility);
nativeSetFocusedApplication(long ptr, InputApplicationHandle application)216     private static native void nativeSetFocusedApplication(long ptr,
217             InputApplicationHandle application);
nativeTransferTouchFocus(long ptr, InputChannel fromChannel, InputChannel toChannel)218     private static native boolean nativeTransferTouchFocus(long ptr,
219             InputChannel fromChannel, InputChannel toChannel);
nativeSetPointerSpeed(long ptr, int speed)220     private static native void nativeSetPointerSpeed(long ptr, int speed);
nativeSetShowTouches(long ptr, boolean enabled)221     private static native void nativeSetShowTouches(long ptr, boolean enabled);
nativeSetInteractive(long ptr, boolean interactive)222     private static native void nativeSetInteractive(long ptr, boolean interactive);
nativeReloadCalibration(long ptr)223     private static native void nativeReloadCalibration(long ptr);
nativeVibrate(long ptr, int deviceId, long[] pattern, int repeat, int token)224     private static native void nativeVibrate(long ptr, int deviceId, long[] pattern,
225             int repeat, int token);
nativeCancelVibrate(long ptr, int deviceId, int token)226     private static native void nativeCancelVibrate(long ptr, int deviceId, int token);
nativeReloadKeyboardLayouts(long ptr)227     private static native void nativeReloadKeyboardLayouts(long ptr);
nativeReloadDeviceAliases(long ptr)228     private static native void nativeReloadDeviceAliases(long ptr);
nativeDump(long ptr)229     private static native String nativeDump(long ptr);
nativeMonitor(long ptr)230     private static native void nativeMonitor(long ptr);
nativeSetPointerIconType(long ptr, int iconId)231     private static native void nativeSetPointerIconType(long ptr, int iconId);
nativeReloadPointerIcons(long ptr)232     private static native void nativeReloadPointerIcons(long ptr);
nativeSetCustomPointerIcon(long ptr, PointerIcon icon)233     private static native void nativeSetCustomPointerIcon(long ptr, PointerIcon icon);
234 
235     // Input event injection constants defined in InputDispatcher.h.
236     private static final int INPUT_EVENT_INJECTION_SUCCEEDED = 0;
237     private static final int INPUT_EVENT_INJECTION_PERMISSION_DENIED = 1;
238     private static final int INPUT_EVENT_INJECTION_FAILED = 2;
239     private static final int INPUT_EVENT_INJECTION_TIMED_OUT = 3;
240 
241     // Maximum number of milliseconds to wait for input event injection.
242     private static final int INJECTION_TIMEOUT_MILLIS = 30 * 1000;
243 
244     // Key states (may be returned by queries about the current state of a
245     // particular key code, scan code or switch).
246 
247     /** The key state is unknown or the requested key itself is not supported. */
248     public static final int KEY_STATE_UNKNOWN = -1;
249 
250     /** The key is up. /*/
251     public static final int KEY_STATE_UP = 0;
252 
253     /** The key is down. */
254     public static final int KEY_STATE_DOWN = 1;
255 
256     /** The key is down but is a virtual key press that is being emulated by the system. */
257     public static final int KEY_STATE_VIRTUAL = 2;
258 
259     /** Scan code: Mouse / trackball button. */
260     public static final int BTN_MOUSE = 0x110;
261 
262     // Switch code values must match bionic/libc/kernel/common/linux/input.h
263     /** Switch code: Lid switch.  When set, lid is shut. */
264     public static final int SW_LID = 0x00;
265 
266     /** Switch code: Tablet mode switch.
267      * When set, the device is in tablet mode (i.e. no keyboard is connected).
268      */
269     public static final int SW_TABLET_MODE = 0x01;
270 
271     /** Switch code: Keypad slide.  When set, keyboard is exposed. */
272     public static final int SW_KEYPAD_SLIDE = 0x0a;
273 
274     /** Switch code: Headphone.  When set, headphone is inserted. */
275     public static final int SW_HEADPHONE_INSERT = 0x02;
276 
277     /** Switch code: Microphone.  When set, microphone is inserted. */
278     public static final int SW_MICROPHONE_INSERT = 0x04;
279 
280     /** Switch code: Line out.  When set, Line out (hi-Z) is inserted. */
281     public static final int SW_LINEOUT_INSERT = 0x06;
282 
283     /** Switch code: Headphone/Microphone Jack.  When set, something is inserted. */
284     public static final int SW_JACK_PHYSICAL_INSERT = 0x07;
285 
286     /** Switch code: Camera lens cover. When set the lens is covered. */
287     public static final int SW_CAMERA_LENS_COVER = 0x09;
288 
289     public static final int SW_LID_BIT = 1 << SW_LID;
290     public static final int SW_TABLET_MODE_BIT = 1 << SW_TABLET_MODE;
291     public static final int SW_KEYPAD_SLIDE_BIT = 1 << SW_KEYPAD_SLIDE;
292     public static final int SW_HEADPHONE_INSERT_BIT = 1 << SW_HEADPHONE_INSERT;
293     public static final int SW_MICROPHONE_INSERT_BIT = 1 << SW_MICROPHONE_INSERT;
294     public static final int SW_LINEOUT_INSERT_BIT = 1 << SW_LINEOUT_INSERT;
295     public static final int SW_JACK_PHYSICAL_INSERT_BIT = 1 << SW_JACK_PHYSICAL_INSERT;
296     public static final int SW_JACK_BITS =
297             SW_HEADPHONE_INSERT_BIT | SW_MICROPHONE_INSERT_BIT | SW_JACK_PHYSICAL_INSERT_BIT | SW_LINEOUT_INSERT_BIT;
298     public static final int SW_CAMERA_LENS_COVER_BIT = 1 << SW_CAMERA_LENS_COVER;
299 
300     /** Whether to use the dev/input/event or uevent subsystem for the audio jack. */
301     final boolean mUseDevInputEventForAudioJack;
302 
InputManagerService(Context context)303     public InputManagerService(Context context) {
304         this.mContext = context;
305         this.mHandler = new InputManagerHandler(DisplayThread.get().getLooper());
306 
307         mUseDevInputEventForAudioJack =
308                 context.getResources().getBoolean(R.bool.config_useDevInputEventForAudioJack);
309         Slog.i(TAG, "Initializing input manager, mUseDevInputEventForAudioJack="
310                 + mUseDevInputEventForAudioJack);
311         mPtr = nativeInit(this, mContext, mHandler.getLooper().getQueue());
312 
313         String doubleTouchGestureEnablePath = context.getResources().getString(
314                 R.string.config_doubleTouchGestureEnableFile);
315         mDoubleTouchGestureEnableFile = TextUtils.isEmpty(doubleTouchGestureEnablePath) ? null :
316             new File(doubleTouchGestureEnablePath);
317 
318         LocalServices.addService(InputManagerInternal.class, new LocalService());
319     }
320 
setWindowManagerCallbacks(WindowManagerCallbacks callbacks)321     public void setWindowManagerCallbacks(WindowManagerCallbacks callbacks) {
322         mWindowManagerCallbacks = callbacks;
323     }
324 
setWiredAccessoryCallbacks(WiredAccessoryCallbacks callbacks)325     public void setWiredAccessoryCallbacks(WiredAccessoryCallbacks callbacks) {
326         mWiredAccessoryCallbacks = callbacks;
327     }
328 
start()329     public void start() {
330         Slog.i(TAG, "Starting input manager");
331         nativeStart(mPtr);
332 
333         // Add ourself to the Watchdog monitors.
334         Watchdog.getInstance().addMonitor(this);
335 
336         registerPointerSpeedSettingObserver();
337         registerShowTouchesSettingObserver();
338         registerAccessibilityLargePointerSettingObserver();
339 
340         mContext.registerReceiver(new BroadcastReceiver() {
341             @Override
342             public void onReceive(Context context, Intent intent) {
343                 updatePointerSpeedFromSettings();
344                 updateShowTouchesFromSettings();
345                 updateAccessibilityLargePointerFromSettings();
346             }
347         }, new IntentFilter(Intent.ACTION_USER_SWITCHED), null, mHandler);
348 
349         updatePointerSpeedFromSettings();
350         updateShowTouchesFromSettings();
351         updateAccessibilityLargePointerFromSettings();
352     }
353 
354     // TODO(BT) Pass in paramter for bluetooth system
systemRunning()355     public void systemRunning() {
356         if (DEBUG) {
357             Slog.d(TAG, "System ready.");
358         }
359         mNotificationManager = (NotificationManager)mContext.getSystemService(
360                 Context.NOTIFICATION_SERVICE);
361         mSystemReady = true;
362 
363         IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
364         filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
365         filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
366         filter.addAction(Intent.ACTION_PACKAGE_REPLACED);
367         filter.addDataScheme("package");
368         mContext.registerReceiver(new BroadcastReceiver() {
369             @Override
370             public void onReceive(Context context, Intent intent) {
371                 updateKeyboardLayouts();
372             }
373         }, filter, null, mHandler);
374 
375         filter = new IntentFilter(BluetoothDevice.ACTION_ALIAS_CHANGED);
376         mContext.registerReceiver(new BroadcastReceiver() {
377             @Override
378             public void onReceive(Context context, Intent intent) {
379                 reloadDeviceAliases();
380             }
381         }, filter, null, mHandler);
382 
383         mHandler.sendEmptyMessage(MSG_RELOAD_DEVICE_ALIASES);
384         mHandler.sendEmptyMessage(MSG_UPDATE_KEYBOARD_LAYOUTS);
385 
386         if (mWiredAccessoryCallbacks != null) {
387             mWiredAccessoryCallbacks.systemReady();
388         }
389     }
390 
reloadKeyboardLayouts()391     private void reloadKeyboardLayouts() {
392         if (DEBUG) {
393             Slog.d(TAG, "Reloading keyboard layouts.");
394         }
395         nativeReloadKeyboardLayouts(mPtr);
396     }
397 
reloadDeviceAliases()398     private void reloadDeviceAliases() {
399         if (DEBUG) {
400             Slog.d(TAG, "Reloading device names.");
401         }
402         nativeReloadDeviceAliases(mPtr);
403     }
404 
setDisplayViewportsInternal(DisplayViewport defaultViewport, DisplayViewport externalTouchViewport)405     private void setDisplayViewportsInternal(DisplayViewport defaultViewport,
406             DisplayViewport externalTouchViewport) {
407         if (defaultViewport.valid) {
408             setDisplayViewport(false, defaultViewport);
409         }
410 
411         if (externalTouchViewport.valid) {
412             setDisplayViewport(true, externalTouchViewport);
413         } else if (defaultViewport.valid) {
414             setDisplayViewport(true, defaultViewport);
415         }
416     }
417 
setDisplayViewport(boolean external, DisplayViewport viewport)418     private void setDisplayViewport(boolean external, DisplayViewport viewport) {
419         nativeSetDisplayViewport(mPtr, external,
420                 viewport.displayId, viewport.orientation,
421                 viewport.logicalFrame.left, viewport.logicalFrame.top,
422                 viewport.logicalFrame.right, viewport.logicalFrame.bottom,
423                 viewport.physicalFrame.left, viewport.physicalFrame.top,
424                 viewport.physicalFrame.right, viewport.physicalFrame.bottom,
425                 viewport.deviceWidth, viewport.deviceHeight);
426     }
427 
428     /**
429      * Gets the current state of a key or button by key code.
430      * @param deviceId The input device id, or -1 to consult all devices.
431      * @param sourceMask The input sources to consult, or {@link InputDevice#SOURCE_ANY} to
432      * consider all input sources.  An input device is consulted if at least one of its
433      * non-class input source bits matches the specified source mask.
434      * @param keyCode The key code to check.
435      * @return The key state.
436      */
getKeyCodeState(int deviceId, int sourceMask, int keyCode)437     public int getKeyCodeState(int deviceId, int sourceMask, int keyCode) {
438         return nativeGetKeyCodeState(mPtr, deviceId, sourceMask, keyCode);
439     }
440 
441     /**
442      * Gets the current state of a key or button by scan code.
443      * @param deviceId The input device id, or -1 to consult all devices.
444      * @param sourceMask The input sources to consult, or {@link InputDevice#SOURCE_ANY} to
445      * consider all input sources.  An input device is consulted if at least one of its
446      * non-class input source bits matches the specified source mask.
447      * @param scanCode The scan code to check.
448      * @return The key state.
449      */
getScanCodeState(int deviceId, int sourceMask, int scanCode)450     public int getScanCodeState(int deviceId, int sourceMask, int scanCode) {
451         return nativeGetScanCodeState(mPtr, deviceId, sourceMask, scanCode);
452     }
453 
454     /**
455      * Gets the current state of a switch by switch code.
456      * @param deviceId The input device id, or -1 to consult all devices.
457      * @param sourceMask The input sources to consult, or {@link InputDevice#SOURCE_ANY} to
458      * consider all input sources.  An input device is consulted if at least one of its
459      * non-class input source bits matches the specified source mask.
460      * @param switchCode The switch code to check.
461      * @return The switch state.
462      */
getSwitchState(int deviceId, int sourceMask, int switchCode)463     public int getSwitchState(int deviceId, int sourceMask, int switchCode) {
464         return nativeGetSwitchState(mPtr, deviceId, sourceMask, switchCode);
465     }
466 
467     /**
468      * Determines whether the specified key codes are supported by a particular device.
469      * @param deviceId The input device id, or -1 to consult all devices.
470      * @param sourceMask The input sources to consult, or {@link InputDevice#SOURCE_ANY} to
471      * consider all input sources.  An input device is consulted if at least one of its
472      * non-class input source bits matches the specified source mask.
473      * @param keyCodes The array of key codes to check.
474      * @param keyExists An array at least as large as keyCodes whose entries will be set
475      * to true or false based on the presence or absence of support for the corresponding
476      * key codes.
477      * @return True if the lookup was successful, false otherwise.
478      */
479     @Override // Binder call
hasKeys(int deviceId, int sourceMask, int[] keyCodes, boolean[] keyExists)480     public boolean hasKeys(int deviceId, int sourceMask, int[] keyCodes, boolean[] keyExists) {
481         if (keyCodes == null) {
482             throw new IllegalArgumentException("keyCodes must not be null.");
483         }
484         if (keyExists == null || keyExists.length < keyCodes.length) {
485             throw new IllegalArgumentException("keyExists must not be null and must be at "
486                     + "least as large as keyCodes.");
487         }
488 
489         return nativeHasKeys(mPtr, deviceId, sourceMask, keyCodes, keyExists);
490     }
491 
492     /**
493      * Creates an input channel that will receive all input from the input dispatcher.
494      * @param inputChannelName The input channel name.
495      * @return The input channel.
496      */
monitorInput(String inputChannelName)497     public InputChannel monitorInput(String inputChannelName) {
498         if (inputChannelName == null) {
499             throw new IllegalArgumentException("inputChannelName must not be null.");
500         }
501 
502         InputChannel[] inputChannels = InputChannel.openInputChannelPair(inputChannelName);
503         nativeRegisterInputChannel(mPtr, inputChannels[0], null, true);
504         inputChannels[0].dispose(); // don't need to retain the Java object reference
505         return inputChannels[1];
506     }
507 
508     /**
509      * Registers an input channel so that it can be used as an input event target.
510      * @param inputChannel The input channel to register.
511      * @param inputWindowHandle The handle of the input window associated with the
512      * input channel, or null if none.
513      */
registerInputChannel(InputChannel inputChannel, InputWindowHandle inputWindowHandle)514     public void registerInputChannel(InputChannel inputChannel,
515             InputWindowHandle inputWindowHandle) {
516         if (inputChannel == null) {
517             throw new IllegalArgumentException("inputChannel must not be null.");
518         }
519 
520         nativeRegisterInputChannel(mPtr, inputChannel, inputWindowHandle, false);
521     }
522 
523     /**
524      * Unregisters an input channel.
525      * @param inputChannel The input channel to unregister.
526      */
unregisterInputChannel(InputChannel inputChannel)527     public void unregisterInputChannel(InputChannel inputChannel) {
528         if (inputChannel == null) {
529             throw new IllegalArgumentException("inputChannel must not be null.");
530         }
531 
532         nativeUnregisterInputChannel(mPtr, inputChannel);
533     }
534 
535     /**
536      * Sets an input filter that will receive all input events before they are dispatched.
537      * The input filter may then reinterpret input events or inject new ones.
538      *
539      * To ensure consistency, the input dispatcher automatically drops all events
540      * in progress whenever an input filter is installed or uninstalled.  After an input
541      * filter is uninstalled, it can no longer send input events unless it is reinstalled.
542      * Any events it attempts to send after it has been uninstalled will be dropped.
543      *
544      * @param filter The input filter, or null to remove the current filter.
545      */
setInputFilter(IInputFilter filter)546     public void setInputFilter(IInputFilter filter) {
547         synchronized (mInputFilterLock) {
548             final IInputFilter oldFilter = mInputFilter;
549             if (oldFilter == filter) {
550                 return; // nothing to do
551             }
552 
553             if (oldFilter != null) {
554                 mInputFilter = null;
555                 mInputFilterHost.disconnectLocked();
556                 mInputFilterHost = null;
557                 try {
558                     oldFilter.uninstall();
559                 } catch (RemoteException re) {
560                     /* ignore */
561                 }
562             }
563 
564             if (filter != null) {
565                 mInputFilter = filter;
566                 mInputFilterHost = new InputFilterHost();
567                 try {
568                     filter.install(mInputFilterHost);
569                 } catch (RemoteException re) {
570                     /* ignore */
571                 }
572             }
573 
574             nativeSetInputFilterEnabled(mPtr, filter != null);
575         }
576     }
577 
578     @Override // Binder call
injectInputEvent(InputEvent event, int mode)579     public boolean injectInputEvent(InputEvent event, int mode) {
580         return injectInputEventInternal(event, Display.DEFAULT_DISPLAY, mode);
581     }
582 
injectInputEventInternal(InputEvent event, int displayId, int mode)583     private boolean injectInputEventInternal(InputEvent event, int displayId, int mode) {
584         if (event == null) {
585             throw new IllegalArgumentException("event must not be null");
586         }
587         if (mode != InputManager.INJECT_INPUT_EVENT_MODE_ASYNC
588                 && mode != InputManager.INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH
589                 && mode != InputManager.INJECT_INPUT_EVENT_MODE_WAIT_FOR_RESULT) {
590             throw new IllegalArgumentException("mode is invalid");
591         }
592 
593         final int pid = Binder.getCallingPid();
594         final int uid = Binder.getCallingUid();
595         final long ident = Binder.clearCallingIdentity();
596         final int result;
597         try {
598             result = nativeInjectInputEvent(mPtr, event, displayId, pid, uid, mode,
599                     INJECTION_TIMEOUT_MILLIS, WindowManagerPolicy.FLAG_DISABLE_KEY_REPEAT);
600         } finally {
601             Binder.restoreCallingIdentity(ident);
602         }
603         switch (result) {
604             case INPUT_EVENT_INJECTION_PERMISSION_DENIED:
605                 Slog.w(TAG, "Input event injection from pid " + pid + " permission denied.");
606                 throw new SecurityException(
607                         "Injecting to another application requires INJECT_EVENTS permission");
608             case INPUT_EVENT_INJECTION_SUCCEEDED:
609                 return true;
610             case INPUT_EVENT_INJECTION_TIMED_OUT:
611                 Slog.w(TAG, "Input event injection from pid " + pid + " timed out.");
612                 return false;
613             case INPUT_EVENT_INJECTION_FAILED:
614             default:
615                 Slog.w(TAG, "Input event injection from pid " + pid + " failed.");
616                 return false;
617         }
618     }
619 
620     /**
621      * Gets information about the input device with the specified id.
622      * @param deviceId The device id.
623      * @return The input device or null if not found.
624      */
625     @Override // Binder call
getInputDevice(int deviceId)626     public InputDevice getInputDevice(int deviceId) {
627         synchronized (mInputDevicesLock) {
628             final int count = mInputDevices.length;
629             for (int i = 0; i < count; i++) {
630                 final InputDevice inputDevice = mInputDevices[i];
631                 if (inputDevice.getId() == deviceId) {
632                     return inputDevice;
633                 }
634             }
635         }
636         return null;
637     }
638 
639     /**
640      * Gets the ids of all input devices in the system.
641      * @return The input device ids.
642      */
643     @Override // Binder call
getInputDeviceIds()644     public int[] getInputDeviceIds() {
645         synchronized (mInputDevicesLock) {
646             final int count = mInputDevices.length;
647             int[] ids = new int[count];
648             for (int i = 0; i < count; i++) {
649                 ids[i] = mInputDevices[i].getId();
650             }
651             return ids;
652         }
653     }
654 
655     /**
656      * Gets all input devices in the system.
657      * @return The array of input devices.
658      */
getInputDevices()659     public InputDevice[] getInputDevices() {
660         synchronized (mInputDevicesLock) {
661             return mInputDevices;
662         }
663     }
664 
665     @Override // Binder call
registerInputDevicesChangedListener(IInputDevicesChangedListener listener)666     public void registerInputDevicesChangedListener(IInputDevicesChangedListener listener) {
667         if (listener == null) {
668             throw new IllegalArgumentException("listener must not be null");
669         }
670 
671         synchronized (mInputDevicesLock) {
672             int callingPid = Binder.getCallingPid();
673             if (mInputDevicesChangedListeners.get(callingPid) != null) {
674                 throw new SecurityException("The calling process has already "
675                         + "registered an InputDevicesChangedListener.");
676             }
677 
678             InputDevicesChangedListenerRecord record =
679                     new InputDevicesChangedListenerRecord(callingPid, listener);
680             try {
681                 IBinder binder = listener.asBinder();
682                 binder.linkToDeath(record, 0);
683             } catch (RemoteException ex) {
684                 // give up
685                 throw new RuntimeException(ex);
686             }
687 
688             mInputDevicesChangedListeners.put(callingPid, record);
689         }
690     }
691 
onInputDevicesChangedListenerDied(int pid)692     private void onInputDevicesChangedListenerDied(int pid) {
693         synchronized (mInputDevicesLock) {
694             mInputDevicesChangedListeners.remove(pid);
695         }
696     }
697 
698     // Must be called on handler.
deliverInputDevicesChanged(InputDevice[] oldInputDevices)699     private void deliverInputDevicesChanged(InputDevice[] oldInputDevices) {
700         // Scan for changes.
701         int numFullKeyboardsAdded = 0;
702         mTempInputDevicesChangedListenersToNotify.clear();
703         mTempFullKeyboards.clear();
704         final int numListeners;
705         final int[] deviceIdAndGeneration;
706         synchronized (mInputDevicesLock) {
707             if (!mInputDevicesChangedPending) {
708                 return;
709             }
710             mInputDevicesChangedPending = false;
711 
712             numListeners = mInputDevicesChangedListeners.size();
713             for (int i = 0; i < numListeners; i++) {
714                 mTempInputDevicesChangedListenersToNotify.add(
715                         mInputDevicesChangedListeners.valueAt(i));
716             }
717 
718             final int numDevices = mInputDevices.length;
719             deviceIdAndGeneration = new int[numDevices * 2];
720             for (int i = 0; i < numDevices; i++) {
721                 final InputDevice inputDevice = mInputDevices[i];
722                 deviceIdAndGeneration[i * 2] = inputDevice.getId();
723                 deviceIdAndGeneration[i * 2 + 1] = inputDevice.getGeneration();
724 
725                 if (!inputDevice.isVirtual() && inputDevice.isFullKeyboard()) {
726                     if (!containsInputDeviceWithDescriptor(oldInputDevices,
727                             inputDevice.getDescriptor())) {
728                         mTempFullKeyboards.add(numFullKeyboardsAdded++, inputDevice);
729                     } else {
730                         mTempFullKeyboards.add(inputDevice);
731                     }
732                 }
733             }
734         }
735 
736         // Notify listeners.
737         for (int i = 0; i < numListeners; i++) {
738             mTempInputDevicesChangedListenersToNotify.get(i).notifyInputDevicesChanged(
739                     deviceIdAndGeneration);
740         }
741         mTempInputDevicesChangedListenersToNotify.clear();
742 
743         // Check for missing keyboard layouts.
744         List<InputDevice> keyboardsMissingLayout = new ArrayList<>();
745         final int numFullKeyboards = mTempFullKeyboards.size();
746         synchronized (mDataStore) {
747             for (int i = 0; i < numFullKeyboards; i++) {
748                 final InputDevice inputDevice = mTempFullKeyboards.get(i);
749                 String layout =
750                     getCurrentKeyboardLayoutForInputDevice(inputDevice.getIdentifier());
751                 if (layout == null) {
752                     layout = getDefaultKeyboardLayout(inputDevice);
753                     if (layout != null) {
754                         setCurrentKeyboardLayoutForInputDevice(
755                                 inputDevice.getIdentifier(), layout);
756                     }
757                 }
758                 if (layout == null) {
759                     keyboardsMissingLayout.add(inputDevice);
760                 }
761             }
762         }
763 
764         if (mNotificationManager != null) {
765             if (!keyboardsMissingLayout.isEmpty()) {
766                 if (keyboardsMissingLayout.size() > 1) {
767                     // We have more than one keyboard missing a layout, so drop the
768                     // user at the generic input methods page so they can pick which
769                     // one to set.
770                     showMissingKeyboardLayoutNotification(null);
771                 } else {
772                     showMissingKeyboardLayoutNotification(keyboardsMissingLayout.get(0));
773                 }
774             } else if (mKeyboardLayoutNotificationShown) {
775                 hideMissingKeyboardLayoutNotification();
776             }
777         }
778         mTempFullKeyboards.clear();
779     }
780 
getDefaultKeyboardLayout(final InputDevice d)781     private String getDefaultKeyboardLayout(final InputDevice d) {
782         final Locale systemLocale = mContext.getResources().getConfiguration().locale;
783         // If our locale doesn't have a language for some reason, then we don't really have a
784         // reasonable default.
785         if (TextUtils.isEmpty(systemLocale.getLanguage())) {
786             return null;
787         }
788         final List<KeyboardLayout> layouts = new ArrayList<>();
789         visitAllKeyboardLayouts(new KeyboardLayoutVisitor() {
790             @Override
791             public void visitKeyboardLayout(Resources resources,
792                     int keyboardLayoutResId, KeyboardLayout layout) {
793                 // Only select a default when we know the layout is appropriate. For now, this
794                 // means its a custom layout for a specific keyboard.
795                 if (layout.getVendorId() != d.getVendorId()
796                         || layout.getProductId() != d.getProductId()) {
797                     return;
798                 }
799                 final LocaleList locales = layout.getLocales();
800                 final int numLocales = locales.size();
801                 for (int localeIndex = 0; localeIndex < numLocales; ++localeIndex) {
802                     if (isCompatibleLocale(systemLocale, locales.get(localeIndex))) {
803                         layouts.add(layout);
804                         break;
805                     }
806                 }
807             }
808         });
809 
810         if (layouts.isEmpty()) {
811             return null;
812         }
813 
814         // First sort so that ones with higher priority are listed at the top
815         Collections.sort(layouts);
816         // Next we want to try to find an exact match of language, country and variant.
817         final int N = layouts.size();
818         for (int i = 0; i < N; i++) {
819             KeyboardLayout layout = layouts.get(i);
820             final LocaleList locales = layout.getLocales();
821             final int numLocales = locales.size();
822             for (int localeIndex = 0; localeIndex < numLocales; ++localeIndex) {
823                 final Locale locale = locales.get(localeIndex);
824                 if (locale.getCountry().equals(systemLocale.getCountry())
825                         && locale.getVariant().equals(systemLocale.getVariant())) {
826                     return layout.getDescriptor();
827                 }
828             }
829         }
830         // Then try an exact match of language and country
831         for (int i = 0; i < N; i++) {
832             KeyboardLayout layout = layouts.get(i);
833             final LocaleList locales = layout.getLocales();
834             final int numLocales = locales.size();
835             for (int localeIndex = 0; localeIndex < numLocales; ++localeIndex) {
836                 final Locale locale = locales.get(localeIndex);
837                 if (locale.getCountry().equals(systemLocale.getCountry())) {
838                     return layout.getDescriptor();
839                 }
840             }
841         }
842 
843         // Give up and just use the highest priority layout with matching language
844         return layouts.get(0).getDescriptor();
845     }
846 
isCompatibleLocale(Locale systemLocale, Locale keyboardLocale)847     private static boolean isCompatibleLocale(Locale systemLocale, Locale keyboardLocale) {
848         // Different languages are never compatible
849         if (!systemLocale.getLanguage().equals(keyboardLocale.getLanguage())) {
850             return false;
851         }
852         // If both the system and the keyboard layout have a country specifier, they must be equal.
853         if (!TextUtils.isEmpty(systemLocale.getCountry())
854                 && !TextUtils.isEmpty(keyboardLocale.getCountry())
855                 && !systemLocale.getCountry().equals(keyboardLocale.getCountry())) {
856             return false;
857         }
858         return true;
859     }
860 
861     @Override // Binder call & native callback
getTouchCalibrationForInputDevice(String inputDeviceDescriptor, int surfaceRotation)862     public TouchCalibration getTouchCalibrationForInputDevice(String inputDeviceDescriptor,
863             int surfaceRotation) {
864         if (inputDeviceDescriptor == null) {
865             throw new IllegalArgumentException("inputDeviceDescriptor must not be null");
866         }
867 
868         synchronized (mDataStore) {
869             return mDataStore.getTouchCalibration(inputDeviceDescriptor, surfaceRotation);
870         }
871     }
872 
873     @Override // Binder call
setTouchCalibrationForInputDevice(String inputDeviceDescriptor, int surfaceRotation, TouchCalibration calibration)874     public void setTouchCalibrationForInputDevice(String inputDeviceDescriptor, int surfaceRotation,
875             TouchCalibration calibration) {
876         if (!checkCallingPermission(android.Manifest.permission.SET_INPUT_CALIBRATION,
877                 "setTouchCalibrationForInputDevice()")) {
878             throw new SecurityException("Requires SET_INPUT_CALIBRATION permission");
879         }
880         if (inputDeviceDescriptor == null) {
881             throw new IllegalArgumentException("inputDeviceDescriptor must not be null");
882         }
883         if (calibration == null) {
884             throw new IllegalArgumentException("calibration must not be null");
885         }
886         if (surfaceRotation < Surface.ROTATION_0 || surfaceRotation > Surface.ROTATION_270) {
887             throw new IllegalArgumentException("surfaceRotation value out of bounds");
888         }
889 
890         synchronized (mDataStore) {
891             try {
892                 if (mDataStore.setTouchCalibration(inputDeviceDescriptor, surfaceRotation,
893                         calibration)) {
894                     nativeReloadCalibration(mPtr);
895                 }
896             } finally {
897                 mDataStore.saveIfNeeded();
898             }
899         }
900     }
901 
902     @Override // Binder call
isInTabletMode()903     public int isInTabletMode() {
904         if (!checkCallingPermission(android.Manifest.permission.TABLET_MODE,
905                 "isInTabletMode()")) {
906             throw new SecurityException("Requires TABLET_MODE permission");
907         }
908         return getSwitchState(-1, InputDevice.SOURCE_ANY, SW_TABLET_MODE);
909     }
910 
911     @Override // Binder call
registerTabletModeChangedListener(ITabletModeChangedListener listener)912     public void registerTabletModeChangedListener(ITabletModeChangedListener listener) {
913         if (!checkCallingPermission(android.Manifest.permission.TABLET_MODE,
914                 "registerTabletModeChangedListener()")) {
915             throw new SecurityException("Requires TABLET_MODE_LISTENER permission");
916         }
917         if (listener == null) {
918             throw new IllegalArgumentException("listener must not be null");
919         }
920 
921         synchronized (mTabletModeLock) {
922             final int callingPid = Binder.getCallingPid();
923             if (mTabletModeChangedListeners.get(callingPid) != null) {
924                 throw new IllegalStateException("The calling process has already registered "
925                         + "a TabletModeChangedListener.");
926             }
927             TabletModeChangedListenerRecord record =
928                     new TabletModeChangedListenerRecord(callingPid, listener);
929             try {
930                 IBinder binder = listener.asBinder();
931                 binder.linkToDeath(record, 0);
932             } catch (RemoteException ex) {
933                 throw new RuntimeException(ex);
934             }
935             mTabletModeChangedListeners.put(callingPid, record);
936         }
937     }
938 
onTabletModeChangedListenerDied(int pid)939     private void onTabletModeChangedListenerDied(int pid) {
940         synchronized (mTabletModeLock) {
941             mTabletModeChangedListeners.remove(pid);
942         }
943     }
944 
945     // Must be called on handler
deliverTabletModeChanged(long whenNanos, boolean inTabletMode)946     private void deliverTabletModeChanged(long whenNanos, boolean inTabletMode) {
947         mTempTabletModeChangedListenersToNotify.clear();
948         final int numListeners;
949         synchronized (mTabletModeLock) {
950             numListeners = mTabletModeChangedListeners.size();
951             for (int i = 0; i < numListeners; i++) {
952                 mTempTabletModeChangedListenersToNotify.add(
953                         mTabletModeChangedListeners.valueAt(i));
954             }
955         }
956         for (int i = 0; i < numListeners; i++) {
957             mTempTabletModeChangedListenersToNotify.get(i).notifyTabletModeChanged(
958                     whenNanos, inTabletMode);
959         }
960     }
961 
962     // Must be called on handler.
showMissingKeyboardLayoutNotification(InputDevice device)963     private void showMissingKeyboardLayoutNotification(InputDevice device) {
964         if (!mKeyboardLayoutNotificationShown) {
965             final Intent intent = new Intent(Settings.ACTION_HARD_KEYBOARD_SETTINGS);
966             if (device != null) {
967                 intent.putExtra(Settings.EXTRA_INPUT_DEVICE_IDENTIFIER, device.getIdentifier());
968             }
969             intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
970                     | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
971                     | Intent.FLAG_ACTIVITY_CLEAR_TOP);
972             final PendingIntent keyboardLayoutIntent = PendingIntent.getActivityAsUser(mContext, 0,
973                     intent, 0, null, UserHandle.CURRENT);
974 
975             Resources r = mContext.getResources();
976             Notification notification = new Notification.Builder(mContext)
977                     .setContentTitle(r.getString(
978                             R.string.select_keyboard_layout_notification_title))
979                     .setContentText(r.getString(
980                             R.string.select_keyboard_layout_notification_message))
981                     .setContentIntent(keyboardLayoutIntent)
982                     .setSmallIcon(R.drawable.ic_settings_language)
983                     .setPriority(Notification.PRIORITY_LOW)
984                     .setColor(mContext.getColor(
985                             com.android.internal.R.color.system_notification_accent_color))
986                     .build();
987             mNotificationManager.notifyAsUser(null,
988                     R.string.select_keyboard_layout_notification_title,
989                     notification, UserHandle.ALL);
990             mKeyboardLayoutNotificationShown = true;
991         }
992     }
993 
994     // Must be called on handler.
hideMissingKeyboardLayoutNotification()995     private void hideMissingKeyboardLayoutNotification() {
996         if (mKeyboardLayoutNotificationShown) {
997             mKeyboardLayoutNotificationShown = false;
998             mNotificationManager.cancelAsUser(null,
999                     R.string.select_keyboard_layout_notification_title,
1000                     UserHandle.ALL);
1001         }
1002     }
1003 
1004     // Must be called on handler.
updateKeyboardLayouts()1005     private void updateKeyboardLayouts() {
1006         // Scan all input devices state for keyboard layouts that have been uninstalled.
1007         final HashSet<String> availableKeyboardLayouts = new HashSet<String>();
1008         visitAllKeyboardLayouts(new KeyboardLayoutVisitor() {
1009             @Override
1010             public void visitKeyboardLayout(Resources resources,
1011                     int keyboardLayoutResId, KeyboardLayout layout) {
1012                 availableKeyboardLayouts.add(layout.getDescriptor());
1013             }
1014         });
1015         synchronized (mDataStore) {
1016             try {
1017                 mDataStore.removeUninstalledKeyboardLayouts(availableKeyboardLayouts);
1018             } finally {
1019                 mDataStore.saveIfNeeded();
1020             }
1021         }
1022 
1023         // Reload keyboard layouts.
1024         reloadKeyboardLayouts();
1025     }
1026 
containsInputDeviceWithDescriptor(InputDevice[] inputDevices, String descriptor)1027     private static boolean containsInputDeviceWithDescriptor(InputDevice[] inputDevices,
1028             String descriptor) {
1029         final int numDevices = inputDevices.length;
1030         for (int i = 0; i < numDevices; i++) {
1031             final InputDevice inputDevice = inputDevices[i];
1032             if (inputDevice.getDescriptor().equals(descriptor)) {
1033                 return true;
1034             }
1035         }
1036         return false;
1037     }
1038 
1039     @Override // Binder call
getKeyboardLayouts()1040     public KeyboardLayout[] getKeyboardLayouts() {
1041         final ArrayList<KeyboardLayout> list = new ArrayList<KeyboardLayout>();
1042         visitAllKeyboardLayouts(new KeyboardLayoutVisitor() {
1043             @Override
1044             public void visitKeyboardLayout(Resources resources,
1045                     int keyboardLayoutResId, KeyboardLayout layout) {
1046                 list.add(layout);
1047             }
1048         });
1049         return list.toArray(new KeyboardLayout[list.size()]);
1050     }
1051 
1052     @Override // Binder call
getKeyboardLayoutsForInputDevice( final InputDeviceIdentifier identifier)1053     public KeyboardLayout[] getKeyboardLayoutsForInputDevice(
1054             final InputDeviceIdentifier identifier) {
1055         final String[] enabledLayoutDescriptors =
1056             getEnabledKeyboardLayoutsForInputDevice(identifier);
1057         final ArrayList<KeyboardLayout> enabledLayouts =
1058             new ArrayList<KeyboardLayout>(enabledLayoutDescriptors.length);
1059         final ArrayList<KeyboardLayout> potentialLayouts = new ArrayList<KeyboardLayout>();
1060         visitAllKeyboardLayouts(new KeyboardLayoutVisitor() {
1061             boolean mHasSeenDeviceSpecificLayout;
1062 
1063             @Override
1064             public void visitKeyboardLayout(Resources resources,
1065                     int keyboardLayoutResId, KeyboardLayout layout) {
1066                 // First check if it's enabled. If the keyboard layout is enabled then we always
1067                 // want to return it as a possible layout for the device.
1068                 for (String s : enabledLayoutDescriptors) {
1069                     if (s != null && s.equals(layout.getDescriptor())) {
1070                         enabledLayouts.add(layout);
1071                         return;
1072                     }
1073                 }
1074                 // Next find any potential layouts that aren't yet enabled for the device. For
1075                 // devices that have special layouts we assume there's a reason that the generic
1076                 // layouts don't work for them so we don't want to return them since it's likely
1077                 // to result in a poor user experience.
1078                 if (layout.getVendorId() == identifier.getVendorId()
1079                         && layout.getProductId() == identifier.getProductId()) {
1080                     if (!mHasSeenDeviceSpecificLayout) {
1081                         mHasSeenDeviceSpecificLayout = true;
1082                         potentialLayouts.clear();
1083                     }
1084                     potentialLayouts.add(layout);
1085                 } else if (layout.getVendorId() == -1 && layout.getProductId() == -1
1086                         && !mHasSeenDeviceSpecificLayout) {
1087                     potentialLayouts.add(layout);
1088                 }
1089             }
1090         });
1091         final int enabledLayoutSize = enabledLayouts.size();
1092         final int potentialLayoutSize = potentialLayouts.size();
1093         KeyboardLayout[] layouts = new KeyboardLayout[enabledLayoutSize + potentialLayoutSize];
1094         enabledLayouts.toArray(layouts);
1095         for (int i = 0; i < potentialLayoutSize; i++) {
1096             layouts[enabledLayoutSize + i] = potentialLayouts.get(i);
1097         }
1098         return layouts;
1099     }
1100 
1101     @Override // Binder call
getKeyboardLayout(String keyboardLayoutDescriptor)1102     public KeyboardLayout getKeyboardLayout(String keyboardLayoutDescriptor) {
1103         if (keyboardLayoutDescriptor == null) {
1104             throw new IllegalArgumentException("keyboardLayoutDescriptor must not be null");
1105         }
1106 
1107         final KeyboardLayout[] result = new KeyboardLayout[1];
1108         visitKeyboardLayout(keyboardLayoutDescriptor, new KeyboardLayoutVisitor() {
1109             @Override
1110             public void visitKeyboardLayout(Resources resources,
1111                     int keyboardLayoutResId, KeyboardLayout layout) {
1112                 result[0] = layout;
1113             }
1114         });
1115         if (result[0] == null) {
1116             Slog.w(TAG, "Could not get keyboard layout with descriptor '"
1117                     + keyboardLayoutDescriptor + "'.");
1118         }
1119         return result[0];
1120     }
1121 
visitAllKeyboardLayouts(KeyboardLayoutVisitor visitor)1122     private void visitAllKeyboardLayouts(KeyboardLayoutVisitor visitor) {
1123         final PackageManager pm = mContext.getPackageManager();
1124         Intent intent = new Intent(InputManager.ACTION_QUERY_KEYBOARD_LAYOUTS);
1125         for (ResolveInfo resolveInfo : pm.queryBroadcastReceivers(intent,
1126                 PackageManager.GET_META_DATA | PackageManager.MATCH_DIRECT_BOOT_AWARE
1127                         | PackageManager.MATCH_DIRECT_BOOT_UNAWARE)) {
1128             final ActivityInfo activityInfo = resolveInfo.activityInfo;
1129             final int priority = resolveInfo.priority;
1130             visitKeyboardLayoutsInPackage(pm, activityInfo, null, priority, visitor);
1131         }
1132     }
1133 
visitKeyboardLayout(String keyboardLayoutDescriptor, KeyboardLayoutVisitor visitor)1134     private void visitKeyboardLayout(String keyboardLayoutDescriptor,
1135             KeyboardLayoutVisitor visitor) {
1136         KeyboardLayoutDescriptor d = KeyboardLayoutDescriptor.parse(keyboardLayoutDescriptor);
1137         if (d != null) {
1138             final PackageManager pm = mContext.getPackageManager();
1139             try {
1140                 ActivityInfo receiver = pm.getReceiverInfo(
1141                         new ComponentName(d.packageName, d.receiverName),
1142                         PackageManager.GET_META_DATA
1143                                 | PackageManager.MATCH_DIRECT_BOOT_AWARE
1144                                 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE);
1145                 visitKeyboardLayoutsInPackage(pm, receiver, d.keyboardLayoutName, 0, visitor);
1146             } catch (NameNotFoundException ex) {
1147             }
1148         }
1149     }
1150 
visitKeyboardLayoutsInPackage(PackageManager pm, ActivityInfo receiver, String keyboardName, int requestedPriority, KeyboardLayoutVisitor visitor)1151     private void visitKeyboardLayoutsInPackage(PackageManager pm, ActivityInfo receiver,
1152             String keyboardName, int requestedPriority, KeyboardLayoutVisitor visitor) {
1153         Bundle metaData = receiver.metaData;
1154         if (metaData == null) {
1155             return;
1156         }
1157 
1158         int configResId = metaData.getInt(InputManager.META_DATA_KEYBOARD_LAYOUTS);
1159         if (configResId == 0) {
1160             Slog.w(TAG, "Missing meta-data '" + InputManager.META_DATA_KEYBOARD_LAYOUTS
1161                     + "' on receiver " + receiver.packageName + "/" + receiver.name);
1162             return;
1163         }
1164 
1165         CharSequence receiverLabel = receiver.loadLabel(pm);
1166         String collection = receiverLabel != null ? receiverLabel.toString() : "";
1167         int priority;
1168         if ((receiver.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
1169             priority = requestedPriority;
1170         } else {
1171             priority = 0;
1172         }
1173 
1174         try {
1175             Resources resources = pm.getResourcesForApplication(receiver.applicationInfo);
1176             XmlResourceParser parser = resources.getXml(configResId);
1177             try {
1178                 XmlUtils.beginDocument(parser, "keyboard-layouts");
1179 
1180                 for (;;) {
1181                     XmlUtils.nextElement(parser);
1182                     String element = parser.getName();
1183                     if (element == null) {
1184                         break;
1185                     }
1186                     if (element.equals("keyboard-layout")) {
1187                         TypedArray a = resources.obtainAttributes(
1188                                 parser, com.android.internal.R.styleable.KeyboardLayout);
1189                         try {
1190                             String name = a.getString(
1191                                     com.android.internal.R.styleable.KeyboardLayout_name);
1192                             String label = a.getString(
1193                                     com.android.internal.R.styleable.KeyboardLayout_label);
1194                             int keyboardLayoutResId = a.getResourceId(
1195                                     com.android.internal.R.styleable.KeyboardLayout_keyboardLayout,
1196                                     0);
1197                             String languageTags = a.getString(
1198                                     com.android.internal.R.styleable.KeyboardLayout_locale);
1199                             LocaleList locales = getLocalesFromLanguageTags(languageTags);
1200                             int vid = a.getInt(
1201                                     com.android.internal.R.styleable.KeyboardLayout_vendorId, -1);
1202                             int pid = a.getInt(
1203                                     com.android.internal.R.styleable.KeyboardLayout_productId, -1);
1204 
1205                             if (name == null || label == null || keyboardLayoutResId == 0) {
1206                                 Slog.w(TAG, "Missing required 'name', 'label' or 'keyboardLayout' "
1207                                         + "attributes in keyboard layout "
1208                                         + "resource from receiver "
1209                                         + receiver.packageName + "/" + receiver.name);
1210                             } else {
1211                                 String descriptor = KeyboardLayoutDescriptor.format(
1212                                         receiver.packageName, receiver.name, name);
1213                                 if (keyboardName == null || name.equals(keyboardName)) {
1214                                     KeyboardLayout layout = new KeyboardLayout(
1215                                             descriptor, label, collection, priority,
1216                                             locales, vid, pid);
1217                                     visitor.visitKeyboardLayout(
1218                                             resources, keyboardLayoutResId, layout);
1219                                 }
1220                             }
1221                         } finally {
1222                             a.recycle();
1223                         }
1224                     } else {
1225                         Slog.w(TAG, "Skipping unrecognized element '" + element
1226                                 + "' in keyboard layout resource from receiver "
1227                                 + receiver.packageName + "/" + receiver.name);
1228                     }
1229                 }
1230             } finally {
1231                 parser.close();
1232             }
1233         } catch (Exception ex) {
1234             Slog.w(TAG, "Could not parse keyboard layout resource from receiver "
1235                     + receiver.packageName + "/" + receiver.name, ex);
1236         }
1237     }
1238 
1239     @NonNull
getLocalesFromLanguageTags(String languageTags)1240     private static LocaleList getLocalesFromLanguageTags(String languageTags) {
1241         if (TextUtils.isEmpty(languageTags)) {
1242             return LocaleList.getEmptyLocaleList();
1243         }
1244         return LocaleList.forLanguageTags(languageTags.replace('|', ','));
1245     }
1246 
1247     /**
1248      * Builds a layout descriptor for the vendor/product. This returns the
1249      * descriptor for ids that aren't useful (such as the default 0, 0).
1250      */
getLayoutDescriptor(InputDeviceIdentifier identifier)1251     private String getLayoutDescriptor(InputDeviceIdentifier identifier) {
1252         if (identifier == null || identifier.getDescriptor() == null) {
1253             throw new IllegalArgumentException("identifier and descriptor must not be null");
1254         }
1255 
1256         if (identifier.getVendorId() == 0 && identifier.getProductId() == 0) {
1257             return identifier.getDescriptor();
1258         }
1259         StringBuilder bob = new StringBuilder();
1260         bob.append("vendor:").append(identifier.getVendorId());
1261         bob.append(",product:").append(identifier.getProductId());
1262         return bob.toString();
1263     }
1264 
1265     @Override // Binder call
getCurrentKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier)1266     public String getCurrentKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier) {
1267 
1268         String key = getLayoutDescriptor(identifier);
1269         synchronized (mDataStore) {
1270             String layout = null;
1271             // try loading it using the layout descriptor if we have it
1272             layout = mDataStore.getCurrentKeyboardLayout(key);
1273             if (layout == null && !key.equals(identifier.getDescriptor())) {
1274                 // if it doesn't exist fall back to the device descriptor
1275                 layout = mDataStore.getCurrentKeyboardLayout(identifier.getDescriptor());
1276             }
1277             if (DEBUG) {
1278                 Slog.d(TAG, "Loaded keyboard layout id for " + key + " and got "
1279                         + layout);
1280             }
1281             return layout;
1282         }
1283     }
1284 
1285     @Override // Binder call
setCurrentKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier, String keyboardLayoutDescriptor)1286     public void setCurrentKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier,
1287             String keyboardLayoutDescriptor) {
1288         if (!checkCallingPermission(android.Manifest.permission.SET_KEYBOARD_LAYOUT,
1289                 "setCurrentKeyboardLayoutForInputDevice()")) {
1290             throw new SecurityException("Requires SET_KEYBOARD_LAYOUT permission");
1291         }
1292         if (keyboardLayoutDescriptor == null) {
1293             throw new IllegalArgumentException("keyboardLayoutDescriptor must not be null");
1294         }
1295 
1296         String key = getLayoutDescriptor(identifier);
1297         synchronized (mDataStore) {
1298             try {
1299                 if (mDataStore.setCurrentKeyboardLayout(key, keyboardLayoutDescriptor)) {
1300                     if (DEBUG) {
1301                         Slog.d(TAG, "Saved keyboard layout using " + key);
1302                     }
1303                     mHandler.sendEmptyMessage(MSG_RELOAD_KEYBOARD_LAYOUTS);
1304                 }
1305             } finally {
1306                 mDataStore.saveIfNeeded();
1307             }
1308         }
1309     }
1310 
1311     @Override // Binder call
getEnabledKeyboardLayoutsForInputDevice(InputDeviceIdentifier identifier)1312     public String[] getEnabledKeyboardLayoutsForInputDevice(InputDeviceIdentifier identifier) {
1313         String key = getLayoutDescriptor(identifier);
1314         synchronized (mDataStore) {
1315             String[] layouts = mDataStore.getKeyboardLayouts(key);
1316             if ((layouts == null || layouts.length == 0)
1317                     && !key.equals(identifier.getDescriptor())) {
1318                 layouts = mDataStore.getKeyboardLayouts(identifier.getDescriptor());
1319             }
1320             return layouts;
1321         }
1322     }
1323 
1324     @Override // Binder call
1325     @Nullable
getKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier, InputMethodInfo imeInfo, InputMethodSubtype imeSubtype)1326     public KeyboardLayout getKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier,
1327             InputMethodInfo imeInfo, InputMethodSubtype imeSubtype) {
1328         InputMethodSubtypeHandle handle = new InputMethodSubtypeHandle(imeInfo, imeSubtype);
1329         String key = getLayoutDescriptor(identifier);
1330         final String keyboardLayoutDescriptor;
1331         synchronized (mDataStore) {
1332             keyboardLayoutDescriptor = mDataStore.getKeyboardLayout(key, handle);
1333         }
1334 
1335         if (keyboardLayoutDescriptor == null) {
1336             return null;
1337         }
1338 
1339         final KeyboardLayout[] result = new KeyboardLayout[1];
1340         visitKeyboardLayout(keyboardLayoutDescriptor, new KeyboardLayoutVisitor() {
1341             @Override
1342             public void visitKeyboardLayout(Resources resources,
1343                     int keyboardLayoutResId, KeyboardLayout layout) {
1344                 result[0] = layout;
1345             }
1346         });
1347         if (result[0] == null) {
1348             Slog.w(TAG, "Could not get keyboard layout with descriptor '"
1349                     + keyboardLayoutDescriptor + "'.");
1350         }
1351         return result[0];
1352     }
1353 
1354     @Override
setKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier, InputMethodInfo imeInfo, InputMethodSubtype imeSubtype, String keyboardLayoutDescriptor)1355     public void setKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier,
1356             InputMethodInfo imeInfo, InputMethodSubtype imeSubtype,
1357             String keyboardLayoutDescriptor) {
1358         if (!checkCallingPermission(android.Manifest.permission.SET_KEYBOARD_LAYOUT,
1359                 "setKeyboardLayoutForInputDevice()")) {
1360             throw new SecurityException("Requires SET_KEYBOARD_LAYOUT permission");
1361         }
1362         if (keyboardLayoutDescriptor == null) {
1363             throw new IllegalArgumentException("keyboardLayoutDescriptor must not be null");
1364         }
1365         if (imeInfo == null) {
1366             throw new IllegalArgumentException("imeInfo must not be null");
1367         }
1368         InputMethodSubtypeHandle handle = new InputMethodSubtypeHandle(imeInfo, imeSubtype);
1369         setKeyboardLayoutForInputDeviceInner(identifier, handle, keyboardLayoutDescriptor);
1370     }
1371 
setKeyboardLayoutForInputDeviceInner(InputDeviceIdentifier identifier, InputMethodSubtypeHandle imeHandle, String keyboardLayoutDescriptor)1372     private void setKeyboardLayoutForInputDeviceInner(InputDeviceIdentifier identifier,
1373             InputMethodSubtypeHandle imeHandle, String keyboardLayoutDescriptor) {
1374         String key = getLayoutDescriptor(identifier);
1375         synchronized (mDataStore) {
1376             try {
1377                 if (mDataStore.setKeyboardLayout(key, imeHandle, keyboardLayoutDescriptor)) {
1378                     if (DEBUG) {
1379                         Slog.d(TAG, "Set keyboard layout " + keyboardLayoutDescriptor +
1380                                 " for subtype " + imeHandle + " and device " + identifier +
1381                                 " using key " + key);
1382                     }
1383                     if (imeHandle.equals(mCurrentImeHandle)) {
1384                         if (DEBUG) {
1385                             Slog.d(TAG, "Layout for current subtype changed, switching layout");
1386                         }
1387                         SomeArgs args = SomeArgs.obtain();
1388                         args.arg1 = identifier;
1389                         args.arg2 = imeHandle;
1390                         mHandler.obtainMessage(MSG_SWITCH_KEYBOARD_LAYOUT, args).sendToTarget();
1391                     }
1392                     mHandler.sendEmptyMessage(MSG_RELOAD_KEYBOARD_LAYOUTS);
1393                 }
1394             } finally {
1395                 mDataStore.saveIfNeeded();
1396             }
1397         }
1398     }
1399 
1400     @Override // Binder call
addKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier, String keyboardLayoutDescriptor)1401     public void addKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier,
1402             String keyboardLayoutDescriptor) {
1403         if (!checkCallingPermission(android.Manifest.permission.SET_KEYBOARD_LAYOUT,
1404                 "addKeyboardLayoutForInputDevice()")) {
1405             throw new SecurityException("Requires SET_KEYBOARD_LAYOUT permission");
1406         }
1407         if (keyboardLayoutDescriptor == null) {
1408             throw new IllegalArgumentException("keyboardLayoutDescriptor must not be null");
1409         }
1410 
1411         String key = getLayoutDescriptor(identifier);
1412         synchronized (mDataStore) {
1413             try {
1414                 String oldLayout = mDataStore.getCurrentKeyboardLayout(key);
1415                 if (oldLayout == null && !key.equals(identifier.getDescriptor())) {
1416                     oldLayout = mDataStore.getCurrentKeyboardLayout(identifier.getDescriptor());
1417                 }
1418                 if (mDataStore.addKeyboardLayout(key, keyboardLayoutDescriptor)
1419                         && !Objects.equal(oldLayout, mDataStore.getCurrentKeyboardLayout(key))) {
1420                     mHandler.sendEmptyMessage(MSG_RELOAD_KEYBOARD_LAYOUTS);
1421                 }
1422             } finally {
1423                 mDataStore.saveIfNeeded();
1424             }
1425         }
1426     }
1427 
1428     @Override // Binder call
removeKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier, String keyboardLayoutDescriptor)1429     public void removeKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier,
1430             String keyboardLayoutDescriptor) {
1431         if (!checkCallingPermission(android.Manifest.permission.SET_KEYBOARD_LAYOUT,
1432                 "removeKeyboardLayoutForInputDevice()")) {
1433             throw new SecurityException("Requires SET_KEYBOARD_LAYOUT permission");
1434         }
1435         if (keyboardLayoutDescriptor == null) {
1436             throw new IllegalArgumentException("keyboardLayoutDescriptor must not be null");
1437         }
1438 
1439         String key = getLayoutDescriptor(identifier);
1440         synchronized (mDataStore) {
1441             try {
1442                 String oldLayout = mDataStore.getCurrentKeyboardLayout(key);
1443                 if (oldLayout == null && !key.equals(identifier.getDescriptor())) {
1444                     oldLayout = mDataStore.getCurrentKeyboardLayout(identifier.getDescriptor());
1445                 }
1446                 boolean removed = mDataStore.removeKeyboardLayout(key, keyboardLayoutDescriptor);
1447                 if (!key.equals(identifier.getDescriptor())) {
1448                     // We need to remove from both places to ensure it is gone
1449                     removed |= mDataStore.removeKeyboardLayout(identifier.getDescriptor(),
1450                             keyboardLayoutDescriptor);
1451                 }
1452                 if (removed && !Objects.equal(oldLayout,
1453                                 mDataStore.getCurrentKeyboardLayout(key))) {
1454                     mHandler.sendEmptyMessage(MSG_RELOAD_KEYBOARD_LAYOUTS);
1455                 }
1456             } finally {
1457                 mDataStore.saveIfNeeded();
1458             }
1459         }
1460     }
1461 
1462     // Must be called on handler.
handleSwitchInputMethodSubtype(int userId, @Nullable InputMethodInfo inputMethodInfo, @Nullable InputMethodSubtype subtype)1463     private void handleSwitchInputMethodSubtype(int userId,
1464             @Nullable InputMethodInfo inputMethodInfo, @Nullable InputMethodSubtype subtype) {
1465         if (DEBUG) {
1466             Slog.i(TAG, "InputMethodSubtype changed: userId=" + userId
1467                     + " ime=" + inputMethodInfo + " subtype=" + subtype);
1468         }
1469         if (inputMethodInfo == null) {
1470             Slog.d(TAG, "No InputMethod is running, ignoring change");
1471             return;
1472         }
1473         if (subtype != null && !"keyboard".equals(subtype.getMode())) {
1474             Slog.d(TAG, "InputMethodSubtype changed to non-keyboard subtype, ignoring change");
1475             return;
1476         }
1477         InputMethodSubtypeHandle handle = new InputMethodSubtypeHandle(inputMethodInfo, subtype);
1478         if (!handle.equals(mCurrentImeHandle)) {
1479             mCurrentImeHandle = handle;
1480             handleSwitchKeyboardLayout(null, handle);
1481         }
1482     }
1483 
1484     // Must be called on handler.
handleSwitchKeyboardLayout(@ullable InputDeviceIdentifier identifier, InputMethodSubtypeHandle handle)1485     private void handleSwitchKeyboardLayout(@Nullable InputDeviceIdentifier identifier,
1486             InputMethodSubtypeHandle handle) {
1487         synchronized (mInputDevicesLock) {
1488             for (InputDevice device : mInputDevices) {
1489                 if (identifier != null && !device.getIdentifier().equals(identifier) ||
1490                         !device.isFullKeyboard()) {
1491                     continue;
1492                 }
1493                 String key = getLayoutDescriptor(device.getIdentifier());
1494                 boolean changed = false;
1495                 synchronized (mDataStore) {
1496                     try {
1497                         if (mDataStore.switchKeyboardLayout(key, handle)) {
1498                             changed = true;
1499                         }
1500                     } finally {
1501                         mDataStore.saveIfNeeded();
1502                     }
1503                 }
1504                 if (changed) {
1505                     reloadKeyboardLayouts();
1506                 }
1507             }
1508         }
1509     }
1510 
setInputWindows(InputWindowHandle[] windowHandles)1511     public void setInputWindows(InputWindowHandle[] windowHandles) {
1512         nativeSetInputWindows(mPtr, windowHandles);
1513     }
1514 
setFocusedApplication(InputApplicationHandle application)1515     public void setFocusedApplication(InputApplicationHandle application) {
1516         nativeSetFocusedApplication(mPtr, application);
1517     }
1518 
setInputDispatchMode(boolean enabled, boolean frozen)1519     public void setInputDispatchMode(boolean enabled, boolean frozen) {
1520         nativeSetInputDispatchMode(mPtr, enabled, frozen);
1521     }
1522 
setSystemUiVisibility(int visibility)1523     public void setSystemUiVisibility(int visibility) {
1524         nativeSetSystemUiVisibility(mPtr, visibility);
1525     }
1526 
1527     /**
1528      * Atomically transfers touch focus from one window to another as identified by
1529      * their input channels.  It is possible for multiple windows to have
1530      * touch focus if they support split touch dispatch
1531      * {@link android.view.WindowManager.LayoutParams#FLAG_SPLIT_TOUCH} but this
1532      * method only transfers touch focus of the specified window without affecting
1533      * other windows that may also have touch focus at the same time.
1534      * @param fromChannel The channel of a window that currently has touch focus.
1535      * @param toChannel The channel of the window that should receive touch focus in
1536      * place of the first.
1537      * @return True if the transfer was successful.  False if the window with the
1538      * specified channel did not actually have touch focus at the time of the request.
1539      */
transferTouchFocus(InputChannel fromChannel, InputChannel toChannel)1540     public boolean transferTouchFocus(InputChannel fromChannel, InputChannel toChannel) {
1541         if (fromChannel == null) {
1542             throw new IllegalArgumentException("fromChannel must not be null.");
1543         }
1544         if (toChannel == null) {
1545             throw new IllegalArgumentException("toChannel must not be null.");
1546         }
1547         return nativeTransferTouchFocus(mPtr, fromChannel, toChannel);
1548     }
1549 
1550     @Override // Binder call
tryPointerSpeed(int speed)1551     public void tryPointerSpeed(int speed) {
1552         if (!checkCallingPermission(android.Manifest.permission.SET_POINTER_SPEED,
1553                 "tryPointerSpeed()")) {
1554             throw new SecurityException("Requires SET_POINTER_SPEED permission");
1555         }
1556 
1557         if (speed < InputManager.MIN_POINTER_SPEED || speed > InputManager.MAX_POINTER_SPEED) {
1558             throw new IllegalArgumentException("speed out of range");
1559         }
1560 
1561         setPointerSpeedUnchecked(speed);
1562     }
1563 
updatePointerSpeedFromSettings()1564     public void updatePointerSpeedFromSettings() {
1565         int speed = getPointerSpeedSetting();
1566         setPointerSpeedUnchecked(speed);
1567     }
1568 
setPointerSpeedUnchecked(int speed)1569     private void setPointerSpeedUnchecked(int speed) {
1570         speed = Math.min(Math.max(speed, InputManager.MIN_POINTER_SPEED),
1571                 InputManager.MAX_POINTER_SPEED);
1572         nativeSetPointerSpeed(mPtr, speed);
1573     }
1574 
registerPointerSpeedSettingObserver()1575     private void registerPointerSpeedSettingObserver() {
1576         mContext.getContentResolver().registerContentObserver(
1577                 Settings.System.getUriFor(Settings.System.POINTER_SPEED), true,
1578                 new ContentObserver(mHandler) {
1579                     @Override
1580                     public void onChange(boolean selfChange) {
1581                         updatePointerSpeedFromSettings();
1582                     }
1583                 }, UserHandle.USER_ALL);
1584     }
1585 
getPointerSpeedSetting()1586     private int getPointerSpeedSetting() {
1587         int speed = InputManager.DEFAULT_POINTER_SPEED;
1588         try {
1589             speed = Settings.System.getIntForUser(mContext.getContentResolver(),
1590                     Settings.System.POINTER_SPEED, UserHandle.USER_CURRENT);
1591         } catch (SettingNotFoundException snfe) {
1592         }
1593         return speed;
1594     }
1595 
updateShowTouchesFromSettings()1596     public void updateShowTouchesFromSettings() {
1597         int setting = getShowTouchesSetting(0);
1598         nativeSetShowTouches(mPtr, setting != 0);
1599     }
1600 
registerShowTouchesSettingObserver()1601     private void registerShowTouchesSettingObserver() {
1602         mContext.getContentResolver().registerContentObserver(
1603                 Settings.System.getUriFor(Settings.System.SHOW_TOUCHES), true,
1604                 new ContentObserver(mHandler) {
1605                     @Override
1606                     public void onChange(boolean selfChange) {
1607                         updateShowTouchesFromSettings();
1608                     }
1609                 }, UserHandle.USER_ALL);
1610     }
1611 
updateAccessibilityLargePointerFromSettings()1612     public void updateAccessibilityLargePointerFromSettings() {
1613         final int accessibilityConfig = Settings.Secure.getIntForUser(
1614                 mContext.getContentResolver(), Settings.Secure.ACCESSIBILITY_LARGE_POINTER_ICON,
1615                 0, UserHandle.USER_CURRENT);
1616         PointerIcon.setUseLargeIcons(accessibilityConfig == 1);
1617         nativeReloadPointerIcons(mPtr);
1618     }
1619 
registerAccessibilityLargePointerSettingObserver()1620     private void registerAccessibilityLargePointerSettingObserver() {
1621         mContext.getContentResolver().registerContentObserver(
1622                 Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_LARGE_POINTER_ICON), true,
1623                 new ContentObserver(mHandler) {
1624                     @Override
1625                     public void onChange(boolean selfChange) {
1626                         updateAccessibilityLargePointerFromSettings();
1627                     }
1628                 }, UserHandle.USER_ALL);
1629     }
1630 
getShowTouchesSetting(int defaultValue)1631     private int getShowTouchesSetting(int defaultValue) {
1632         int result = defaultValue;
1633         try {
1634             result = Settings.System.getIntForUser(mContext.getContentResolver(),
1635                     Settings.System.SHOW_TOUCHES, UserHandle.USER_CURRENT);
1636         } catch (SettingNotFoundException snfe) {
1637         }
1638         return result;
1639     }
1640 
1641     // Binder call
1642     @Override
vibrate(int deviceId, long[] pattern, int repeat, IBinder token)1643     public void vibrate(int deviceId, long[] pattern, int repeat, IBinder token) {
1644         if (repeat >= pattern.length) {
1645             throw new ArrayIndexOutOfBoundsException();
1646         }
1647 
1648         VibratorToken v;
1649         synchronized (mVibratorLock) {
1650             v = mVibratorTokens.get(token);
1651             if (v == null) {
1652                 v = new VibratorToken(deviceId, token, mNextVibratorTokenValue++);
1653                 try {
1654                     token.linkToDeath(v, 0);
1655                 } catch (RemoteException ex) {
1656                     // give up
1657                     throw new RuntimeException(ex);
1658                 }
1659                 mVibratorTokens.put(token, v);
1660             }
1661         }
1662 
1663         synchronized (v) {
1664             v.mVibrating = true;
1665             nativeVibrate(mPtr, deviceId, pattern, repeat, v.mTokenValue);
1666         }
1667     }
1668 
1669     // Binder call
1670     @Override
cancelVibrate(int deviceId, IBinder token)1671     public void cancelVibrate(int deviceId, IBinder token) {
1672         VibratorToken v;
1673         synchronized (mVibratorLock) {
1674             v = mVibratorTokens.get(token);
1675             if (v == null || v.mDeviceId != deviceId) {
1676                 return; // nothing to cancel
1677             }
1678         }
1679 
1680         cancelVibrateIfNeeded(v);
1681     }
1682 
onVibratorTokenDied(VibratorToken v)1683     void onVibratorTokenDied(VibratorToken v) {
1684         synchronized (mVibratorLock) {
1685             mVibratorTokens.remove(v.mToken);
1686         }
1687 
1688         cancelVibrateIfNeeded(v);
1689     }
1690 
cancelVibrateIfNeeded(VibratorToken v)1691     private void cancelVibrateIfNeeded(VibratorToken v) {
1692         synchronized (v) {
1693             if (v.mVibrating) {
1694                 nativeCancelVibrate(mPtr, v.mDeviceId, v.mTokenValue);
1695                 v.mVibrating = false;
1696             }
1697         }
1698     }
1699 
1700     // Binder call
1701     @Override
setPointerIconType(int iconId)1702     public void setPointerIconType(int iconId) {
1703         nativeSetPointerIconType(mPtr, iconId);
1704     }
1705 
1706     // Binder call
1707     @Override
setCustomPointerIcon(PointerIcon icon)1708     public void setCustomPointerIcon(PointerIcon icon) {
1709         Preconditions.checkNotNull(icon);
1710         nativeSetCustomPointerIcon(mPtr, icon);
1711     }
1712 
1713     @Override
dump(FileDescriptor fd, final PrintWriter pw, String[] args)1714     public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) {
1715         if (mContext.checkCallingOrSelfPermission(Manifest.permission.DUMP)
1716                 != PackageManager.PERMISSION_GRANTED) {
1717             pw.println("Permission Denial: can't dump InputManager from from pid="
1718                     + Binder.getCallingPid()
1719                     + ", uid=" + Binder.getCallingUid());
1720             return;
1721         }
1722 
1723         pw.println("INPUT MANAGER (dumpsys input)\n");
1724         String dumpStr = nativeDump(mPtr);
1725         if (dumpStr != null) {
1726             pw.println(dumpStr);
1727         }
1728         pw.println("  Keyboard Layouts:");
1729         visitAllKeyboardLayouts(new KeyboardLayoutVisitor() {
1730             @Override
1731             public void visitKeyboardLayout(Resources resources,
1732                     int keyboardLayoutResId, KeyboardLayout layout) {
1733                 pw.println("    \"" + layout + "\": " + layout.getDescriptor());
1734             }
1735         });
1736         pw.println();
1737         synchronized(mDataStore) {
1738             mDataStore.dump(pw, "  ");
1739         }
1740     }
1741 
1742     @Override
onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err, String[] args, ResultReceiver resultReceiver)1743     public void onShellCommand(FileDescriptor in, FileDescriptor out,
1744             FileDescriptor err, String[] args, ResultReceiver resultReceiver) {
1745         (new Shell()).exec(this, in, out, err, args, resultReceiver);
1746     }
1747 
onShellCommand(Shell shell, String cmd)1748     public int onShellCommand(Shell shell, String cmd) {
1749         if (TextUtils.isEmpty(cmd)) {
1750             shell.onHelp();
1751             return 1;
1752         }
1753         if (cmd.equals("setlayout")) {
1754             if (!checkCallingPermission(android.Manifest.permission.SET_KEYBOARD_LAYOUT,
1755                     "onShellCommand()")) {
1756                 throw new SecurityException("Requires SET_KEYBOARD_LAYOUT permission");
1757             }
1758             InputMethodSubtypeHandle handle = new InputMethodSubtypeHandle(
1759                     shell.getNextArgRequired(), Integer.parseInt(shell.getNextArgRequired()));
1760             String descriptor = shell.getNextArgRequired();
1761             int vid = Integer.decode(shell.getNextArgRequired());
1762             int pid = Integer.decode(shell.getNextArgRequired());
1763             InputDeviceIdentifier id = new InputDeviceIdentifier(descriptor, vid, pid);
1764             setKeyboardLayoutForInputDeviceInner(id, handle, shell.getNextArgRequired());
1765         }
1766         return 0;
1767     }
1768 
1769 
checkCallingPermission(String permission, String func)1770     private boolean checkCallingPermission(String permission, String func) {
1771         // Quick check: if the calling permission is me, it's all okay.
1772         if (Binder.getCallingPid() == Process.myPid()) {
1773             return true;
1774         }
1775 
1776         if (mContext.checkCallingPermission(permission) == PackageManager.PERMISSION_GRANTED) {
1777             return true;
1778         }
1779         String msg = "Permission Denial: " + func + " from pid="
1780                 + Binder.getCallingPid()
1781                 + ", uid=" + Binder.getCallingUid()
1782                 + " requires " + permission;
1783         Slog.w(TAG, msg);
1784         return false;
1785     }
1786 
1787     // Called by the heartbeat to ensure locks are not held indefinitely (for deadlock detection).
1788     @Override
monitor()1789     public void monitor() {
1790         synchronized (mInputFilterLock) { }
1791         nativeMonitor(mPtr);
1792     }
1793 
1794     // Native callback.
notifyConfigurationChanged(long whenNanos)1795     private void notifyConfigurationChanged(long whenNanos) {
1796         mWindowManagerCallbacks.notifyConfigurationChanged();
1797     }
1798 
1799     // Native callback.
notifyInputDevicesChanged(InputDevice[] inputDevices)1800     private void notifyInputDevicesChanged(InputDevice[] inputDevices) {
1801         synchronized (mInputDevicesLock) {
1802             if (!mInputDevicesChangedPending) {
1803                 mInputDevicesChangedPending = true;
1804                 mHandler.obtainMessage(MSG_DELIVER_INPUT_DEVICES_CHANGED,
1805                         mInputDevices).sendToTarget();
1806             }
1807 
1808             mInputDevices = inputDevices;
1809         }
1810     }
1811 
1812     // Native callback.
notifySwitch(long whenNanos, int switchValues, int switchMask)1813     private void notifySwitch(long whenNanos, int switchValues, int switchMask) {
1814         if (DEBUG) {
1815             Slog.d(TAG, "notifySwitch: values=" + Integer.toHexString(switchValues)
1816                     + ", mask=" + Integer.toHexString(switchMask));
1817         }
1818 
1819         if ((switchMask & SW_LID_BIT) != 0) {
1820             final boolean lidOpen = ((switchValues & SW_LID_BIT) == 0);
1821             mWindowManagerCallbacks.notifyLidSwitchChanged(whenNanos, lidOpen);
1822         }
1823 
1824         if ((switchMask & SW_CAMERA_LENS_COVER_BIT) != 0) {
1825             final boolean lensCovered = ((switchValues & SW_CAMERA_LENS_COVER_BIT) != 0);
1826             mWindowManagerCallbacks.notifyCameraLensCoverSwitchChanged(whenNanos, lensCovered);
1827         }
1828 
1829         if (mUseDevInputEventForAudioJack && (switchMask & SW_JACK_BITS) != 0) {
1830             mWiredAccessoryCallbacks.notifyWiredAccessoryChanged(whenNanos, switchValues,
1831                     switchMask);
1832         }
1833 
1834         if ((switchMask & SW_TABLET_MODE_BIT) != 0) {
1835             SomeArgs args = SomeArgs.obtain();
1836             args.argi1 = (int) (whenNanos & 0xFFFFFFFF);
1837             args.argi2 = (int) (whenNanos >> 32);
1838             args.arg1 = Boolean.valueOf((switchValues & SW_TABLET_MODE_BIT) != 0);
1839             mHandler.obtainMessage(MSG_DELIVER_TABLET_MODE_CHANGED,
1840                     args).sendToTarget();
1841         }
1842     }
1843 
1844     // Native callback.
notifyInputChannelBroken(InputWindowHandle inputWindowHandle)1845     private void notifyInputChannelBroken(InputWindowHandle inputWindowHandle) {
1846         mWindowManagerCallbacks.notifyInputChannelBroken(inputWindowHandle);
1847     }
1848 
1849     // Native callback.
notifyANR(InputApplicationHandle inputApplicationHandle, InputWindowHandle inputWindowHandle, String reason)1850     private long notifyANR(InputApplicationHandle inputApplicationHandle,
1851             InputWindowHandle inputWindowHandle, String reason) {
1852         return mWindowManagerCallbacks.notifyANR(
1853                 inputApplicationHandle, inputWindowHandle, reason);
1854     }
1855 
1856     // Native callback.
filterInputEvent(InputEvent event, int policyFlags)1857     final boolean filterInputEvent(InputEvent event, int policyFlags) {
1858         synchronized (mInputFilterLock) {
1859             if (mInputFilter != null) {
1860                 try {
1861                     mInputFilter.filterInputEvent(event, policyFlags);
1862                 } catch (RemoteException e) {
1863                     /* ignore */
1864                 }
1865                 return false;
1866             }
1867         }
1868         event.recycle();
1869         return true;
1870     }
1871 
1872     // Native callback.
interceptKeyBeforeQueueing(KeyEvent event, int policyFlags)1873     private int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {
1874         return mWindowManagerCallbacks.interceptKeyBeforeQueueing(event, policyFlags);
1875     }
1876 
1877     // Native callback.
interceptMotionBeforeQueueingNonInteractive(long whenNanos, int policyFlags)1878     private int interceptMotionBeforeQueueingNonInteractive(long whenNanos, int policyFlags) {
1879         return mWindowManagerCallbacks.interceptMotionBeforeQueueingNonInteractive(
1880                 whenNanos, policyFlags);
1881     }
1882 
1883     // Native callback.
interceptKeyBeforeDispatching(InputWindowHandle focus, KeyEvent event, int policyFlags)1884     private long interceptKeyBeforeDispatching(InputWindowHandle focus,
1885             KeyEvent event, int policyFlags) {
1886         return mWindowManagerCallbacks.interceptKeyBeforeDispatching(focus, event, policyFlags);
1887     }
1888 
1889     // Native callback.
dispatchUnhandledKey(InputWindowHandle focus, KeyEvent event, int policyFlags)1890     private KeyEvent dispatchUnhandledKey(InputWindowHandle focus,
1891             KeyEvent event, int policyFlags) {
1892         return mWindowManagerCallbacks.dispatchUnhandledKey(focus, event, policyFlags);
1893     }
1894 
1895     // Native callback.
checkInjectEventsPermission(int injectorPid, int injectorUid)1896     private boolean checkInjectEventsPermission(int injectorPid, int injectorUid) {
1897         return mContext.checkPermission(android.Manifest.permission.INJECT_EVENTS,
1898                 injectorPid, injectorUid) == PackageManager.PERMISSION_GRANTED;
1899     }
1900 
1901     // Native callback.
getVirtualKeyQuietTimeMillis()1902     private int getVirtualKeyQuietTimeMillis() {
1903         return mContext.getResources().getInteger(
1904                 com.android.internal.R.integer.config_virtualKeyQuietTimeMillis);
1905     }
1906 
1907     // Native callback.
getExcludedDeviceNames()1908     private String[] getExcludedDeviceNames() {
1909         ArrayList<String> names = new ArrayList<String>();
1910 
1911         // Read partner-provided list of excluded input devices
1912         XmlPullParser parser = null;
1913         // Environment.getRootDirectory() is a fancy way of saying ANDROID_ROOT or "/system".
1914         File confFile = new File(Environment.getRootDirectory(), EXCLUDED_DEVICES_PATH);
1915         FileReader confreader = null;
1916         try {
1917             confreader = new FileReader(confFile);
1918             parser = Xml.newPullParser();
1919             parser.setInput(confreader);
1920             XmlUtils.beginDocument(parser, "devices");
1921 
1922             while (true) {
1923                 XmlUtils.nextElement(parser);
1924                 if (!"device".equals(parser.getName())) {
1925                     break;
1926                 }
1927                 String name = parser.getAttributeValue(null, "name");
1928                 if (name != null) {
1929                     names.add(name);
1930                 }
1931             }
1932         } catch (FileNotFoundException e) {
1933             // It's ok if the file does not exist.
1934         } catch (Exception e) {
1935             Slog.e(TAG, "Exception while parsing '" + confFile.getAbsolutePath() + "'", e);
1936         } finally {
1937             try { if (confreader != null) confreader.close(); } catch (IOException e) { }
1938         }
1939 
1940         return names.toArray(new String[names.size()]);
1941     }
1942 
1943     // Native callback.
getKeyRepeatTimeout()1944     private int getKeyRepeatTimeout() {
1945         return ViewConfiguration.getKeyRepeatTimeout();
1946     }
1947 
1948     // Native callback.
getKeyRepeatDelay()1949     private int getKeyRepeatDelay() {
1950         return ViewConfiguration.getKeyRepeatDelay();
1951     }
1952 
1953     // Native callback.
getHoverTapTimeout()1954     private int getHoverTapTimeout() {
1955         return ViewConfiguration.getHoverTapTimeout();
1956     }
1957 
1958     // Native callback.
getHoverTapSlop()1959     private int getHoverTapSlop() {
1960         return ViewConfiguration.getHoverTapSlop();
1961     }
1962 
1963     // Native callback.
getDoubleTapTimeout()1964     private int getDoubleTapTimeout() {
1965         return ViewConfiguration.getDoubleTapTimeout();
1966     }
1967 
1968     // Native callback.
getLongPressTimeout()1969     private int getLongPressTimeout() {
1970         return ViewConfiguration.getLongPressTimeout();
1971     }
1972 
1973     // Native callback.
getPointerLayer()1974     private int getPointerLayer() {
1975         return mWindowManagerCallbacks.getPointerLayer();
1976     }
1977 
1978     // Native callback.
getPointerIcon()1979     private PointerIcon getPointerIcon() {
1980         return PointerIcon.getDefaultIcon(mContext);
1981     }
1982 
1983     // Native callback.
getKeyboardLayoutOverlay(InputDeviceIdentifier identifier)1984     private String[] getKeyboardLayoutOverlay(InputDeviceIdentifier identifier) {
1985         if (!mSystemReady) {
1986             return null;
1987         }
1988 
1989         String keyboardLayoutDescriptor = getCurrentKeyboardLayoutForInputDevice(identifier);
1990         if (keyboardLayoutDescriptor == null) {
1991             return null;
1992         }
1993 
1994         final String[] result = new String[2];
1995         visitKeyboardLayout(keyboardLayoutDescriptor, new KeyboardLayoutVisitor() {
1996             @Override
1997             public void visitKeyboardLayout(Resources resources,
1998                     int keyboardLayoutResId, KeyboardLayout layout) {
1999                 try {
2000                     result[0] = layout.getDescriptor();
2001                     result[1] = Streams.readFully(new InputStreamReader(
2002                             resources.openRawResource(keyboardLayoutResId)));
2003                 } catch (IOException ex) {
2004                 } catch (NotFoundException ex) {
2005                 }
2006             }
2007         });
2008         if (result[0] == null) {
2009             Slog.w(TAG, "Could not get keyboard layout with descriptor '"
2010                     + keyboardLayoutDescriptor + "'.");
2011             return null;
2012         }
2013         return result;
2014     }
2015 
2016     // Native callback.
getDeviceAlias(String uniqueId)2017     private String getDeviceAlias(String uniqueId) {
2018         if (BluetoothAdapter.checkBluetoothAddress(uniqueId)) {
2019             // TODO(BT) mBluetoothService.getRemoteAlias(uniqueId)
2020             return null;
2021         }
2022         return null;
2023     }
2024 
2025     /**
2026      * Callback interface implemented by the Window Manager.
2027      */
2028     public interface WindowManagerCallbacks {
notifyConfigurationChanged()2029         public void notifyConfigurationChanged();
2030 
notifyLidSwitchChanged(long whenNanos, boolean lidOpen)2031         public void notifyLidSwitchChanged(long whenNanos, boolean lidOpen);
2032 
notifyCameraLensCoverSwitchChanged(long whenNanos, boolean lensCovered)2033         public void notifyCameraLensCoverSwitchChanged(long whenNanos, boolean lensCovered);
2034 
notifyInputChannelBroken(InputWindowHandle inputWindowHandle)2035         public void notifyInputChannelBroken(InputWindowHandle inputWindowHandle);
2036 
notifyANR(InputApplicationHandle inputApplicationHandle, InputWindowHandle inputWindowHandle, String reason)2037         public long notifyANR(InputApplicationHandle inputApplicationHandle,
2038                 InputWindowHandle inputWindowHandle, String reason);
2039 
interceptKeyBeforeQueueing(KeyEvent event, int policyFlags)2040         public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags);
2041 
interceptMotionBeforeQueueingNonInteractive(long whenNanos, int policyFlags)2042         public int interceptMotionBeforeQueueingNonInteractive(long whenNanos, int policyFlags);
2043 
interceptKeyBeforeDispatching(InputWindowHandle focus, KeyEvent event, int policyFlags)2044         public long interceptKeyBeforeDispatching(InputWindowHandle focus,
2045                 KeyEvent event, int policyFlags);
2046 
dispatchUnhandledKey(InputWindowHandle focus, KeyEvent event, int policyFlags)2047         public KeyEvent dispatchUnhandledKey(InputWindowHandle focus,
2048                 KeyEvent event, int policyFlags);
2049 
getPointerLayer()2050         public int getPointerLayer();
2051     }
2052 
2053     /**
2054      * Callback interface implemented by WiredAccessoryObserver.
2055      */
2056     public interface WiredAccessoryCallbacks {
notifyWiredAccessoryChanged(long whenNanos, int switchValues, int switchMask)2057         public void notifyWiredAccessoryChanged(long whenNanos, int switchValues, int switchMask);
systemReady()2058         public void systemReady();
2059     }
2060 
2061     /**
2062      * Private handler for the input manager.
2063      */
2064     private final class InputManagerHandler extends Handler {
InputManagerHandler(Looper looper)2065         public InputManagerHandler(Looper looper) {
2066             super(looper, null, true /*async*/);
2067         }
2068 
2069         @Override
handleMessage(Message msg)2070         public void handleMessage(Message msg) {
2071             switch (msg.what) {
2072                 case MSG_DELIVER_INPUT_DEVICES_CHANGED:
2073                     deliverInputDevicesChanged((InputDevice[])msg.obj);
2074                     break;
2075                 case MSG_SWITCH_KEYBOARD_LAYOUT: {
2076                     SomeArgs args = (SomeArgs)msg.obj;
2077                     handleSwitchKeyboardLayout((InputDeviceIdentifier)args.arg1,
2078                             (InputMethodSubtypeHandle)args.arg2);
2079                     break;
2080                 }
2081                 case MSG_RELOAD_KEYBOARD_LAYOUTS:
2082                     reloadKeyboardLayouts();
2083                     break;
2084                 case MSG_UPDATE_KEYBOARD_LAYOUTS:
2085                     updateKeyboardLayouts();
2086                     break;
2087                 case MSG_RELOAD_DEVICE_ALIASES:
2088                     reloadDeviceAliases();
2089                     break;
2090                 case MSG_DELIVER_TABLET_MODE_CHANGED: {
2091                     SomeArgs args = (SomeArgs) msg.obj;
2092                     long whenNanos = (args.argi1 & 0xFFFFFFFFl) | ((long) args.argi2 << 32);
2093                     boolean inTabletMode = (boolean) args.arg1;
2094                     deliverTabletModeChanged(whenNanos, inTabletMode);
2095                     break;
2096                 }
2097                 case MSG_INPUT_METHOD_SUBTYPE_CHANGED: {
2098                     final int userId = msg.arg1;
2099                     final SomeArgs args = (SomeArgs) msg.obj;
2100                     final InputMethodInfo inputMethodInfo = (InputMethodInfo) args.arg1;
2101                     final InputMethodSubtype subtype = (InputMethodSubtype) args.arg2;
2102                     args.recycle();
2103                     handleSwitchInputMethodSubtype(userId, inputMethodInfo, subtype);
2104                     break;
2105                 }
2106             }
2107         }
2108     }
2109 
2110     /**
2111      * Hosting interface for input filters to call back into the input manager.
2112      */
2113     private final class InputFilterHost extends IInputFilterHost.Stub {
2114         private boolean mDisconnected;
2115 
disconnectLocked()2116         public void disconnectLocked() {
2117             mDisconnected = true;
2118         }
2119 
2120         @Override
sendInputEvent(InputEvent event, int policyFlags)2121         public void sendInputEvent(InputEvent event, int policyFlags) {
2122             if (event == null) {
2123                 throw new IllegalArgumentException("event must not be null");
2124             }
2125 
2126             synchronized (mInputFilterLock) {
2127                 if (!mDisconnected) {
2128                     nativeInjectInputEvent(mPtr, event, Display.DEFAULT_DISPLAY, 0, 0,
2129                             InputManager.INJECT_INPUT_EVENT_MODE_ASYNC, 0,
2130                             policyFlags | WindowManagerPolicy.FLAG_FILTERED);
2131                 }
2132             }
2133         }
2134     }
2135 
2136     private static final class KeyboardLayoutDescriptor {
2137         public String packageName;
2138         public String receiverName;
2139         public String keyboardLayoutName;
2140 
format(String packageName, String receiverName, String keyboardName)2141         public static String format(String packageName,
2142                 String receiverName, String keyboardName) {
2143             return packageName + "/" + receiverName + "/" + keyboardName;
2144         }
2145 
parse(String descriptor)2146         public static KeyboardLayoutDescriptor parse(String descriptor) {
2147             int pos = descriptor.indexOf('/');
2148             if (pos < 0 || pos + 1 == descriptor.length()) {
2149                 return null;
2150             }
2151             int pos2 = descriptor.indexOf('/', pos + 1);
2152             if (pos2 < pos + 2 || pos2 + 1 == descriptor.length()) {
2153                 return null;
2154             }
2155 
2156             KeyboardLayoutDescriptor result = new KeyboardLayoutDescriptor();
2157             result.packageName = descriptor.substring(0, pos);
2158             result.receiverName = descriptor.substring(pos + 1, pos2);
2159             result.keyboardLayoutName = descriptor.substring(pos2 + 1);
2160             return result;
2161         }
2162     }
2163 
2164     private interface KeyboardLayoutVisitor {
visitKeyboardLayout(Resources resources, int keyboardLayoutResId, KeyboardLayout layout)2165         void visitKeyboardLayout(Resources resources,
2166                 int keyboardLayoutResId, KeyboardLayout layout);
2167     }
2168 
2169     private final class InputDevicesChangedListenerRecord implements DeathRecipient {
2170         private final int mPid;
2171         private final IInputDevicesChangedListener mListener;
2172 
InputDevicesChangedListenerRecord(int pid, IInputDevicesChangedListener listener)2173         public InputDevicesChangedListenerRecord(int pid, IInputDevicesChangedListener listener) {
2174             mPid = pid;
2175             mListener = listener;
2176         }
2177 
2178         @Override
binderDied()2179         public void binderDied() {
2180             if (DEBUG) {
2181                 Slog.d(TAG, "Input devices changed listener for pid " + mPid + " died.");
2182             }
2183             onInputDevicesChangedListenerDied(mPid);
2184         }
2185 
notifyInputDevicesChanged(int[] info)2186         public void notifyInputDevicesChanged(int[] info) {
2187             try {
2188                 mListener.onInputDevicesChanged(info);
2189             } catch (RemoteException ex) {
2190                 Slog.w(TAG, "Failed to notify process "
2191                         + mPid + " that input devices changed, assuming it died.", ex);
2192                 binderDied();
2193             }
2194         }
2195     }
2196 
2197     private final class TabletModeChangedListenerRecord implements DeathRecipient {
2198         private final int mPid;
2199         private final ITabletModeChangedListener mListener;
2200 
TabletModeChangedListenerRecord(int pid, ITabletModeChangedListener listener)2201         public TabletModeChangedListenerRecord(int pid, ITabletModeChangedListener listener) {
2202             mPid = pid;
2203             mListener = listener;
2204         }
2205 
2206         @Override
binderDied()2207         public void binderDied() {
2208             if (DEBUG) {
2209                 Slog.d(TAG, "Tablet mode changed listener for pid " + mPid + " died.");
2210             }
2211             onTabletModeChangedListenerDied(mPid);
2212         }
2213 
notifyTabletModeChanged(long whenNanos, boolean inTabletMode)2214         public void notifyTabletModeChanged(long whenNanos, boolean inTabletMode) {
2215             try {
2216                 mListener.onTabletModeChanged(whenNanos, inTabletMode);
2217             } catch (RemoteException ex) {
2218                 Slog.w(TAG, "Failed to notify process " + mPid +
2219                         " that tablet mode changed, assuming it died.", ex);
2220                 binderDied();
2221             }
2222         }
2223     }
2224 
2225     private final class VibratorToken implements DeathRecipient {
2226         public final int mDeviceId;
2227         public final IBinder mToken;
2228         public final int mTokenValue;
2229 
2230         public boolean mVibrating;
2231 
VibratorToken(int deviceId, IBinder token, int tokenValue)2232         public VibratorToken(int deviceId, IBinder token, int tokenValue) {
2233             mDeviceId = deviceId;
2234             mToken = token;
2235             mTokenValue = tokenValue;
2236         }
2237 
2238         @Override
binderDied()2239         public void binderDied() {
2240             if (DEBUG) {
2241                 Slog.d(TAG, "Vibrator token died.");
2242             }
2243             onVibratorTokenDied(this);
2244         }
2245     }
2246 
2247     private class Shell extends ShellCommand {
2248         @Override
onCommand(String cmd)2249         public int onCommand(String cmd) {
2250             return onShellCommand(this, cmd);
2251         }
2252 
2253         @Override
onHelp()2254         public void onHelp() {
2255             final PrintWriter pw = getOutPrintWriter();
2256             pw.println("Input manager commands:");
2257             pw.println("  help");
2258             pw.println("    Print this help text.");
2259             pw.println("");
2260             pw.println("  setlayout IME_ID IME_SUPTYPE_HASH_CODE"
2261                     + " DEVICE_DESCRIPTOR VENDOR_ID PRODUCT_ID KEYBOARD_DESCRIPTOR");
2262             pw.println("    Sets a keyboard layout for a given IME subtype and input device pair");
2263         }
2264     }
2265 
2266     private final class LocalService extends InputManagerInternal {
2267         @Override
setDisplayViewports( DisplayViewport defaultViewport, DisplayViewport externalTouchViewport)2268         public void setDisplayViewports(
2269                 DisplayViewport defaultViewport, DisplayViewport externalTouchViewport) {
2270             setDisplayViewportsInternal(defaultViewport, externalTouchViewport);
2271         }
2272 
2273         @Override
injectInputEvent(InputEvent event, int displayId, int mode)2274         public boolean injectInputEvent(InputEvent event, int displayId, int mode) {
2275             return injectInputEventInternal(event, displayId, mode);
2276         }
2277 
2278         @Override
setInteractive(boolean interactive)2279         public void setInteractive(boolean interactive) {
2280             nativeSetInteractive(mPtr, interactive);
2281         }
2282 
2283         @Override
onInputMethodSubtypeChanged(int userId, @Nullable InputMethodInfo inputMethodInfo, @Nullable InputMethodSubtype subtype)2284         public void onInputMethodSubtypeChanged(int userId,
2285                 @Nullable InputMethodInfo inputMethodInfo, @Nullable InputMethodSubtype subtype) {
2286             final SomeArgs someArgs = SomeArgs.obtain();
2287             someArgs.arg1 = inputMethodInfo;
2288             someArgs.arg2 = subtype;
2289             mHandler.obtainMessage(MSG_INPUT_METHOD_SUBTYPE_CHANGED, userId, 0, someArgs)
2290                     .sendToTarget();
2291         }
2292 
2293         @Override
toggleCapsLock(int deviceId)2294         public void toggleCapsLock(int deviceId) {
2295             nativeToggleCapsLock(mPtr, deviceId);
2296         }
2297 
2298         @Override
setPulseGestureEnabled(boolean enabled)2299         public void setPulseGestureEnabled(boolean enabled) {
2300             if (mDoubleTouchGestureEnableFile != null) {
2301                 FileWriter writer = null;
2302                 try {
2303                     writer = new FileWriter(mDoubleTouchGestureEnableFile);
2304                     writer.write(enabled ? "1" : "0");
2305                 } catch (IOException e) {
2306                     Log.wtf(TAG, "Unable to setPulseGestureEnabled", e);
2307                 } finally {
2308                     IoUtils.closeQuietly(writer);
2309                 }
2310             }
2311         }
2312     }
2313 }
2314