diff --git a/core/api/current.txt b/core/api/current.txt index a646537bfe329..2074115469788 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -48689,6 +48689,7 @@ package android.view { method public static int[] getDeviceIds(); method public int getId(); method public android.view.KeyCharacterMap getKeyCharacterMap(); + method public int getKeyCodeForKeyLocation(int); method public int getKeyboardType(); method @NonNull public android.hardware.lights.LightsManager getLightsManager(); method public android.view.InputDevice.MotionRange getMotionRange(int); diff --git a/core/api/test-current.txt b/core/api/test-current.txt index e97ef6c912f5d..ff26ae8d09fcc 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -38,6 +38,7 @@ package android { field public static final String RESET_APP_ERRORS = "android.permission.RESET_APP_ERRORS"; field public static final String REVOKE_POST_NOTIFICATIONS_WITHOUT_KILL = "android.permission.REVOKE_POST_NOTIFICATIONS_WITHOUT_KILL"; field public static final String SET_AND_VERIFY_LOCKSCREEN_CREDENTIALS = "android.permission.SET_AND_VERIFY_LOCKSCREEN_CREDENTIALS"; + field public static final String SET_KEYBOARD_LAYOUT = "android.permission.SET_KEYBOARD_LAYOUT"; field public static final String START_TASKS_FROM_RECENTS = "android.permission.START_TASKS_FROM_RECENTS"; field public static final String SUSPEND_APPS = "android.permission.SUSPEND_APPS"; field public static final String TEST_BIOMETRIC = "android.permission.TEST_BIOMETRIC"; @@ -1180,9 +1181,23 @@ package android.hardware.hdmi { package android.hardware.input { + public final class InputDeviceIdentifier implements android.os.Parcelable { + ctor public InputDeviceIdentifier(@NonNull String, int, int); + method public int describeContents(); + method @NonNull public String getDescriptor(); + method public int getProductId(); + method public int getVendorId(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator CREATOR; + } + public final class InputManager { method public int getBlockUntrustedTouchesMode(@NonNull android.content.Context); + method @Nullable public String getCurrentKeyboardLayoutForInputDevice(@NonNull android.hardware.input.InputDeviceIdentifier); + method @NonNull public java.util.List getKeyboardLayoutDescriptorsForInputDevice(@NonNull android.view.InputDevice); + method @RequiresPermission(android.Manifest.permission.SET_KEYBOARD_LAYOUT) public void removeKeyboardLayoutForInputDevice(@NonNull android.hardware.input.InputDeviceIdentifier, @NonNull String); method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void setBlockUntrustedTouchesMode(@NonNull android.content.Context, int); + method @RequiresPermission(android.Manifest.permission.SET_KEYBOARD_LAYOUT) public void setCurrentKeyboardLayoutForInputDevice(@NonNull android.hardware.input.InputDeviceIdentifier, @NonNull String); method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void setMaximumObscuringOpacityForTouch(@FloatRange(from=0, to=1) float); field public static final long BLOCK_UNTRUSTED_TOUCHES = 158002302L; // 0x96aec7eL } @@ -2740,6 +2755,7 @@ package android.view { public final class InputDevice implements android.os.Parcelable { 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(); } public class KeyEvent extends android.view.InputEvent implements android.os.Parcelable { diff --git a/core/java/android/hardware/input/IInputManager.aidl b/core/java/android/hardware/input/IInputManager.aidl index 2ac194b671923..0304815ef8fe2 100644 --- a/core/java/android/hardware/input/IInputManager.aidl +++ b/core/java/android/hardware/input/IInputManager.aidl @@ -50,6 +50,10 @@ interface IInputManager { // Reports whether the hardware supports the given keys; returns true if successful boolean hasKeys(int deviceId, int sourceMask, in int[] keyCodes, out boolean[] keyExists); + // Returns the keyCode produced when pressing the key at the specified location, given the + // active keyboard layout. + int getKeyCodeForKeyLocation(int deviceId, in int locationKeyCode); + // Temporarily changes the pointer speed. void tryPointerSpeed(int speed); diff --git a/core/java/android/hardware/input/InputDeviceIdentifier.java b/core/java/android/hardware/input/InputDeviceIdentifier.java index c673e7ab7c537..a5b9a2a4da766 100644 --- a/core/java/android/hardware/input/InputDeviceIdentifier.java +++ b/core/java/android/hardware/input/InputDeviceIdentifier.java @@ -16,7 +16,9 @@ package android.hardware.input; +import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.TestApi; import android.os.Parcel; import android.os.Parcelable; import android.text.TextUtils; @@ -28,12 +30,13 @@ import java.util.Objects; * * @hide */ +@TestApi public final class InputDeviceIdentifier implements Parcelable { private final String mDescriptor; private final int mVendorId; private final int mProductId; - public InputDeviceIdentifier(String descriptor, int vendorId, int productId) { + public InputDeviceIdentifier(@NonNull String descriptor, int vendorId, int productId) { this.mDescriptor = descriptor; this.mVendorId = vendorId; this.mProductId = productId; @@ -51,12 +54,13 @@ public final class InputDeviceIdentifier implements Parcelable { } @Override - public void writeToParcel(Parcel dest, int flags) { + public void writeToParcel(@NonNull Parcel dest, int flags) { dest.writeString(mDescriptor); dest.writeInt(mVendorId); dest.writeInt(mProductId); } + @NonNull public String getDescriptor() { return mDescriptor; } diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java index ef349a96ee17e..cbc837393b6b7 100644 --- a/core/java/android/hardware/input/InputManager.java +++ b/core/java/android/hardware/input/InputManager.java @@ -58,6 +58,7 @@ import android.util.SparseArray; import android.view.InputDevice; import android.view.InputEvent; import android.view.InputMonitor; +import android.view.KeyEvent; import android.view.MotionEvent; import android.view.PointerIcon; import android.view.VerifiedInputEvent; @@ -636,6 +637,30 @@ public final class InputManager { } } + /** + * Returns the descriptors of all supported keyboard layouts appropriate for the specified + * input device. + *

+ * The input manager consults the built-in keyboard layouts as well as all keyboard layouts + * advertised by applications using a {@link #ACTION_QUERY_KEYBOARD_LAYOUTS} broadcast receiver. + *

+ * + * @param device The input device to query. + * @return The ids of all keyboard layouts which are supported by the specified input device. + * + * @hide + */ + @TestApi + @NonNull + public List getKeyboardLayoutDescriptorsForInputDevice(@NonNull InputDevice device) { + KeyboardLayout[] layouts = getKeyboardLayoutsForInputDevice(device.getIdentifier()); + List res = new ArrayList<>(); + for (KeyboardLayout kl : layouts) { + res.add(kl.getDescriptor()); + } + return res; + } + /** * Gets information about all supported keyboard layouts appropriate * for a specific input device. @@ -650,7 +675,9 @@ public final class InputManager { * * @hide */ - public KeyboardLayout[] getKeyboardLayoutsForInputDevice(InputDeviceIdentifier identifier) { + @NonNull + public KeyboardLayout[] getKeyboardLayoutsForInputDevice( + @NonNull InputDeviceIdentifier identifier) { try { return mIm.getKeyboardLayoutsForInputDevice(identifier); } catch (RemoteException ex) { @@ -680,15 +707,17 @@ public final class InputManager { } /** - * Gets the current keyboard layout descriptor for the specified input - * device. + * Gets the current keyboard layout descriptor for the specified input device. * * @param identifier Identifier for the input device - * @return The keyboard layout descriptor, or null if no keyboard layout has - * been set. + * @return The keyboard layout descriptor, or null if no keyboard layout has been set. + * * @hide */ - public String getCurrentKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier) { + @TestApi + @Nullable + public String getCurrentKeyboardLayoutForInputDevice( + @NonNull InputDeviceIdentifier identifier) { try { return mIm.getCurrentKeyboardLayoutForInputDevice(identifier); } catch (RemoteException ex) { @@ -697,20 +726,21 @@ public final class InputManager { } /** - * Sets the current keyboard layout descriptor for the specified input - * device. + * Sets the current keyboard layout descriptor for the specified input device. *

- * This method may have the side-effect of causing the input device in - * question to be reconfigured. + * This method may have the side-effect of causing the input device in question to be + * reconfigured. *

* * @param identifier The identifier for the input device. - * @param keyboardLayoutDescriptor The keyboard layout descriptor to use, - * must not be null. + * @param keyboardLayoutDescriptor The keyboard layout descriptor to use, must not be null. + * * @hide */ - public void setCurrentKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier, - String keyboardLayoutDescriptor) { + @TestApi + @RequiresPermission(Manifest.permission.SET_KEYBOARD_LAYOUT) + public void setCurrentKeyboardLayoutForInputDevice(@NonNull InputDeviceIdentifier identifier, + @NonNull String keyboardLayoutDescriptor) { if (identifier == null) { throw new IllegalArgumentException("identifier must not be null"); } @@ -727,11 +757,11 @@ public final class InputManager { } /** - * Gets all keyboard layout descriptors that are enabled for the specified - * input device. + * Gets all keyboard layout descriptors that are enabled for the specified input device. * * @param identifier The identifier for the input device. * @return The keyboard layout descriptors. + * * @hide */ public String[] getEnabledKeyboardLayoutsForInputDevice(InputDeviceIdentifier identifier) { @@ -749,15 +779,16 @@ public final class InputManager { /** * Adds the keyboard layout descriptor for the specified input device. *

- * This method may have the side-effect of causing the input device in - * question to be reconfigured. + * This method may have the side-effect of causing the input device in question to be + * reconfigured. *

* * @param identifier The identifier for the input device. - * @param keyboardLayoutDescriptor The descriptor of the keyboard layout to - * add. + * @param keyboardLayoutDescriptor The descriptor of the keyboard layout to add. + * * @hide */ + @RequiresPermission(Manifest.permission.SET_KEYBOARD_LAYOUT) public void addKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier, String keyboardLayoutDescriptor) { if (identifier == null) { @@ -777,17 +808,19 @@ public final class InputManager { /** * Removes the keyboard layout descriptor for the specified input device. *

- * This method may have the side-effect of causing the input device in - * question to be reconfigured. + * This method may have the side-effect of causing the input device in question to be + * reconfigured. *

* * @param identifier The identifier for the input device. - * @param keyboardLayoutDescriptor The descriptor of the keyboard layout to - * remove. + * @param keyboardLayoutDescriptor The descriptor of the keyboard layout to remove. + * * @hide */ - public void removeKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier, - String keyboardLayoutDescriptor) { + @TestApi + @RequiresPermission(Manifest.permission.SET_KEYBOARD_LAYOUT) + public void removeKeyboardLayoutForInputDevice(@NonNull InputDeviceIdentifier identifier, + @NonNull String keyboardLayoutDescriptor) { if (identifier == null) { throw new IllegalArgumentException("inputDeviceDescriptor must not be null"); } @@ -1044,6 +1077,27 @@ public final class InputManager { return ret; } + /** + * Gets the key code produced by the specified location on a US keyboard layout. + * Key code as defined in {@link android.view.KeyEvent}. + * This API is only functional for devices with {@link InputDevice#SOURCE_KEYBOARD} available + * which can alter their key mapping using country specific keyboard layouts. + * + * @param deviceId The input device id. + * @param locationKeyCode The location of a key on a US keyboard layout. + * @return The key code produced when pressing the key at the specified location, given the + * active keyboard layout. Returns {@link KeyEvent#KEYCODE_UNKNOWN} if the requested + * mapping could not be determined, or if an error occurred. + * @hide + */ + public int getKeyCodeForKeyLocation(int deviceId, int locationKeyCode) { + try { + return mIm.getKeyCodeForKeyLocation(deviceId, locationKeyCode); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + /** * Injects an input event into the event system on behalf of an application. diff --git a/core/java/android/view/InputDevice.java b/core/java/android/view/InputDevice.java index 4f1354d7eee6a..188d7459f9a7f 100644 --- a/core/java/android/view/InputDevice.java +++ b/core/java/android/view/InputDevice.java @@ -572,6 +572,8 @@ public final class InputDevice implements Parcelable { * @return The identifier object for this device * @hide */ + @TestApi + @NonNull public InputDeviceIdentifier getIdentifier() { return mIdentifier; } @@ -734,6 +736,21 @@ public final class InputDevice implements Parcelable { return InputManager.getInstance().deviceHasKeys(mId, keys); } + /** + * Gets the key code produced by the specified location on a US keyboard layout. + * Key code as defined in {@link android.view.KeyEvent}. + * This API is only functional for devices with {@link InputDevice#SOURCE_KEYBOARD} available + * which can alter their key mapping using country specific keyboard layouts. + * + * @param locationKeyCode The location of a key on a US keyboard layout. + * @return The key code produced when pressing the key at the specified location, given the + * active keyboard layout. Returns {@link KeyEvent#KEYCODE_UNKNOWN} if the requested + * mapping could not be determined, or if an error occurred. + */ + public int getKeyCodeForKeyLocation(int locationKeyCode) { + return InputManager.getInstance().getKeyCodeForKeyLocation(mId, locationKeyCode); + } + /** * Gets information about the range of values for a particular {@link MotionEvent} axis. * If the device supports multiple sources, the same axis may have different meanings diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index a4d406929b799..3a842ee6e7f3f 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -4251,7 +4251,8 @@ + @hide + @TestApi --> diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java index 4de39bcd24e0b..2dd7a10ffe290 100644 --- a/services/core/java/com/android/server/input/InputManagerService.java +++ b/services/core/java/com/android/server/input/InputManagerService.java @@ -16,6 +16,8 @@ package com.android.server.input; +import static android.view.KeyEvent.KEYCODE_UNKNOWN; + import android.annotation.NonNull; import android.annotation.Nullable; import android.app.Notification; @@ -289,6 +291,8 @@ public class InputManagerService extends IInputManager.Stub int deviceId, int sourceMask, int sw); private static native boolean nativeHasKeys(long ptr, int deviceId, int sourceMask, int[] keyCodes, boolean[] keyExists); + private static native int nativeGetKeyCodeForKeyLocation(long ptr, int deviceId, + int locationKeyCode); private static native InputChannel nativeCreateInputChannel(long ptr, String name); private static native InputChannel nativeCreateInputMonitor(long ptr, int displayId, boolean isGestureMonitor, String name, int pid); @@ -665,6 +669,22 @@ public class InputManagerService extends IInputManager.Stub return nativeHasKeys(mPtr, deviceId, sourceMask, keyCodes, keyExists); } + /** + * Returns the keyCode generated by the specified location on a US keyboard layout. + * This takes into consideration the currently active keyboard layout. + * + * @param deviceId The input device id. + * @param locationKeyCode The location of a key on a US keyboard layout. + * @return The KeyCode this physical key location produces. + */ + @Override // Binder call + public int getKeyCodeForKeyLocation(int deviceId, int locationKeyCode) { + if (locationKeyCode <= KEYCODE_UNKNOWN || locationKeyCode > KeyEvent.getMaxKeyCode()) { + return KEYCODE_UNKNOWN; + } + return nativeGetKeyCodeForKeyLocation(mPtr, deviceId, locationKeyCode); + } + /** * Transfer the current touch gesture to the provided window. * diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp index 4524bb71d218e..c71686abe9de5 100644 --- a/services/core/jni/com_android_server_input_InputManagerService.cpp +++ b/services/core/jni/com_android_server_input_InputManagerService.cpp @@ -1586,6 +1586,13 @@ static jboolean nativeHasKeys(JNIEnv* env, jclass /* clazz */, return result; } +static jint nativeGetKeyCodeForKeyLocation(JNIEnv* env, jclass /* clazz */, jlong ptr, + jint deviceId, jint locationKeyCode) { + NativeInputManager* im = reinterpret_cast(ptr); + return (jint)im->getInputManager()->getReader().getKeyCodeForKeyLocation(deviceId, + locationKeyCode); +} + static void handleInputChannelDisposed(JNIEnv* env, jobject /* inputChannelObj */, const std::shared_ptr& inputChannel, void* data) { @@ -2337,6 +2344,7 @@ static const JNINativeMethod gInputManagerMethods[] = { {"nativeGetKeyCodeState", "(JIII)I", (void*)nativeGetKeyCodeState}, {"nativeGetSwitchState", "(JIII)I", (void*)nativeGetSwitchState}, {"nativeHasKeys", "(JII[I[Z)Z", (void*)nativeHasKeys}, + {"nativeGetKeyCodeForKeyLocation", "(JII)I", (void*)nativeGetKeyCodeForKeyLocation}, {"nativeCreateInputChannel", "(JLjava/lang/String;)Landroid/view/InputChannel;", (void*)nativeCreateInputChannel}, {"nativeCreateInputMonitor", "(JIZLjava/lang/String;I)Landroid/view/InputChannel;",