diff options
5 files changed, 121 insertions, 5 deletions
diff --git a/core/java/android/hardware/input/IInputManager.aidl b/core/java/android/hardware/input/IInputManager.aidl index 2bb28a1b6b0b..7fc7e4d81afa 100644 --- a/core/java/android/hardware/input/IInputManager.aidl +++ b/core/java/android/hardware/input/IInputManager.aidl @@ -106,6 +106,12 @@ interface IInputManager { @EnforcePermission("SET_KEYBOARD_LAYOUT") @JavaPassthrough(annotation="@android.annotation.RequiresPermission(value = " + "android.Manifest.permission.SET_KEYBOARD_LAYOUT)") + void setKeyboardLayoutOverrideForInputDevice(in InputDeviceIdentifier identifier, + String keyboardLayoutDescriptor); + + @EnforcePermission("SET_KEYBOARD_LAYOUT") + @JavaPassthrough(annotation="@android.annotation.RequiresPermission(value = " + + "android.Manifest.permission.SET_KEYBOARD_LAYOUT)") void setKeyboardLayoutForInputDevice(in InputDeviceIdentifier identifier, int userId, in InputMethodInfo imeInfo, in InputMethodSubtype imeSubtype, String keyboardLayoutDescriptor); diff --git a/core/java/android/hardware/input/InputManagerGlobal.java b/core/java/android/hardware/input/InputManagerGlobal.java index 3ef90e4b8a5f..e79416162fc2 100644 --- a/core/java/android/hardware/input/InputManagerGlobal.java +++ b/core/java/android/hardware/input/InputManagerGlobal.java @@ -57,6 +57,8 @@ import android.view.InputMonitor; import android.view.KeyCharacterMap; import android.view.KeyEvent; import android.view.PointerIcon; +import android.view.inputmethod.InputMethodInfo; +import android.view.inputmethod.InputMethodSubtype; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; @@ -1256,6 +1258,43 @@ public final class InputManagerGlobal { } /** + * Sets the keyboard layout override for the specified input device. This will set the + * keyboard layout as the default for the input device irrespective of the underlying IME + * configuration. + * + * <p> + * Prefer using {@link InputManager#setKeyboardLayoutForInputDevice(InputDeviceIdentifier, int, + * InputMethodInfo, InputMethodSubtype, String)} for normal use cases. + * </p><p> + * This method is to be used only for special cases where we knowingly want to set a + * particular keyboard layout for a keyboard, ignoring the IME configuration. e.g. Setting a + * default layout for an Android Emulator where we know the preferred H/W keyboard layout. + * </p><p> + * NOTE: This may affect the typing experience if the layout isn't compatible with the IME + * configuration. + * </p><p> + * NOTE: User can still change the keyboard layout configuration from the settings page. + * </p> + * + * @param identifier The identifier for the input device. + * @param keyboardLayoutDescriptor The keyboard layout descriptor to use. + * + * @hide + */ + @RequiresPermission(Manifest.permission.SET_KEYBOARD_LAYOUT) + public void setKeyboardLayoutOverrideForInputDevice(@NonNull InputDeviceIdentifier identifier, + @NonNull String keyboardLayoutDescriptor) { + Objects.requireNonNull(identifier, "identifier should not be null"); + Objects.requireNonNull(keyboardLayoutDescriptor, + "keyboardLayoutDescriptor should not be null"); + try { + mIm.setKeyboardLayoutOverrideForInputDevice(identifier, keyboardLayoutDescriptor); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** * TODO(b/330517633): Cleanup the unsupported API */ @NonNull diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java index 2ba35d6a70d2..4e03e86dfe36 100644 --- a/services/core/java/com/android/server/input/InputManagerService.java +++ b/services/core/java/com/android/server/input/InputManagerService.java @@ -1261,6 +1261,15 @@ public class InputManagerService extends IInputManager.Stub @EnforcePermission(Manifest.permission.SET_KEYBOARD_LAYOUT) @Override // Binder call + public void setKeyboardLayoutOverrideForInputDevice(InputDeviceIdentifier identifier, + String keyboardLayoutDescriptor) { + super.setKeyboardLayoutOverrideForInputDevice_enforcePermission(); + mKeyboardLayoutManager.setKeyboardLayoutOverrideForInputDevice(identifier, + keyboardLayoutDescriptor); + } + + @EnforcePermission(Manifest.permission.SET_KEYBOARD_LAYOUT) + @Override // Binder call public void setKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier, @UserIdInt int userId, @NonNull InputMethodInfo imeInfo, @Nullable InputMethodSubtype imeSubtype, String keyboardLayoutDescriptor) { diff --git a/services/core/java/com/android/server/input/KeyboardLayoutManager.java b/services/core/java/com/android/server/input/KeyboardLayoutManager.java index 1d1a178ff20b..b8ce86b7c98c 100644 --- a/services/core/java/com/android/server/input/KeyboardLayoutManager.java +++ b/services/core/java/com/android/server/input/KeyboardLayoutManager.java @@ -113,6 +113,7 @@ class KeyboardLayoutManager implements InputManager.InputDeviceListener { private static final int MSG_UPDATE_EXISTING_DEVICES = 1; private static final int MSG_RELOAD_KEYBOARD_LAYOUTS = 2; private static final int MSG_UPDATE_KEYBOARD_LAYOUTS = 3; + private static final String GLOBAL_OVERRIDE_KEY = "GLOBAL_OVERRIDE_KEY"; private final Context mContext; private final NativeInputManagerService mNative; @@ -508,26 +509,44 @@ class KeyboardLayoutManager implements InputManager.InputDeviceListener { } @AnyThread + public void setKeyboardLayoutOverrideForInputDevice(InputDeviceIdentifier identifier, + String keyboardLayoutDescriptor) { + InputDevice inputDevice = getInputDevice(identifier); + if (inputDevice == null || inputDevice.isVirtual() || !inputDevice.isFullKeyboard()) { + return; + } + KeyboardIdentifier keyboardIdentifier = new KeyboardIdentifier(inputDevice); + setKeyboardLayoutForInputDeviceInternal(keyboardIdentifier, GLOBAL_OVERRIDE_KEY, + keyboardLayoutDescriptor); + } + + @AnyThread public void setKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier, @UserIdInt int userId, @NonNull InputMethodInfo imeInfo, @Nullable InputMethodSubtype imeSubtype, String keyboardLayoutDescriptor) { - Objects.requireNonNull(keyboardLayoutDescriptor, - "keyboardLayoutDescriptor must not be null"); InputDevice inputDevice = getInputDevice(identifier); if (inputDevice == null || inputDevice.isVirtual() || !inputDevice.isFullKeyboard()) { return; } KeyboardIdentifier keyboardIdentifier = new KeyboardIdentifier(inputDevice); - String layoutKey = new LayoutKey(keyboardIdentifier, + final String datastoreKey = new LayoutKey(keyboardIdentifier, new ImeInfo(userId, imeInfo, imeSubtype)).toString(); + setKeyboardLayoutForInputDeviceInternal(keyboardIdentifier, datastoreKey, + keyboardLayoutDescriptor); + } + + private void setKeyboardLayoutForInputDeviceInternal(KeyboardIdentifier identifier, + String datastoreKey, String keyboardLayoutDescriptor) { + Objects.requireNonNull(keyboardLayoutDescriptor, + "keyboardLayoutDescriptor must not be null"); synchronized (mDataStore) { try { - if (mDataStore.setKeyboardLayout(keyboardIdentifier.toString(), layoutKey, + if (mDataStore.setKeyboardLayout(identifier.toString(), datastoreKey, keyboardLayoutDescriptor)) { if (DEBUG) { Slog.d(TAG, "setKeyboardLayoutForInputDevice() " + identifier - + " key: " + layoutKey + + " key: " + datastoreKey + " keyboardLayoutDescriptor: " + keyboardLayoutDescriptor); } mHandler.sendEmptyMessage(MSG_RELOAD_KEYBOARD_LAYOUTS); @@ -635,6 +654,12 @@ class KeyboardLayoutManager implements InputManager.InputDeviceListener { if (layout != null) { return new KeyboardLayoutSelectionResult(layout, LAYOUT_SELECTION_CRITERIA_USER); } + + layout = mDataStore.getKeyboardLayout(keyboardIdentifier.toString(), + GLOBAL_OVERRIDE_KEY); + if (layout != null) { + return new KeyboardLayoutSelectionResult(layout, LAYOUT_SELECTION_CRITERIA_DEVICE); + } } synchronized (mKeyboardLayoutCache) { diff --git a/tests/Input/src/com/android/server/input/KeyboardLayoutManagerTests.kt b/tests/Input/src/com/android/server/input/KeyboardLayoutManagerTests.kt index d6654cceb458..4440a839caef 100644 --- a/tests/Input/src/com/android/server/input/KeyboardLayoutManagerTests.kt +++ b/tests/Input/src/com/android/server/input/KeyboardLayoutManagerTests.kt @@ -28,6 +28,8 @@ import android.hardware.input.InputManager import android.hardware.input.InputManagerGlobal import android.hardware.input.KeyboardLayout import android.hardware.input.KeyboardLayoutSelectionResult +import android.hardware.input.KeyboardLayoutSelectionResult.LAYOUT_SELECTION_CRITERIA_DEVICE +import android.hardware.input.KeyboardLayoutSelectionResult.LAYOUT_SELECTION_CRITERIA_USER import android.icu.util.ULocale import android.os.Bundle import android.os.test.TestLooper @@ -281,6 +283,41 @@ class KeyboardLayoutManagerTests { } @Test + fun testGetSetKeyboardLayoutOverrideForInputDevice() { + val imeSubtype = createImeSubtype() + + keyboardLayoutManager.setKeyboardLayoutOverrideForInputDevice( + keyboardDevice.identifier, + ENGLISH_UK_LAYOUT_DESCRIPTOR + ) + var result = + keyboardLayoutManager.getKeyboardLayoutForInputDevice( + keyboardDevice.identifier, USER_ID, imeInfo, imeSubtype + ) + assertEquals(LAYOUT_SELECTION_CRITERIA_DEVICE, result.selectionCriteria) + assertEquals( + "getKeyboardLayoutForInputDevice API should return the set layout", + ENGLISH_UK_LAYOUT_DESCRIPTOR, + result.layoutDescriptor + ) + + // This should replace the overriding layout set above + keyboardLayoutManager.setKeyboardLayoutForInputDevice( + keyboardDevice.identifier, USER_ID, imeInfo, imeSubtype, + ENGLISH_US_LAYOUT_DESCRIPTOR + ) + result = keyboardLayoutManager.getKeyboardLayoutForInputDevice( + keyboardDevice.identifier, USER_ID, imeInfo, imeSubtype + ) + assertEquals(LAYOUT_SELECTION_CRITERIA_USER, result.selectionCriteria) + assertEquals( + "getKeyboardLayoutForInputDevice API should return the user set layout", + ENGLISH_US_LAYOUT_DESCRIPTOR, + result.layoutDescriptor + ) + } + + @Test fun testGetKeyboardLayoutListForInputDevice() { // Check Layouts for "hi-Latn". It should return all 'Latn' keyboard layouts var keyboardLayouts = |