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