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