Merge "Add KeyGestureEventHandler APIs" into main
diff --git a/core/java/android/hardware/input/AidlKeyGestureEvent.aidl b/core/java/android/hardware/input/AidlKeyGestureEvent.aidl
new file mode 100644
index 0000000..7cf8795
--- /dev/null
+++ b/core/java/android/hardware/input/AidlKeyGestureEvent.aidl
@@ -0,0 +1,29 @@
+/*
+ * 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;
+
+/** @hide */
+@JavaDerive(equals=true)
+parcelable AidlKeyGestureEvent {
+    int deviceId;
+    int[] keycodes;
+    int modifierState;
+    int gestureType;
+    int action;
+    int displayId;
+    int flags;
+}
diff --git a/core/java/android/hardware/input/IInputManager.aidl b/core/java/android/hardware/input/IInputManager.aidl
index 2d96bba..102f56e 100644
--- a/core/java/android/hardware/input/IInputManager.aidl
+++ b/core/java/android/hardware/input/IInputManager.aidl
@@ -26,6 +26,7 @@
 import android.hardware.input.IKeyboardBacklightListener;
 import android.hardware.input.IKeyboardBacklightState;
 import android.hardware.input.IKeyGestureEventListener;
+import android.hardware.input.IKeyGestureHandler;
 import android.hardware.input.IStickyModifierStateListener;
 import android.hardware.input.ITabletModeChangedListener;
 import android.hardware.input.KeyboardLayoutSelectionResult;
@@ -250,4 +251,14 @@
     @JavaPassthrough(annotation="@android.annotation.RequiresPermission(value = "
             + "android.Manifest.permission.MANAGE_KEY_GESTURES)")
     void unregisterKeyGestureEventListener(IKeyGestureEventListener listener);
+
+    @PermissionManuallyEnforced
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(value = "
+            + "android.Manifest.permission.MANAGE_KEY_GESTURES)")
+    void registerKeyGestureHandler(IKeyGestureHandler handler);
+
+    @PermissionManuallyEnforced
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(value = "
+            + "android.Manifest.permission.MANAGE_KEY_GESTURES)")
+    void unregisterKeyGestureHandler(IKeyGestureHandler handler);
 }
diff --git a/core/java/android/hardware/input/IKeyGestureEventListener.aidl b/core/java/android/hardware/input/IKeyGestureEventListener.aidl
index 2c430f1..6b5f837 100644
--- a/core/java/android/hardware/input/IKeyGestureEventListener.aidl
+++ b/core/java/android/hardware/input/IKeyGestureEventListener.aidl
@@ -16,11 +16,13 @@
 
 package android.hardware.input;
 
+import android.hardware.input.AidlKeyGestureEvent;
+
 /** @hide */
 oneway interface IKeyGestureEventListener {
 
     /**
      * Called when a key gesture event occurs.
      */
-    void onKeyGestureEvent(int deviceId, in int[] keycodes, int modifierState, int shortcut);
+    void onKeyGestureEvent(in AidlKeyGestureEvent event);
 }
diff --git a/core/java/android/hardware/input/IKeyGestureHandler.aidl b/core/java/android/hardware/input/IKeyGestureHandler.aidl
new file mode 100644
index 0000000..509b948
--- /dev/null
+++ b/core/java/android/hardware/input/IKeyGestureHandler.aidl
@@ -0,0 +1,42 @@
+/*
+ * 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.hardware.input.AidlKeyGestureEvent;
+import android.os.IBinder;
+
+/** @hide */
+interface IKeyGestureHandler {
+
+    /**
+     * Called when a key gesture starts, ends, or is cancelled. If a handler returns {@code true},
+     * it means they intend to handle the full gesture and should handle all the events pertaining
+     * to that gesture.
+     */
+    boolean handleKeyGesture(in AidlKeyGestureEvent event, in IBinder focusedToken);
+
+    /**
+     * Called to know if a particular gesture type is supported by the handler.
+     *
+     * TODO(b/358569822): Remove this call to reduce the binder calls to single call for
+     *  handleKeyGesture. For this we need to remove dependency of multi-key gestures to identify if
+     *  a key gesture is supported on first relevant key down.
+     *  Also, for now we prioritize handlers in the system server process above external handlers to
+     *  reduce IPC binder calls.
+     */
+    boolean isKeyGestureSupported(int gestureType);
+}
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index 04cfcd8..22728f7 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -1406,6 +1406,33 @@
     }
 
     /**
+     * Registers a key gesture event handler for {@link KeyGestureEvent} handling.
+     *
+     * @param handler the {@link KeyGestureEventHandler}
+     * @throws IllegalArgumentException if {@code handler} has already been registered previously.
+     * @throws NullPointerException     if {@code handler} or {@code executor} is null.
+     * @hide
+     * @see #unregisterKeyGestureEventHandler(KeyGestureEventHandler)
+     */
+    @RequiresPermission(Manifest.permission.MANAGE_KEY_GESTURES)
+    public void registerKeyGestureEventHandler(@NonNull KeyGestureEventHandler handler)
+            throws IllegalArgumentException {
+        mGlobal.registerKeyGestureEventHandler(handler);
+    }
+
+    /**
+     * Unregisters a previously added key gesture event handler.
+     *
+     * @param handler the {@link KeyGestureEventHandler}
+     * @hide
+     * @see #registerKeyGestureEventHandler(KeyGestureEventHandler)
+     */
+    @RequiresPermission(Manifest.permission.MANAGE_KEY_GESTURES)
+    public void unregisterKeyGestureEventHandler(@NonNull KeyGestureEventHandler handler) {
+        mGlobal.unregisterKeyGestureEventHandler(handler);
+    }
+
+    /**
      * A callback used to be notified about battery state changes for an input device. The
      * {@link #onBatteryStateChanged(int, long, BatteryState)} method will be called once after the
      * listener is successfully registered to provide the initial battery state of the device.
@@ -1522,4 +1549,35 @@
          */
         void onKeyGestureEvent(@NonNull KeyGestureEvent event);
     }
+
+    /**
+     * A callback used to notify about key gesture event start, complete and cancel. Unlike
+     * {@see KeyGestureEventListener} which is to listen to successfully handled key gestures, this
+     * interface allows system components to register handler for handling key gestures.
+     *
+     * @see #registerKeyGestureEventHandler(KeyGestureEventHandler)
+     * @see #unregisterKeyGestureEventHandler(KeyGestureEventHandler)
+     *
+     * <p> NOTE: All callbacks will occur on system main and input threads, so the caller needs
+     * to move time-consuming operations to appropriate handler threads.
+     * @hide
+     */
+    public interface KeyGestureEventHandler {
+        /**
+         * Called when a key gesture event starts, is completed, or is cancelled. If a handler
+         * returns {@code true}, it implies that the handler intends to handle the key gesture and
+         * only this handler will receive the future events for this key gesture.
+         *
+         * @param event the gesture event
+         */
+        boolean handleKeyGestureEvent(@NonNull KeyGestureEvent event,
+                @Nullable IBinder focusedToken);
+
+        /**
+         * Called to identify if a particular gesture is of interest to a handler.
+         *
+         * NOTE: If no active handler supports certain gestures, the gestures will not be captured.
+         */
+        boolean isKeyGestureSupported(@KeyGestureEvent.KeyGestureType int gestureType);
+    }
 }
diff --git a/core/java/android/hardware/input/InputManagerGlobal.java b/core/java/android/hardware/input/InputManagerGlobal.java
index 2a36238..5c11346 100644
--- a/core/java/android/hardware/input/InputManagerGlobal.java
+++ b/core/java/android/hardware/input/InputManagerGlobal.java
@@ -25,6 +25,7 @@
 import android.hardware.SensorManager;
 import android.hardware.input.InputManager.InputDeviceBatteryListener;
 import android.hardware.input.InputManager.InputDeviceListener;
+import android.hardware.input.InputManager.KeyGestureEventHandler;
 import android.hardware.input.InputManager.KeyGestureEventListener;
 import android.hardware.input.InputManager.KeyboardBacklightListener;
 import android.hardware.input.InputManager.OnTabletModeChangedListener;
@@ -119,6 +120,14 @@
     @Nullable
     private IKeyGestureEventListener mKeyGestureEventListener;
 
+    private final Object mKeyGestureEventHandlerLock = new Object();
+    @GuardedBy("mKeyGestureEventHandlerLock")
+    @Nullable
+    private ArrayList<KeyGestureEventHandler> mKeyGestureEventHandlers;
+    @GuardedBy("mKeyGestureEventHandlerLock")
+    @Nullable
+    private IKeyGestureHandler mKeyGestureHandler;
+
     // InputDeviceSensorManager gets notified synchronously from the binder thread when input
     // devices change, so it must be synchronized with the input device listeners.
     @GuardedBy("mInputDeviceListeners")
@@ -1080,18 +1089,14 @@
     }
 
     private class LocalKeyGestureEventListener extends IKeyGestureEventListener.Stub {
-
         @Override
-        public void onKeyGestureEvent(int deviceId, int[] keycodes, int modifierState,
-                int gestureType) {
+        public void onKeyGestureEvent(@NonNull AidlKeyGestureEvent ev) {
             synchronized (mKeyGestureEventListenerLock) {
                 if (mKeyGestureEventListeners == null) return;
                 final int numListeners = mKeyGestureEventListeners.size();
+                final KeyGestureEvent event = new KeyGestureEvent(ev);
                 for (int i = 0; i < numListeners; i++) {
-                    mKeyGestureEventListeners.get(i)
-                            .onKeyGestureEvent(
-                                    new KeyGestureEvent(deviceId, keycodes, modifierState,
-                                            gestureType));
+                    mKeyGestureEventListeners.get(i).onKeyGestureEvent(event);
                 }
             }
         }
@@ -1154,6 +1159,96 @@
         }
     }
 
+    private class LocalKeyGestureHandler extends IKeyGestureHandler.Stub {
+        @Override
+        public boolean handleKeyGesture(@NonNull AidlKeyGestureEvent ev, IBinder focusedToken) {
+            synchronized (mKeyGestureEventHandlerLock) {
+                if (mKeyGestureEventHandlers == null) {
+                    return false;
+                }
+                final int numHandlers = mKeyGestureEventHandlers.size();
+                final KeyGestureEvent event = new KeyGestureEvent(ev);
+                for (int i = 0; i < numHandlers; i++) {
+                    KeyGestureEventHandler handler = mKeyGestureEventHandlers.get(i);
+                    if (handler.handleKeyGestureEvent(event, focusedToken)) {
+                        return true;
+                    }
+                }
+            }
+            return false;
+        }
+
+        @Override
+        public boolean isKeyGestureSupported(@KeyGestureEvent.KeyGestureType int gestureType) {
+            synchronized (mKeyGestureEventHandlerLock) {
+                if (mKeyGestureEventHandlers == null) {
+                    return false;
+                }
+                final int numHandlers = mKeyGestureEventHandlers.size();
+                for (int i = 0; i < numHandlers; i++) {
+                    KeyGestureEventHandler handler = mKeyGestureEventHandlers.get(i);
+                    if (handler.isKeyGestureSupported(gestureType)) {
+                        return true;
+                    }
+                }
+            }
+            return false;
+        }
+    }
+
+    /**
+     * @see InputManager#registerKeyGestureEventHandler(KeyGestureEventHandler)
+     */
+    @RequiresPermission(Manifest.permission.MANAGE_KEY_GESTURES)
+    void registerKeyGestureEventHandler(@NonNull KeyGestureEventHandler handler)
+            throws IllegalArgumentException {
+        Objects.requireNonNull(handler, "handler should not be null");
+
+        synchronized (mKeyGestureEventHandlerLock) {
+            if (mKeyGestureHandler == null) {
+                mKeyGestureEventHandlers = new ArrayList<>();
+                mKeyGestureHandler = new LocalKeyGestureHandler();
+
+                try {
+                    mIm.registerKeyGestureHandler(mKeyGestureHandler);
+                } catch (RemoteException e) {
+                    throw e.rethrowFromSystemServer();
+                }
+            }
+            final int numHandlers = mKeyGestureEventHandlers.size();
+            for (int i = 0; i < numHandlers; i++) {
+                if (mKeyGestureEventHandlers.get(i) == handler) {
+                    throw new IllegalArgumentException("Handler has already been registered!");
+                }
+            }
+            mKeyGestureEventHandlers.add(handler);
+        }
+    }
+
+    /**
+     * @see InputManager#unregisterKeyGestureEventHandler(KeyGestureEventHandler)
+     */
+    @RequiresPermission(Manifest.permission.MANAGE_KEY_GESTURES)
+    void unregisterKeyGestureEventHandler(@NonNull KeyGestureEventHandler handler) {
+        Objects.requireNonNull(handler, "handler should not be null");
+
+        synchronized (mKeyGestureEventHandlerLock) {
+            if (mKeyGestureEventHandlers == null) {
+                return;
+            }
+            mKeyGestureEventHandlers.removeIf(existingHandler -> existingHandler == handler);
+            if (mKeyGestureEventHandlers.isEmpty()) {
+                try {
+                    mIm.unregisterKeyGestureHandler(mKeyGestureHandler);
+                } catch (RemoteException e) {
+                    throw e.rethrowFromSystemServer();
+                }
+                mKeyGestureEventHandlers = null;
+                mKeyGestureHandler = null;
+            }
+        }
+    }
+
     /**
      * TODO(b/330517633): Cleanup the unsupported API
      */
diff --git a/core/java/android/hardware/input/KeyGestureEvent.java b/core/java/android/hardware/input/KeyGestureEvent.java
index 7a8dd33..c7ebc63 100644
--- a/core/java/android/hardware/input/KeyGestureEvent.java
+++ b/core/java/android/hardware/input/KeyGestureEvent.java
@@ -19,8 +19,11 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.view.Display;
+import android.view.KeyCharacterMap;
 
-import com.android.internal.util.DataClass;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.AnnotationValidations;
 import com.android.internal.util.FrameworkStatsLog;
 
 import java.lang.annotation.Retention;
@@ -31,501 +34,511 @@
  *
  * @hide
  */
-@DataClass(genToString = true, genEqualsHashCode = true)
-public class KeyGestureEvent {
+public final class KeyGestureEvent {
 
-    private final int mDeviceId;
     @NonNull
-    private final int[] mKeycodes;
-    private final int mModifierState;
-    @KeyGestureType
-    private final int mKeyGestureType;
+    private AidlKeyGestureEvent mKeyGestureEvent;
 
+    public static final int KEY_GESTURE_TYPE_UNSPECIFIED = 0;
+    public static final int KEY_GESTURE_TYPE_HOME = 1;
+    public static final int KEY_GESTURE_TYPE_RECENT_APPS = 2;
+    public static final int KEY_GESTURE_TYPE_BACK = 3;
+    public static final int KEY_GESTURE_TYPE_APP_SWITCH = 4;
+    public static final int KEY_GESTURE_TYPE_LAUNCH_ASSISTANT = 5;
+    public static final int KEY_GESTURE_TYPE_LAUNCH_VOICE_ASSISTANT = 6;
+    public static final int KEY_GESTURE_TYPE_LAUNCH_SYSTEM_SETTINGS = 7;
+    public static final int KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL = 8;
+    public static final int KEY_GESTURE_TYPE_TOGGLE_TASKBAR = 9;
+    public static final int KEY_GESTURE_TYPE_TAKE_SCREENSHOT = 10;
+    public static final int KEY_GESTURE_TYPE_OPEN_SHORTCUT_HELPER = 11;
+    public static final int KEY_GESTURE_TYPE_BRIGHTNESS_UP = 12;
+    public static final int KEY_GESTURE_TYPE_BRIGHTNESS_DOWN = 13;
+    public static final int KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_UP = 14;
+    public static final int KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_DOWN = 15;
+    public static final int KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_TOGGLE = 16;
+    public static final int KEY_GESTURE_TYPE_VOLUME_UP = 17;
+    public static final int KEY_GESTURE_TYPE_VOLUME_DOWN = 18;
+    public static final int KEY_GESTURE_TYPE_VOLUME_MUTE = 19;
+    public static final int KEY_GESTURE_TYPE_ALL_APPS = 20;
+    public static final int KEY_GESTURE_TYPE_LAUNCH_SEARCH = 21;
+    public static final int KEY_GESTURE_TYPE_LANGUAGE_SWITCH = 22;
+    public static final int KEY_GESTURE_TYPE_ACCESSIBILITY_ALL_APPS = 23;
+    public static final int KEY_GESTURE_TYPE_TOGGLE_CAPS_LOCK = 24;
+    public static final int KEY_GESTURE_TYPE_SYSTEM_MUTE = 25;
+    public static final int KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION = 26;
+    public static final int KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS = 27;
+    public static final int KEY_GESTURE_TYPE_TRIGGER_BUG_REPORT = 28;
+    public static final int KEY_GESTURE_TYPE_LOCK_SCREEN = 29;
+    public static final int KEY_GESTURE_TYPE_OPEN_NOTES = 30;
+    public static final int KEY_GESTURE_TYPE_TOGGLE_POWER = 31;
+    public static final int KEY_GESTURE_TYPE_SYSTEM_NAVIGATION = 32;
+    public static final int KEY_GESTURE_TYPE_SLEEP = 33;
+    public static final int KEY_GESTURE_TYPE_WAKEUP = 34;
+    public static final int KEY_GESTURE_TYPE_MEDIA_KEY = 35;
+    public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_BROWSER = 36;
+    public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_EMAIL = 37;
+    public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CONTACTS = 38;
+    public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALENDAR = 39;
+    public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALCULATOR = 40;
+    public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MUSIC = 41;
+    public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MAPS = 42;
+    public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MESSAGING = 43;
+    public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_GALLERY = 44;
+    public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_FILES = 45;
+    public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_WEATHER = 46;
+    public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_FITNESS = 47;
+    public static final int KEY_GESTURE_TYPE_LAUNCH_APPLICATION_BY_PACKAGE_NAME = 48;
+    public static final int KEY_GESTURE_TYPE_DESKTOP_MODE = 49;
+    public static final int KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION = 50;
 
-    public static final int KEY_GESTURE_TYPE_UNSPECIFIED =
-            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__UNSPECIFIED;
-    public static final int KEY_GESTURE_TYPE_HOME =
-            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__HOME;
-    public static final int KEY_GESTURE_TYPE_RECENT_APPS =
-            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__RECENT_APPS;
-    public static final int KEY_GESTURE_TYPE_BACK =
-            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__BACK;
-    public static final int KEY_GESTURE_TYPE_APP_SWITCH =
-            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__APP_SWITCH;
-    public static final int KEY_GESTURE_TYPE_LAUNCH_ASSISTANT =
-            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_ASSISTANT;
-    public static final int KEY_GESTURE_TYPE_LAUNCH_VOICE_ASSISTANT =
-            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_VOICE_ASSISTANT;
-    public static final int KEY_GESTURE_TYPE_LAUNCH_SYSTEM_SETTINGS =
-            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_SYSTEM_SETTINGS;
-    public static final int KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL =
-            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__TOGGLE_NOTIFICATION_PANEL;
-    public static final int KEY_GESTURE_TYPE_TOGGLE_TASKBAR =
-            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__TOGGLE_TASKBAR;
-    public static final int KEY_GESTURE_TYPE_TAKE_SCREENSHOT =
-            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__TAKE_SCREENSHOT;
-    public static final int KEY_GESTURE_TYPE_OPEN_SHORTCUT_HELPER =
-            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__OPEN_SHORTCUT_HELPER;
-    public static final int KEY_GESTURE_TYPE_BRIGHTNESS_UP =
-            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__BRIGHTNESS_UP;
-    public static final int KEY_GESTURE_TYPE_BRIGHTNESS_DOWN =
-            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__BRIGHTNESS_DOWN;
-    public static final int KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_UP =
-            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__KEYBOARD_BACKLIGHT_UP;
-    public static final int KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_DOWN =
-            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__KEYBOARD_BACKLIGHT_DOWN;
-    public static final int KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_TOGGLE =
-            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__KEYBOARD_BACKLIGHT_TOGGLE;
-    public static final int KEY_GESTURE_TYPE_VOLUME_UP =
-            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__VOLUME_UP;
-    public static final int KEY_GESTURE_TYPE_VOLUME_DOWN =
-            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__VOLUME_DOWN;
-    public static final int KEY_GESTURE_TYPE_VOLUME_MUTE =
-            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__VOLUME_MUTE;
-    public static final int KEY_GESTURE_TYPE_ALL_APPS =
-            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__ALL_APPS;
-    public static final int KEY_GESTURE_TYPE_LAUNCH_SEARCH =
-            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_SEARCH;
-    public static final int KEY_GESTURE_TYPE_LANGUAGE_SWITCH =
-            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LANGUAGE_SWITCH;
-    public static final int KEY_GESTURE_TYPE_ACCESSIBILITY_ALL_APPS =
-            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__ACCESSIBILITY_ALL_APPS;
-    public static final int KEY_GESTURE_TYPE_TOGGLE_CAPS_LOCK =
-            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__TOGGLE_CAPS_LOCK;
-    public static final int KEY_GESTURE_TYPE_SYSTEM_MUTE =
-            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__SYSTEM_MUTE;
-    public static final int KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION =
-            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__SPLIT_SCREEN_NAVIGATION;
-    public static final int KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS =
-            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__CHANGE_SPLITSCREEN_FOCUS;
-    public static final int KEY_GESTURE_TYPE_TRIGGER_BUG_REPORT =
-            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__TRIGGER_BUG_REPORT;
-    public static final int KEY_GESTURE_TYPE_LOCK_SCREEN =
-            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LOCK_SCREEN;
-    public static final int KEY_GESTURE_TYPE_OPEN_NOTES =
-            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__OPEN_NOTES;
-    public static final int KEY_GESTURE_TYPE_TOGGLE_POWER =
-            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__TOGGLE_POWER;
-    public static final int KEY_GESTURE_TYPE_SYSTEM_NAVIGATION =
-            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__SYSTEM_NAVIGATION;
-    public static final int KEY_GESTURE_TYPE_SLEEP =
-            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__SLEEP;
-    public static final int KEY_GESTURE_TYPE_WAKEUP =
-            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__WAKEUP;
-    public static final int KEY_GESTURE_TYPE_MEDIA_KEY =
-            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__MEDIA_KEY;
-    public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_BROWSER =
-            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_BROWSER;
-    public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_EMAIL =
-            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_EMAIL;
-    public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CONTACTS =
-            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_CONTACTS;
-    public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALENDAR =
-            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_CALENDAR;
-    public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALCULATOR =
-            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_CALCULATOR;
-    public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MUSIC =
-            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_MUSIC;
-    public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MAPS =
-            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_MAPS;
-    public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MESSAGING =
-            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_MESSAGING;
-    public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_GALLERY =
-            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_GALLERY;
-    public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_FILES =
-            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_FILES;
-    public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_WEATHER =
-            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_WEATHER;
-    public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_FITNESS =
-            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_FITNESS;
-    public static final int KEY_GESTURE_TYPE_LAUNCH_APPLICATION_BY_PACKAGE_NAME =
-            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_APPLICATION_BY_PACKAGE_NAME;
-    public static final int KEY_GESTURE_TYPE_DESKTOP_MODE =
-            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__DESKTOP_MODE;
-    public static final int KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION =
-            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__MULTI_WINDOW_NAVIGATION;
+    public static final int FLAG_CANCELLED = 1;
 
+    // NOTE: Valid KeyGestureEvent streams:
+    //       - GESTURE_START -> GESTURE_CANCEL
+    //       - GESTURE_START -> GESTURE_COMPLETE
+    //       - GESTURE_COMPLETE
 
-
-    // Code below generated by codegen v1.0.23.
-    //
-    // DO NOT MODIFY!
-    // CHECKSTYLE:OFF Generated code
-    //
-    // To regenerate run:
-    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/hardware/input/KeyGestureEvent.java
-    //
-    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
-    //   Settings > Editor > Code Style > Formatter Control
-    //@formatter:off
-
+    /** Key gesture started (e.g. Key down of the relevant key) */
+    public static final int ACTION_GESTURE_START = 1;
+    /** Key gesture completed (e.g. Key up of the relevant key) */
+    public static final int ACTION_GESTURE_COMPLETE = 2;
 
     @IntDef(prefix = "KEY_GESTURE_TYPE_", value = {
-        KEY_GESTURE_TYPE_UNSPECIFIED,
-        KEY_GESTURE_TYPE_HOME,
-        KEY_GESTURE_TYPE_RECENT_APPS,
-        KEY_GESTURE_TYPE_BACK,
-        KEY_GESTURE_TYPE_APP_SWITCH,
-        KEY_GESTURE_TYPE_LAUNCH_ASSISTANT,
-        KEY_GESTURE_TYPE_LAUNCH_VOICE_ASSISTANT,
-        KEY_GESTURE_TYPE_LAUNCH_SYSTEM_SETTINGS,
-        KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL,
-        KEY_GESTURE_TYPE_TOGGLE_TASKBAR,
-        KEY_GESTURE_TYPE_TAKE_SCREENSHOT,
-        KEY_GESTURE_TYPE_OPEN_SHORTCUT_HELPER,
-        KEY_GESTURE_TYPE_BRIGHTNESS_UP,
-        KEY_GESTURE_TYPE_BRIGHTNESS_DOWN,
-        KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_UP,
-        KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_DOWN,
-        KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_TOGGLE,
-        KEY_GESTURE_TYPE_VOLUME_UP,
-        KEY_GESTURE_TYPE_VOLUME_DOWN,
-        KEY_GESTURE_TYPE_VOLUME_MUTE,
-        KEY_GESTURE_TYPE_ALL_APPS,
-        KEY_GESTURE_TYPE_LAUNCH_SEARCH,
-        KEY_GESTURE_TYPE_LANGUAGE_SWITCH,
-        KEY_GESTURE_TYPE_ACCESSIBILITY_ALL_APPS,
-        KEY_GESTURE_TYPE_TOGGLE_CAPS_LOCK,
-        KEY_GESTURE_TYPE_SYSTEM_MUTE,
-        KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION,
-        KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS,
-        KEY_GESTURE_TYPE_TRIGGER_BUG_REPORT,
-        KEY_GESTURE_TYPE_LOCK_SCREEN,
-        KEY_GESTURE_TYPE_OPEN_NOTES,
-        KEY_GESTURE_TYPE_TOGGLE_POWER,
-        KEY_GESTURE_TYPE_SYSTEM_NAVIGATION,
-        KEY_GESTURE_TYPE_SLEEP,
-        KEY_GESTURE_TYPE_WAKEUP,
-        KEY_GESTURE_TYPE_MEDIA_KEY,
-        KEY_GESTURE_TYPE_LAUNCH_DEFAULT_BROWSER,
-        KEY_GESTURE_TYPE_LAUNCH_DEFAULT_EMAIL,
-        KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CONTACTS,
-        KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALENDAR,
-        KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALCULATOR,
-        KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MUSIC,
-        KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MAPS,
-        KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MESSAGING,
-        KEY_GESTURE_TYPE_LAUNCH_DEFAULT_GALLERY,
-        KEY_GESTURE_TYPE_LAUNCH_DEFAULT_FILES,
-        KEY_GESTURE_TYPE_LAUNCH_DEFAULT_WEATHER,
-        KEY_GESTURE_TYPE_LAUNCH_DEFAULT_FITNESS,
-        KEY_GESTURE_TYPE_LAUNCH_APPLICATION_BY_PACKAGE_NAME,
-        KEY_GESTURE_TYPE_DESKTOP_MODE,
-        KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION
+            KEY_GESTURE_TYPE_UNSPECIFIED,
+            KEY_GESTURE_TYPE_HOME,
+            KEY_GESTURE_TYPE_RECENT_APPS,
+            KEY_GESTURE_TYPE_BACK,
+            KEY_GESTURE_TYPE_APP_SWITCH,
+            KEY_GESTURE_TYPE_LAUNCH_ASSISTANT,
+            KEY_GESTURE_TYPE_LAUNCH_VOICE_ASSISTANT,
+            KEY_GESTURE_TYPE_LAUNCH_SYSTEM_SETTINGS,
+            KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL,
+            KEY_GESTURE_TYPE_TOGGLE_TASKBAR,
+            KEY_GESTURE_TYPE_TAKE_SCREENSHOT,
+            KEY_GESTURE_TYPE_OPEN_SHORTCUT_HELPER,
+            KEY_GESTURE_TYPE_BRIGHTNESS_UP,
+            KEY_GESTURE_TYPE_BRIGHTNESS_DOWN,
+            KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_UP,
+            KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_DOWN,
+            KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_TOGGLE,
+            KEY_GESTURE_TYPE_VOLUME_UP,
+            KEY_GESTURE_TYPE_VOLUME_DOWN,
+            KEY_GESTURE_TYPE_VOLUME_MUTE,
+            KEY_GESTURE_TYPE_ALL_APPS,
+            KEY_GESTURE_TYPE_LAUNCH_SEARCH,
+            KEY_GESTURE_TYPE_LANGUAGE_SWITCH,
+            KEY_GESTURE_TYPE_ACCESSIBILITY_ALL_APPS,
+            KEY_GESTURE_TYPE_TOGGLE_CAPS_LOCK,
+            KEY_GESTURE_TYPE_SYSTEM_MUTE,
+            KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION,
+            KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS,
+            KEY_GESTURE_TYPE_TRIGGER_BUG_REPORT,
+            KEY_GESTURE_TYPE_LOCK_SCREEN,
+            KEY_GESTURE_TYPE_OPEN_NOTES,
+            KEY_GESTURE_TYPE_TOGGLE_POWER,
+            KEY_GESTURE_TYPE_SYSTEM_NAVIGATION,
+            KEY_GESTURE_TYPE_SLEEP,
+            KEY_GESTURE_TYPE_WAKEUP,
+            KEY_GESTURE_TYPE_MEDIA_KEY,
+            KEY_GESTURE_TYPE_LAUNCH_DEFAULT_BROWSER,
+            KEY_GESTURE_TYPE_LAUNCH_DEFAULT_EMAIL,
+            KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CONTACTS,
+            KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALENDAR,
+            KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALCULATOR,
+            KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MUSIC,
+            KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MAPS,
+            KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MESSAGING,
+            KEY_GESTURE_TYPE_LAUNCH_DEFAULT_GALLERY,
+            KEY_GESTURE_TYPE_LAUNCH_DEFAULT_FILES,
+            KEY_GESTURE_TYPE_LAUNCH_DEFAULT_WEATHER,
+            KEY_GESTURE_TYPE_LAUNCH_DEFAULT_FITNESS,
+            KEY_GESTURE_TYPE_LAUNCH_APPLICATION_BY_PACKAGE_NAME,
+            KEY_GESTURE_TYPE_DESKTOP_MODE,
+            KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION,
     })
     @Retention(RetentionPolicy.SOURCE)
-    @DataClass.Generated.Member
-    public @interface KeyGestureType {}
+    public @interface KeyGestureType {
+    }
 
-    @DataClass.Generated.Member
-    public static String keyGestureTypeToString(@KeyGestureType int value) {
-        switch (value) {
-            case KEY_GESTURE_TYPE_UNSPECIFIED:
-                    return "KEY_GESTURE_TYPE_UNSPECIFIED";
-            case KEY_GESTURE_TYPE_HOME:
-                    return "KEY_GESTURE_TYPE_HOME";
-            case KEY_GESTURE_TYPE_RECENT_APPS:
-                    return "KEY_GESTURE_TYPE_RECENT_APPS";
-            case KEY_GESTURE_TYPE_BACK:
-                    return "KEY_GESTURE_TYPE_BACK";
-            case KEY_GESTURE_TYPE_APP_SWITCH:
-                    return "KEY_GESTURE_TYPE_APP_SWITCH";
-            case KEY_GESTURE_TYPE_LAUNCH_ASSISTANT:
-                    return "KEY_GESTURE_TYPE_LAUNCH_ASSISTANT";
-            case KEY_GESTURE_TYPE_LAUNCH_VOICE_ASSISTANT:
-                    return "KEY_GESTURE_TYPE_LAUNCH_VOICE_ASSISTANT";
-            case KEY_GESTURE_TYPE_LAUNCH_SYSTEM_SETTINGS:
-                    return "KEY_GESTURE_TYPE_LAUNCH_SYSTEM_SETTINGS";
-            case KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL:
-                    return "KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL";
-            case KEY_GESTURE_TYPE_TOGGLE_TASKBAR:
-                    return "KEY_GESTURE_TYPE_TOGGLE_TASKBAR";
-            case KEY_GESTURE_TYPE_TAKE_SCREENSHOT:
-                    return "KEY_GESTURE_TYPE_TAKE_SCREENSHOT";
-            case KEY_GESTURE_TYPE_OPEN_SHORTCUT_HELPER:
-                    return "KEY_GESTURE_TYPE_OPEN_SHORTCUT_HELPER";
-            case KEY_GESTURE_TYPE_BRIGHTNESS_UP:
-                    return "KEY_GESTURE_TYPE_BRIGHTNESS_UP";
-            case KEY_GESTURE_TYPE_BRIGHTNESS_DOWN:
-                    return "KEY_GESTURE_TYPE_BRIGHTNESS_DOWN";
-            case KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_UP:
-                    return "KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_UP";
-            case KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_DOWN:
-                    return "KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_DOWN";
-            case KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_TOGGLE:
-                    return "KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_TOGGLE";
-            case KEY_GESTURE_TYPE_VOLUME_UP:
-                    return "KEY_GESTURE_TYPE_VOLUME_UP";
-            case KEY_GESTURE_TYPE_VOLUME_DOWN:
-                    return "KEY_GESTURE_TYPE_VOLUME_DOWN";
-            case KEY_GESTURE_TYPE_VOLUME_MUTE:
-                    return "KEY_GESTURE_TYPE_VOLUME_MUTE";
-            case KEY_GESTURE_TYPE_ALL_APPS:
-                    return "KEY_GESTURE_TYPE_ALL_APPS";
-            case KEY_GESTURE_TYPE_LAUNCH_SEARCH:
-                    return "KEY_GESTURE_TYPE_LAUNCH_SEARCH";
-            case KEY_GESTURE_TYPE_LANGUAGE_SWITCH:
-                    return "KEY_GESTURE_TYPE_LANGUAGE_SWITCH";
-            case KEY_GESTURE_TYPE_ACCESSIBILITY_ALL_APPS:
-                    return "KEY_GESTURE_TYPE_ACCESSIBILITY_ALL_APPS";
-            case KEY_GESTURE_TYPE_TOGGLE_CAPS_LOCK:
-                    return "KEY_GESTURE_TYPE_TOGGLE_CAPS_LOCK";
-            case KEY_GESTURE_TYPE_SYSTEM_MUTE:
-                    return "KEY_GESTURE_TYPE_SYSTEM_MUTE";
-            case KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION:
-                    return "KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION";
-            case KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS:
-                    return "KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS";
-            case KEY_GESTURE_TYPE_TRIGGER_BUG_REPORT:
-                    return "KEY_GESTURE_TYPE_TRIGGER_BUG_REPORT";
-            case KEY_GESTURE_TYPE_LOCK_SCREEN:
-                    return "KEY_GESTURE_TYPE_LOCK_SCREEN";
-            case KEY_GESTURE_TYPE_OPEN_NOTES:
-                    return "KEY_GESTURE_TYPE_OPEN_NOTES";
-            case KEY_GESTURE_TYPE_TOGGLE_POWER:
-                    return "KEY_GESTURE_TYPE_TOGGLE_POWER";
-            case KEY_GESTURE_TYPE_SYSTEM_NAVIGATION:
-                    return "KEY_GESTURE_TYPE_SYSTEM_NAVIGATION";
-            case KEY_GESTURE_TYPE_SLEEP:
-                    return "KEY_GESTURE_TYPE_SLEEP";
-            case KEY_GESTURE_TYPE_WAKEUP:
-                    return "KEY_GESTURE_TYPE_WAKEUP";
-            case KEY_GESTURE_TYPE_MEDIA_KEY:
-                    return "KEY_GESTURE_TYPE_MEDIA_KEY";
-            case KEY_GESTURE_TYPE_LAUNCH_DEFAULT_BROWSER:
-                    return "KEY_GESTURE_TYPE_LAUNCH_DEFAULT_BROWSER";
-            case KEY_GESTURE_TYPE_LAUNCH_DEFAULT_EMAIL:
-                    return "KEY_GESTURE_TYPE_LAUNCH_DEFAULT_EMAIL";
-            case KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CONTACTS:
-                    return "KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CONTACTS";
-            case KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALENDAR:
-                    return "KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALENDAR";
-            case KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALCULATOR:
-                    return "KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALCULATOR";
-            case KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MUSIC:
-                    return "KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MUSIC";
-            case KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MAPS:
-                    return "KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MAPS";
-            case KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MESSAGING:
-                    return "KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MESSAGING";
-            case KEY_GESTURE_TYPE_LAUNCH_DEFAULT_GALLERY:
-                    return "KEY_GESTURE_TYPE_LAUNCH_DEFAULT_GALLERY";
-            case KEY_GESTURE_TYPE_LAUNCH_DEFAULT_FILES:
-                    return "KEY_GESTURE_TYPE_LAUNCH_DEFAULT_FILES";
-            case KEY_GESTURE_TYPE_LAUNCH_DEFAULT_WEATHER:
-                    return "KEY_GESTURE_TYPE_LAUNCH_DEFAULT_WEATHER";
-            case KEY_GESTURE_TYPE_LAUNCH_DEFAULT_FITNESS:
-                    return "KEY_GESTURE_TYPE_LAUNCH_DEFAULT_FITNESS";
-            case KEY_GESTURE_TYPE_LAUNCH_APPLICATION_BY_PACKAGE_NAME:
-                    return "KEY_GESTURE_TYPE_LAUNCH_APPLICATION_BY_PACKAGE_NAME";
-            case KEY_GESTURE_TYPE_DESKTOP_MODE:
-                    return "KEY_GESTURE_TYPE_DESKTOP_MODE";
-            case KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION:
-                    return "KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION";
-            default: return Integer.toHexString(value);
+    public KeyGestureEvent(@NonNull AidlKeyGestureEvent keyGestureEvent) {
+        this.mKeyGestureEvent = keyGestureEvent;
+    }
+
+    /**
+     * Key gesture event builder used to create a KeyGestureEvent for tests in Java.
+     *
+     * @hide
+     */
+    public static class Builder {
+        private int mDeviceId = KeyCharacterMap.VIRTUAL_KEYBOARD;
+        private int[] mKeycodes = new int[0];
+        private int mModifierState = 0;
+        @KeyGestureType
+        private int mKeyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_UNSPECIFIED;
+        private int mAction = KeyGestureEvent.ACTION_GESTURE_COMPLETE;
+        private int mDisplayId = Display.DEFAULT_DISPLAY;
+        private int mFlags = 0;
+
+        /**
+         * @see KeyGestureEvent#getDeviceId()
+         */
+        public Builder setDeviceId(int deviceId) {
+            mDeviceId = deviceId;
+            return this;
+        }
+
+        /**
+         * @see KeyGestureEvent#getKeycodes()
+         */
+        public Builder setKeycodes(@NonNull int[] keycodes) {
+            mKeycodes = keycodes;
+            return this;
+        }
+
+        /**
+         * @see KeyGestureEvent#getModifierState()
+         */
+        public Builder setModifierState(int modifierState) {
+            mModifierState = modifierState;
+            return this;
+        }
+
+        /**
+         * @see KeyGestureEvent#getKeyGestureType()
+         */
+        public Builder setKeyGestureType(@KeyGestureEvent.KeyGestureType int keyGestureType) {
+            mKeyGestureType = keyGestureType;
+            return this;
+        }
+
+        /**
+         * @see KeyGestureEvent#getAction()
+         */
+        public Builder setAction(int action) {
+            mAction = action;
+            return this;
+        }
+
+        /**
+         * @see KeyGestureEvent#getDisplayId()
+         */
+        public Builder setDisplayId(int displayId) {
+            mDisplayId = displayId;
+            return this;
+        }
+
+        /**
+         * @see KeyGestureEvent#getFlags()
+         */
+        public Builder setFlags(int flags) {
+            mFlags = flags;
+            return this;
+        }
+
+        /**
+         * Build {@link KeyGestureEvent}
+         */
+        public KeyGestureEvent build() {
+            AidlKeyGestureEvent event = new AidlKeyGestureEvent();
+            event.deviceId = mDeviceId;
+            event.keycodes = mKeycodes;
+            event.modifierState = mModifierState;
+            event.gestureType = mKeyGestureType;
+            event.action = mAction;
+            event.displayId = mDisplayId;
+            event.flags = mFlags;
+            return new KeyGestureEvent(event);
         }
     }
 
-    @DataClass.Generated.Member
-    public KeyGestureEvent(
-            int deviceId,
-            @NonNull int[] keycodes,
-            int modifierState,
-            @KeyGestureType int keyGestureType) {
-        this.mDeviceId = deviceId;
-        this.mKeycodes = keycodes;
-        com.android.internal.util.AnnotationValidations.validate(
-                NonNull.class, null, mKeycodes);
-        this.mModifierState = modifierState;
-        this.mKeyGestureType = keyGestureType;
-
-        if (!(mKeyGestureType == KEY_GESTURE_TYPE_UNSPECIFIED)
-                && !(mKeyGestureType == KEY_GESTURE_TYPE_HOME)
-                && !(mKeyGestureType == KEY_GESTURE_TYPE_RECENT_APPS)
-                && !(mKeyGestureType == KEY_GESTURE_TYPE_BACK)
-                && !(mKeyGestureType == KEY_GESTURE_TYPE_APP_SWITCH)
-                && !(mKeyGestureType == KEY_GESTURE_TYPE_LAUNCH_ASSISTANT)
-                && !(mKeyGestureType == KEY_GESTURE_TYPE_LAUNCH_VOICE_ASSISTANT)
-                && !(mKeyGestureType == KEY_GESTURE_TYPE_LAUNCH_SYSTEM_SETTINGS)
-                && !(mKeyGestureType == KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL)
-                && !(mKeyGestureType == KEY_GESTURE_TYPE_TOGGLE_TASKBAR)
-                && !(mKeyGestureType == KEY_GESTURE_TYPE_TAKE_SCREENSHOT)
-                && !(mKeyGestureType == KEY_GESTURE_TYPE_OPEN_SHORTCUT_HELPER)
-                && !(mKeyGestureType == KEY_GESTURE_TYPE_BRIGHTNESS_UP)
-                && !(mKeyGestureType == KEY_GESTURE_TYPE_BRIGHTNESS_DOWN)
-                && !(mKeyGestureType == KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_UP)
-                && !(mKeyGestureType == KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_DOWN)
-                && !(mKeyGestureType == KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_TOGGLE)
-                && !(mKeyGestureType == KEY_GESTURE_TYPE_VOLUME_UP)
-                && !(mKeyGestureType == KEY_GESTURE_TYPE_VOLUME_DOWN)
-                && !(mKeyGestureType == KEY_GESTURE_TYPE_VOLUME_MUTE)
-                && !(mKeyGestureType == KEY_GESTURE_TYPE_ALL_APPS)
-                && !(mKeyGestureType == KEY_GESTURE_TYPE_LAUNCH_SEARCH)
-                && !(mKeyGestureType == KEY_GESTURE_TYPE_LANGUAGE_SWITCH)
-                && !(mKeyGestureType == KEY_GESTURE_TYPE_ACCESSIBILITY_ALL_APPS)
-                && !(mKeyGestureType == KEY_GESTURE_TYPE_TOGGLE_CAPS_LOCK)
-                && !(mKeyGestureType == KEY_GESTURE_TYPE_SYSTEM_MUTE)
-                && !(mKeyGestureType == KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION)
-                && !(mKeyGestureType == KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS)
-                && !(mKeyGestureType == KEY_GESTURE_TYPE_TRIGGER_BUG_REPORT)
-                && !(mKeyGestureType == KEY_GESTURE_TYPE_LOCK_SCREEN)
-                && !(mKeyGestureType == KEY_GESTURE_TYPE_OPEN_NOTES)
-                && !(mKeyGestureType == KEY_GESTURE_TYPE_TOGGLE_POWER)
-                && !(mKeyGestureType == KEY_GESTURE_TYPE_SYSTEM_NAVIGATION)
-                && !(mKeyGestureType == KEY_GESTURE_TYPE_SLEEP)
-                && !(mKeyGestureType == KEY_GESTURE_TYPE_WAKEUP)
-                && !(mKeyGestureType == KEY_GESTURE_TYPE_MEDIA_KEY)
-                && !(mKeyGestureType == KEY_GESTURE_TYPE_LAUNCH_DEFAULT_BROWSER)
-                && !(mKeyGestureType == KEY_GESTURE_TYPE_LAUNCH_DEFAULT_EMAIL)
-                && !(mKeyGestureType == KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CONTACTS)
-                && !(mKeyGestureType == KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALENDAR)
-                && !(mKeyGestureType == KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALCULATOR)
-                && !(mKeyGestureType == KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MUSIC)
-                && !(mKeyGestureType == KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MAPS)
-                && !(mKeyGestureType == KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MESSAGING)
-                && !(mKeyGestureType == KEY_GESTURE_TYPE_LAUNCH_DEFAULT_GALLERY)
-                && !(mKeyGestureType == KEY_GESTURE_TYPE_LAUNCH_DEFAULT_FILES)
-                && !(mKeyGestureType == KEY_GESTURE_TYPE_LAUNCH_DEFAULT_WEATHER)
-                && !(mKeyGestureType == KEY_GESTURE_TYPE_LAUNCH_DEFAULT_FITNESS)
-                && !(mKeyGestureType == KEY_GESTURE_TYPE_LAUNCH_APPLICATION_BY_PACKAGE_NAME)
-                && !(mKeyGestureType == KEY_GESTURE_TYPE_DESKTOP_MODE)
-                && !(mKeyGestureType == KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION)) {
-            throw new java.lang.IllegalArgumentException(
-                    "keyGestureType was " + mKeyGestureType + " but must be one of: "
-                            + "KEY_GESTURE_TYPE_UNSPECIFIED(" + KEY_GESTURE_TYPE_UNSPECIFIED + "), "
-                            + "KEY_GESTURE_TYPE_HOME(" + KEY_GESTURE_TYPE_HOME + "), "
-                            + "KEY_GESTURE_TYPE_RECENT_APPS(" + KEY_GESTURE_TYPE_RECENT_APPS + "), "
-                            + "KEY_GESTURE_TYPE_BACK(" + KEY_GESTURE_TYPE_BACK + "), "
-                            + "KEY_GESTURE_TYPE_APP_SWITCH(" + KEY_GESTURE_TYPE_APP_SWITCH + "), "
-                            + "KEY_GESTURE_TYPE_LAUNCH_ASSISTANT(" + KEY_GESTURE_TYPE_LAUNCH_ASSISTANT + "), "
-                            + "KEY_GESTURE_TYPE_LAUNCH_VOICE_ASSISTANT(" + KEY_GESTURE_TYPE_LAUNCH_VOICE_ASSISTANT + "), "
-                            + "KEY_GESTURE_TYPE_LAUNCH_SYSTEM_SETTINGS(" + KEY_GESTURE_TYPE_LAUNCH_SYSTEM_SETTINGS + "), "
-                            + "KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL(" + KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL + "), "
-                            + "KEY_GESTURE_TYPE_TOGGLE_TASKBAR(" + KEY_GESTURE_TYPE_TOGGLE_TASKBAR + "), "
-                            + "KEY_GESTURE_TYPE_TAKE_SCREENSHOT(" + KEY_GESTURE_TYPE_TAKE_SCREENSHOT + "), "
-                            + "KEY_GESTURE_TYPE_OPEN_SHORTCUT_HELPER(" + KEY_GESTURE_TYPE_OPEN_SHORTCUT_HELPER + "), "
-                            + "KEY_GESTURE_TYPE_BRIGHTNESS_UP(" + KEY_GESTURE_TYPE_BRIGHTNESS_UP + "), "
-                            + "KEY_GESTURE_TYPE_BRIGHTNESS_DOWN(" + KEY_GESTURE_TYPE_BRIGHTNESS_DOWN + "), "
-                            + "KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_UP(" + KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_UP + "), "
-                            + "KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_DOWN(" + KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_DOWN + "), "
-                            + "KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_TOGGLE(" + KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_TOGGLE + "), "
-                            + "KEY_GESTURE_TYPE_VOLUME_UP(" + KEY_GESTURE_TYPE_VOLUME_UP + "), "
-                            + "KEY_GESTURE_TYPE_VOLUME_DOWN(" + KEY_GESTURE_TYPE_VOLUME_DOWN + "), "
-                            + "KEY_GESTURE_TYPE_VOLUME_MUTE(" + KEY_GESTURE_TYPE_VOLUME_MUTE + "), "
-                            + "KEY_GESTURE_TYPE_ALL_APPS(" + KEY_GESTURE_TYPE_ALL_APPS + "), "
-                            + "KEY_GESTURE_TYPE_LAUNCH_SEARCH(" + KEY_GESTURE_TYPE_LAUNCH_SEARCH + "), "
-                            + "KEY_GESTURE_TYPE_LANGUAGE_SWITCH(" + KEY_GESTURE_TYPE_LANGUAGE_SWITCH + "), "
-                            + "KEY_GESTURE_TYPE_ACCESSIBILITY_ALL_APPS(" + KEY_GESTURE_TYPE_ACCESSIBILITY_ALL_APPS + "), "
-                            + "KEY_GESTURE_TYPE_TOGGLE_CAPS_LOCK(" + KEY_GESTURE_TYPE_TOGGLE_CAPS_LOCK + "), "
-                            + "KEY_GESTURE_TYPE_SYSTEM_MUTE(" + KEY_GESTURE_TYPE_SYSTEM_MUTE + "), "
-                            + "KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION(" + KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION + "), "
-                            + "KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS(" + KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS + "), "
-                            + "KEY_GESTURE_TYPE_TRIGGER_BUG_REPORT(" + KEY_GESTURE_TYPE_TRIGGER_BUG_REPORT + "), "
-                            + "KEY_GESTURE_TYPE_LOCK_SCREEN(" + KEY_GESTURE_TYPE_LOCK_SCREEN + "), "
-                            + "KEY_GESTURE_TYPE_OPEN_NOTES(" + KEY_GESTURE_TYPE_OPEN_NOTES + "), "
-                            + "KEY_GESTURE_TYPE_TOGGLE_POWER(" + KEY_GESTURE_TYPE_TOGGLE_POWER + "), "
-                            + "KEY_GESTURE_TYPE_SYSTEM_NAVIGATION(" + KEY_GESTURE_TYPE_SYSTEM_NAVIGATION + "), "
-                            + "KEY_GESTURE_TYPE_SLEEP(" + KEY_GESTURE_TYPE_SLEEP + "), "
-                            + "KEY_GESTURE_TYPE_WAKEUP(" + KEY_GESTURE_TYPE_WAKEUP + "), "
-                            + "KEY_GESTURE_TYPE_MEDIA_KEY(" + KEY_GESTURE_TYPE_MEDIA_KEY + "), "
-                            + "KEY_GESTURE_TYPE_LAUNCH_DEFAULT_BROWSER(" + KEY_GESTURE_TYPE_LAUNCH_DEFAULT_BROWSER + "), "
-                            + "KEY_GESTURE_TYPE_LAUNCH_DEFAULT_EMAIL(" + KEY_GESTURE_TYPE_LAUNCH_DEFAULT_EMAIL + "), "
-                            + "KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CONTACTS(" + KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CONTACTS + "), "
-                            + "KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALENDAR(" + KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALENDAR + "), "
-                            + "KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALCULATOR(" + KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALCULATOR + "), "
-                            + "KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MUSIC(" + KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MUSIC + "), "
-                            + "KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MAPS(" + KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MAPS + "), "
-                            + "KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MESSAGING(" + KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MESSAGING + "), "
-                            + "KEY_GESTURE_TYPE_LAUNCH_DEFAULT_GALLERY(" + KEY_GESTURE_TYPE_LAUNCH_DEFAULT_GALLERY + "), "
-                            + "KEY_GESTURE_TYPE_LAUNCH_DEFAULT_FILES(" + KEY_GESTURE_TYPE_LAUNCH_DEFAULT_FILES + "), "
-                            + "KEY_GESTURE_TYPE_LAUNCH_DEFAULT_WEATHER(" + KEY_GESTURE_TYPE_LAUNCH_DEFAULT_WEATHER + "), "
-                            + "KEY_GESTURE_TYPE_LAUNCH_DEFAULT_FITNESS(" + KEY_GESTURE_TYPE_LAUNCH_DEFAULT_FITNESS + "), "
-                            + "KEY_GESTURE_TYPE_LAUNCH_APPLICATION_BY_PACKAGE_NAME(" + KEY_GESTURE_TYPE_LAUNCH_APPLICATION_BY_PACKAGE_NAME + "), "
-                            + "KEY_GESTURE_TYPE_DESKTOP_MODE(" + KEY_GESTURE_TYPE_DESKTOP_MODE + "), "
-                            + "KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION(" + KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION + ")");
-        }
-
-
-        // onConstructed(); // You can define this method to get a callback
-    }
-
-    @DataClass.Generated.Member
     public int getDeviceId() {
-        return mDeviceId;
+        return mKeyGestureEvent.deviceId;
     }
 
-    @DataClass.Generated.Member
     public @NonNull int[] getKeycodes() {
-        return mKeycodes;
+        return mKeyGestureEvent.keycodes;
     }
 
-    @DataClass.Generated.Member
     public int getModifierState() {
-        return mModifierState;
+        return mKeyGestureEvent.modifierState;
     }
 
-    @DataClass.Generated.Member
     public @KeyGestureType int getKeyGestureType() {
-        return mKeyGestureType;
+        return mKeyGestureEvent.gestureType;
+    }
+
+    public int getAction() {
+        return mKeyGestureEvent.action;
+    }
+
+    public int getDisplayId() {
+        return mKeyGestureEvent.displayId;
+    }
+
+    public int getFlags() {
+        return mKeyGestureEvent.flags;
+    }
+
+    public boolean isCancelled() {
+        return (mKeyGestureEvent.flags & FLAG_CANCELLED) != 0;
     }
 
     @Override
-    @DataClass.Generated.Member
     public String toString() {
-        // You can override field toString logic by defining methods like:
-        // String fieldNameToString() { ... }
-
-        return "KeyGestureEvent { " +
-                "deviceId = " + mDeviceId + ", " +
-                "keycodes = " + java.util.Arrays.toString(mKeycodes) + ", " +
-                "modifierState = " + mModifierState + ", " +
-                "keyGestureType = " + keyGestureTypeToString(mKeyGestureType) +
-        " }";
+        return "KeyGestureEvent { "
+                + "deviceId = " + mKeyGestureEvent.deviceId + ", "
+                + "keycodes = " + java.util.Arrays.toString(mKeyGestureEvent.keycodes) + ", "
+                + "modifierState = " + mKeyGestureEvent.modifierState + ", "
+                + "keyGestureType = " + keyGestureTypeToString(mKeyGestureEvent.gestureType) + ", "
+                + "action = " + mKeyGestureEvent.action + ", "
+                + "displayId = " + mKeyGestureEvent.displayId + ", "
+                + "flags = " + mKeyGestureEvent.flags
+                + " }";
     }
 
     @Override
-    @DataClass.Generated.Member
     public boolean equals(@Nullable Object o) {
-        // You can override field equality logic by defining either of the methods like:
-        // boolean fieldNameEquals(KeyGestureEvent other) { ... }
-        // boolean fieldNameEquals(FieldType otherValue) { ... }
-
         if (this == o) return true;
         if (o == null || getClass() != o.getClass()) return false;
-        @SuppressWarnings("unchecked")
         KeyGestureEvent that = (KeyGestureEvent) o;
-        //noinspection PointlessBooleanExpression
-        return true
-                && mDeviceId == that.mDeviceId
-                && java.util.Arrays.equals(mKeycodes, that.mKeycodes)
-                && mModifierState == that.mModifierState
-                && mKeyGestureType == that.mKeyGestureType;
+        return mKeyGestureEvent.deviceId == that.mKeyGestureEvent.deviceId
+                && java.util.Arrays.equals(mKeyGestureEvent.keycodes, that.mKeyGestureEvent.keycodes)
+                && mKeyGestureEvent.modifierState == that.mKeyGestureEvent.modifierState
+                && mKeyGestureEvent.gestureType == that.mKeyGestureEvent.gestureType
+                && mKeyGestureEvent.action == that.mKeyGestureEvent.action
+                && mKeyGestureEvent.displayId == that.mKeyGestureEvent.displayId
+                && mKeyGestureEvent.flags == that.mKeyGestureEvent.flags;
     }
 
     @Override
-    @DataClass.Generated.Member
     public int hashCode() {
-        // You can override field hashCode logic by defining methods like:
-        // int fieldNameHashCode() { ... }
-
         int _hash = 1;
-        _hash = 31 * _hash + mDeviceId;
-        _hash = 31 * _hash + java.util.Arrays.hashCode(mKeycodes);
-        _hash = 31 * _hash + mModifierState;
-        _hash = 31 * _hash + mKeyGestureType;
+        _hash = 31 * _hash + mKeyGestureEvent.deviceId;
+        _hash = 31 * _hash + java.util.Arrays.hashCode(mKeyGestureEvent.keycodes);
+        _hash = 31 * _hash + mKeyGestureEvent.modifierState;
+        _hash = 31 * _hash + mKeyGestureEvent.gestureType;
+        _hash = 31 * _hash + mKeyGestureEvent.action;
+        _hash = 31 * _hash + mKeyGestureEvent.displayId;
+        _hash = 31 * _hash + mKeyGestureEvent.flags;
         return _hash;
     }
 
-    @DataClass.Generated(
-            time = 1723409092192L,
-            codegenVersion = "1.0.23",
-            sourceFile = "frameworks/base/core/java/android/hardware/input/KeyGestureEvent.java",
-            inputSignatures = "private final  int mDeviceId\nprivate final @android.annotation.NonNull int[] mKeycodes\nprivate final  int mModifierState\nprivate final @android.hardware.input.KeyGestureEvent.KeyGestureType int mKeyGestureType\npublic static final  int KEY_GESTURE_TYPE_UNSPECIFIED\npublic static final  int KEY_GESTURE_TYPE_HOME\npublic static final  int KEY_GESTURE_TYPE_RECENT_APPS\npublic static final  int KEY_GESTURE_TYPE_BACK\npublic static final  int KEY_GESTURE_TYPE_APP_SWITCH\npublic static final  int KEY_GESTURE_TYPE_LAUNCH_ASSISTANT\npublic static final  int KEY_GESTURE_TYPE_LAUNCH_VOICE_ASSISTANT\npublic static final  int KEY_GESTURE_TYPE_LAUNCH_SYSTEM_SETTINGS\npublic static final  int KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL\npublic static final  int KEY_GESTURE_TYPE_TOGGLE_TASKBAR\npublic static final  int KEY_GESTURE_TYPE_TAKE_SCREENSHOT\npublic static final  int KEY_GESTURE_TYPE_OPEN_SHORTCUT_HELPER\npublic static final  int KEY_GESTURE_TYPE_BRIGHTNESS_UP\npublic static final  int KEY_GESTURE_TYPE_BRIGHTNESS_DOWN\npublic static final  int KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_UP\npublic static final  int KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_DOWN\npublic static final  int KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_TOGGLE\npublic static final  int KEY_GESTURE_TYPE_VOLUME_UP\npublic static final  int KEY_GESTURE_TYPE_VOLUME_DOWN\npublic static final  int KEY_GESTURE_TYPE_VOLUME_MUTE\npublic static final  int KEY_GESTURE_TYPE_ALL_APPS\npublic static final  int KEY_GESTURE_TYPE_LAUNCH_SEARCH\npublic static final  int KEY_GESTURE_TYPE_LANGUAGE_SWITCH\npublic static final  int KEY_GESTURE_TYPE_ACCESSIBILITY_ALL_APPS\npublic static final  int KEY_GESTURE_TYPE_TOGGLE_CAPS_LOCK\npublic static final  int KEY_GESTURE_TYPE_SYSTEM_MUTE\npublic static final  int KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION\npublic static final  int KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS\npublic static final  int KEY_GESTURE_TYPE_TRIGGER_BUG_REPORT\npublic static final  int KEY_GESTURE_TYPE_LOCK_SCREEN\npublic static final  int KEY_GESTURE_TYPE_OPEN_NOTES\npublic static final  int KEY_GESTURE_TYPE_TOGGLE_POWER\npublic static final  int KEY_GESTURE_TYPE_SYSTEM_NAVIGATION\npublic static final  int KEY_GESTURE_TYPE_SLEEP\npublic static final  int KEY_GESTURE_TYPE_WAKEUP\npublic static final  int KEY_GESTURE_TYPE_MEDIA_KEY\npublic static final  int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_BROWSER\npublic static final  int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_EMAIL\npublic static final  int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CONTACTS\npublic static final  int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALENDAR\npublic static final  int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALCULATOR\npublic static final  int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MUSIC\npublic static final  int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MAPS\npublic static final  int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MESSAGING\npublic static final  int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_GALLERY\npublic static final  int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_FILES\npublic static final  int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_WEATHER\npublic static final  int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_FITNESS\npublic static final  int KEY_GESTURE_TYPE_LAUNCH_APPLICATION_BY_PACKAGE_NAME\npublic static final  int KEY_GESTURE_TYPE_DESKTOP_MODE\npublic static final  int KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION\nclass KeyGestureEvent extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genToString=true, genEqualsHashCode=true)")
-    @Deprecated
-    private void __metadata() {}
+    /**
+     * Convert KeyGestureEvent type to corresponding log event got KeyboardSystemsEvent
+     */
+    public static int keyGestureTypeToLogEvent(@KeyGestureType int value) {
+        switch (value) {
+            case KEY_GESTURE_TYPE_HOME:
+                return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__HOME;
+            case KEY_GESTURE_TYPE_RECENT_APPS:
+                return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__RECENT_APPS;
+            case KEY_GESTURE_TYPE_BACK:
+                return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__BACK;
+            case KEY_GESTURE_TYPE_APP_SWITCH:
+                return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__APP_SWITCH;
+            case KEY_GESTURE_TYPE_LAUNCH_ASSISTANT:
+                return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_ASSISTANT;
+            case KEY_GESTURE_TYPE_LAUNCH_VOICE_ASSISTANT:
+                return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_VOICE_ASSISTANT;
+            case KEY_GESTURE_TYPE_LAUNCH_SYSTEM_SETTINGS:
+                return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_SYSTEM_SETTINGS;
+            case KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL:
+                return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__TOGGLE_NOTIFICATION_PANEL;
+            case KEY_GESTURE_TYPE_TOGGLE_TASKBAR:
+                return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__TOGGLE_TASKBAR;
+            case KEY_GESTURE_TYPE_TAKE_SCREENSHOT:
+                return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__TAKE_SCREENSHOT;
+            case KEY_GESTURE_TYPE_OPEN_SHORTCUT_HELPER:
+                return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__OPEN_SHORTCUT_HELPER;
+            case KEY_GESTURE_TYPE_BRIGHTNESS_UP:
+                return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__BRIGHTNESS_UP;
+            case KEY_GESTURE_TYPE_BRIGHTNESS_DOWN:
+                return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__BRIGHTNESS_DOWN;
+            case KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_UP:
+                return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__KEYBOARD_BACKLIGHT_UP;
+            case KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_DOWN:
+                return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__KEYBOARD_BACKLIGHT_DOWN;
+            case KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_TOGGLE:
+                return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__KEYBOARD_BACKLIGHT_TOGGLE;
+            case KEY_GESTURE_TYPE_VOLUME_UP:
+                return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__VOLUME_UP;
+            case KEY_GESTURE_TYPE_VOLUME_DOWN:
+                return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__VOLUME_DOWN;
+            case KEY_GESTURE_TYPE_VOLUME_MUTE:
+                return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__VOLUME_MUTE;
+            case KEY_GESTURE_TYPE_ALL_APPS:
+                return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__ALL_APPS;
+            case KEY_GESTURE_TYPE_LAUNCH_SEARCH:
+                return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_SEARCH;
+            case KEY_GESTURE_TYPE_LANGUAGE_SWITCH:
+                return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LANGUAGE_SWITCH;
+            case KEY_GESTURE_TYPE_ACCESSIBILITY_ALL_APPS:
+                return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__ACCESSIBILITY_ALL_APPS;
+            case KEY_GESTURE_TYPE_TOGGLE_CAPS_LOCK:
+                return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__TOGGLE_CAPS_LOCK;
+            case KEY_GESTURE_TYPE_SYSTEM_MUTE:
+                return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__SYSTEM_MUTE;
+            case KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION:
+                return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__SPLIT_SCREEN_NAVIGATION;
+            case KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS:
+                return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__CHANGE_SPLITSCREEN_FOCUS;
+            case KEY_GESTURE_TYPE_TRIGGER_BUG_REPORT:
+                return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__TRIGGER_BUG_REPORT;
+            case KEY_GESTURE_TYPE_LOCK_SCREEN:
+                return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LOCK_SCREEN;
+            case KEY_GESTURE_TYPE_OPEN_NOTES:
+                return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__OPEN_NOTES;
+            case KEY_GESTURE_TYPE_TOGGLE_POWER:
+                return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__TOGGLE_POWER;
+            case KEY_GESTURE_TYPE_SYSTEM_NAVIGATION:
+                return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__SYSTEM_NAVIGATION;
+            case KEY_GESTURE_TYPE_SLEEP:
+                return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__SLEEP;
+            case KEY_GESTURE_TYPE_WAKEUP:
+                return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__WAKEUP;
+            case KEY_GESTURE_TYPE_MEDIA_KEY:
+                return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__MEDIA_KEY;
+            case KEY_GESTURE_TYPE_LAUNCH_DEFAULT_BROWSER:
+                return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_BROWSER;
+            case KEY_GESTURE_TYPE_LAUNCH_DEFAULT_EMAIL:
+                return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_EMAIL;
+            case KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CONTACTS:
+                return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_CONTACTS;
+            case KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALENDAR:
+                return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_CALENDAR;
+            case KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALCULATOR:
+                return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_CALCULATOR;
+            case KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MUSIC:
+                return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_MUSIC;
+            case KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MAPS:
+                return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_MAPS;
+            case KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MESSAGING:
+                return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_MESSAGING;
+            case KEY_GESTURE_TYPE_LAUNCH_DEFAULT_GALLERY:
+                return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_GALLERY;
+            case KEY_GESTURE_TYPE_LAUNCH_DEFAULT_FILES:
+                return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_FILES;
+            case KEY_GESTURE_TYPE_LAUNCH_DEFAULT_WEATHER:
+                return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_WEATHER;
+            case KEY_GESTURE_TYPE_LAUNCH_DEFAULT_FITNESS:
+                return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_FITNESS;
+            case KEY_GESTURE_TYPE_LAUNCH_APPLICATION_BY_PACKAGE_NAME:
+                return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_APPLICATION_BY_PACKAGE_NAME;
+            case KEY_GESTURE_TYPE_DESKTOP_MODE:
+                return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__DESKTOP_MODE;
+            case KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION:
+                return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__MULTI_WINDOW_NAVIGATION;
+            default:
+                return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__UNSPECIFIED;
+        }
+    }
 
-
-    //@formatter:on
-    // End of generated code
-
+    private static String keyGestureTypeToString(@KeyGestureType int value) {
+        switch (value) {
+            case KEY_GESTURE_TYPE_UNSPECIFIED:
+                return "KEY_GESTURE_TYPE_UNSPECIFIED";
+            case KEY_GESTURE_TYPE_HOME:
+                return "KEY_GESTURE_TYPE_HOME";
+            case KEY_GESTURE_TYPE_RECENT_APPS:
+                return "KEY_GESTURE_TYPE_RECENT_APPS";
+            case KEY_GESTURE_TYPE_BACK:
+                return "KEY_GESTURE_TYPE_BACK";
+            case KEY_GESTURE_TYPE_APP_SWITCH:
+                return "KEY_GESTURE_TYPE_APP_SWITCH";
+            case KEY_GESTURE_TYPE_LAUNCH_ASSISTANT:
+                return "KEY_GESTURE_TYPE_LAUNCH_ASSISTANT";
+            case KEY_GESTURE_TYPE_LAUNCH_VOICE_ASSISTANT:
+                return "KEY_GESTURE_TYPE_LAUNCH_VOICE_ASSISTANT";
+            case KEY_GESTURE_TYPE_LAUNCH_SYSTEM_SETTINGS:
+                return "KEY_GESTURE_TYPE_LAUNCH_SYSTEM_SETTINGS";
+            case KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL:
+                return "KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL";
+            case KEY_GESTURE_TYPE_TOGGLE_TASKBAR:
+                return "KEY_GESTURE_TYPE_TOGGLE_TASKBAR";
+            case KEY_GESTURE_TYPE_TAKE_SCREENSHOT:
+                return "KEY_GESTURE_TYPE_TAKE_SCREENSHOT";
+            case KEY_GESTURE_TYPE_OPEN_SHORTCUT_HELPER:
+                return "KEY_GESTURE_TYPE_OPEN_SHORTCUT_HELPER";
+            case KEY_GESTURE_TYPE_BRIGHTNESS_UP:
+                return "KEY_GESTURE_TYPE_BRIGHTNESS_UP";
+            case KEY_GESTURE_TYPE_BRIGHTNESS_DOWN:
+                return "KEY_GESTURE_TYPE_BRIGHTNESS_DOWN";
+            case KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_UP:
+                return "KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_UP";
+            case KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_DOWN:
+                return "KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_DOWN";
+            case KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_TOGGLE:
+                return "KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_TOGGLE";
+            case KEY_GESTURE_TYPE_VOLUME_UP:
+                return "KEY_GESTURE_TYPE_VOLUME_UP";
+            case KEY_GESTURE_TYPE_VOLUME_DOWN:
+                return "KEY_GESTURE_TYPE_VOLUME_DOWN";
+            case KEY_GESTURE_TYPE_VOLUME_MUTE:
+                return "KEY_GESTURE_TYPE_VOLUME_MUTE";
+            case KEY_GESTURE_TYPE_ALL_APPS:
+                return "KEY_GESTURE_TYPE_ALL_APPS";
+            case KEY_GESTURE_TYPE_LAUNCH_SEARCH:
+                return "KEY_GESTURE_TYPE_LAUNCH_SEARCH";
+            case KEY_GESTURE_TYPE_LANGUAGE_SWITCH:
+                return "KEY_GESTURE_TYPE_LANGUAGE_SWITCH";
+            case KEY_GESTURE_TYPE_ACCESSIBILITY_ALL_APPS:
+                return "KEY_GESTURE_TYPE_ACCESSIBILITY_ALL_APPS";
+            case KEY_GESTURE_TYPE_TOGGLE_CAPS_LOCK:
+                return "KEY_GESTURE_TYPE_TOGGLE_CAPS_LOCK";
+            case KEY_GESTURE_TYPE_SYSTEM_MUTE:
+                return "KEY_GESTURE_TYPE_SYSTEM_MUTE";
+            case KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION:
+                return "KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION";
+            case KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS:
+                return "KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS";
+            case KEY_GESTURE_TYPE_TRIGGER_BUG_REPORT:
+                return "KEY_GESTURE_TYPE_TRIGGER_BUG_REPORT";
+            case KEY_GESTURE_TYPE_LOCK_SCREEN:
+                return "KEY_GESTURE_TYPE_LOCK_SCREEN";
+            case KEY_GESTURE_TYPE_OPEN_NOTES:
+                return "KEY_GESTURE_TYPE_OPEN_NOTES";
+            case KEY_GESTURE_TYPE_TOGGLE_POWER:
+                return "KEY_GESTURE_TYPE_TOGGLE_POWER";
+            case KEY_GESTURE_TYPE_SYSTEM_NAVIGATION:
+                return "KEY_GESTURE_TYPE_SYSTEM_NAVIGATION";
+            case KEY_GESTURE_TYPE_SLEEP:
+                return "KEY_GESTURE_TYPE_SLEEP";
+            case KEY_GESTURE_TYPE_WAKEUP:
+                return "KEY_GESTURE_TYPE_WAKEUP";
+            case KEY_GESTURE_TYPE_MEDIA_KEY:
+                return "KEY_GESTURE_TYPE_MEDIA_KEY";
+            case KEY_GESTURE_TYPE_LAUNCH_DEFAULT_BROWSER:
+                return "KEY_GESTURE_TYPE_LAUNCH_DEFAULT_BROWSER";
+            case KEY_GESTURE_TYPE_LAUNCH_DEFAULT_EMAIL:
+                return "KEY_GESTURE_TYPE_LAUNCH_DEFAULT_EMAIL";
+            case KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CONTACTS:
+                return "KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CONTACTS";
+            case KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALENDAR:
+                return "KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALENDAR";
+            case KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALCULATOR:
+                return "KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALCULATOR";
+            case KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MUSIC:
+                return "KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MUSIC";
+            case KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MAPS:
+                return "KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MAPS";
+            case KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MESSAGING:
+                return "KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MESSAGING";
+            case KEY_GESTURE_TYPE_LAUNCH_DEFAULT_GALLERY:
+                return "KEY_GESTURE_TYPE_LAUNCH_DEFAULT_GALLERY";
+            case KEY_GESTURE_TYPE_LAUNCH_DEFAULT_FILES:
+                return "KEY_GESTURE_TYPE_LAUNCH_DEFAULT_FILES";
+            case KEY_GESTURE_TYPE_LAUNCH_DEFAULT_WEATHER:
+                return "KEY_GESTURE_TYPE_LAUNCH_DEFAULT_WEATHER";
+            case KEY_GESTURE_TYPE_LAUNCH_DEFAULT_FITNESS:
+                return "KEY_GESTURE_TYPE_LAUNCH_DEFAULT_FITNESS";
+            case KEY_GESTURE_TYPE_LAUNCH_APPLICATION_BY_PACKAGE_NAME:
+                return "KEY_GESTURE_TYPE_LAUNCH_APPLICATION_BY_PACKAGE_NAME";
+            case KEY_GESTURE_TYPE_DESKTOP_MODE:
+                return "KEY_GESTURE_TYPE_DESKTOP_MODE";
+            case KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION:
+                return "KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION";
+            default:
+                return Integer.toHexString(value);
+        }
+    }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorTest.kt
index 25c5336..64915fb 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorTest.kt
@@ -261,12 +261,12 @@
                 .registerKeyGestureEventListener(any(), listenerCaptor.capture())
 
             val allAppsKeyGestureEvent =
-                KeyGestureEvent(
-                    /* deviceId= */ 1,
-                    IntArray(0),
-                    KeyEvent.META_META_ON,
-                    KeyGestureEvent.KEY_GESTURE_TYPE_ALL_APPS
-                )
+                KeyGestureEvent.Builder()
+                    .setDeviceId(1)
+                    .setModifierState(KeyEvent.META_META_ON)
+                    .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_ALL_APPS)
+                    .setAction(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+                    .build()
             listenerCaptor.value.onKeyGestureEvent(allAppsKeyGestureEvent)
 
             val model by
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index dfcea9f..ca8ae6e 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -52,6 +52,7 @@
 import android.hardware.input.IInputManager;
 import android.hardware.input.IInputSensorEventListener;
 import android.hardware.input.IKeyGestureEventListener;
+import android.hardware.input.IKeyGestureHandler;
 import android.hardware.input.IKeyboardBacklightListener;
 import android.hardware.input.IStickyModifierStateListener;
 import android.hardware.input.ITabletModeChangedListener;
@@ -164,7 +165,6 @@
     private static final int MSG_DELIVER_INPUT_DEVICES_CHANGED = 1;
     private static final int MSG_RELOAD_DEVICE_ALIASES = 2;
     private static final int MSG_DELIVER_TABLET_MODE_CHANGED = 3;
-    private static final int MSG_KEY_GESTURE_COMPLETED = 4;
 
     private static final int DEFAULT_VIBRATION_MAGNITUDE = 192;
     private static final AdditionalDisplayInputProperties
@@ -476,7 +476,7 @@
                         injector.getLooper(), injector.getUEventManager())
                 : new KeyboardBacklightControllerInterface() {};
         mStickyModifierStateController = new StickyModifierStateController();
-        mKeyGestureController = new KeyGestureController();
+        mKeyGestureController = new KeyGestureController(mContext, injector.getLooper());
         mKeyboardLedController = new KeyboardLedController(mContext, injector.getLooper(),
                 mNative);
         mKeyRemapper = new KeyRemapper(mContext, mNative, mDataStore, injector.getLooper());
@@ -2458,6 +2458,11 @@
     // Native callback.
     @SuppressWarnings("unused")
     private long interceptKeyBeforeDispatching(IBinder focus, KeyEvent event, int policyFlags) {
+        // TODO(b/358569822): Move shortcut trigger logic from PWM to KeyGestureController
+        long value = mKeyGestureController.interceptKeyBeforeDispatching(focus, event, policyFlags);
+        if (value != 0) { // If key is consumed (i.e. non-zero value)
+            return value;
+        }
         return mWindowManagerCallbacks.interceptKeyBeforeDispatching(focus, event, policyFlags);
     }
 
@@ -2763,33 +2768,38 @@
 
     @Override
     @PermissionManuallyEnforced
-    public void registerKeyGestureEventListener(
-            @NonNull IKeyGestureEventListener listener) {
+    public void registerKeyGestureEventListener(@NonNull IKeyGestureEventListener listener) {
         enforceManageKeyGesturePermission();
 
         Objects.requireNonNull(listener);
-        mKeyGestureController.registerKeyGestureEventListener(listener,
-                Binder.getCallingPid());
+        mKeyGestureController.registerKeyGestureEventListener(listener, Binder.getCallingPid());
     }
 
     @Override
     @PermissionManuallyEnforced
-    public void unregisterKeyGestureEventListener(
-            @NonNull IKeyGestureEventListener listener) {
+    public void unregisterKeyGestureEventListener(@NonNull IKeyGestureEventListener listener) {
         enforceManageKeyGesturePermission();
 
         Objects.requireNonNull(listener);
-        mKeyGestureController.unregisterKeyGestureEventListener(listener,
-                Binder.getCallingPid());
+        mKeyGestureController.unregisterKeyGestureEventListener(listener, Binder.getCallingPid());
     }
 
-    private void handleKeyGestureCompleted(KeyGestureEvent event) {
-        InputDevice device = getInputDevice(event.getDeviceId());
-        if (device == null || device.isVirtual()) {
-            return;
-        }
-        KeyboardMetricsCollector.logKeyboardSystemsEventReportedAtom(device, event);
-        mKeyGestureController.onKeyGestureEvent(event);
+    @Override
+    @PermissionManuallyEnforced
+    public void registerKeyGestureHandler(@NonNull IKeyGestureHandler handler) {
+        enforceManageKeyGesturePermission();
+
+        Objects.requireNonNull(handler);
+        mKeyGestureController.registerKeyGestureHandler(handler, Binder.getCallingPid());
+    }
+
+    @Override
+    @PermissionManuallyEnforced
+    public void unregisterKeyGestureHandler(@NonNull IKeyGestureHandler handler) {
+        enforceManageKeyGesturePermission();
+
+        Objects.requireNonNull(handler);
+        mKeyGestureController.unregisterKeyGestureHandler(handler, Binder.getCallingPid());
     }
 
     /**
@@ -2960,9 +2970,6 @@
                     boolean inTabletMode = (boolean) args.arg1;
                     deliverTabletModeChanged(whenNanos, inTabletMode);
                     break;
-                case MSG_KEY_GESTURE_COMPLETED:
-                    KeyGestureEvent event = (KeyGestureEvent) msg.obj;
-                    handleKeyGestureCompleted(event);
             }
         }
     }
@@ -3292,9 +3299,8 @@
         @Override
         public void notifyKeyGestureCompleted(int deviceId, int[] keycodes, int modifierState,
                 @KeyGestureEvent.KeyGestureType int gestureType) {
-            mHandler.obtainMessage(MSG_KEY_GESTURE_COMPLETED,
-                    new KeyGestureEvent(deviceId, keycodes, modifierState,
-                            gestureType)).sendToTarget();
+            mKeyGestureController.notifyKeyGestureCompleted(deviceId, keycodes, modifierState,
+                    gestureType);
         }
     }
 
diff --git a/services/core/java/com/android/server/input/KeyGestureController.java b/services/core/java/com/android/server/input/KeyGestureController.java
index 674d3c4..bfdb1c1 100644
--- a/services/core/java/com/android/server/input/KeyGestureController.java
+++ b/services/core/java/com/android/server/input/KeyGestureController.java
@@ -17,15 +17,32 @@
 package com.android.server.input;
 
 import android.annotation.BinderThread;
+import android.annotation.MainThread;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.hardware.input.AidlKeyGestureEvent;
 import android.hardware.input.IKeyGestureEventListener;
+import android.hardware.input.IKeyGestureHandler;
+import android.hardware.input.InputManager;
 import android.hardware.input.KeyGestureEvent;
+import android.os.Handler;
 import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Process;
 import android.os.RemoteException;
 import android.util.Log;
 import android.util.Slog;
 import android.util.SparseArray;
+import android.view.Display;
+import android.view.InputDevice;
+import android.view.KeyEvent;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.Objects;
+import java.util.TreeMap;
 
 /**
  * A thread-safe component of {@link InputManagerService} responsible for managing callbacks when a
@@ -39,12 +56,101 @@
     // 'adb shell setprop log.tag.KeyGestureController DEBUG' (requires restart)
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
+    private static final int MSG_NOTIFY_KEY_GESTURE_EVENT = 1;
+
+    private final Context mContext;
+    private final Handler mHandler;
+    private final int mSystemPid;
+
     // List of currently registered key gesture event listeners keyed by process pid
     @GuardedBy("mKeyGestureEventListenerRecords")
     private final SparseArray<KeyGestureEventListenerRecord>
             mKeyGestureEventListenerRecords = new SparseArray<>();
 
-    public void onKeyGestureEvent(KeyGestureEvent event) {
+    // List of currently registered key gesture event handler keyed by process pid. The map sorts
+    // in the order of preference of the handlers, and we prioritize handlers in system server
+    // over external handlers..
+    @GuardedBy("mKeyGestureHandlerRecords")
+    private final TreeMap<Integer, KeyGestureHandlerRecord> mKeyGestureHandlerRecords;
+
+    KeyGestureController(Context context, Looper looper) {
+        mContext = context;
+        mHandler = new Handler(looper, this::handleMessage);
+        mSystemPid = Process.myPid();
+        mKeyGestureHandlerRecords = new TreeMap<>((p1, p2) -> {
+            if (Objects.equals(p1, p2)) {
+                return 0;
+            }
+            if (p1 == mSystemPid) {
+                return -1;
+            } else if (p2 == mSystemPid) {
+                return 1;
+            } else {
+                return Integer.compare(p1, p2);
+            }
+        });
+    }
+
+    public int interceptKeyBeforeDispatching(IBinder focus, KeyEvent event, int policyFlags) {
+        // TODO(b/358569822): Handle shortcuts trigger logic here and pass it to appropriate
+        //  KeyGestureHandler (PWM is one of the handlers)
+        return 0;
+    }
+
+    @VisibleForTesting
+    boolean handleKeyGesture(int deviceId, int[] keycodes, int modifierState,
+            @KeyGestureEvent.KeyGestureType int gestureType, int action, int displayId,
+            IBinder focusedToken, int flags) {
+        AidlKeyGestureEvent event = createKeyGestureEvent(deviceId, keycodes,
+                modifierState, gestureType, action, displayId, flags);
+        synchronized (mKeyGestureHandlerRecords) {
+            for (KeyGestureHandlerRecord handler : mKeyGestureHandlerRecords.values()) {
+                if (handler.handleKeyGesture(event, focusedToken)) {
+                    Message msg = Message.obtain(mHandler, MSG_NOTIFY_KEY_GESTURE_EVENT, event);
+                    mHandler.sendMessage(msg);
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    private boolean isKeyGestureSupported(@KeyGestureEvent.KeyGestureType int gestureType) {
+        synchronized (mKeyGestureHandlerRecords) {
+            for (KeyGestureHandlerRecord handler : mKeyGestureHandlerRecords.values()) {
+                if (handler.isKeyGestureSupported(gestureType)) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    public void notifyKeyGestureCompleted(int deviceId, int[] keycodes, int modifierState,
+            @KeyGestureEvent.KeyGestureType int gestureType) {
+        // TODO(b/358569822): Once we move the gesture detection logic to IMS, we ideally
+        //  should not rely on PWM to tell us about the gesture start and end.
+        AidlKeyGestureEvent event = createKeyGestureEvent(deviceId, keycodes, modifierState,
+                gestureType, KeyGestureEvent.ACTION_GESTURE_COMPLETE, Display.DEFAULT_DISPLAY, 0);
+        mHandler.obtainMessage(MSG_NOTIFY_KEY_GESTURE_EVENT, event).sendToTarget();
+    }
+
+    @MainThread
+    private void notifyKeyGestureEvent(AidlKeyGestureEvent event) {
+        InputDevice device = getInputDevice(event.deviceId);
+        if (device == null || device.isVirtual()) {
+            return;
+        }
+        if (event.action == KeyGestureEvent.ACTION_GESTURE_COMPLETE) {
+            KeyboardMetricsCollector.logKeyboardSystemsEventReportedAtom(device, event.keycodes,
+                    event.modifierState,
+                    KeyGestureEvent.keyGestureTypeToLogEvent(event.gestureType));
+        }
+        notifyAllListeners(event);
+    }
+
+    @MainThread
+    private void notifyAllListeners(AidlKeyGestureEvent event) {
         if (DEBUG) {
             Slog.d(TAG, "Key gesture event occurred, event = " + event);
         }
@@ -56,17 +162,26 @@
         }
     }
 
+    @MainThread
+    private boolean handleMessage(Message msg) {
+        switch (msg.what) {
+            case MSG_NOTIFY_KEY_GESTURE_EVENT:
+                AidlKeyGestureEvent event = (AidlKeyGestureEvent) msg.obj;
+                notifyKeyGestureEvent(event);
+                break;
+        }
+        return true;
+    }
+
     /** Register the key gesture event listener for a process. */
     @BinderThread
-    public void registerKeyGestureEventListener(IKeyGestureEventListener listener,
-            int pid) {
+    public void registerKeyGestureEventListener(IKeyGestureEventListener listener, int pid) {
         synchronized (mKeyGestureEventListenerRecords) {
             if (mKeyGestureEventListenerRecords.get(pid) != null) {
                 throw new IllegalStateException("The calling process has already registered "
                         + "a KeyGestureEventListener.");
             }
-            KeyGestureEventListenerRecord record = new KeyGestureEventListenerRecord(
-                    pid, listener);
+            KeyGestureEventListenerRecord record = new KeyGestureEventListenerRecord(pid, listener);
             try {
                 listener.asBinder().linkToDeath(record, 0);
             } catch (RemoteException ex) {
@@ -78,8 +193,7 @@
 
     /** Unregister the key gesture event listener for a process. */
     @BinderThread
-    public void unregisterKeyGestureEventListener(IKeyGestureEventListener listener,
-            int pid) {
+    public void unregisterKeyGestureEventListener(IKeyGestureEventListener listener, int pid) {
         synchronized (mKeyGestureEventListenerRecords) {
             KeyGestureEventListenerRecord record =
                     mKeyGestureEventListenerRecords.get(pid);
@@ -120,10 +234,9 @@
             onKeyGestureEventListenerDied(mPid);
         }
 
-        public void onKeyGestureEvent(KeyGestureEvent event) {
+        public void onKeyGestureEvent(AidlKeyGestureEvent event) {
             try {
-                mListener.onKeyGestureEvent(event.getDeviceId(), event.getKeycodes(),
-                        event.getModifierState(), event.getKeyGestureType());
+                mListener.onKeyGestureEvent(event);
             } catch (RemoteException ex) {
                 Slog.w(TAG, "Failed to notify process " + mPid
                         + " that key gesture event occurred, assuming it died.", ex);
@@ -131,4 +244,107 @@
             }
         }
     }
+
+    /** Register the key gesture event handler for a process. */
+    @BinderThread
+    public void registerKeyGestureHandler(IKeyGestureHandler handler, int pid) {
+        synchronized (mKeyGestureHandlerRecords) {
+            if (mKeyGestureHandlerRecords.get(pid) != null) {
+                throw new IllegalStateException("The calling process has already registered "
+                        + "a KeyGestureHandler.");
+            }
+            KeyGestureHandlerRecord record = new KeyGestureHandlerRecord(pid, handler);
+            try {
+                handler.asBinder().linkToDeath(record, 0);
+            } catch (RemoteException ex) {
+                throw new RuntimeException(ex);
+            }
+            mKeyGestureHandlerRecords.put(pid, record);
+        }
+    }
+
+    /** Unregister the key gesture event handler for a process. */
+    @BinderThread
+    public void unregisterKeyGestureHandler(IKeyGestureHandler handler, int pid) {
+        synchronized (mKeyGestureHandlerRecords) {
+            KeyGestureHandlerRecord record = mKeyGestureHandlerRecords.get(pid);
+            if (record == null) {
+                throw new IllegalStateException("The calling process has no registered "
+                        + "KeyGestureHandler.");
+            }
+            if (record.mKeyGestureHandler.asBinder() != handler.asBinder()) {
+                throw new IllegalStateException("The calling process has a different registered "
+                        + "KeyGestureHandler.");
+            }
+            record.mKeyGestureHandler.asBinder().unlinkToDeath(record, 0);
+            mKeyGestureHandlerRecords.remove(pid);
+        }
+    }
+
+    private void onKeyGestureHandlerDied(int pid) {
+        synchronized (mKeyGestureHandlerRecords) {
+            mKeyGestureHandlerRecords.remove(pid);
+        }
+    }
+
+    // A record of a registered key gesture event listener from one process.
+    private class KeyGestureHandlerRecord implements IBinder.DeathRecipient {
+        public final int mPid;
+        public final IKeyGestureHandler mKeyGestureHandler;
+
+        KeyGestureHandlerRecord(int pid, IKeyGestureHandler keyGestureHandler) {
+            mPid = pid;
+            mKeyGestureHandler = keyGestureHandler;
+        }
+
+        @Override
+        public void binderDied() {
+            if (DEBUG) {
+                Slog.d(TAG, "Key gesture event handler for pid " + mPid + " died.");
+            }
+            onKeyGestureHandlerDied(mPid);
+        }
+
+        public boolean handleKeyGesture(AidlKeyGestureEvent event, IBinder focusedToken) {
+            try {
+                return mKeyGestureHandler.handleKeyGesture(event, focusedToken);
+            } catch (RemoteException ex) {
+                Slog.w(TAG, "Failed to send key gesture to process " + mPid
+                        + ", assuming it died.", ex);
+                binderDied();
+            }
+            return false;
+        }
+
+        public boolean isKeyGestureSupported(@KeyGestureEvent.KeyGestureType int gestureType) {
+            try {
+                return mKeyGestureHandler.isKeyGestureSupported(gestureType);
+            } catch (RemoteException ex) {
+                Slog.w(TAG, "Failed to identify if key gesture type is supported by the "
+                        + "process " + mPid + ", assuming it died.", ex);
+                binderDied();
+            }
+            return false;
+        }
+    }
+
+    @Nullable
+    private InputDevice getInputDevice(int deviceId) {
+        InputManager inputManager = mContext.getSystemService(InputManager.class);
+        return inputManager != null ? inputManager.getInputDevice(deviceId) : null;
+    }
+
+    private AidlKeyGestureEvent createKeyGestureEvent(int deviceId, int[] keycodes,
+            int modifierState, @KeyGestureEvent.KeyGestureType int gestureType, int action,
+            int displayId, int flags) {
+        AidlKeyGestureEvent event = new AidlKeyGestureEvent();
+        event.deviceId = deviceId;
+        event.keycodes = keycodes;
+        event.modifierState = modifierState;
+        event.gestureType = gestureType;
+        event.action = action;
+        event.displayId = displayId;
+        event.flags = flags;
+        return event;
+    }
 }
diff --git a/services/core/java/com/android/server/input/KeyboardMetricsCollector.java b/services/core/java/com/android/server/input/KeyboardMetricsCollector.java
index 1daf4db..609164a4 100644
--- a/services/core/java/com/android/server/input/KeyboardMetricsCollector.java
+++ b/services/core/java/com/android/server/input/KeyboardMetricsCollector.java
@@ -24,7 +24,6 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.hardware.input.KeyGestureEvent;
 import android.hardware.input.KeyboardLayout;
 import android.hardware.input.KeyboardLayoutSelectionResult.LayoutSelectionCriteria;
 import android.icu.util.ULocale;
@@ -41,6 +40,7 @@
 import com.android.internal.util.FrameworkStatsLog;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 import java.util.Objects;
 
@@ -60,23 +60,26 @@
     @VisibleForTesting
     public static final String DEFAULT_LANGUAGE_TAG = "None";
 
+    private static final int INVALID_SYSTEMS_EVENT = FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__UNSPECIFIED;
+
     /**
      * Log keyboard system shortcuts for the proto
      * {@link com.android.os.input.KeyboardSystemsEventReported}
      * defined in "stats/atoms/input/input_extension_atoms.proto"
      */
     public static void logKeyboardSystemsEventReportedAtom(@NonNull InputDevice inputDevice,
-            @NonNull KeyGestureEvent keyGestureEvent) {
-        if (inputDevice.isVirtual() || !inputDevice.isFullKeyboard()) {
+            int[] keycodes, int modifierState, int systemsEvent) {
+        if (systemsEvent == INVALID_SYSTEMS_EVENT || inputDevice.isVirtual()
+                || !inputDevice.isFullKeyboard()) {
             return;
         }
         FrameworkStatsLog.write(FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED,
                 inputDevice.getVendorId(), inputDevice.getProductId(),
-                keyGestureEvent.getKeyGestureType(), keyGestureEvent.getKeycodes(),
-                keyGestureEvent.getModifierState(), inputDevice.getDeviceBus());
+                systemsEvent, keycodes, modifierState, inputDevice.getDeviceBus());
 
         if (DEBUG) {
-            Slog.d(TAG, "Logging Keyboard system event: " + keyGestureEvent);
+            Slog.d(TAG, "Logging Keyboard system event: " + modifierState + " + " + Arrays.toString(
+                    keycodes) + " -> " + systemsEvent);
         }
     }
 
diff --git a/tests/Input/src/android/hardware/input/KeyGestureEventHandlerTest.kt b/tests/Input/src/android/hardware/input/KeyGestureEventHandlerTest.kt
new file mode 100644
index 0000000..072341d
--- /dev/null
+++ b/tests/Input/src/android/hardware/input/KeyGestureEventHandlerTest.kt
@@ -0,0 +1,236 @@
+/*
+ * 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.content.Context
+import android.content.ContextWrapper
+import android.os.Handler
+import android.os.IBinder
+import android.os.test.TestLooper
+import android.platform.test.annotations.Presubmit
+import android.platform.test.flag.junit.SetFlagsRule
+import android.view.KeyEvent
+import androidx.test.core.app.ApplicationProvider
+import com.android.server.testutils.any
+import org.junit.After
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito
+import org.mockito.Mockito.doAnswer
+import org.mockito.Mockito.`when`
+import org.mockito.junit.MockitoJUnitRunner
+import kotlin.test.assertEquals
+import kotlin.test.assertNotNull
+import kotlin.test.assertNull
+import kotlin.test.fail
+
+/**
+ * Tests for [InputManager.KeyGestureEventHandler].
+ *
+ * Build/Install/Run:
+ * atest InputTests:KeyGestureEventHandlerTest
+ */
+@Presubmit
+@RunWith(MockitoJUnitRunner::class)
+class KeyGestureEventHandlerTest {
+
+    companion object {
+        const val DEVICE_ID = 1
+        val HOME_GESTURE_EVENT = KeyGestureEvent.Builder()
+            .setDeviceId(DEVICE_ID)
+            .setKeycodes(intArrayOf(KeyEvent.KEYCODE_H))
+            .setModifierState(KeyEvent.META_META_ON or KeyEvent.META_META_LEFT_ON)
+            .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_HOME)
+            .build()
+        val BACK_GESTURE_EVENT = KeyGestureEvent.Builder()
+            .setDeviceId(DEVICE_ID)
+            .setKeycodes(intArrayOf(KeyEvent.KEYCODE_DEL))
+            .setModifierState(KeyEvent.META_META_ON or KeyEvent.META_META_LEFT_ON)
+            .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_BACK)
+            .build()
+    }
+
+    @get:Rule
+    val rule = SetFlagsRule()
+
+    private val testLooper = TestLooper()
+    private var registeredListener: IKeyGestureHandler? = null
+    private lateinit var context: Context
+    private lateinit var inputManager: InputManager
+    private lateinit var inputManagerGlobalSession: InputManagerGlobal.TestSession
+
+    @Mock
+    private lateinit var iInputManagerMock: IInputManager
+
+    @Before
+    fun setUp() {
+        context = Mockito.spy(ContextWrapper(ApplicationProvider.getApplicationContext()))
+        inputManagerGlobalSession = InputManagerGlobal.createTestSession(iInputManagerMock)
+        inputManager = InputManager(context)
+        `when`(context.getSystemService(Mockito.eq(Context.INPUT_SERVICE)))
+                .thenReturn(inputManager)
+
+        // Handle key gesture handler registration.
+        doAnswer {
+            val listener = it.getArgument(0) as IKeyGestureHandler
+            if (registeredListener != null &&
+                    registeredListener!!.asBinder() != listener.asBinder()) {
+                // There can only be one registered key gesture handler per process.
+                fail("Trying to register a new listener when one already exists")
+            }
+            registeredListener = listener
+            null
+        }.`when`(iInputManagerMock).registerKeyGestureHandler(any())
+
+        // Handle key gesture handler being unregistered.
+        doAnswer {
+            val listener = it.getArgument(0) as IKeyGestureHandler
+            if (registeredListener == null ||
+                    registeredListener!!.asBinder() != listener.asBinder()) {
+                fail("Trying to unregister a listener that is not registered")
+            }
+            registeredListener = null
+            null
+        }.`when`(iInputManagerMock).unregisterKeyGestureHandler(any())
+    }
+
+    @After
+    fun tearDown() {
+        if (this::inputManagerGlobalSession.isInitialized) {
+            inputManagerGlobalSession.close()
+        }
+    }
+
+    private fun handleKeyGestureEvent(event: KeyGestureEvent) {
+        val eventToSend = AidlKeyGestureEvent()
+        eventToSend.deviceId = event.deviceId
+        eventToSend.keycodes = event.keycodes
+        eventToSend.modifierState = event.modifierState
+        eventToSend.gestureType = event.keyGestureType
+        eventToSend.action = event.action
+        eventToSend.displayId = event.displayId
+        eventToSend.flags = event.flags
+        registeredListener!!.handleKeyGesture(eventToSend, null)
+    }
+
+    @Test
+    fun testHandlerHasCorrectGestureNotified() {
+        var callbackCount = 0
+
+        // Add a key gesture event listener
+        inputManager.registerKeyGestureEventHandler(KeyGestureHandler { event, _ ->
+            assertEquals(HOME_GESTURE_EVENT, event)
+            callbackCount++
+            true
+        })
+
+        // Request handling for key gesture event will notify the handler.
+        handleKeyGestureEvent(HOME_GESTURE_EVENT)
+        assertEquals(1, callbackCount)
+    }
+
+    @Test
+    fun testAddingHandlersRegistersInternalCallbackHandler() {
+        // Set up two callbacks.
+        val callback1 = KeyGestureHandler { _, _ -> false }
+        val callback2 = KeyGestureHandler { _, _ -> false }
+
+        assertNull(registeredListener)
+
+        // Adding the handler should register the callback with InputManagerService.
+        inputManager.registerKeyGestureEventHandler(callback1)
+        assertNotNull(registeredListener)
+
+        // Adding another handler should not register new internal listener.
+        val currListener = registeredListener
+        inputManager.registerKeyGestureEventHandler(callback2)
+        assertEquals(currListener, registeredListener)
+    }
+
+    @Test
+    fun testRemovingHandlersUnregistersInternalCallbackHandler() {
+        // Set up two callbacks.
+        val callback1 = KeyGestureHandler { _, _ -> false }
+        val callback2 = KeyGestureHandler { _, _ -> false }
+
+        inputManager.registerKeyGestureEventHandler(callback1)
+        inputManager.registerKeyGestureEventHandler(callback2)
+
+        // Only removing all handlers should remove the internal callback
+        inputManager.unregisterKeyGestureEventHandler(callback1)
+        assertNotNull(registeredListener)
+        inputManager.unregisterKeyGestureEventHandler(callback2)
+        assertNull(registeredListener)
+    }
+
+    @Test
+    fun testMultipleHandlers() {
+        // Set up two callbacks.
+        var callbackCount1 = 0
+        var callbackCount2 = 0
+        // Handler 1 captures all home gestures
+        val callback1 = KeyGestureHandler { event, _ ->
+            callbackCount1++
+            event.keyGestureType == KeyGestureEvent.KEY_GESTURE_TYPE_HOME
+        }
+        // Handler 2 captures all gestures
+        val callback2 = KeyGestureHandler { _, _ ->
+            callbackCount2++
+            true
+        }
+
+        // Add both key gesture event handlers
+        inputManager.registerKeyGestureEventHandler(callback1)
+        inputManager.registerKeyGestureEventHandler(callback2)
+
+        // Request handling for key gesture event, should notify callbacks in order. So, only the
+        // first handler should receive a callback since it captures the event.
+        handleKeyGestureEvent(HOME_GESTURE_EVENT)
+        assertEquals(1, callbackCount1)
+        assertEquals(0, callbackCount2)
+
+        // Second handler should receive the event since the first handler doesn't capture the event
+        handleKeyGestureEvent(BACK_GESTURE_EVENT)
+        assertEquals(2, callbackCount1)
+        assertEquals(1, callbackCount2)
+
+        inputManager.unregisterKeyGestureEventHandler(callback1)
+        // Request handling for key gesture event, should still trigger callback2 but not callback1.
+        handleKeyGestureEvent(HOME_GESTURE_EVENT)
+        assertEquals(2, callbackCount1)
+        assertEquals(2, callbackCount2)
+    }
+
+    inner class KeyGestureHandler(
+        private var handler: (event: KeyGestureEvent, token: IBinder?) -> Boolean
+    ) : InputManager.KeyGestureEventHandler {
+
+        override fun handleKeyGestureEvent(
+            event: KeyGestureEvent,
+            focusedToken: IBinder?
+        ): Boolean {
+            return handler(event, focusedToken)
+        }
+
+        override fun isKeyGestureSupported(gestureType: Int): Boolean {
+            return true
+        }
+    }
+}
diff --git a/tests/Input/src/android/hardware/input/KeyGestureEventListenerTest.kt b/tests/Input/src/android/hardware/input/KeyGestureEventListenerTest.kt
index 14aac66..ca9de60 100644
--- a/tests/Input/src/android/hardware/input/KeyGestureEventListenerTest.kt
+++ b/tests/Input/src/android/hardware/input/KeyGestureEventListenerTest.kt
@@ -53,12 +53,12 @@
 
     companion object {
         const val DEVICE_ID = 1
-        val HOME_GESTURE_EVENT = KeyGestureEvent(
-            DEVICE_ID,
-            intArrayOf(KeyEvent.KEYCODE_H),
-            KeyEvent.META_META_ON or KeyEvent.META_META_LEFT_ON,
-            KeyGestureEvent.KEY_GESTURE_TYPE_HOME
-        )
+        val HOME_GESTURE_EVENT = KeyGestureEvent.Builder()
+            .setDeviceId(DEVICE_ID)
+            .setKeycodes(intArrayOf(KeyEvent.KEYCODE_H))
+            .setModifierState(KeyEvent.META_META_ON or KeyEvent.META_META_LEFT_ON)
+            .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_HOME)
+            .build()
     }
 
     @get:Rule
@@ -114,12 +114,15 @@
     }
 
     private fun notifyKeyGestureEvent(event: KeyGestureEvent) {
-        registeredListener!!.onKeyGestureEvent(
-            event.deviceId,
-            event.keycodes,
-            event.modifierState,
-            event.keyGestureType
-        )
+        val eventToSend = AidlKeyGestureEvent()
+        eventToSend.deviceId = event.deviceId
+        eventToSend.keycodes = event.keycodes
+        eventToSend.modifierState = event.modifierState
+        eventToSend.gestureType = event.keyGestureType
+        eventToSend.action = event.action
+        eventToSend.displayId = event.displayId
+        eventToSend.flags = event.flags
+        registeredListener!!.onKeyGestureEvent(eventToSend)
     }
 
     @Test
diff --git a/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt b/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt
index 3f611e0..e126797 100644
--- a/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt
+++ b/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt
@@ -18,18 +18,28 @@
 
 import android.content.Context
 import android.content.ContextWrapper
+import android.hardware.input.IInputManager
+import android.hardware.input.AidlKeyGestureEvent
 import android.hardware.input.IKeyGestureEventListener
+import android.hardware.input.IKeyGestureHandler
+import android.hardware.input.InputManager
+import android.hardware.input.InputManagerGlobal
 import android.hardware.input.KeyGestureEvent
+import android.os.IBinder
+import android.os.Process
+import android.os.test.TestLooper
 import android.platform.test.annotations.Presubmit
+import android.view.InputDevice
 import android.view.KeyEvent
 import androidx.test.core.app.ApplicationProvider
+import com.android.internal.util.FrameworkStatsLog
+import com.android.modules.utils.testing.ExtendedMockitoRule
 import org.junit.Assert.assertEquals
-import org.junit.Assert.assertNull
 import org.junit.Before
 import org.junit.Rule
 import org.junit.Test
+import org.mockito.Mock
 import org.mockito.Mockito
-import org.mockito.junit.MockitoJUnit
 
 /**
  * Tests for {@link KeyGestureController}.
@@ -41,26 +51,55 @@
 class KeyGestureControllerTests {
 
     companion object {
-        val DEVICE_ID = 1
-        val HOME_GESTURE_EVENT = KeyGestureEvent(
-            DEVICE_ID,
-            intArrayOf(KeyEvent.KEYCODE_H),
-            KeyEvent.META_META_ON or KeyEvent.META_META_LEFT_ON,
-            KeyGestureEvent.KEY_GESTURE_TYPE_HOME
-        )
+        const val DEVICE_ID = 1
+        val HOME_GESTURE_COMPLETE_EVENT = KeyGestureEvent.Builder()
+            .setDeviceId(DEVICE_ID)
+            .setKeycodes(intArrayOf(KeyEvent.KEYCODE_H))
+            .setModifierState(KeyEvent.META_META_ON or KeyEvent.META_META_LEFT_ON)
+            .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_HOME)
+            .setAction(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+            .build()
     }
 
-    @get:Rule
-    val rule = MockitoJUnit.rule()!!
+    @JvmField
+    @Rule
+    val extendedMockitoRule = ExtendedMockitoRule.Builder(this)
+        .mockStatic(FrameworkStatsLog::class.java).build()!!
 
+    @Mock
+    private lateinit var iInputManager: IInputManager
+
+    private var currentPid = 0
     private lateinit var keyGestureController: KeyGestureController
     private lateinit var context: Context
-    private var lastEvent: KeyGestureEvent? = null
+    private lateinit var inputManagerGlobalSession: InputManagerGlobal.TestSession
+    private lateinit var testLooper: TestLooper
+    private var events = mutableListOf<KeyGestureEvent>()
 
     @Before
     fun setup() {
         context = Mockito.spy(ContextWrapper(ApplicationProvider.getApplicationContext()))
-        keyGestureController = KeyGestureController()
+        inputManagerGlobalSession = InputManagerGlobal.createTestSession(iInputManager)
+        setupInputDevices()
+        testLooper = TestLooper()
+        currentPid = Process.myPid()
+        keyGestureController = KeyGestureController(context, testLooper.looper)
+    }
+
+    private fun setupInputDevices() {
+        val inputManager = InputManager(context)
+        Mockito.`when`(context.getSystemService(Mockito.eq(Context.INPUT_SERVICE)))
+            .thenReturn(inputManager)
+
+        val keyboardDevice = InputDevice.Builder().setId(DEVICE_ID).build()
+        Mockito.`when`(iInputManager.inputDeviceIds).thenReturn(intArrayOf(DEVICE_ID))
+        Mockito.`when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardDevice)
+    }
+
+    private fun notifyHomeGestureCompleted() {
+        keyGestureController.notifyKeyGestureCompleted(DEVICE_ID, intArrayOf(KeyEvent.KEYCODE_H),
+            KeyEvent.META_META_ON or KeyEvent.META_META_LEFT_ON,
+            KeyGestureEvent.KEY_GESTURE_TYPE_HOME)
     }
 
     @Test
@@ -69,28 +108,97 @@
 
         // Register key gesture event listener
         keyGestureController.registerKeyGestureEventListener(listener, 0)
-        keyGestureController.onKeyGestureEvent(HOME_GESTURE_EVENT)
+        notifyHomeGestureCompleted()
+        testLooper.dispatchAll()
         assertEquals(
-            "Listener should get callback on key gesture event",
-            HOME_GESTURE_EVENT,
-            lastEvent!!
+            "Listener should get callbacks on key gesture event completed",
+            1,
+            events.size
+        )
+        assertEquals(
+            "Listener should get callback for key gesture complete event",
+            HOME_GESTURE_COMPLETE_EVENT,
+            events[0]
         )
 
         // Unregister listener
-        lastEvent = null
+        events.clear()
         keyGestureController.unregisterKeyGestureEventListener(listener, 0)
-        keyGestureController.onKeyGestureEvent(HOME_GESTURE_EVENT)
-        assertNull("Listener should not get callback after being unregistered", lastEvent)
+        notifyHomeGestureCompleted()
+        testLooper.dispatchAll()
+        assertEquals(
+            "Listener should not get callback after being unregistered",
+            0,
+            events.size
+        )
+    }
+
+    @Test
+    fun testKeyGestureEvent_multipleGestureHandlers() {
+        // Set up two callbacks.
+        var callbackCount1 = 0
+        var callbackCount2 = 0
+        var selfCallback = 0
+        val externalHandler1 = KeyGestureHandler { _, _ ->
+            callbackCount1++;
+            true
+        }
+        val externalHandler2 = KeyGestureHandler { _, _ ->
+            callbackCount2++;
+            true
+        }
+        val selfHandler = KeyGestureHandler { _, _ ->
+            selfCallback++;
+            false
+        }
+
+        // Register key gesture handler: External process (last in priority)
+        keyGestureController.registerKeyGestureHandler(externalHandler1, currentPid + 1)
+
+        // Register key gesture handler: External process (second in priority)
+        keyGestureController.registerKeyGestureHandler(externalHandler2, currentPid - 1)
+
+        // Register key gesture handler: Self process (first in priority)
+        keyGestureController.registerKeyGestureHandler(selfHandler, currentPid)
+
+        keyGestureController.handleKeyGesture(/* deviceId = */ 0, intArrayOf(KeyEvent.KEYCODE_HOME),
+            /* modifierState = */ 0, KeyGestureEvent.KEY_GESTURE_TYPE_HOME,
+            KeyGestureEvent.ACTION_GESTURE_COMPLETE, /* displayId */ 0,
+            /* focusedToken = */ null, /* flags = */ 0
+        )
+
+        assertEquals(
+            "Self handler should get callbacks first",
+            1,
+            selfCallback
+        )
+        assertEquals(
+            "Higher priority handler should get callbacks first",
+            1,
+            callbackCount2
+        )
+        assertEquals(
+            "Lower priority handler should not get callbacks if already handled",
+            0,
+            callbackCount1
+        )
     }
 
     inner class KeyGestureEventListener : IKeyGestureEventListener.Stub() {
-        override fun onKeyGestureEvent(
-                deviceId: Int,
-                keycodes: IntArray,
-                modifierState: Int,
-                gestureType: Int
-        ) {
-            lastEvent = KeyGestureEvent(deviceId, keycodes, modifierState, gestureType)
+        override fun onKeyGestureEvent(event: AidlKeyGestureEvent) {
+            events.add(KeyGestureEvent(event))
+        }
+    }
+
+    inner class KeyGestureHandler(
+        private var handler: (event: AidlKeyGestureEvent, token: IBinder?) -> Boolean
+    ) : IKeyGestureHandler.Stub() {
+        override fun handleKeyGesture(event: AidlKeyGestureEvent, token: IBinder?): Boolean {
+            return handler(event, token)
+        }
+
+        override fun isKeyGestureSupported(gestureType: Int): Boolean {
+            return true
         }
     }
 }
\ No newline at end of file