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