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