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