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.view.Display;
     20 import com.android.internal.R;
     21 import com.android.internal.util.XmlUtils;
     22 import com.android.server.DisplayThread;
     23 import com.android.server.LocalServices;
     24 import com.android.server.Watchdog;
     25 
     26 import org.xmlpull.v1.XmlPullParser;
     27 
     28 import android.Manifest;
     29 import android.app.Notification;
     30 import android.app.NotificationManager;
     31 import android.app.PendingIntent;
     32 import android.bluetooth.BluetoothAdapter;
     33 import android.bluetooth.BluetoothDevice;
     34 import android.content.BroadcastReceiver;
     35 import android.content.ComponentName;
     36 import android.content.Context;
     37 import android.content.Intent;
     38 import android.content.IntentFilter;
     39 import android.content.pm.ActivityInfo;
     40 import android.content.pm.ApplicationInfo;
     41 import android.content.pm.PackageManager;
     42 import android.content.pm.ResolveInfo;
     43 import android.content.pm.PackageManager.NameNotFoundException;
     44 import android.content.res.Resources;
     45 import android.content.res.Resources.NotFoundException;
     46 import android.content.res.TypedArray;
     47 import android.content.res.XmlResourceParser;
     48 import android.database.ContentObserver;
     49 import android.hardware.display.DisplayViewport;
     50 import android.hardware.input.IInputDevicesChangedListener;
     51 import android.hardware.input.IInputManager;
     52 import android.hardware.input.InputDeviceIdentifier;
     53 import android.hardware.input.InputManager;
     54 import android.hardware.input.InputManagerInternal;
     55 import android.hardware.input.KeyboardLayout;
     56 import android.hardware.input.TouchCalibration;
     57 import android.os.Binder;
     58 import android.os.Bundle;
     59 import android.os.Environment;
     60 import android.os.Handler;
     61 import android.os.IBinder;
     62 import android.os.Looper;
     63 import android.os.Message;
     64 import android.os.MessageQueue;
     65 import android.os.Process;
     66 import android.os.RemoteException;
     67 import android.os.UserHandle;
     68 import android.provider.Settings;
     69 import android.provider.Settings.SettingNotFoundException;
     70 import android.util.Log;
     71 import android.util.Slog;
     72 import android.util.SparseArray;
     73 import android.util.Xml;
     74 import android.view.IInputFilter;
     75 import android.view.IInputFilterHost;
     76 import android.view.InputChannel;
     77 import android.view.InputDevice;
     78 import android.view.InputEvent;
     79 import android.view.KeyEvent;
     80 import android.view.PointerIcon;
     81 import android.view.Surface;
     82 import android.view.ViewConfiguration;
     83 import android.view.WindowManagerPolicy;
     84 import android.widget.Toast;
     85 
     86 import java.io.File;
     87 import java.io.FileDescriptor;
     88 import java.io.FileNotFoundException;
     89 import java.io.FileReader;
     90 import java.io.IOException;
     91 import java.io.InputStreamReader;
     92 import java.io.PrintWriter;
     93 import java.util.ArrayList;
     94 import java.util.HashMap;
     95 import java.util.HashSet;
     96 
     97 import libcore.io.Streams;
     98 import libcore.util.Objects;
     99 
    100 /*
    101  * Wraps the C++ InputManager and provides its callbacks.
    102  */
    103 public class InputManagerService extends IInputManager.Stub
    104         implements Watchdog.Monitor {
    105     static final String TAG = "InputManager";
    106     static final boolean DEBUG = false;
    107 
    108     private static final String EXCLUDED_DEVICES_PATH = "etc/excluded-input-devices.xml";
    109 
    110     private static final int MSG_DELIVER_INPUT_DEVICES_CHANGED = 1;
    111     private static final int MSG_SWITCH_KEYBOARD_LAYOUT = 2;
    112     private static final int MSG_RELOAD_KEYBOARD_LAYOUTS = 3;
    113     private static final int MSG_UPDATE_KEYBOARD_LAYOUTS = 4;
    114     private static final int MSG_RELOAD_DEVICE_ALIASES = 5;
    115 
    116     // Pointer to native input manager service object.
    117     private final long mPtr;
    118 
    119     private final Context mContext;
    120     private final InputManagerHandler mHandler;
    121 
    122     private WindowManagerCallbacks mWindowManagerCallbacks;
    123     private WiredAccessoryCallbacks mWiredAccessoryCallbacks;
    124     private boolean mSystemReady;
    125     private NotificationManager mNotificationManager;
    126 
    127     // Persistent data store.  Must be locked each time during use.
    128     private final PersistentDataStore mDataStore = new PersistentDataStore();
    129 
    130     // List of currently registered input devices changed listeners by process id.
    131     private Object mInputDevicesLock = new Object();
    132     private boolean mInputDevicesChangedPending; // guarded by mInputDevicesLock
    133     private InputDevice[] mInputDevices = new InputDevice[0];
    134     private final SparseArray<InputDevicesChangedListenerRecord> mInputDevicesChangedListeners =
    135             new SparseArray<InputDevicesChangedListenerRecord>(); // guarded by mInputDevicesLock
    136     private final ArrayList<InputDevicesChangedListenerRecord>
    137             mTempInputDevicesChangedListenersToNotify =
    138                     new ArrayList<InputDevicesChangedListenerRecord>(); // handler thread only
    139     private final ArrayList<InputDevice>
    140             mTempFullKeyboards = new ArrayList<InputDevice>(); // handler thread only
    141     private boolean mKeyboardLayoutNotificationShown;
    142     private PendingIntent mKeyboardLayoutIntent;
    143     private Toast mSwitchedKeyboardLayoutToast;
    144 
    145     // State for vibrator tokens.
    146     private Object mVibratorLock = new Object();
    147     private HashMap<IBinder, VibratorToken> mVibratorTokens =
    148             new HashMap<IBinder, VibratorToken>();
    149     private int mNextVibratorTokenValue;
    150 
    151     // State for the currently installed input filter.
    152     final Object mInputFilterLock = new Object();
    153     IInputFilter mInputFilter; // guarded by mInputFilterLock
    154     InputFilterHost mInputFilterHost; // guarded by mInputFilterLock
    155 
    156     private static native long nativeInit(InputManagerService service,
    157             Context context, MessageQueue messageQueue);
    158     private static native void nativeStart(long ptr);
    159     private static native void nativeSetDisplayViewport(long ptr, boolean external,
    160             int displayId, int rotation,
    161             int logicalLeft, int logicalTop, int logicalRight, int logicalBottom,
    162             int physicalLeft, int physicalTop, int physicalRight, int physicalBottom,
    163             int deviceWidth, int deviceHeight);
    164 
    165     private static native int nativeGetScanCodeState(long ptr,
    166             int deviceId, int sourceMask, int scanCode);
    167     private static native int nativeGetKeyCodeState(long ptr,
    168             int deviceId, int sourceMask, int keyCode);
    169     private static native int nativeGetSwitchState(long ptr,
    170             int deviceId, int sourceMask, int sw);
    171     private static native boolean nativeHasKeys(long ptr,
    172             int deviceId, int sourceMask, int[] keyCodes, boolean[] keyExists);
    173     private static native void nativeRegisterInputChannel(long ptr, InputChannel inputChannel,
    174             InputWindowHandle inputWindowHandle, boolean monitor);
    175     private static native void nativeUnregisterInputChannel(long ptr, InputChannel inputChannel);
    176     private static native void nativeSetInputFilterEnabled(long ptr, boolean enable);
    177     private static native int nativeInjectInputEvent(long ptr, InputEvent event, int displayId,
    178             int injectorPid, int injectorUid, int syncMode, int timeoutMillis,
    179             int policyFlags);
    180     private static native void nativeSetInputWindows(long ptr, InputWindowHandle[] windowHandles);
    181     private static native void nativeSetInputDispatchMode(long ptr, boolean enabled, boolean frozen);
    182     private static native void nativeSetSystemUiVisibility(long ptr, int visibility);
    183     private static native void nativeSetFocusedApplication(long ptr,
    184             InputApplicationHandle application);
    185     private static native boolean nativeTransferTouchFocus(long ptr,
    186             InputChannel fromChannel, InputChannel toChannel);
    187     private static native void nativeSetPointerSpeed(long ptr, int speed);
    188     private static native void nativeSetShowTouches(long ptr, boolean enabled);
    189     private static native void nativeSetInteractive(long ptr, boolean interactive);
    190     private static native void nativeReloadCalibration(long ptr);
    191     private static native void nativeVibrate(long ptr, int deviceId, long[] pattern,
    192             int repeat, int token);
    193     private static native void nativeCancelVibrate(long ptr, int deviceId, int token);
    194     private static native void nativeReloadKeyboardLayouts(long ptr);
    195     private static native void nativeReloadDeviceAliases(long ptr);
    196     private static native String nativeDump(long ptr);
    197     private static native void nativeMonitor(long ptr);
    198 
    199     // Input event injection constants defined in InputDispatcher.h.
    200     private static final int INPUT_EVENT_INJECTION_SUCCEEDED = 0;
    201     private static final int INPUT_EVENT_INJECTION_PERMISSION_DENIED = 1;
    202     private static final int INPUT_EVENT_INJECTION_FAILED = 2;
    203     private static final int INPUT_EVENT_INJECTION_TIMED_OUT = 3;
    204 
    205     // Maximum number of milliseconds to wait for input event injection.
    206     private static final int INJECTION_TIMEOUT_MILLIS = 30 * 1000;
    207 
    208     // Key states (may be returned by queries about the current state of a
    209     // particular key code, scan code or switch).
    210 
    211     /** The key state is unknown or the requested key itself is not supported. */
    212     public static final int KEY_STATE_UNKNOWN = -1;
    213 
    214     /** The key is up. /*/
    215     public static final int KEY_STATE_UP = 0;
    216 
    217     /** The key is down. */
    218     public static final int KEY_STATE_DOWN = 1;
    219 
    220     /** The key is down but is a virtual key press that is being emulated by the system. */
    221     public static final int KEY_STATE_VIRTUAL = 2;
    222 
    223     /** Scan code: Mouse / trackball button. */
    224     public static final int BTN_MOUSE = 0x110;
    225 
    226     // Switch code values must match bionic/libc/kernel/common/linux/input.h
    227     /** Switch code: Lid switch.  When set, lid is shut. */
    228     public static final int SW_LID = 0x00;
    229 
    230     /** Switch code: Keypad slide.  When set, keyboard is exposed. */
    231     public static final int SW_KEYPAD_SLIDE = 0x0a;
    232 
    233     /** Switch code: Headphone.  When set, headphone is inserted. */
    234     public static final int SW_HEADPHONE_INSERT = 0x02;
    235 
    236     /** Switch code: Microphone.  When set, microphone is inserted. */
    237     public static final int SW_MICROPHONE_INSERT = 0x04;
    238 
    239     /** Switch code: Line out.  When set, Line out (hi-Z) is inserted. */
    240     public static final int SW_LINEOUT_INSERT = 0x06;
    241 
    242     /** Switch code: Headphone/Microphone Jack.  When set, something is inserted. */
    243     public static final int SW_JACK_PHYSICAL_INSERT = 0x07;
    244 
    245     /** Switch code: Camera lens cover. When set the lens is covered. */
    246     public static final int SW_CAMERA_LENS_COVER = 0x09;
    247 
    248     public static final int SW_LID_BIT = 1 << SW_LID;
    249     public static final int SW_KEYPAD_SLIDE_BIT = 1 << SW_KEYPAD_SLIDE;
    250     public static final int SW_HEADPHONE_INSERT_BIT = 1 << SW_HEADPHONE_INSERT;
    251     public static final int SW_MICROPHONE_INSERT_BIT = 1 << SW_MICROPHONE_INSERT;
    252     public static final int SW_LINEOUT_INSERT_BIT = 1 << SW_LINEOUT_INSERT;
    253     public static final int SW_JACK_PHYSICAL_INSERT_BIT = 1 << SW_JACK_PHYSICAL_INSERT;
    254     public static final int SW_JACK_BITS =
    255             SW_HEADPHONE_INSERT_BIT | SW_MICROPHONE_INSERT_BIT | SW_JACK_PHYSICAL_INSERT_BIT | SW_LINEOUT_INSERT_BIT;
    256     public static final int SW_CAMERA_LENS_COVER_BIT = 1 << SW_CAMERA_LENS_COVER;
    257 
    258     /** Whether to use the dev/input/event or uevent subsystem for the audio jack. */
    259     final boolean mUseDevInputEventForAudioJack;
    260 
    261     public InputManagerService(Context context) {
    262         this.mContext = context;
    263         this.mHandler = new InputManagerHandler(DisplayThread.get().getLooper());
    264 
    265         mUseDevInputEventForAudioJack =
    266                 context.getResources().getBoolean(R.bool.config_useDevInputEventForAudioJack);
    267         Slog.i(TAG, "Initializing input manager, mUseDevInputEventForAudioJack="
    268                 + mUseDevInputEventForAudioJack);
    269         mPtr = nativeInit(this, mContext, mHandler.getLooper().getQueue());
    270 
    271         LocalServices.addService(InputManagerInternal.class, new LocalService());
    272     }
    273 
    274     public void setWindowManagerCallbacks(WindowManagerCallbacks callbacks) {
    275         mWindowManagerCallbacks = callbacks;
    276     }
    277 
    278     public void setWiredAccessoryCallbacks(WiredAccessoryCallbacks callbacks) {
    279         mWiredAccessoryCallbacks = callbacks;
    280     }
    281 
    282     public void start() {
    283         Slog.i(TAG, "Starting input manager");
    284         nativeStart(mPtr);
    285 
    286         // Add ourself to the Watchdog monitors.
    287         Watchdog.getInstance().addMonitor(this);
    288 
    289         registerPointerSpeedSettingObserver();
    290         registerShowTouchesSettingObserver();
    291 
    292         mContext.registerReceiver(new BroadcastReceiver() {
    293             @Override
    294             public void onReceive(Context context, Intent intent) {
    295                 updatePointerSpeedFromSettings();
    296                 updateShowTouchesFromSettings();
    297             }
    298         }, new IntentFilter(Intent.ACTION_USER_SWITCHED), null, mHandler);
    299 
    300         updatePointerSpeedFromSettings();
    301         updateShowTouchesFromSettings();
    302     }
    303 
    304     // TODO(BT) Pass in paramter for bluetooth system
    305     public void systemRunning() {
    306         if (DEBUG) {
    307             Slog.d(TAG, "System ready.");
    308         }
    309         mNotificationManager = (NotificationManager)mContext.getSystemService(
    310                 Context.NOTIFICATION_SERVICE);
    311         mSystemReady = true;
    312 
    313         IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
    314         filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
    315         filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
    316         filter.addAction(Intent.ACTION_PACKAGE_REPLACED);
    317         filter.addDataScheme("package");
    318         mContext.registerReceiver(new BroadcastReceiver() {
    319             @Override
    320             public void onReceive(Context context, Intent intent) {
    321                 updateKeyboardLayouts();
    322             }
    323         }, filter, null, mHandler);
    324 
    325         filter = new IntentFilter(BluetoothDevice.ACTION_ALIAS_CHANGED);
    326         mContext.registerReceiver(new BroadcastReceiver() {
    327             @Override
    328             public void onReceive(Context context, Intent intent) {
    329                 reloadDeviceAliases();
    330             }
    331         }, filter, null, mHandler);
    332 
    333         mHandler.sendEmptyMessage(MSG_RELOAD_DEVICE_ALIASES);
    334         mHandler.sendEmptyMessage(MSG_UPDATE_KEYBOARD_LAYOUTS);
    335 
    336         if (mWiredAccessoryCallbacks != null) {
    337             mWiredAccessoryCallbacks.systemReady();
    338         }
    339     }
    340 
    341     private void reloadKeyboardLayouts() {
    342         if (DEBUG) {
    343             Slog.d(TAG, "Reloading keyboard layouts.");
    344         }
    345         nativeReloadKeyboardLayouts(mPtr);
    346     }
    347 
    348     private void reloadDeviceAliases() {
    349         if (DEBUG) {
    350             Slog.d(TAG, "Reloading device names.");
    351         }
    352         nativeReloadDeviceAliases(mPtr);
    353     }
    354 
    355     private void setDisplayViewportsInternal(DisplayViewport defaultViewport,
    356             DisplayViewport externalTouchViewport) {
    357         if (defaultViewport.valid) {
    358             setDisplayViewport(false, defaultViewport);
    359         }
    360 
    361         if (externalTouchViewport.valid) {
    362             setDisplayViewport(true, externalTouchViewport);
    363         } else if (defaultViewport.valid) {
    364             setDisplayViewport(true, defaultViewport);
    365         }
    366     }
    367 
    368     private void setDisplayViewport(boolean external, DisplayViewport viewport) {
    369         nativeSetDisplayViewport(mPtr, external,
    370                 viewport.displayId, viewport.orientation,
    371                 viewport.logicalFrame.left, viewport.logicalFrame.top,
    372                 viewport.logicalFrame.right, viewport.logicalFrame.bottom,
    373                 viewport.physicalFrame.left, viewport.physicalFrame.top,
    374                 viewport.physicalFrame.right, viewport.physicalFrame.bottom,
    375                 viewport.deviceWidth, viewport.deviceHeight);
    376     }
    377 
    378     /**
    379      * Gets the current state of a key or button by key code.
    380      * @param deviceId The input device id, or -1 to consult all devices.
    381      * @param sourceMask The input sources to consult, or {@link InputDevice#SOURCE_ANY} to
    382      * consider all input sources.  An input device is consulted if at least one of its
    383      * non-class input source bits matches the specified source mask.
    384      * @param keyCode The key code to check.
    385      * @return The key state.
    386      */
    387     public int getKeyCodeState(int deviceId, int sourceMask, int keyCode) {
    388         return nativeGetKeyCodeState(mPtr, deviceId, sourceMask, keyCode);
    389     }
    390 
    391     /**
    392      * Gets the current state of a key or button by scan code.
    393      * @param deviceId The input device id, or -1 to consult all devices.
    394      * @param sourceMask The input sources to consult, or {@link InputDevice#SOURCE_ANY} to
    395      * consider all input sources.  An input device is consulted if at least one of its
    396      * non-class input source bits matches the specified source mask.
    397      * @param scanCode The scan code to check.
    398      * @return The key state.
    399      */
    400     public int getScanCodeState(int deviceId, int sourceMask, int scanCode) {
    401         return nativeGetScanCodeState(mPtr, deviceId, sourceMask, scanCode);
    402     }
    403 
    404     /**
    405      * Gets the current state of a switch by switch code.
    406      * @param deviceId The input device id, or -1 to consult all devices.
    407      * @param sourceMask The input sources to consult, or {@link InputDevice#SOURCE_ANY} to
    408      * consider all input sources.  An input device is consulted if at least one of its
    409      * non-class input source bits matches the specified source mask.
    410      * @param switchCode The switch code to check.
    411      * @return The switch state.
    412      */
    413     public int getSwitchState(int deviceId, int sourceMask, int switchCode) {
    414         return nativeGetSwitchState(mPtr, deviceId, sourceMask, switchCode);
    415     }
    416 
    417     /**
    418      * Determines whether the specified key codes are supported by a particular device.
    419      * @param deviceId The input device id, or -1 to consult all devices.
    420      * @param sourceMask The input sources to consult, or {@link InputDevice#SOURCE_ANY} to
    421      * consider all input sources.  An input device is consulted if at least one of its
    422      * non-class input source bits matches the specified source mask.
    423      * @param keyCodes The array of key codes to check.
    424      * @param keyExists An array at least as large as keyCodes whose entries will be set
    425      * to true or false based on the presence or absence of support for the corresponding
    426      * key codes.
    427      * @return True if the lookup was successful, false otherwise.
    428      */
    429     @Override // Binder call
    430     public boolean hasKeys(int deviceId, int sourceMask, int[] keyCodes, boolean[] keyExists) {
    431         if (keyCodes == null) {
    432             throw new IllegalArgumentException("keyCodes must not be null.");
    433         }
    434         if (keyExists == null || keyExists.length < keyCodes.length) {
    435             throw new IllegalArgumentException("keyExists must not be null and must be at "
    436                     + "least as large as keyCodes.");
    437         }
    438 
    439         return nativeHasKeys(mPtr, deviceId, sourceMask, keyCodes, keyExists);
    440     }
    441 
    442     /**
    443      * Creates an input channel that will receive all input from the input dispatcher.
    444      * @param inputChannelName The input channel name.
    445      * @return The input channel.
    446      */
    447     public InputChannel monitorInput(String inputChannelName) {
    448         if (inputChannelName == null) {
    449             throw new IllegalArgumentException("inputChannelName must not be null.");
    450         }
    451 
    452         InputChannel[] inputChannels = InputChannel.openInputChannelPair(inputChannelName);
    453         nativeRegisterInputChannel(mPtr, inputChannels[0], null, true);
    454         inputChannels[0].dispose(); // don't need to retain the Java object reference
    455         return inputChannels[1];
    456     }
    457 
    458     /**
    459      * Registers an input channel so that it can be used as an input event target.
    460      * @param inputChannel The input channel to register.
    461      * @param inputWindowHandle The handle of the input window associated with the
    462      * input channel, or null if none.
    463      */
    464     public void registerInputChannel(InputChannel inputChannel,
    465             InputWindowHandle inputWindowHandle) {
    466         if (inputChannel == null) {
    467             throw new IllegalArgumentException("inputChannel must not be null.");
    468         }
    469 
    470         nativeRegisterInputChannel(mPtr, inputChannel, inputWindowHandle, false);
    471     }
    472 
    473     /**
    474      * Unregisters an input channel.
    475      * @param inputChannel The input channel to unregister.
    476      */
    477     public void unregisterInputChannel(InputChannel inputChannel) {
    478         if (inputChannel == null) {
    479             throw new IllegalArgumentException("inputChannel must not be null.");
    480         }
    481 
    482         nativeUnregisterInputChannel(mPtr, inputChannel);
    483     }
    484 
    485     /**
    486      * Sets an input filter that will receive all input events before they are dispatched.
    487      * The input filter may then reinterpret input events or inject new ones.
    488      *
    489      * To ensure consistency, the input dispatcher automatically drops all events
    490      * in progress whenever an input filter is installed or uninstalled.  After an input
    491      * filter is uninstalled, it can no longer send input events unless it is reinstalled.
    492      * Any events it attempts to send after it has been uninstalled will be dropped.
    493      *
    494      * @param filter The input filter, or null to remove the current filter.
    495      */
    496     public void setInputFilter(IInputFilter filter) {
    497         synchronized (mInputFilterLock) {
    498             final IInputFilter oldFilter = mInputFilter;
    499             if (oldFilter == filter) {
    500                 return; // nothing to do
    501             }
    502 
    503             if (oldFilter != null) {
    504                 mInputFilter = null;
    505                 mInputFilterHost.disconnectLocked();
    506                 mInputFilterHost = null;
    507                 try {
    508                     oldFilter.uninstall();
    509                 } catch (RemoteException re) {
    510                     /* ignore */
    511                 }
    512             }
    513 
    514             if (filter != null) {
    515                 mInputFilter = filter;
    516                 mInputFilterHost = new InputFilterHost();
    517                 try {
    518                     filter.install(mInputFilterHost);
    519                 } catch (RemoteException re) {
    520                     /* ignore */
    521                 }
    522             }
    523 
    524             nativeSetInputFilterEnabled(mPtr, filter != null);
    525         }
    526     }
    527 
    528     @Override // Binder call
    529     public boolean injectInputEvent(InputEvent event, int mode) {
    530         return injectInputEventInternal(event, Display.DEFAULT_DISPLAY, mode);
    531     }
    532 
    533     private boolean injectInputEventInternal(InputEvent event, int displayId, int mode) {
    534         if (event == null) {
    535             throw new IllegalArgumentException("event must not be null");
    536         }
    537         if (mode != InputManager.INJECT_INPUT_EVENT_MODE_ASYNC
    538                 && mode != InputManager.INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH
    539                 && mode != InputManager.INJECT_INPUT_EVENT_MODE_WAIT_FOR_RESULT) {
    540             throw new IllegalArgumentException("mode is invalid");
    541         }
    542 
    543         final int pid = Binder.getCallingPid();
    544         final int uid = Binder.getCallingUid();
    545         final long ident = Binder.clearCallingIdentity();
    546         final int result;
    547         try {
    548             result = nativeInjectInputEvent(mPtr, event, displayId, pid, uid, mode,
    549                     INJECTION_TIMEOUT_MILLIS, WindowManagerPolicy.FLAG_DISABLE_KEY_REPEAT);
    550         } finally {
    551             Binder.restoreCallingIdentity(ident);
    552         }
    553         switch (result) {
    554             case INPUT_EVENT_INJECTION_PERMISSION_DENIED:
    555                 Slog.w(TAG, "Input event injection from pid " + pid + " permission denied.");
    556                 throw new SecurityException(
    557                         "Injecting to another application requires INJECT_EVENTS permission");
    558             case INPUT_EVENT_INJECTION_SUCCEEDED:
    559                 return true;
    560             case INPUT_EVENT_INJECTION_TIMED_OUT:
    561                 Slog.w(TAG, "Input event injection from pid " + pid + " timed out.");
    562                 return false;
    563             case INPUT_EVENT_INJECTION_FAILED:
    564             default:
    565                 Slog.w(TAG, "Input event injection from pid " + pid + " failed.");
    566                 return false;
    567         }
    568     }
    569 
    570     /**
    571      * Gets information about the input device with the specified id.
    572      * @param deviceId The device id.
    573      * @return The input device or null if not found.
    574      */
    575     @Override // Binder call
    576     public InputDevice getInputDevice(int deviceId) {
    577         synchronized (mInputDevicesLock) {
    578             final int count = mInputDevices.length;
    579             for (int i = 0; i < count; i++) {
    580                 final InputDevice inputDevice = mInputDevices[i];
    581                 if (inputDevice.getId() == deviceId) {
    582                     return inputDevice;
    583                 }
    584             }
    585         }
    586         return null;
    587     }
    588 
    589     /**
    590      * Gets the ids of all input devices in the system.
    591      * @return The input device ids.
    592      */
    593     @Override // Binder call
    594     public int[] getInputDeviceIds() {
    595         synchronized (mInputDevicesLock) {
    596             final int count = mInputDevices.length;
    597             int[] ids = new int[count];
    598             for (int i = 0; i < count; i++) {
    599                 ids[i] = mInputDevices[i].getId();
    600             }
    601             return ids;
    602         }
    603     }
    604 
    605     /**
    606      * Gets all input devices in the system.
    607      * @return The array of input devices.
    608      */
    609     public InputDevice[] getInputDevices() {
    610         synchronized (mInputDevicesLock) {
    611             return mInputDevices;
    612         }
    613     }
    614 
    615     @Override // Binder call
    616     public void registerInputDevicesChangedListener(IInputDevicesChangedListener listener) {
    617         if (listener == null) {
    618             throw new IllegalArgumentException("listener must not be null");
    619         }
    620 
    621         synchronized (mInputDevicesLock) {
    622             int callingPid = Binder.getCallingPid();
    623             if (mInputDevicesChangedListeners.get(callingPid) != null) {
    624                 throw new SecurityException("The calling process has already "
    625                         + "registered an InputDevicesChangedListener.");
    626             }
    627 
    628             InputDevicesChangedListenerRecord record =
    629                     new InputDevicesChangedListenerRecord(callingPid, listener);
    630             try {
    631                 IBinder binder = listener.asBinder();
    632                 binder.linkToDeath(record, 0);
    633             } catch (RemoteException ex) {
    634                 // give up
    635                 throw new RuntimeException(ex);
    636             }
    637 
    638             mInputDevicesChangedListeners.put(callingPid, record);
    639         }
    640     }
    641 
    642     private void onInputDevicesChangedListenerDied(int pid) {
    643         synchronized (mInputDevicesLock) {
    644             mInputDevicesChangedListeners.remove(pid);
    645         }
    646     }
    647 
    648     // Must be called on handler.
    649     private void deliverInputDevicesChanged(InputDevice[] oldInputDevices) {
    650         // Scan for changes.
    651         int numFullKeyboardsAdded = 0;
    652         mTempInputDevicesChangedListenersToNotify.clear();
    653         mTempFullKeyboards.clear();
    654         final int numListeners;
    655         final int[] deviceIdAndGeneration;
    656         synchronized (mInputDevicesLock) {
    657             if (!mInputDevicesChangedPending) {
    658                 return;
    659             }
    660             mInputDevicesChangedPending = false;
    661 
    662             numListeners = mInputDevicesChangedListeners.size();
    663             for (int i = 0; i < numListeners; i++) {
    664                 mTempInputDevicesChangedListenersToNotify.add(
    665                         mInputDevicesChangedListeners.valueAt(i));
    666             }
    667 
    668             final int numDevices = mInputDevices.length;
    669             deviceIdAndGeneration = new int[numDevices * 2];
    670             for (int i = 0; i < numDevices; i++) {
    671                 final InputDevice inputDevice = mInputDevices[i];
    672                 deviceIdAndGeneration[i * 2] = inputDevice.getId();
    673                 deviceIdAndGeneration[i * 2 + 1] = inputDevice.getGeneration();
    674 
    675                 if (!inputDevice.isVirtual() && inputDevice.isFullKeyboard()) {
    676                     if (!containsInputDeviceWithDescriptor(oldInputDevices,
    677                             inputDevice.getDescriptor())) {
    678                         mTempFullKeyboards.add(numFullKeyboardsAdded++, inputDevice);
    679                     } else {
    680                         mTempFullKeyboards.add(inputDevice);
    681                     }
    682                 }
    683             }
    684         }
    685 
    686         // Notify listeners.
    687         for (int i = 0; i < numListeners; i++) {
    688             mTempInputDevicesChangedListenersToNotify.get(i).notifyInputDevicesChanged(
    689                     deviceIdAndGeneration);
    690         }
    691         mTempInputDevicesChangedListenersToNotify.clear();
    692 
    693         // Check for missing keyboard layouts.
    694         if (mNotificationManager != null) {
    695             final int numFullKeyboards = mTempFullKeyboards.size();
    696             boolean missingLayoutForExternalKeyboard = false;
    697             boolean missingLayoutForExternalKeyboardAdded = false;
    698             boolean multipleMissingLayoutsForExternalKeyboardsAdded = false;
    699             InputDevice keyboardMissingLayout = null;
    700             synchronized (mDataStore) {
    701                 for (int i = 0; i < numFullKeyboards; i++) {
    702                     final InputDevice inputDevice = mTempFullKeyboards.get(i);
    703                     final String layout =
    704                             getCurrentKeyboardLayoutForInputDevice(inputDevice.getIdentifier());
    705                     if (layout == null) {
    706                         missingLayoutForExternalKeyboard = true;
    707                         if (i < numFullKeyboardsAdded) {
    708                             missingLayoutForExternalKeyboardAdded = true;
    709                             if (keyboardMissingLayout == null) {
    710                                 keyboardMissingLayout = inputDevice;
    711                             } else {
    712                                 multipleMissingLayoutsForExternalKeyboardsAdded = true;
    713                             }
    714                         }
    715                     }
    716                 }
    717             }
    718             if (missingLayoutForExternalKeyboard) {
    719                 if (missingLayoutForExternalKeyboardAdded) {
    720                     if (multipleMissingLayoutsForExternalKeyboardsAdded) {
    721                         // We have more than one keyboard missing a layout, so drop the
    722                         // user at the generic input methods page so they can pick which
    723                         // one to set.
    724                         showMissingKeyboardLayoutNotification(null);
    725                     } else {
    726                         showMissingKeyboardLayoutNotification(keyboardMissingLayout);
    727                     }
    728                 }
    729             } else if (mKeyboardLayoutNotificationShown) {
    730                 hideMissingKeyboardLayoutNotification();
    731             }
    732         }
    733         mTempFullKeyboards.clear();
    734     }
    735 
    736     @Override // Binder call & native callback
    737     public TouchCalibration getTouchCalibrationForInputDevice(String inputDeviceDescriptor,
    738             int surfaceRotation) {
    739         if (inputDeviceDescriptor == null) {
    740             throw new IllegalArgumentException("inputDeviceDescriptor must not be null");
    741         }
    742 
    743         synchronized (mDataStore) {
    744             return mDataStore.getTouchCalibration(inputDeviceDescriptor, surfaceRotation);
    745         }
    746     }
    747 
    748     @Override // Binder call
    749     public void setTouchCalibrationForInputDevice(String inputDeviceDescriptor, int surfaceRotation,
    750             TouchCalibration calibration) {
    751         if (!checkCallingPermission(android.Manifest.permission.SET_INPUT_CALIBRATION,
    752                 "setTouchCalibrationForInputDevice()")) {
    753             throw new SecurityException("Requires SET_INPUT_CALIBRATION permission");
    754         }
    755         if (inputDeviceDescriptor == null) {
    756             throw new IllegalArgumentException("inputDeviceDescriptor must not be null");
    757         }
    758         if (calibration == null) {
    759             throw new IllegalArgumentException("calibration must not be null");
    760         }
    761         if (surfaceRotation < Surface.ROTATION_0 || surfaceRotation > Surface.ROTATION_270) {
    762             throw new IllegalArgumentException("surfaceRotation value out of bounds");
    763         }
    764 
    765         synchronized (mDataStore) {
    766             try {
    767                 if (mDataStore.setTouchCalibration(inputDeviceDescriptor, surfaceRotation,
    768                         calibration)) {
    769                     nativeReloadCalibration(mPtr);
    770                 }
    771             } finally {
    772                 mDataStore.saveIfNeeded();
    773             }
    774         }
    775     }
    776 
    777     // Must be called on handler.
    778     private void showMissingKeyboardLayoutNotification(InputDevice device) {
    779         if (!mKeyboardLayoutNotificationShown) {
    780             final Intent intent = new Intent(Settings.ACTION_INPUT_METHOD_SETTINGS);
    781             if (device != null) {
    782                 intent.putExtra(Settings.EXTRA_INPUT_DEVICE_IDENTIFIER, device.getIdentifier());
    783             }
    784             intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
    785                     | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
    786                     | Intent.FLAG_ACTIVITY_CLEAR_TOP);
    787             final PendingIntent keyboardLayoutIntent = PendingIntent.getActivityAsUser(mContext, 0,
    788                     intent, 0, null, UserHandle.CURRENT);
    789 
    790             Resources r = mContext.getResources();
    791             Notification notification = new Notification.Builder(mContext)
    792                     .setContentTitle(r.getString(
    793                             R.string.select_keyboard_layout_notification_title))
    794                     .setContentText(r.getString(
    795                             R.string.select_keyboard_layout_notification_message))
    796                     .setContentIntent(keyboardLayoutIntent)
    797                     .setSmallIcon(R.drawable.ic_settings_language)
    798                     .setPriority(Notification.PRIORITY_LOW)
    799                     .setColor(mContext.getResources().getColor(
    800                             com.android.internal.R.color.system_notification_accent_color))
    801                     .build();
    802             mNotificationManager.notifyAsUser(null,
    803                     R.string.select_keyboard_layout_notification_title,
    804                     notification, UserHandle.ALL);
    805             mKeyboardLayoutNotificationShown = true;
    806         }
    807     }
    808 
    809     // Must be called on handler.
    810     private void hideMissingKeyboardLayoutNotification() {
    811         if (mKeyboardLayoutNotificationShown) {
    812             mKeyboardLayoutNotificationShown = false;
    813             mNotificationManager.cancelAsUser(null,
    814                     R.string.select_keyboard_layout_notification_title,
    815                     UserHandle.ALL);
    816         }
    817     }
    818 
    819     // Must be called on handler.
    820     private void updateKeyboardLayouts() {
    821         // Scan all input devices state for keyboard layouts that have been uninstalled.
    822         final HashSet<String> availableKeyboardLayouts = new HashSet<String>();
    823         visitAllKeyboardLayouts(new KeyboardLayoutVisitor() {
    824             @Override
    825             public void visitKeyboardLayout(Resources resources, String descriptor, String label,
    826                     String collection, int keyboardLayoutResId, int priority) {
    827                 availableKeyboardLayouts.add(descriptor);
    828             }
    829         });
    830         synchronized (mDataStore) {
    831             try {
    832                 mDataStore.removeUninstalledKeyboardLayouts(availableKeyboardLayouts);
    833             } finally {
    834                 mDataStore.saveIfNeeded();
    835             }
    836         }
    837 
    838         // Reload keyboard layouts.
    839         reloadKeyboardLayouts();
    840     }
    841 
    842     private static boolean containsInputDeviceWithDescriptor(InputDevice[] inputDevices,
    843             String descriptor) {
    844         final int numDevices = inputDevices.length;
    845         for (int i = 0; i < numDevices; i++) {
    846             final InputDevice inputDevice = inputDevices[i];
    847             if (inputDevice.getDescriptor().equals(descriptor)) {
    848                 return true;
    849             }
    850         }
    851         return false;
    852     }
    853 
    854     @Override // Binder call
    855     public KeyboardLayout[] getKeyboardLayouts() {
    856         final ArrayList<KeyboardLayout> list = new ArrayList<KeyboardLayout>();
    857         visitAllKeyboardLayouts(new KeyboardLayoutVisitor() {
    858             @Override
    859             public void visitKeyboardLayout(Resources resources, String descriptor, String label,
    860                     String collection, int keyboardLayoutResId, int priority) {
    861                 list.add(new KeyboardLayout(descriptor, label, collection, priority));
    862             }
    863         });
    864         return list.toArray(new KeyboardLayout[list.size()]);
    865     }
    866 
    867     @Override // Binder call
    868     public KeyboardLayout getKeyboardLayout(String keyboardLayoutDescriptor) {
    869         if (keyboardLayoutDescriptor == null) {
    870             throw new IllegalArgumentException("keyboardLayoutDescriptor must not be null");
    871         }
    872 
    873         final KeyboardLayout[] result = new KeyboardLayout[1];
    874         visitKeyboardLayout(keyboardLayoutDescriptor, new KeyboardLayoutVisitor() {
    875             @Override
    876             public void visitKeyboardLayout(Resources resources, String descriptor,
    877                     String label, String collection, int keyboardLayoutResId, int priority) {
    878                 result[0] = new KeyboardLayout(descriptor, label, collection, priority);
    879             }
    880         });
    881         if (result[0] == null) {
    882             Log.w(TAG, "Could not get keyboard layout with descriptor '"
    883                     + keyboardLayoutDescriptor + "'.");
    884         }
    885         return result[0];
    886     }
    887 
    888     private void visitAllKeyboardLayouts(KeyboardLayoutVisitor visitor) {
    889         final PackageManager pm = mContext.getPackageManager();
    890         Intent intent = new Intent(InputManager.ACTION_QUERY_KEYBOARD_LAYOUTS);
    891         for (ResolveInfo resolveInfo : pm.queryBroadcastReceivers(intent,
    892                 PackageManager.GET_META_DATA)) {
    893             final ActivityInfo activityInfo = resolveInfo.activityInfo;
    894             final int priority = resolveInfo.priority;
    895             visitKeyboardLayoutsInPackage(pm, activityInfo, null, priority, visitor);
    896         }
    897     }
    898 
    899     private void visitKeyboardLayout(String keyboardLayoutDescriptor,
    900             KeyboardLayoutVisitor visitor) {
    901         KeyboardLayoutDescriptor d = KeyboardLayoutDescriptor.parse(keyboardLayoutDescriptor);
    902         if (d != null) {
    903             final PackageManager pm = mContext.getPackageManager();
    904             try {
    905                 ActivityInfo receiver = pm.getReceiverInfo(
    906                         new ComponentName(d.packageName, d.receiverName),
    907                         PackageManager.GET_META_DATA);
    908                 visitKeyboardLayoutsInPackage(pm, receiver, d.keyboardLayoutName, 0, visitor);
    909             } catch (NameNotFoundException ex) {
    910             }
    911         }
    912     }
    913 
    914     private void visitKeyboardLayoutsInPackage(PackageManager pm, ActivityInfo receiver,
    915             String keyboardName, int requestedPriority, KeyboardLayoutVisitor visitor) {
    916         Bundle metaData = receiver.metaData;
    917         if (metaData == null) {
    918             return;
    919         }
    920 
    921         int configResId = metaData.getInt(InputManager.META_DATA_KEYBOARD_LAYOUTS);
    922         if (configResId == 0) {
    923             Log.w(TAG, "Missing meta-data '" + InputManager.META_DATA_KEYBOARD_LAYOUTS
    924                     + "' on receiver " + receiver.packageName + "/" + receiver.name);
    925             return;
    926         }
    927 
    928         CharSequence receiverLabel = receiver.loadLabel(pm);
    929         String collection = receiverLabel != null ? receiverLabel.toString() : "";
    930         int priority;
    931         if ((receiver.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
    932             priority = requestedPriority;
    933         } else {
    934             priority = 0;
    935         }
    936 
    937         try {
    938             Resources resources = pm.getResourcesForApplication(receiver.applicationInfo);
    939             XmlResourceParser parser = resources.getXml(configResId);
    940             try {
    941                 XmlUtils.beginDocument(parser, "keyboard-layouts");
    942 
    943                 for (;;) {
    944                     XmlUtils.nextElement(parser);
    945                     String element = parser.getName();
    946                     if (element == null) {
    947                         break;
    948                     }
    949                     if (element.equals("keyboard-layout")) {
    950                         TypedArray a = resources.obtainAttributes(
    951                                 parser, com.android.internal.R.styleable.KeyboardLayout);
    952                         try {
    953                             String name = a.getString(
    954                                     com.android.internal.R.styleable.KeyboardLayout_name);
    955                             String label = a.getString(
    956                                     com.android.internal.R.styleable.KeyboardLayout_label);
    957                             int keyboardLayoutResId = a.getResourceId(
    958                                     com.android.internal.R.styleable.KeyboardLayout_keyboardLayout,
    959                                     0);
    960                             if (name == null || label == null || keyboardLayoutResId == 0) {
    961                                 Log.w(TAG, "Missing required 'name', 'label' or 'keyboardLayout' "
    962                                         + "attributes in keyboard layout "
    963                                         + "resource from receiver "
    964                                         + receiver.packageName + "/" + receiver.name);
    965                             } else {
    966                                 String descriptor = KeyboardLayoutDescriptor.format(
    967                                         receiver.packageName, receiver.name, name);
    968                                 if (keyboardName == null || name.equals(keyboardName)) {
    969                                     visitor.visitKeyboardLayout(resources, descriptor,
    970                                             label, collection, keyboardLayoutResId, priority);
    971                                 }
    972                             }
    973                         } finally {
    974                             a.recycle();
    975                         }
    976                     } else {
    977                         Log.w(TAG, "Skipping unrecognized element '" + element
    978                                 + "' in keyboard layout resource from receiver "
    979                                 + receiver.packageName + "/" + receiver.name);
    980                     }
    981                 }
    982             } finally {
    983                 parser.close();
    984             }
    985         } catch (Exception ex) {
    986             Log.w(TAG, "Could not parse keyboard layout resource from receiver "
    987                     + receiver.packageName + "/" + receiver.name, ex);
    988         }
    989     }
    990 
    991     /**
    992      * Builds a layout descriptor for the vendor/product. This returns the
    993      * descriptor for ids that aren't useful (such as the default 0, 0).
    994      */
    995     private String getLayoutDescriptor(InputDeviceIdentifier identifier) {
    996         if (identifier == null || identifier.getDescriptor() == null) {
    997             throw new IllegalArgumentException("identifier and descriptor must not be null");
    998         }
    999 
   1000         if (identifier.getVendorId() == 0 && identifier.getProductId() == 0) {
   1001             return identifier.getDescriptor();
   1002         }
   1003         StringBuilder bob = new StringBuilder();
   1004         bob.append("vendor:").append(identifier.getVendorId());
   1005         bob.append(",product:").append(identifier.getProductId());
   1006         return bob.toString();
   1007     }
   1008 
   1009     @Override // Binder call
   1010     public String getCurrentKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier) {
   1011 
   1012         String key = getLayoutDescriptor(identifier);
   1013         synchronized (mDataStore) {
   1014             String layout = null;
   1015             // try loading it using the layout descriptor if we have it
   1016             layout = mDataStore.getCurrentKeyboardLayout(key);
   1017             if (layout == null && !key.equals(identifier.getDescriptor())) {
   1018                 // if it doesn't exist fall back to the device descriptor
   1019                 layout = mDataStore.getCurrentKeyboardLayout(identifier.getDescriptor());
   1020             }
   1021             if (DEBUG) {
   1022                 Slog.d(TAG, "Loaded keyboard layout id for " + key + " and got "
   1023                         + layout);
   1024             }
   1025             return layout;
   1026         }
   1027     }
   1028 
   1029     @Override // Binder call
   1030     public void setCurrentKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier,
   1031             String keyboardLayoutDescriptor) {
   1032         if (!checkCallingPermission(android.Manifest.permission.SET_KEYBOARD_LAYOUT,
   1033                 "setCurrentKeyboardLayoutForInputDevice()")) {
   1034             throw new SecurityException("Requires SET_KEYBOARD_LAYOUT permission");
   1035         }
   1036         if (keyboardLayoutDescriptor == null) {
   1037             throw new IllegalArgumentException("keyboardLayoutDescriptor must not be null");
   1038         }
   1039 
   1040         String key = getLayoutDescriptor(identifier);
   1041         synchronized (mDataStore) {
   1042             try {
   1043                 if (mDataStore.setCurrentKeyboardLayout(key, keyboardLayoutDescriptor)) {
   1044                     if (DEBUG) {
   1045                         Slog.d(TAG, "Saved keyboard layout using " + key);
   1046                     }
   1047                     mHandler.sendEmptyMessage(MSG_RELOAD_KEYBOARD_LAYOUTS);
   1048                 }
   1049             } finally {
   1050                 mDataStore.saveIfNeeded();
   1051             }
   1052         }
   1053     }
   1054 
   1055     @Override // Binder call
   1056     public String[] getKeyboardLayoutsForInputDevice(InputDeviceIdentifier identifier) {
   1057         String key = getLayoutDescriptor(identifier);
   1058         synchronized (mDataStore) {
   1059             String[] layouts = mDataStore.getKeyboardLayouts(key);
   1060             if ((layouts == null || layouts.length == 0)
   1061                     && !key.equals(identifier.getDescriptor())) {
   1062                 layouts = mDataStore.getKeyboardLayouts(identifier.getDescriptor());
   1063             }
   1064             return layouts;
   1065         }
   1066     }
   1067 
   1068     @Override // Binder call
   1069     public void addKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier,
   1070             String keyboardLayoutDescriptor) {
   1071         if (!checkCallingPermission(android.Manifest.permission.SET_KEYBOARD_LAYOUT,
   1072                 "addKeyboardLayoutForInputDevice()")) {
   1073             throw new SecurityException("Requires SET_KEYBOARD_LAYOUT permission");
   1074         }
   1075         if (keyboardLayoutDescriptor == null) {
   1076             throw new IllegalArgumentException("keyboardLayoutDescriptor must not be null");
   1077         }
   1078 
   1079         String key = getLayoutDescriptor(identifier);
   1080         synchronized (mDataStore) {
   1081             try {
   1082                 String oldLayout = mDataStore.getCurrentKeyboardLayout(key);
   1083                 if (oldLayout == null && !key.equals(identifier.getDescriptor())) {
   1084                     oldLayout = mDataStore.getCurrentKeyboardLayout(identifier.getDescriptor());
   1085                 }
   1086                 if (mDataStore.addKeyboardLayout(key, keyboardLayoutDescriptor)
   1087                         && !Objects.equal(oldLayout,
   1088                                 mDataStore.getCurrentKeyboardLayout(key))) {
   1089                     mHandler.sendEmptyMessage(MSG_RELOAD_KEYBOARD_LAYOUTS);
   1090                 }
   1091             } finally {
   1092                 mDataStore.saveIfNeeded();
   1093             }
   1094         }
   1095     }
   1096 
   1097     @Override // Binder call
   1098     public void removeKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier,
   1099             String keyboardLayoutDescriptor) {
   1100         if (!checkCallingPermission(android.Manifest.permission.SET_KEYBOARD_LAYOUT,
   1101                 "removeKeyboardLayoutForInputDevice()")) {
   1102             throw new SecurityException("Requires SET_KEYBOARD_LAYOUT permission");
   1103         }
   1104         if (keyboardLayoutDescriptor == null) {
   1105             throw new IllegalArgumentException("keyboardLayoutDescriptor must not be null");
   1106         }
   1107 
   1108         String key = getLayoutDescriptor(identifier);
   1109         synchronized (mDataStore) {
   1110             try {
   1111                 String oldLayout = mDataStore.getCurrentKeyboardLayout(key);
   1112                 if (oldLayout == null && !key.equals(identifier.getDescriptor())) {
   1113                     oldLayout = mDataStore.getCurrentKeyboardLayout(identifier.getDescriptor());
   1114                 }
   1115                 boolean removed = mDataStore.removeKeyboardLayout(key, keyboardLayoutDescriptor);
   1116                 if (!key.equals(identifier.getDescriptor())) {
   1117                     // We need to remove from both places to ensure it is gone
   1118                     removed |= mDataStore.removeKeyboardLayout(identifier.getDescriptor(),
   1119                             keyboardLayoutDescriptor);
   1120                 }
   1121                 if (removed && !Objects.equal(oldLayout,
   1122                                 mDataStore.getCurrentKeyboardLayout(key))) {
   1123                     mHandler.sendEmptyMessage(MSG_RELOAD_KEYBOARD_LAYOUTS);
   1124                 }
   1125             } finally {
   1126                 mDataStore.saveIfNeeded();
   1127             }
   1128         }
   1129     }
   1130 
   1131     public void switchKeyboardLayout(int deviceId, int direction) {
   1132         mHandler.obtainMessage(MSG_SWITCH_KEYBOARD_LAYOUT, deviceId, direction).sendToTarget();
   1133     }
   1134 
   1135     // Must be called on handler.
   1136     private void handleSwitchKeyboardLayout(int deviceId, int direction) {
   1137         final InputDevice device = getInputDevice(deviceId);
   1138         if (device != null) {
   1139             final boolean changed;
   1140             final String keyboardLayoutDescriptor;
   1141 
   1142             String key = getLayoutDescriptor(device.getIdentifier());
   1143             synchronized (mDataStore) {
   1144                 try {
   1145                     changed = mDataStore.switchKeyboardLayout(key, direction);
   1146                     keyboardLayoutDescriptor = mDataStore.getCurrentKeyboardLayout(
   1147                             key);
   1148                 } finally {
   1149                     mDataStore.saveIfNeeded();
   1150                 }
   1151             }
   1152 
   1153             if (changed) {
   1154                 if (mSwitchedKeyboardLayoutToast != null) {
   1155                     mSwitchedKeyboardLayoutToast.cancel();
   1156                     mSwitchedKeyboardLayoutToast = null;
   1157                 }
   1158                 if (keyboardLayoutDescriptor != null) {
   1159                     KeyboardLayout keyboardLayout = getKeyboardLayout(keyboardLayoutDescriptor);
   1160                     if (keyboardLayout != null) {
   1161                         mSwitchedKeyboardLayoutToast = Toast.makeText(
   1162                                 mContext, keyboardLayout.getLabel(), Toast.LENGTH_SHORT);
   1163                         mSwitchedKeyboardLayoutToast.show();
   1164                     }
   1165                 }
   1166 
   1167                 reloadKeyboardLayouts();
   1168             }
   1169         }
   1170     }
   1171 
   1172     public void setInputWindows(InputWindowHandle[] windowHandles) {
   1173         nativeSetInputWindows(mPtr, windowHandles);
   1174     }
   1175 
   1176     public void setFocusedApplication(InputApplicationHandle application) {
   1177         nativeSetFocusedApplication(mPtr, application);
   1178     }
   1179 
   1180     public void setInputDispatchMode(boolean enabled, boolean frozen) {
   1181         nativeSetInputDispatchMode(mPtr, enabled, frozen);
   1182     }
   1183 
   1184     public void setSystemUiVisibility(int visibility) {
   1185         nativeSetSystemUiVisibility(mPtr, visibility);
   1186     }
   1187 
   1188     /**
   1189      * Atomically transfers touch focus from one window to another as identified by
   1190      * their input channels.  It is possible for multiple windows to have
   1191      * touch focus if they support split touch dispatch
   1192      * {@link android.view.WindowManager.LayoutParams#FLAG_SPLIT_TOUCH} but this
   1193      * method only transfers touch focus of the specified window without affecting
   1194      * other windows that may also have touch focus at the same time.
   1195      * @param fromChannel The channel of a window that currently has touch focus.
   1196      * @param toChannel The channel of the window that should receive touch focus in
   1197      * place of the first.
   1198      * @return True if the transfer was successful.  False if the window with the
   1199      * specified channel did not actually have touch focus at the time of the request.
   1200      */
   1201     public boolean transferTouchFocus(InputChannel fromChannel, InputChannel toChannel) {
   1202         if (fromChannel == null) {
   1203             throw new IllegalArgumentException("fromChannel must not be null.");
   1204         }
   1205         if (toChannel == null) {
   1206             throw new IllegalArgumentException("toChannel must not be null.");
   1207         }
   1208         return nativeTransferTouchFocus(mPtr, fromChannel, toChannel);
   1209     }
   1210 
   1211     @Override // Binder call
   1212     public void tryPointerSpeed(int speed) {
   1213         if (!checkCallingPermission(android.Manifest.permission.SET_POINTER_SPEED,
   1214                 "tryPointerSpeed()")) {
   1215             throw new SecurityException("Requires SET_POINTER_SPEED permission");
   1216         }
   1217 
   1218         if (speed < InputManager.MIN_POINTER_SPEED || speed > InputManager.MAX_POINTER_SPEED) {
   1219             throw new IllegalArgumentException("speed out of range");
   1220         }
   1221 
   1222         setPointerSpeedUnchecked(speed);
   1223     }
   1224 
   1225     public void updatePointerSpeedFromSettings() {
   1226         int speed = getPointerSpeedSetting();
   1227         setPointerSpeedUnchecked(speed);
   1228     }
   1229 
   1230     private void setPointerSpeedUnchecked(int speed) {
   1231         speed = Math.min(Math.max(speed, InputManager.MIN_POINTER_SPEED),
   1232                 InputManager.MAX_POINTER_SPEED);
   1233         nativeSetPointerSpeed(mPtr, speed);
   1234     }
   1235 
   1236     private void registerPointerSpeedSettingObserver() {
   1237         mContext.getContentResolver().registerContentObserver(
   1238                 Settings.System.getUriFor(Settings.System.POINTER_SPEED), true,
   1239                 new ContentObserver(mHandler) {
   1240                     @Override
   1241                     public void onChange(boolean selfChange) {
   1242                         updatePointerSpeedFromSettings();
   1243                     }
   1244                 }, UserHandle.USER_ALL);
   1245     }
   1246 
   1247     private int getPointerSpeedSetting() {
   1248         int speed = InputManager.DEFAULT_POINTER_SPEED;
   1249         try {
   1250             speed = Settings.System.getIntForUser(mContext.getContentResolver(),
   1251                     Settings.System.POINTER_SPEED, UserHandle.USER_CURRENT);
   1252         } catch (SettingNotFoundException snfe) {
   1253         }
   1254         return speed;
   1255     }
   1256 
   1257     public void updateShowTouchesFromSettings() {
   1258         int setting = getShowTouchesSetting(0);
   1259         nativeSetShowTouches(mPtr, setting != 0);
   1260     }
   1261 
   1262     private void registerShowTouchesSettingObserver() {
   1263         mContext.getContentResolver().registerContentObserver(
   1264                 Settings.System.getUriFor(Settings.System.SHOW_TOUCHES), true,
   1265                 new ContentObserver(mHandler) {
   1266                     @Override
   1267                     public void onChange(boolean selfChange) {
   1268                         updateShowTouchesFromSettings();
   1269                     }
   1270                 }, UserHandle.USER_ALL);
   1271     }
   1272 
   1273     private int getShowTouchesSetting(int defaultValue) {
   1274         int result = defaultValue;
   1275         try {
   1276             result = Settings.System.getIntForUser(mContext.getContentResolver(),
   1277                     Settings.System.SHOW_TOUCHES, UserHandle.USER_CURRENT);
   1278         } catch (SettingNotFoundException snfe) {
   1279         }
   1280         return result;
   1281     }
   1282 
   1283     // Binder call
   1284     @Override
   1285     public void vibrate(int deviceId, long[] pattern, int repeat, IBinder token) {
   1286         if (repeat >= pattern.length) {
   1287             throw new ArrayIndexOutOfBoundsException();
   1288         }
   1289 
   1290         VibratorToken v;
   1291         synchronized (mVibratorLock) {
   1292             v = mVibratorTokens.get(token);
   1293             if (v == null) {
   1294                 v = new VibratorToken(deviceId, token, mNextVibratorTokenValue++);
   1295                 try {
   1296                     token.linkToDeath(v, 0);
   1297                 } catch (RemoteException ex) {
   1298                     // give up
   1299                     throw new RuntimeException(ex);
   1300                 }
   1301                 mVibratorTokens.put(token, v);
   1302             }
   1303         }
   1304 
   1305         synchronized (v) {
   1306             v.mVibrating = true;
   1307             nativeVibrate(mPtr, deviceId, pattern, repeat, v.mTokenValue);
   1308         }
   1309     }
   1310 
   1311     // Binder call
   1312     @Override
   1313     public void cancelVibrate(int deviceId, IBinder token) {
   1314         VibratorToken v;
   1315         synchronized (mVibratorLock) {
   1316             v = mVibratorTokens.get(token);
   1317             if (v == null || v.mDeviceId != deviceId) {
   1318                 return; // nothing to cancel
   1319             }
   1320         }
   1321 
   1322         cancelVibrateIfNeeded(v);
   1323     }
   1324 
   1325     void onVibratorTokenDied(VibratorToken v) {
   1326         synchronized (mVibratorLock) {
   1327             mVibratorTokens.remove(v.mToken);
   1328         }
   1329 
   1330         cancelVibrateIfNeeded(v);
   1331     }
   1332 
   1333     private void cancelVibrateIfNeeded(VibratorToken v) {
   1334         synchronized (v) {
   1335             if (v.mVibrating) {
   1336                 nativeCancelVibrate(mPtr, v.mDeviceId, v.mTokenValue);
   1337                 v.mVibrating = false;
   1338             }
   1339         }
   1340     }
   1341 
   1342     @Override
   1343     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
   1344         if (mContext.checkCallingOrSelfPermission(Manifest.permission.DUMP)
   1345                 != PackageManager.PERMISSION_GRANTED) {
   1346             pw.println("Permission Denial: can't dump InputManager from from pid="
   1347                     + Binder.getCallingPid()
   1348                     + ", uid=" + Binder.getCallingUid());
   1349             return;
   1350         }
   1351 
   1352         pw.println("INPUT MANAGER (dumpsys input)\n");
   1353         String dumpStr = nativeDump(mPtr);
   1354         if (dumpStr != null) {
   1355             pw.println(dumpStr);
   1356         }
   1357     }
   1358 
   1359     private boolean checkCallingPermission(String permission, String func) {
   1360         // Quick check: if the calling permission is me, it's all okay.
   1361         if (Binder.getCallingPid() == Process.myPid()) {
   1362             return true;
   1363         }
   1364 
   1365         if (mContext.checkCallingPermission(permission) == PackageManager.PERMISSION_GRANTED) {
   1366             return true;
   1367         }
   1368         String msg = "Permission Denial: " + func + " from pid="
   1369                 + Binder.getCallingPid()
   1370                 + ", uid=" + Binder.getCallingUid()
   1371                 + " requires " + permission;
   1372         Slog.w(TAG, msg);
   1373         return false;
   1374     }
   1375 
   1376     // Called by the heartbeat to ensure locks are not held indefinitely (for deadlock detection).
   1377     @Override
   1378     public void monitor() {
   1379         synchronized (mInputFilterLock) { }
   1380         nativeMonitor(mPtr);
   1381     }
   1382 
   1383     // Native callback.
   1384     private void notifyConfigurationChanged(long whenNanos) {
   1385         mWindowManagerCallbacks.notifyConfigurationChanged();
   1386     }
   1387 
   1388     // Native callback.
   1389     private void notifyInputDevicesChanged(InputDevice[] inputDevices) {
   1390         synchronized (mInputDevicesLock) {
   1391             if (!mInputDevicesChangedPending) {
   1392                 mInputDevicesChangedPending = true;
   1393                 mHandler.obtainMessage(MSG_DELIVER_INPUT_DEVICES_CHANGED,
   1394                         mInputDevices).sendToTarget();
   1395             }
   1396 
   1397             mInputDevices = inputDevices;
   1398         }
   1399     }
   1400 
   1401     // Native callback.
   1402     private void notifySwitch(long whenNanos, int switchValues, int switchMask) {
   1403         if (DEBUG) {
   1404             Slog.d(TAG, "notifySwitch: values=" + Integer.toHexString(switchValues)
   1405                     + ", mask=" + Integer.toHexString(switchMask));
   1406         }
   1407 
   1408         if ((switchMask & SW_LID_BIT) != 0) {
   1409             final boolean lidOpen = ((switchValues & SW_LID_BIT) == 0);
   1410             mWindowManagerCallbacks.notifyLidSwitchChanged(whenNanos, lidOpen);
   1411         }
   1412 
   1413         if ((switchMask & SW_CAMERA_LENS_COVER_BIT) != 0) {
   1414             final boolean lensCovered = ((switchValues & SW_CAMERA_LENS_COVER_BIT) != 0);
   1415             mWindowManagerCallbacks.notifyCameraLensCoverSwitchChanged(whenNanos, lensCovered);
   1416         }
   1417 
   1418         if (mUseDevInputEventForAudioJack && (switchMask & SW_JACK_BITS) != 0) {
   1419             mWiredAccessoryCallbacks.notifyWiredAccessoryChanged(whenNanos, switchValues,
   1420                     switchMask);
   1421         }
   1422     }
   1423 
   1424     // Native callback.
   1425     private void notifyInputChannelBroken(InputWindowHandle inputWindowHandle) {
   1426         mWindowManagerCallbacks.notifyInputChannelBroken(inputWindowHandle);
   1427     }
   1428 
   1429     // Native callback.
   1430     private long notifyANR(InputApplicationHandle inputApplicationHandle,
   1431             InputWindowHandle inputWindowHandle, String reason) {
   1432         return mWindowManagerCallbacks.notifyANR(
   1433                 inputApplicationHandle, inputWindowHandle, reason);
   1434     }
   1435 
   1436     // Native callback.
   1437     final boolean filterInputEvent(InputEvent event, int policyFlags) {
   1438         synchronized (mInputFilterLock) {
   1439             if (mInputFilter != null) {
   1440                 try {
   1441                     mInputFilter.filterInputEvent(event, policyFlags);
   1442                 } catch (RemoteException e) {
   1443                     /* ignore */
   1444                 }
   1445                 return false;
   1446             }
   1447         }
   1448         event.recycle();
   1449         return true;
   1450     }
   1451 
   1452     // Native callback.
   1453     private int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {
   1454         return mWindowManagerCallbacks.interceptKeyBeforeQueueing(event, policyFlags);
   1455     }
   1456 
   1457     // Native callback.
   1458     private int interceptMotionBeforeQueueingNonInteractive(long whenNanos, int policyFlags) {
   1459         return mWindowManagerCallbacks.interceptMotionBeforeQueueingNonInteractive(
   1460                 whenNanos, policyFlags);
   1461     }
   1462 
   1463     // Native callback.
   1464     private long interceptKeyBeforeDispatching(InputWindowHandle focus,
   1465             KeyEvent event, int policyFlags) {
   1466         return mWindowManagerCallbacks.interceptKeyBeforeDispatching(focus, event, policyFlags);
   1467     }
   1468 
   1469     // Native callback.
   1470     private KeyEvent dispatchUnhandledKey(InputWindowHandle focus,
   1471             KeyEvent event, int policyFlags) {
   1472         return mWindowManagerCallbacks.dispatchUnhandledKey(focus, event, policyFlags);
   1473     }
   1474 
   1475     // Native callback.
   1476     private boolean checkInjectEventsPermission(int injectorPid, int injectorUid) {
   1477         return mContext.checkPermission(android.Manifest.permission.INJECT_EVENTS,
   1478                 injectorPid, injectorUid) == PackageManager.PERMISSION_GRANTED;
   1479     }
   1480 
   1481     // Native callback.
   1482     private int getVirtualKeyQuietTimeMillis() {
   1483         return mContext.getResources().getInteger(
   1484                 com.android.internal.R.integer.config_virtualKeyQuietTimeMillis);
   1485     }
   1486 
   1487     // Native callback.
   1488     private String[] getExcludedDeviceNames() {
   1489         ArrayList<String> names = new ArrayList<String>();
   1490 
   1491         // Read partner-provided list of excluded input devices
   1492         XmlPullParser parser = null;
   1493         // Environment.getRootDirectory() is a fancy way of saying ANDROID_ROOT or "/system".
   1494         File confFile = new File(Environment.getRootDirectory(), EXCLUDED_DEVICES_PATH);
   1495         FileReader confreader = null;
   1496         try {
   1497             confreader = new FileReader(confFile);
   1498             parser = Xml.newPullParser();
   1499             parser.setInput(confreader);
   1500             XmlUtils.beginDocument(parser, "devices");
   1501 
   1502             while (true) {
   1503                 XmlUtils.nextElement(parser);
   1504                 if (!"device".equals(parser.getName())) {
   1505                     break;
   1506                 }
   1507                 String name = parser.getAttributeValue(null, "name");
   1508                 if (name != null) {
   1509                     names.add(name);
   1510                 }
   1511             }
   1512         } catch (FileNotFoundException e) {
   1513             // It's ok if the file does not exist.
   1514         } catch (Exception e) {
   1515             Slog.e(TAG, "Exception while parsing '" + confFile.getAbsolutePath() + "'", e);
   1516         } finally {
   1517             try { if (confreader != null) confreader.close(); } catch (IOException e) { }
   1518         }
   1519 
   1520         return names.toArray(new String[names.size()]);
   1521     }
   1522 
   1523     // Native callback.
   1524     private int getKeyRepeatTimeout() {
   1525         return ViewConfiguration.getKeyRepeatTimeout();
   1526     }
   1527 
   1528     // Native callback.
   1529     private int getKeyRepeatDelay() {
   1530         return ViewConfiguration.getKeyRepeatDelay();
   1531     }
   1532 
   1533     // Native callback.
   1534     private int getHoverTapTimeout() {
   1535         return ViewConfiguration.getHoverTapTimeout();
   1536     }
   1537 
   1538     // Native callback.
   1539     private int getHoverTapSlop() {
   1540         return ViewConfiguration.getHoverTapSlop();
   1541     }
   1542 
   1543     // Native callback.
   1544     private int getDoubleTapTimeout() {
   1545         return ViewConfiguration.getDoubleTapTimeout();
   1546     }
   1547 
   1548     // Native callback.
   1549     private int getLongPressTimeout() {
   1550         return ViewConfiguration.getLongPressTimeout();
   1551     }
   1552 
   1553     // Native callback.
   1554     private int getPointerLayer() {
   1555         return mWindowManagerCallbacks.getPointerLayer();
   1556     }
   1557 
   1558     // Native callback.
   1559     private PointerIcon getPointerIcon() {
   1560         return PointerIcon.getDefaultIcon(mContext);
   1561     }
   1562 
   1563     // Native callback.
   1564     private String[] getKeyboardLayoutOverlay(InputDeviceIdentifier identifier) {
   1565         if (!mSystemReady) {
   1566             return null;
   1567         }
   1568 
   1569         String keyboardLayoutDescriptor = getCurrentKeyboardLayoutForInputDevice(identifier);
   1570         if (keyboardLayoutDescriptor == null) {
   1571             return null;
   1572         }
   1573 
   1574         final String[] result = new String[2];
   1575         visitKeyboardLayout(keyboardLayoutDescriptor, new KeyboardLayoutVisitor() {
   1576             @Override
   1577             public void visitKeyboardLayout(Resources resources, String descriptor, String label,
   1578                     String collection, int keyboardLayoutResId, int priority) {
   1579                 try {
   1580                     result[0] = descriptor;
   1581                     result[1] = Streams.readFully(new InputStreamReader(
   1582                             resources.openRawResource(keyboardLayoutResId)));
   1583                 } catch (IOException ex) {
   1584                 } catch (NotFoundException ex) {
   1585                 }
   1586             }
   1587         });
   1588         if (result[0] == null) {
   1589             Log.w(TAG, "Could not get keyboard layout with descriptor '"
   1590                     + keyboardLayoutDescriptor + "'.");
   1591             return null;
   1592         }
   1593         return result;
   1594     }
   1595 
   1596     // Native callback.
   1597     private String getDeviceAlias(String uniqueId) {
   1598         if (BluetoothAdapter.checkBluetoothAddress(uniqueId)) {
   1599             // TODO(BT) mBluetoothService.getRemoteAlias(uniqueId)
   1600             return null;
   1601         }
   1602         return null;
   1603     }
   1604 
   1605     /**
   1606      * Callback interface implemented by the Window Manager.
   1607      */
   1608     public interface WindowManagerCallbacks {
   1609         public void notifyConfigurationChanged();
   1610 
   1611         public void notifyLidSwitchChanged(long whenNanos, boolean lidOpen);
   1612 
   1613         public void notifyCameraLensCoverSwitchChanged(long whenNanos, boolean lensCovered);
   1614 
   1615         public void notifyInputChannelBroken(InputWindowHandle inputWindowHandle);
   1616 
   1617         public long notifyANR(InputApplicationHandle inputApplicationHandle,
   1618                 InputWindowHandle inputWindowHandle, String reason);
   1619 
   1620         public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags);
   1621 
   1622         public int interceptMotionBeforeQueueingNonInteractive(long whenNanos, int policyFlags);
   1623 
   1624         public long interceptKeyBeforeDispatching(InputWindowHandle focus,
   1625                 KeyEvent event, int policyFlags);
   1626 
   1627         public KeyEvent dispatchUnhandledKey(InputWindowHandle focus,
   1628                 KeyEvent event, int policyFlags);
   1629 
   1630         public int getPointerLayer();
   1631     }
   1632 
   1633     /**
   1634      * Callback interface implemented by WiredAccessoryObserver.
   1635      */
   1636     public interface WiredAccessoryCallbacks {
   1637         public void notifyWiredAccessoryChanged(long whenNanos, int switchValues, int switchMask);
   1638         public void systemReady();
   1639     }
   1640 
   1641     /**
   1642      * Private handler for the input manager.
   1643      */
   1644     private final class InputManagerHandler extends Handler {
   1645         public InputManagerHandler(Looper looper) {
   1646             super(looper, null, true /*async*/);
   1647         }
   1648 
   1649         @Override
   1650         public void handleMessage(Message msg) {
   1651             switch (msg.what) {
   1652                 case MSG_DELIVER_INPUT_DEVICES_CHANGED:
   1653                     deliverInputDevicesChanged((InputDevice[])msg.obj);
   1654                     break;
   1655                 case MSG_SWITCH_KEYBOARD_LAYOUT:
   1656                     handleSwitchKeyboardLayout(msg.arg1, msg.arg2);
   1657                     break;
   1658                 case MSG_RELOAD_KEYBOARD_LAYOUTS:
   1659                     reloadKeyboardLayouts();
   1660                     break;
   1661                 case MSG_UPDATE_KEYBOARD_LAYOUTS:
   1662                     updateKeyboardLayouts();
   1663                     break;
   1664                 case MSG_RELOAD_DEVICE_ALIASES:
   1665                     reloadDeviceAliases();
   1666                     break;
   1667             }
   1668         }
   1669     }
   1670 
   1671     /**
   1672      * Hosting interface for input filters to call back into the input manager.
   1673      */
   1674     private final class InputFilterHost extends IInputFilterHost.Stub {
   1675         private boolean mDisconnected;
   1676 
   1677         public void disconnectLocked() {
   1678             mDisconnected = true;
   1679         }
   1680 
   1681         @Override
   1682         public void sendInputEvent(InputEvent event, int policyFlags) {
   1683             if (event == null) {
   1684                 throw new IllegalArgumentException("event must not be null");
   1685             }
   1686 
   1687             synchronized (mInputFilterLock) {
   1688                 if (!mDisconnected) {
   1689                     nativeInjectInputEvent(mPtr, event, Display.DEFAULT_DISPLAY, 0, 0,
   1690                             InputManager.INJECT_INPUT_EVENT_MODE_ASYNC, 0,
   1691                             policyFlags | WindowManagerPolicy.FLAG_FILTERED);
   1692                 }
   1693             }
   1694         }
   1695     }
   1696 
   1697     private static final class KeyboardLayoutDescriptor {
   1698         public String packageName;
   1699         public String receiverName;
   1700         public String keyboardLayoutName;
   1701 
   1702         public static String format(String packageName,
   1703                 String receiverName, String keyboardName) {
   1704             return packageName + "/" + receiverName + "/" + keyboardName;
   1705         }
   1706 
   1707         public static KeyboardLayoutDescriptor parse(String descriptor) {
   1708             int pos = descriptor.indexOf('/');
   1709             if (pos < 0 || pos + 1 == descriptor.length()) {
   1710                 return null;
   1711             }
   1712             int pos2 = descriptor.indexOf('/', pos + 1);
   1713             if (pos2 < pos + 2 || pos2 + 1 == descriptor.length()) {
   1714                 return null;
   1715             }
   1716 
   1717             KeyboardLayoutDescriptor result = new KeyboardLayoutDescriptor();
   1718             result.packageName = descriptor.substring(0, pos);
   1719             result.receiverName = descriptor.substring(pos + 1, pos2);
   1720             result.keyboardLayoutName = descriptor.substring(pos2 + 1);
   1721             return result;
   1722         }
   1723     }
   1724 
   1725     private interface KeyboardLayoutVisitor {
   1726         void visitKeyboardLayout(Resources resources, String descriptor, String label,
   1727                 String collection, int keyboardLayoutResId, int priority);
   1728     }
   1729 
   1730     private final class InputDevicesChangedListenerRecord implements DeathRecipient {
   1731         private final int mPid;
   1732         private final IInputDevicesChangedListener mListener;
   1733 
   1734         public InputDevicesChangedListenerRecord(int pid, IInputDevicesChangedListener listener) {
   1735             mPid = pid;
   1736             mListener = listener;
   1737         }
   1738 
   1739         @Override
   1740         public void binderDied() {
   1741             if (DEBUG) {
   1742                 Slog.d(TAG, "Input devices changed listener for pid " + mPid + " died.");
   1743             }
   1744             onInputDevicesChangedListenerDied(mPid);
   1745         }
   1746 
   1747         public void notifyInputDevicesChanged(int[] info) {
   1748             try {
   1749                 mListener.onInputDevicesChanged(info);
   1750             } catch (RemoteException ex) {
   1751                 Slog.w(TAG, "Failed to notify process "
   1752                         + mPid + " that input devices changed, assuming it died.", ex);
   1753                 binderDied();
   1754             }
   1755         }
   1756     }
   1757 
   1758     private final class VibratorToken implements DeathRecipient {
   1759         public final int mDeviceId;
   1760         public final IBinder mToken;
   1761         public final int mTokenValue;
   1762 
   1763         public boolean mVibrating;
   1764 
   1765         public VibratorToken(int deviceId, IBinder token, int tokenValue) {
   1766             mDeviceId = deviceId;
   1767             mToken = token;
   1768             mTokenValue = tokenValue;
   1769         }
   1770 
   1771         @Override
   1772         public void binderDied() {
   1773             if (DEBUG) {
   1774                 Slog.d(TAG, "Vibrator token died.");
   1775             }
   1776             onVibratorTokenDied(this);
   1777         }
   1778     }
   1779 
   1780     private final class LocalService extends InputManagerInternal {
   1781         @Override
   1782         public void setDisplayViewports(
   1783                 DisplayViewport defaultViewport, DisplayViewport externalTouchViewport) {
   1784             setDisplayViewportsInternal(defaultViewport, externalTouchViewport);
   1785         }
   1786 
   1787         @Override
   1788         public boolean injectInputEvent(InputEvent event, int displayId, int mode) {
   1789             return injectInputEventInternal(event, displayId, mode);
   1790         }
   1791 
   1792         @Override
   1793         public void setInteractive(boolean interactive) {
   1794             nativeSetInteractive(mPtr, interactive);
   1795         }
   1796     }
   1797 }
   1798