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