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