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