Add API to get KeyCode produced by physical key location.

The physical key location is provided as a location keyCode pointing to a location on a US keyboard layout.

Bug: 179812917
Test: atest KeyboardLayoutChangeTest
Test: atest android.hardware.input.cts.tests -m
Change-Id: Ie1b424a0e0bfea2bc15b0b3a644f2f718f16094e
diff --git a/core/api/current.txt b/core/api/current.txt
index e974922..5dce6c0 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -48358,6 +48358,7 @@
     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/java/android/hardware/input/IInputManager.aidl b/core/java/android/hardware/input/IInputManager.aidl
index 2ac194b..0304815 100644
--- a/core/java/android/hardware/input/IInputManager.aidl
+++ b/core/java/android/hardware/input/IInputManager.aidl
@@ -50,6 +50,10 @@
     // 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/InputManager.java b/core/java/android/hardware/input/InputManager.java
index ef349a9..df59fef 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -58,6 +58,7 @@
 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;
@@ -1044,6 +1045,27 @@
         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 4f1354d..cadff69 100644
--- a/core/java/android/view/InputDevice.java
+++ b/core/java/android/view/InputDevice.java
@@ -735,6 +735,21 @@
     }
 
     /**
+     * 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
      * for each source.  Returns information about the first axis found for any source.
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 261aa32..ef0c716 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;
@@ -284,6 +286,8 @@
             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);
@@ -658,6 +662,22 @@
     }
 
     /**
+     * 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.
      *
      * @param destChannelToken The token of the window or input channel that should receive the
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 3cd4e5e..a2475d9 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -1573,6 +1573,13 @@
     return result;
 }
 
+static jint nativeGetKeyCodeForKeyLocation(JNIEnv* env, jclass /* clazz */, jlong ptr,
+                                           jint deviceId, jint locationKeyCode) {
+    NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
+    return (jint)im->getInputManager()->getReader().getKeyCodeForKeyLocation(deviceId,
+                                                                             locationKeyCode);
+}
+
 static void handleInputChannelDisposed(JNIEnv* env, jobject /* inputChannelObj */,
                                        const std::shared_ptr<InputChannel>& inputChannel,
                                        void* data) {
@@ -2312,6 +2319,7 @@
         {"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;",