diff --git a/core/api/test-current.txt b/core/api/test-current.txt index 083c445407dc0..050667b166bc9 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -3527,6 +3527,8 @@ package android.view { method @RequiresPermission("android.permission.DISABLE_INPUT_DEVICE") public void disable(); method @RequiresPermission("android.permission.DISABLE_INPUT_DEVICE") public void enable(); method @NonNull public android.hardware.input.InputDeviceIdentifier getIdentifier(); + method @Nullable public String getKeyboardLanguageTag(); + method @Nullable public String getKeyboardLayoutType(); } public abstract class InputEvent implements android.os.Parcelable { diff --git a/core/java/android/view/InputDevice.java b/core/java/android/view/InputDevice.java index 48fb719279d5f..8b23f19f1067b 100644 --- a/core/java/android/view/InputDevice.java +++ b/core/java/android/view/InputDevice.java @@ -958,6 +958,7 @@ public final class InputDevice implements Parcelable { * @hide */ @Nullable + @TestApi public String getKeyboardLanguageTag() { return mKeyboardLanguageTag; } @@ -968,6 +969,7 @@ public final class InputDevice implements Parcelable { * @hide */ @Nullable + @TestApi public String getKeyboardLayoutType() { return mKeyboardLayoutType; } diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java index e78addaf36c4d..2d0af98c29f6d 100644 --- a/services/core/java/com/android/server/input/InputManagerService.java +++ b/services/core/java/com/android/server/input/InputManagerService.java @@ -2725,11 +2725,12 @@ public class InputManagerService extends IInputManager.Stub // Native callback. @SuppressWarnings("unused") - private String[] getKeyboardLayoutOverlay(InputDeviceIdentifier identifier) { + private String[] getKeyboardLayoutOverlay(InputDeviceIdentifier identifier, String languageTag, + String layoutType) { if (!mSystemReady) { return null; } - return mKeyboardLayoutManager.getKeyboardLayoutOverlay(identifier); + return mKeyboardLayoutManager.getKeyboardLayoutOverlay(identifier, languageTag, layoutType); } @EnforcePermission(Manifest.permission.REMAP_MODIFIER_KEYS) diff --git a/services/core/java/com/android/server/input/KeyboardLayoutManager.java b/services/core/java/com/android/server/input/KeyboardLayoutManager.java index 7a8de341854dd..5bdf26307d117 100644 --- a/services/core/java/com/android/server/input/KeyboardLayoutManager.java +++ b/services/core/java/com/android/server/input/KeyboardLayoutManager.java @@ -25,6 +25,7 @@ import android.annotation.AnyThread; import android.annotation.MainThread; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.SuppressLint; import android.annotation.UserIdInt; import android.app.Notification; import android.app.NotificationManager; @@ -109,7 +110,6 @@ final class KeyboardLayoutManager implements InputManager.InputDeviceListener { private static final int MSG_SWITCH_KEYBOARD_LAYOUT = 2; private static final int MSG_RELOAD_KEYBOARD_LAYOUTS = 3; private static final int MSG_UPDATE_KEYBOARD_LAYOUTS = 4; - private static final int MSG_CURRENT_IME_INFO_CHANGED = 5; private final Context mContext; private final NativeInputManagerService mNative; @@ -186,6 +186,7 @@ final class KeyboardLayoutManager implements InputManager.InputDeviceListener { if (inputDevice == null || inputDevice.isVirtual() || !inputDevice.isFullKeyboard()) { return; } + final KeyboardIdentifier keyboardIdentifier = new KeyboardIdentifier(inputDevice); KeyboardConfiguration config = mConfiguredKeyboards.get(deviceId); if (config == null) { config = new KeyboardConfiguration(); @@ -202,8 +203,6 @@ final class KeyboardLayoutManager implements InputManager.InputDeviceListener { setCurrentKeyboardLayoutForInputDevice(inputDevice.getIdentifier(), layout); } } - config.setCurrentLayout( - new KeyboardLayoutInfo(layout, LAYOUT_SELECTION_CRITERIA_USER)); if (layout == null) { // In old settings show notification always until user manually selects a // layout in the settings. @@ -211,16 +210,14 @@ final class KeyboardLayoutManager implements InputManager.InputDeviceListener { } } } else { - final InputDeviceIdentifier identifier = inputDevice.getIdentifier(); - final String key = getLayoutDescriptor(identifier); Set selectedLayouts = new HashSet<>(); List imeInfoList = getImeInfoListForLayoutMapping(); List layoutInfoList = new ArrayList<>(); boolean hasMissingLayout = false; for (ImeInfo imeInfo : imeInfoList) { // Check if the layout has been previously configured - KeyboardLayoutInfo layoutInfo = getKeyboardLayoutForInputDeviceInternal(identifier, - imeInfo); + KeyboardLayoutInfo layoutInfo = getKeyboardLayoutForInputDeviceInternal( + keyboardIdentifier, imeInfo); boolean noLayoutFound = layoutInfo == null || layoutInfo.mDescriptor == null; if (!noLayoutFound) { selectedLayouts.add(layoutInfo.mDescriptor); @@ -231,8 +228,8 @@ final class KeyboardLayoutManager implements InputManager.InputDeviceListener { if (DEBUG) { Slog.d(TAG, - "Layouts selected for input device: " + identifier + " -> selectedLayouts: " - + selectedLayouts); + "Layouts selected for input device: " + keyboardIdentifier + + " -> selectedLayouts: " + selectedLayouts); } // If even one layout not configured properly, we need to ask user to configure @@ -243,18 +240,9 @@ final class KeyboardLayoutManager implements InputManager.InputDeviceListener { config.setConfiguredLayouts(selectedLayouts); - // Update current layout: If there is a change then need to reload. - synchronized (mImeInfoLock) { - KeyboardLayoutInfo layoutInfo = getKeyboardLayoutForInputDeviceInternal( - inputDevice.getIdentifier(), mCurrentImeInfo); - if (!Objects.equals(layoutInfo, config.getCurrentLayout())) { - config.setCurrentLayout(layoutInfo); - mHandler.sendEmptyMessage(MSG_RELOAD_KEYBOARD_LAYOUTS); - } - } - synchronized (mDataStore) { try { + final String key = keyboardIdentifier.toString(); boolean isFirstConfiguration = !mDataStore.hasInputDeviceEntry(key); if (mDataStore.setSelectedKeyboardLayouts(key, selectedLayouts)) { // Need to show the notification only if layout selection changed @@ -563,35 +551,6 @@ final class KeyboardLayoutManager implements InputManager.InputDeviceListener { return LocaleList.forLanguageTags(languageTags.replace('|', ',')); } - private String getLayoutDescriptor(@NonNull InputDeviceIdentifier identifier) { - Objects.requireNonNull(identifier, "identifier must not be null"); - Objects.requireNonNull(identifier.getDescriptor(), "descriptor must not be null"); - - if (identifier.getVendorId() == 0 && identifier.getProductId() == 0) { - return identifier.getDescriptor(); - } - // If vendor id and product id is available, use it as keys. This allows us to have the - // same setup for all keyboards with same product and vendor id. i.e. User can swap 2 - // identical keyboards and still get the same setup. - StringBuilder key = new StringBuilder(); - key.append("vendor:").append(identifier.getVendorId()).append(",product:").append( - identifier.getProductId()); - - if (useNewSettingsUi()) { - InputDevice inputDevice = getInputDevice(identifier); - Objects.requireNonNull(inputDevice, "Input device must not be null"); - // Some keyboards can have same product ID and vendor ID but different Keyboard info - // like language tag and layout type. - if (!TextUtils.isEmpty(inputDevice.getKeyboardLanguageTag())) { - key.append(",languageTag:").append(inputDevice.getKeyboardLanguageTag()); - } - if (!TextUtils.isEmpty(inputDevice.getKeyboardLayoutType())) { - key.append(",layoutType:").append(inputDevice.getKeyboardLayoutType()); - } - } - return key.toString(); - } - @AnyThread @Nullable public String getCurrentKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier) { @@ -599,7 +558,7 @@ final class KeyboardLayoutManager implements InputManager.InputDeviceListener { Slog.e(TAG, "getCurrentKeyboardLayoutForInputDevice API not supported"); return null; } - String key = getLayoutDescriptor(identifier); + String key = new KeyboardIdentifier(identifier).toString(); synchronized (mDataStore) { String layout; // try loading it using the layout descriptor if we have it @@ -626,7 +585,7 @@ final class KeyboardLayoutManager implements InputManager.InputDeviceListener { Objects.requireNonNull(keyboardLayoutDescriptor, "keyboardLayoutDescriptor must not be null"); - String key = getLayoutDescriptor(identifier); + String key = new KeyboardIdentifier(identifier).toString(); synchronized (mDataStore) { try { if (mDataStore.setCurrentKeyboardLayout(key, keyboardLayoutDescriptor)) { @@ -649,7 +608,7 @@ final class KeyboardLayoutManager implements InputManager.InputDeviceListener { Slog.e(TAG, "getEnabledKeyboardLayoutsForInputDevice API not supported"); return new String[0]; } - String key = getLayoutDescriptor(identifier); + String key = new KeyboardIdentifier(identifier).toString(); synchronized (mDataStore) { String[] layouts = mDataStore.getKeyboardLayouts(key); if ((layouts == null || layouts.length == 0) @@ -670,7 +629,7 @@ final class KeyboardLayoutManager implements InputManager.InputDeviceListener { Objects.requireNonNull(keyboardLayoutDescriptor, "keyboardLayoutDescriptor must not be null"); - String key = getLayoutDescriptor(identifier); + String key = new KeyboardIdentifier(identifier).toString(); synchronized (mDataStore) { try { String oldLayout = mDataStore.getCurrentKeyboardLayout(key); @@ -698,7 +657,7 @@ final class KeyboardLayoutManager implements InputManager.InputDeviceListener { Objects.requireNonNull(keyboardLayoutDescriptor, "keyboardLayoutDescriptor must not be null"); - String key = getLayoutDescriptor(identifier); + String key = new KeyboardIdentifier(identifier).toString(); synchronized (mDataStore) { try { String oldLayout = mDataStore.getCurrentKeyboardLayout(key); @@ -737,7 +696,7 @@ final class KeyboardLayoutManager implements InputManager.InputDeviceListener { final boolean changed; final String keyboardLayoutDescriptor; - String key = getLayoutDescriptor(device.getIdentifier()); + String key = new KeyboardIdentifier(device.getIdentifier()).toString(); synchronized (mDataStore) { try { changed = mDataStore.switchKeyboardLayout(key, direction); @@ -769,11 +728,13 @@ final class KeyboardLayoutManager implements InputManager.InputDeviceListener { @Nullable @AnyThread - public String[] getKeyboardLayoutOverlay(InputDeviceIdentifier identifier) { + public String[] getKeyboardLayoutOverlay(InputDeviceIdentifier identifier, String languageTag, + String layoutType) { String keyboardLayoutDescriptor; if (useNewSettingsUi()) { synchronized (mImeInfoLock) { - KeyboardLayoutInfo layoutInfo = getKeyboardLayoutForInputDeviceInternal(identifier, + KeyboardLayoutInfo layoutInfo = getKeyboardLayoutForInputDeviceInternal( + new KeyboardIdentifier(identifier, languageTag, layoutType), mCurrentImeInfo); keyboardLayoutDescriptor = layoutInfo == null ? null : layoutInfo.mDescriptor; } @@ -811,12 +772,16 @@ final class KeyboardLayoutManager implements InputManager.InputDeviceListener { Slog.e(TAG, "getKeyboardLayoutForInputDevice() API not supported"); return null; } - InputMethodSubtypeHandle subtypeHandle = InputMethodSubtypeHandle.of(imeInfo, imeSubtype); - KeyboardLayoutInfo layoutInfo = getKeyboardLayoutForInputDeviceInternal(identifier, - new ImeInfo(userId, subtypeHandle, imeSubtype)); + InputDevice inputDevice = getInputDevice(identifier); + if (inputDevice == null || inputDevice.isVirtual() || !inputDevice.isFullKeyboard()) { + return null; + } + KeyboardIdentifier keyboardIdentifier = new KeyboardIdentifier(inputDevice); + KeyboardLayoutInfo layoutInfo = getKeyboardLayoutForInputDeviceInternal( + keyboardIdentifier, new ImeInfo(userId, imeInfo, imeSubtype)); if (DEBUG) { Slog.d(TAG, "getKeyboardLayoutForInputDevice() " + identifier.toString() + ", userId : " - + userId + ", subtypeHandle = " + subtypeHandle + " -> " + layoutInfo); + + userId + ", subtype = " + imeSubtype + " -> " + layoutInfo); } return layoutInfo != null ? layoutInfo.mDescriptor : null; } @@ -832,16 +797,20 @@ final class KeyboardLayoutManager implements InputManager.InputDeviceListener { } Objects.requireNonNull(keyboardLayoutDescriptor, "keyboardLayoutDescriptor must not be null"); - String key = createLayoutKey(identifier, - new ImeInfo(userId, InputMethodSubtypeHandle.of(imeInfo, imeSubtype), imeSubtype)); + InputDevice inputDevice = getInputDevice(identifier); + if (inputDevice == null || inputDevice.isVirtual() || !inputDevice.isFullKeyboard()) { + return; + } + KeyboardIdentifier keyboardIdentifier = new KeyboardIdentifier(inputDevice); + String layoutKey = new LayoutKey(keyboardIdentifier, + new ImeInfo(userId, imeInfo, imeSubtype)).toString(); synchronized (mDataStore) { try { - // Key for storing into data store = ,, - if (mDataStore.setKeyboardLayout(getLayoutDescriptor(identifier), key, + if (mDataStore.setKeyboardLayout(keyboardIdentifier.toString(), layoutKey, keyboardLayoutDescriptor)) { if (DEBUG) { Slog.d(TAG, "setKeyboardLayoutForInputDevice() " + identifier - + " key: " + key + + " key: " + layoutKey + " keyboardLayoutDescriptor: " + keyboardLayoutDescriptor); } mHandler.sendEmptyMessage(MSG_RELOAD_KEYBOARD_LAYOUTS); @@ -860,18 +829,23 @@ final class KeyboardLayoutManager implements InputManager.InputDeviceListener { Slog.e(TAG, "getKeyboardLayoutListForInputDevice() API not supported"); return new KeyboardLayout[0]; } - return getKeyboardLayoutListForInputDeviceInternal(identifier, new ImeInfo(userId, - InputMethodSubtypeHandle.of(imeInfo, imeSubtype), imeSubtype)); + InputDevice inputDevice = getInputDevice(identifier); + if (inputDevice == null || inputDevice.isVirtual() || !inputDevice.isFullKeyboard()) { + return new KeyboardLayout[0]; + } + return getKeyboardLayoutListForInputDeviceInternal(new KeyboardIdentifier(inputDevice), + new ImeInfo(userId, imeInfo, imeSubtype)); } private KeyboardLayout[] getKeyboardLayoutListForInputDeviceInternal( - InputDeviceIdentifier identifier, @Nullable ImeInfo imeInfo) { - String key = createLayoutKey(identifier, imeInfo); + KeyboardIdentifier keyboardIdentifier, @Nullable ImeInfo imeInfo) { + String layoutKey = new LayoutKey(keyboardIdentifier, imeInfo).toString(); // Fetch user selected layout and always include it in layout list. String userSelectedLayout; synchronized (mDataStore) { - userSelectedLayout = mDataStore.getKeyboardLayout(getLayoutDescriptor(identifier), key); + userSelectedLayout = mDataStore.getKeyboardLayout(keyboardIdentifier.toString(), + layoutKey); } final ArrayList potentialLayouts = new ArrayList<>(); @@ -894,8 +868,8 @@ final class KeyboardLayoutManager implements InputManager.InputDeviceListener { // devices that have special layouts we assume there's a reason that the generic // layouts don't work for them, so we don't want to return them since it's likely // to result in a poor user experience. - if (layout.getVendorId() == identifier.getVendorId() - && layout.getProductId() == identifier.getProductId()) { + if (layout.getVendorId() == keyboardIdentifier.mIdentifier.getVendorId() + && layout.getProductId() == keyboardIdentifier.mIdentifier.getProductId()) { if (!mDeviceSpecificLayoutAvailable) { mDeviceSpecificLayoutAvailable = true; potentialLayouts.clear(); @@ -934,7 +908,7 @@ final class KeyboardLayoutManager implements InputManager.InputDeviceListener { if (mCurrentImeInfo == null || !subtypeHandle.equals(mCurrentImeInfo.mImeSubtypeHandle) || mCurrentImeInfo.mUserId != userId) { mCurrentImeInfo = new ImeInfo(userId, subtypeHandle, subtype); - mHandler.sendEmptyMessage(MSG_CURRENT_IME_INFO_CHANGED); + mHandler.sendEmptyMessage(MSG_RELOAD_KEYBOARD_LAYOUTS); if (DEBUG) { Slog.d(TAG, "InputMethodSubtype changed: userId=" + userId + " subtypeHandle=" + subtypeHandle); @@ -943,34 +917,12 @@ final class KeyboardLayoutManager implements InputManager.InputDeviceListener { } } - @MainThread - private void onCurrentImeInfoChanged() { - synchronized (mImeInfoLock) { - for (int i = 0; i < mConfiguredKeyboards.size(); i++) { - InputDevice inputDevice = Objects.requireNonNull( - getInputDevice(mConfiguredKeyboards.keyAt(i))); - KeyboardLayoutInfo layoutInfo = getKeyboardLayoutForInputDeviceInternal( - inputDevice.getIdentifier(), mCurrentImeInfo); - KeyboardConfiguration config = mConfiguredKeyboards.valueAt(i); - if (!Objects.equals(layoutInfo, config.getCurrentLayout())) { - config.setCurrentLayout(layoutInfo); - mHandler.sendEmptyMessage(MSG_RELOAD_KEYBOARD_LAYOUTS); - return; - } - } - } - } - @Nullable private KeyboardLayoutInfo getKeyboardLayoutForInputDeviceInternal( - InputDeviceIdentifier identifier, @Nullable ImeInfo imeInfo) { - InputDevice inputDevice = getInputDevice(identifier); - if (inputDevice == null || inputDevice.isVirtual() || !inputDevice.isFullKeyboard()) { - return null; - } - String key = createLayoutKey(identifier, imeInfo); + KeyboardIdentifier keyboardIdentifier, @Nullable ImeInfo imeInfo) { + String layoutKey = new LayoutKey(keyboardIdentifier, imeInfo).toString(); synchronized (mDataStore) { - String layout = mDataStore.getKeyboardLayout(getLayoutDescriptor(identifier), key); + String layout = mDataStore.getKeyboardLayout(keyboardIdentifier.toString(), layoutKey); if (layout != null) { return new KeyboardLayoutInfo(layout, LAYOUT_SELECTION_CRITERIA_USER); } @@ -978,16 +930,17 @@ final class KeyboardLayoutManager implements InputManager.InputDeviceListener { synchronized (mKeyboardLayoutCache) { // Check Auto-selected layout cache to see if layout had been previously selected - if (mKeyboardLayoutCache.containsKey(key)) { - return mKeyboardLayoutCache.get(key); + if (mKeyboardLayoutCache.containsKey(layoutKey)) { + return mKeyboardLayoutCache.get(layoutKey); } else { // NOTE: This list is already filtered based on IME Script code KeyboardLayout[] layoutList = getKeyboardLayoutListForInputDeviceInternal( - identifier, imeInfo); + keyboardIdentifier, imeInfo); // Call auto-matching algorithm to find the best matching layout KeyboardLayoutInfo layoutInfo = - getDefaultKeyboardLayoutBasedOnImeInfo(inputDevice, imeInfo, layoutList); - mKeyboardLayoutCache.put(key, layoutInfo); + getDefaultKeyboardLayoutBasedOnImeInfo(keyboardIdentifier, imeInfo, + layoutList); + mKeyboardLayoutCache.put(layoutKey, layoutInfo); return layoutInfo; } } @@ -995,17 +948,18 @@ final class KeyboardLayoutManager implements InputManager.InputDeviceListener { @Nullable private static KeyboardLayoutInfo getDefaultKeyboardLayoutBasedOnImeInfo( - InputDevice inputDevice, @Nullable ImeInfo imeInfo, KeyboardLayout[] layoutList) { + KeyboardIdentifier keyboardIdentifier, @Nullable ImeInfo imeInfo, + KeyboardLayout[] layoutList) { Arrays.sort(layoutList); // Check matching for explicitly declared custom KCM files. for (KeyboardLayout layout : layoutList) { - if (layout.getVendorId() == inputDevice.getVendorId() - && layout.getProductId() == inputDevice.getProductId()) { + if (layout.getVendorId() == keyboardIdentifier.mIdentifier.getVendorId() + && layout.getProductId() == keyboardIdentifier.mIdentifier.getProductId()) { if (DEBUG) { Slog.d(TAG, "getDefaultKeyboardLayoutBasedOnImeInfo() : Layout found based on " - + "vendor and product Ids. " + inputDevice.getIdentifier() + + "vendor and product Ids. " + keyboardIdentifier + " : " + layout.getDescriptor()); } return new KeyboardLayoutInfo(layout.getDescriptor(), @@ -1014,17 +968,17 @@ final class KeyboardLayoutManager implements InputManager.InputDeviceListener { } // Check layout type, language tag information from InputDevice for matching - String inputLanguageTag = inputDevice.getKeyboardLanguageTag(); + String inputLanguageTag = keyboardIdentifier.mLanguageTag; if (inputLanguageTag != null) { String layoutDesc = getMatchingLayoutForProvidedLanguageTagAndLayoutType(layoutList, - inputLanguageTag, inputDevice.getKeyboardLayoutType()); + inputLanguageTag, keyboardIdentifier.mLayoutType); if (layoutDesc != null) { if (DEBUG) { Slog.d(TAG, "getDefaultKeyboardLayoutBasedOnImeInfo() : Layout found based on " + "HW information (Language tag and Layout type). " - + inputDevice.getIdentifier() + " : " + layoutDesc); + + keyboardIdentifier + " : " + layoutDesc); } return new KeyboardLayoutInfo(layoutDesc, LAYOUT_SELECTION_CRITERIA_DEVICE); } @@ -1045,7 +999,7 @@ final class KeyboardLayoutManager implements InputManager.InputDeviceListener { if (DEBUG) { Slog.d(TAG, "getDefaultKeyboardLayoutBasedOnImeInfo() : Layout found based on " - + "IME locale matching. " + inputDevice.getIdentifier() + " : " + + "IME locale matching. " + keyboardIdentifier + " : " + layoutDesc); } if (layoutDesc != null) { @@ -1105,7 +1059,7 @@ final class KeyboardLayoutManager implements InputManager.InputDeviceListener { } } return layoutMatchingLanguageAndCountry != null - ? layoutMatchingLanguageAndCountry : layoutMatchingLanguage; + ? layoutMatchingLanguageAndCountry : layoutMatchingLanguage; } private void reloadKeyboardLayouts() { @@ -1297,9 +1251,6 @@ final class KeyboardLayoutManager implements InputManager.InputDeviceListener { case MSG_UPDATE_KEYBOARD_LAYOUTS: updateKeyboardLayouts(); return true; - case MSG_CURRENT_IME_INFO_CHANGED: - onCurrentImeInfoChanged(); - return true; default: return false; } @@ -1322,6 +1273,7 @@ final class KeyboardLayoutManager implements InputManager.InputDeviceListener { identifier.getDescriptor()) : null; } + @SuppressLint("MissingPermission") private List getImeInfoListForLayoutMapping() { List imeInfoList = new ArrayList<>(); UserManager userManager = Objects.requireNonNull( @@ -1337,31 +1289,20 @@ final class KeyboardLayoutManager implements InputManager.InputDeviceListener { int userId = userHandle.getIdentifier(); for (InputMethodInfo imeInfo : inputMethodManagerInternal.getEnabledInputMethodListAsUser( - userId)) { + userId)) { for (InputMethodSubtype imeSubtype : inputMethodManager.getEnabledInputMethodSubtypeList( imeInfo, true /* allowsImplicitlyEnabledSubtypes */)) { if (!imeSubtype.isSuitableForPhysicalKeyboardLayoutMapping()) { continue; } - imeInfoList.add( - new ImeInfo(userId, InputMethodSubtypeHandle.of(imeInfo, imeSubtype), - imeSubtype)); + imeInfoList.add(new ImeInfo(userId, imeInfo, imeSubtype)); } } } return imeInfoList; } - private String createLayoutKey(InputDeviceIdentifier identifier, @Nullable ImeInfo imeInfo) { - if (imeInfo == null) { - return getLayoutDescriptor(identifier); - } - Objects.requireNonNull(imeInfo.mImeSubtypeHandle, "subtypeHandle must not be null"); - return "layoutDescriptor:" + getLayoutDescriptor(identifier) + ",userId:" + imeInfo.mUserId - + ",subtypeHandle:" + imeInfo.mImeSubtypeHandle.toStringHandle(); - } - private static boolean isLayoutCompatibleWithLanguageTag(KeyboardLayout layout, @NonNull String languageTag) { LocaleList layoutLocales = layout.getLocales(); @@ -1451,6 +1392,11 @@ final class KeyboardLayoutManager implements InputManager.InputDeviceListener { mImeSubtypeHandle = imeSubtypeHandle; mImeSubtype = imeSubtype; } + + ImeInfo(@UserIdInt int userId, @NonNull InputMethodInfo imeInfo, + @Nullable InputMethodSubtype imeSubtype) { + this(userId, InputMethodSubtypeHandle.of(imeInfo, imeSubtype), imeSubtype); + } } private static class KeyboardConfiguration { @@ -1459,10 +1405,6 @@ final class KeyboardLayoutManager implements InputManager.InputDeviceListener { @Nullable private Set mConfiguredLayouts; - // If null, it means no layout is selected for the device. - @Nullable - private KeyboardLayoutInfo mCurrentLayout; - private boolean hasConfiguredLayouts() { return mConfiguredLayouts != null && !mConfiguredLayouts.isEmpty(); } @@ -1475,15 +1417,6 @@ final class KeyboardLayoutManager implements InputManager.InputDeviceListener { private void setConfiguredLayouts(Set configuredLayouts) { mConfiguredLayouts = configuredLayouts; } - - @Nullable - private KeyboardLayoutInfo getCurrentLayout() { - return mCurrentLayout; - } - - private void setCurrentLayout(KeyboardLayoutInfo currentLayout) { - mCurrentLayout = currentLayout; - } } private static class KeyboardLayoutInfo { @@ -1517,4 +1450,88 @@ final class KeyboardLayoutManager implements InputManager.InputDeviceListener { void visitKeyboardLayout(Resources resources, int keyboardLayoutResId, KeyboardLayout layout); } + + private static class KeyboardIdentifier { + @NonNull + private final InputDeviceIdentifier mIdentifier; + @Nullable + private final String mLanguageTag; + @Nullable + private final String mLayoutType; + + // NOTE: Use this only for old settings UI where we don't use language tag and layout + // type to determine the KCM file. + private KeyboardIdentifier(@NonNull InputDeviceIdentifier inputDeviceIdentifier) { + this(inputDeviceIdentifier, null, null); + } + + private KeyboardIdentifier(@NonNull InputDevice inputDevice) { + this(inputDevice.getIdentifier(), inputDevice.getKeyboardLanguageTag(), + inputDevice.getKeyboardLayoutType()); + } + + private KeyboardIdentifier(@NonNull InputDeviceIdentifier identifier, + @Nullable String languageTag, @Nullable String layoutType) { + Objects.requireNonNull(identifier, "identifier must not be null"); + Objects.requireNonNull(identifier.getDescriptor(), "descriptor must not be null"); + mIdentifier = identifier; + mLanguageTag = languageTag; + mLayoutType = layoutType; + } + + @Override + public int hashCode() { + return Objects.hashCode(toString()); + } + + @Override + public String toString() { + if (mIdentifier.getVendorId() == 0 && mIdentifier.getProductId() == 0) { + return mIdentifier.getDescriptor(); + } + // If vendor id and product id is available, use it as keys. This allows us to have the + // same setup for all keyboards with same product and vendor id. i.e. User can swap 2 + // identical keyboards and still get the same setup. + StringBuilder key = new StringBuilder(); + key.append("vendor:").append(mIdentifier.getVendorId()).append(",product:").append( + mIdentifier.getProductId()); + + // Some keyboards can have same product ID and vendor ID but different Keyboard info + // like language tag and layout type. + if (!TextUtils.isEmpty(mLanguageTag)) { + key.append(",languageTag:").append(mLanguageTag); + } + if (!TextUtils.isEmpty(mLayoutType)) { + key.append(",layoutType:").append(mLayoutType); + } + return key.toString(); + } + } + + private static class LayoutKey { + + private final KeyboardIdentifier mKeyboardIdentifier; + @Nullable + private final ImeInfo mImeInfo; + + private LayoutKey(KeyboardIdentifier keyboardIdentifier, @Nullable ImeInfo imeInfo) { + mKeyboardIdentifier = keyboardIdentifier; + mImeInfo = imeInfo; + } + + @Override + public int hashCode() { + return Objects.hashCode(toString()); + } + + @Override + public String toString() { + if (mImeInfo == null) { + return mKeyboardIdentifier.toString(); + } + Objects.requireNonNull(mImeInfo.mImeSubtypeHandle, "subtypeHandle must not be null"); + return "layoutDescriptor:" + mKeyboardIdentifier + ",userId:" + mImeInfo.mUserId + + ",subtypeHandle:" + mImeInfo.mImeSubtypeHandle.toStringHandle(); + } + } } diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp index cb0b9c9ace4f9..ec6c75bb1991c 100644 --- a/services/core/jni/com_android_server_input_InputManagerService.cpp +++ b/services/core/jni/com_android_server_input_InputManagerService.cpp @@ -313,7 +313,8 @@ public: std::shared_ptr obtainPointerController(int32_t deviceId) override; void notifyInputDevicesChanged(const std::vector& inputDevices) override; std::shared_ptr getKeyboardLayoutOverlay( - const InputDeviceIdentifier& identifier) override; + const InputDeviceIdentifier& identifier, + const std::optional keyboardLayoutInfo) override; std::string getDeviceAlias(const InputDeviceIdentifier& identifier) override; TouchAffineTransformation getTouchAffineTransformation(const std::string& inputDeviceDescriptor, ui::Rotation surfaceRotation) override; @@ -779,17 +780,32 @@ void NativeInputManager::notifyInputDevicesChanged(const std::vector NativeInputManager::getKeyboardLayoutOverlay( - const InputDeviceIdentifier& identifier) { + const InputDeviceIdentifier& identifier, + const std::optional keyboardLayoutInfo) { ATRACE_CALL(); JNIEnv* env = jniEnv(); std::shared_ptr result; ScopedLocalRef descriptor(env, env->NewStringUTF(identifier.descriptor.c_str())); + ScopedLocalRef languageTag(env, + keyboardLayoutInfo + ? env->NewStringUTF( + keyboardLayoutInfo->languageTag.c_str()) + : nullptr); + ScopedLocalRef layoutType(env, + keyboardLayoutInfo + ? env->NewStringUTF( + keyboardLayoutInfo->layoutType.c_str()) + : nullptr); ScopedLocalRef identifierObj(env, env->NewObject(gInputDeviceIdentifierInfo.clazz, gInputDeviceIdentifierInfo.constructor, descriptor.get(), identifier.vendor, identifier.product)); - ScopedLocalRef arrayObj(env, jobjectArray(env->CallObjectMethod(mServiceObj, - gServiceClassInfo.getKeyboardLayoutOverlay, identifierObj.get()))); + ScopedLocalRef + arrayObj(env, + jobjectArray(env->CallObjectMethod(mServiceObj, + gServiceClassInfo.getKeyboardLayoutOverlay, + identifierObj.get(), languageTag.get(), + layoutType.get()))); if (arrayObj.get()) { ScopedLocalRef filenameObj(env, jstring(env->GetObjectArrayElement(arrayObj.get(), 0))); @@ -2803,9 +2819,9 @@ int register_android_server_InputManager(JNIEnv* env) { GET_METHOD_ID(gServiceClassInfo.getPointerIcon, clazz, "getPointerIcon", "(I)Landroid/view/PointerIcon;"); - GET_METHOD_ID(gServiceClassInfo.getKeyboardLayoutOverlay, clazz, - "getKeyboardLayoutOverlay", - "(Landroid/hardware/input/InputDeviceIdentifier;)[Ljava/lang/String;"); + GET_METHOD_ID(gServiceClassInfo.getKeyboardLayoutOverlay, clazz, "getKeyboardLayoutOverlay", + "(Landroid/hardware/input/InputDeviceIdentifier;Ljava/lang/String;Ljava/lang/" + "String;)[Ljava/lang/String;"); GET_METHOD_ID(gServiceClassInfo.getDeviceAlias, clazz, "getDeviceAlias", "(Ljava/lang/String;)Ljava/lang/String;");