Add APIs to support per keyboard glyph maps
DD: go/pk_glyph_map
PRD doc: go/pk_glyph_map_prd
This CL only contains the shell of the APIs, the implementation
will follow in subsequent CLs.
Test: None
Bug: 345440920
Flag: com.android.hardware.input.keyboard_glyph_map
Change-Id: I09e32121b8ed97dd5267b06d1ad86bb242bba880
diff --git a/core/java/android/hardware/input/IInputManager.aidl b/core/java/android/hardware/input/IInputManager.aidl
index 40d4fb6..1767d64 100644
--- a/core/java/android/hardware/input/IInputManager.aidl
+++ b/core/java/android/hardware/input/IInputManager.aidl
@@ -32,6 +32,7 @@
import android.os.CombinedVibration;
import android.hardware.input.IInputSensorEventListener;
import android.hardware.input.InputSensorInfo;
+import android.hardware.input.KeyGlyphMap;
import android.hardware.lights.Light;
import android.hardware.lights.LightState;
import android.os.IBinder;
@@ -236,4 +237,6 @@
@JavaPassthrough(annotation="@android.annotation.RequiresPermission(value = "
+ "android.Manifest.permission.MONITOR_STICKY_MODIFIER_STATE)")
void unregisterStickyModifierStateListener(IStickyModifierStateListener listener);
+
+ KeyGlyphMap getKeyGlyphMap(int deviceId);
}
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index 21c9002..5e46ae0 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -19,6 +19,7 @@
import static com.android.input.flags.Flags.FLAG_INPUT_DEVICE_VIEW_BEHAVIOR_API;
import static com.android.input.flags.Flags.FLAG_DEVICE_ASSOCIATIONS;
import static com.android.hardware.input.Flags.keyboardLayoutPreviewFlag;
+import static com.android.hardware.input.Flags.keyboardGlyphMap;
import android.Manifest;
import android.annotation.FlaggedApi;
@@ -898,6 +899,23 @@
}
/**
+ * Provides associated glyph map for the keyboard device (if available)
+ *
+ * @hide
+ */
+ @Nullable
+ public KeyGlyphMap getKeyGlyphMap(int deviceId) {
+ if (!keyboardGlyphMap()) {
+ return null;
+ }
+ try {
+ return mIm.getKeyGlyphMap(deviceId);
+ } catch (RemoteException ex) {
+ throw ex.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Injects an input event into the event system, targeting windows owned by the provided uid.
*
* If a valid targetUid is provided, the system will only consider injecting the input event
diff --git a/core/java/android/hardware/input/KeyGlyphMap.aidl b/core/java/android/hardware/input/KeyGlyphMap.aidl
new file mode 100644
index 0000000..104f1e4
--- /dev/null
+++ b/core/java/android/hardware/input/KeyGlyphMap.aidl
@@ -0,0 +1,19 @@
+/**
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.input;
+
+parcelable KeyGlyphMap;
\ No newline at end of file
diff --git a/core/java/android/hardware/input/KeyGlyphMap.java b/core/java/android/hardware/input/KeyGlyphMap.java
new file mode 100644
index 0000000..49c47a2
--- /dev/null
+++ b/core/java/android/hardware/input/KeyGlyphMap.java
@@ -0,0 +1,187 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.input;
+
+import android.annotation.DrawableRes;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Log;
+import android.util.SparseIntArray;
+import android.view.KeyEvent;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * This class provides access to device specific key glyphs, modifier glyphs and device specific
+ * shortcuts and keys
+ *
+ * @hide
+ */
+public final class KeyGlyphMap implements Parcelable {
+ private static final String TAG = "KeyGlyphMap";
+
+ @NonNull
+ private final ComponentName mComponentName;
+ @NonNull
+ private final SparseIntArray mKeyGlyphs;
+ @NonNull
+ private final SparseIntArray mModifierGlyphs;
+ @NonNull
+ private final int[] mFunctionRowKeys;
+ @NonNull
+ private final Map<KeyCombination, Integer> mHardwareShortcuts;
+
+ public static final @NonNull Parcelable.Creator<KeyGlyphMap> CREATOR =
+ new Parcelable.Creator<>() {
+ public KeyGlyphMap createFromParcel(Parcel in) {
+ return new KeyGlyphMap(in);
+ }
+
+ public KeyGlyphMap[] newArray(int size) {
+ return new KeyGlyphMap[size];
+ }
+ };
+
+ public KeyGlyphMap(@NonNull ComponentName componentName,
+ @NonNull SparseIntArray keyGlyphs, @NonNull SparseIntArray modifierGlyphs,
+ @NonNull int[] functionRowKeys,
+ @NonNull Map<KeyCombination, Integer> hardwareShortcuts) {
+ mComponentName = componentName;
+ mKeyGlyphs = keyGlyphs;
+ mModifierGlyphs = modifierGlyphs;
+ mFunctionRowKeys = functionRowKeys;
+ mHardwareShortcuts = hardwareShortcuts;
+ }
+
+ public KeyGlyphMap(Parcel in) {
+ mComponentName = in.readParcelable(getClass().getClassLoader(), ComponentName.class);
+ mKeyGlyphs = in.readSparseIntArray();
+ mModifierGlyphs = in.readSparseIntArray();
+ mFunctionRowKeys = new int[in.readInt()];
+ in.readIntArray(mFunctionRowKeys);
+ mHardwareShortcuts = new HashMap<>(in.readInt());
+ in.readMap(mHardwareShortcuts, getClass().getClassLoader(), KeyCombination.class,
+ Integer.class);
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeParcelable(mComponentName, 0);
+ dest.writeSparseIntArray(mKeyGlyphs);
+ dest.writeSparseIntArray(mModifierGlyphs);
+ dest.writeInt(mFunctionRowKeys.length);
+ dest.writeIntArray(mFunctionRowKeys);
+ dest.writeInt(mHardwareShortcuts.size());
+ dest.writeMap(mHardwareShortcuts);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * Defines a key combination that includes a keycode and modifier state.
+ */
+ public record KeyCombination(int modifierState, int keycode) {}
+
+ /**
+ * Returns keycodes generated from the functional row defined for the keyboard.
+ */
+ public int[] getFunctionRowKeys() {
+ return mFunctionRowKeys;
+ }
+
+ /**
+ * Returns hardware defined shortcuts that are handled in the firmware of a particular
+ * keyboard (e.g. Fn+Backspace = Back, etc.)
+ *
+ * @return a map of (modifier + key) combinations to keycode mappings that are handled by the
+ * device hardware/firmware.
+ */
+ public Map<KeyCombination, Integer> getHardwareShortcuts() {
+ return mHardwareShortcuts;
+ }
+
+ /**
+ * Provides the drawable resource for the glyph for a keycode.
+ * Returns null if not available.
+ */
+ @Nullable
+ public Drawable getDrawableForKeycode(Context context, int keycode) {
+ return getDrawable(context, mKeyGlyphs.get(keycode, 0));
+ }
+
+ /**
+ * Provides the drawable resource for the glyph for a modifier key.
+ * Returns null if not available.
+ */
+ @Nullable
+ public Drawable getDrawableForModifier(Context context, int modifierKeycode) {
+ int modifier = switch (modifierKeycode) {
+ case KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_META_RIGHT -> KeyEvent.META_META_ON;
+ case KeyEvent.KEYCODE_CTRL_LEFT, KeyEvent.KEYCODE_CTRL_RIGHT -> KeyEvent.META_CTRL_ON;
+ case KeyEvent.KEYCODE_ALT_LEFT, KeyEvent.KEYCODE_ALT_RIGHT -> KeyEvent.META_ALT_ON;
+ case KeyEvent.KEYCODE_SHIFT_LEFT, KeyEvent.KEYCODE_SHIFT_RIGHT ->
+ KeyEvent.META_SHIFT_ON;
+ case KeyEvent.KEYCODE_FUNCTION -> KeyEvent.META_FUNCTION_ON;
+ case KeyEvent.KEYCODE_SYM -> KeyEvent.META_SYM_ON;
+ case KeyEvent.KEYCODE_CAPS_LOCK -> KeyEvent.META_CAPS_LOCK_ON;
+ case KeyEvent.KEYCODE_NUM_LOCK -> KeyEvent.META_NUM_LOCK_ON;
+ case KeyEvent.KEYCODE_SCROLL_LOCK -> KeyEvent.META_SCROLL_LOCK_ON;
+ default -> 0;
+ };
+ return getDrawable(context, mModifierGlyphs.get(modifier, 0));
+ }
+
+ @Nullable
+ private Drawable getDrawable(Context context, @DrawableRes int drawableRes) {
+ PackageManager pm = context.getPackageManager();
+ try {
+ ActivityInfo receiver = pm.getReceiverInfo(mComponentName,
+ PackageManager.GET_META_DATA
+ | PackageManager.MATCH_DIRECT_BOOT_AWARE
+ | PackageManager.MATCH_DIRECT_BOOT_UNAWARE);
+ Resources resources = pm.getResourcesForApplication(receiver.applicationInfo);
+ return resources.getDrawable(drawableRes, null);
+ } catch (PackageManager.NameNotFoundException ignored) {
+ Log.e(TAG, "Package name not found for " + mComponentName);
+ }
+ return null;
+ }
+
+ @Override
+ public String toString() {
+ return "KeyGlyphMap{"
+ + "mComponentName=" + mComponentName
+ + ", mKeyGlyphs=" + mKeyGlyphs
+ + ", mModifierGlyphs=" + mModifierGlyphs
+ + ", mFunctionRowKeys=" + Arrays.toString(mFunctionRowKeys)
+ + ", mHardwareShortcuts=" + mHardwareShortcuts
+ + '}';
+ }
+}
diff --git a/core/java/android/hardware/input/input_framework.aconfig b/core/java/android/hardware/input/input_framework.aconfig
index ed536ce..acd0d00f 100644
--- a/core/java/android/hardware/input/input_framework.aconfig
+++ b/core/java/android/hardware/input/input_framework.aconfig
@@ -53,4 +53,11 @@
name: "touchpad_tap_dragging"
description: "Offers a setting to enable touchpad tap dragging"
bug: "321978150"
-}
\ No newline at end of file
+}
+
+flag {
+ namespace: "input_native"
+ name: "keyboard_glyph_map"
+ description: "Allows system to provide keyboard specific key drawables and shortcuts via config files"
+ bug: "345440920"
+}
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index e5dbce9..b23546c 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -53,6 +53,7 @@
import android.hardware.input.InputManager;
import android.hardware.input.InputSensorInfo;
import android.hardware.input.InputSettings;
+import android.hardware.input.KeyGlyphMap;
import android.hardware.input.KeyboardLayout;
import android.hardware.input.KeyboardLayoutSelectionResult;
import android.hardware.input.TouchCalibration;
@@ -1208,6 +1209,12 @@
imeInfo, imeSubtype);
}
+ @Override // Binder call
+ public KeyGlyphMap getKeyGlyphMap(int deviceId) {
+ // TODO(b/345440920): Implementation
+ return null;
+ }
+
public void setFocusedApplication(int displayId, InputApplicationHandle application) {
mNative.setFocusedApplication(displayId, application);
}