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