Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b1d338f1 authored by Vaibhav Devmurari's avatar Vaibhav Devmurari
Browse files

(4/n) Shift to oneway binder call for register key handler

handleKeyGestureEvent() is synchronous API that makes system server
dependent on handler and can cause ANR if handlers misbehave.
As a replacement, handlers should provide list of gesture they want
to listen to. For consistency, we only allow single handler per
gesture.

Test: atest InputTests
Test: atest WmTests
Test: Presubmit
Bug: 358569822
Bug: 383602794
Flag: EXEMPT refactor
Change-Id: Iea0f6ba3360ed241e19f0265d6ab28fd7889aafa
parent 20380db5
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -273,7 +273,7 @@ interface IInputManager {
    @PermissionManuallyEnforced
    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(value = "
            + "android.Manifest.permission.MANAGE_KEY_GESTURES)")
    void registerKeyGestureHandler(IKeyGestureHandler handler);
    void registerKeyGestureHandler(in int[] keyGesturesToHandle, IKeyGestureHandler handler);

    @PermissionManuallyEnforced
    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(value = "
+5 −5
Original line number Diff line number Diff line
@@ -20,12 +20,12 @@ import android.hardware.input.AidlKeyGestureEvent;
import android.os.IBinder;

/** @hide */
interface IKeyGestureHandler {
oneway 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.
     * Called when a key gesture starts, ends, or is cancelled. It is only sent to the handler that
     * registered the callback for that particular gesture type.
     * {@see IInputManager#registerKeyGestureHandler(int[], IKeyGestureHandler)}
     */
    boolean handleKeyGesture(in AidlKeyGestureEvent event, in IBinder focusedToken);
    void handleKeyGesture(in AidlKeyGestureEvent event, in IBinder focusedToken);
}
+10 −11
Original line number Diff line number Diff line
@@ -1446,16 +1446,18 @@ public final class InputManager {
    /**
     * Registers a key gesture event handler for {@link KeyGestureEvent} handling.
     *
     * @param keyGesturesToHandle list of KeyGestureTypes to listen to
     * @param handler the {@link KeyGestureEventHandler}
     * @throws IllegalArgumentException if {@code handler} has already been registered previously.
     * @throws IllegalArgumentException if {@code handler} has already been registered previously
     * or key gestures provided are already registered by some other gesture handler.
     * @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);
    public void registerKeyGestureEventHandler(List<Integer> keyGesturesToHandle,
            @NonNull KeyGestureEventHandler handler) throws IllegalArgumentException {
        mGlobal.registerKeyGestureEventHandler(keyGesturesToHandle, handler);
    }

    /**
@@ -1463,7 +1465,7 @@ public final class InputManager {
     *
     * @param handler the {@link KeyGestureEventHandler}
     * @hide
     * @see #registerKeyGestureEventHandler(KeyGestureEventHandler)
     * @see #registerKeyGestureEventHandler(List, KeyGestureEventHandler)
     */
    @RequiresPermission(Manifest.permission.MANAGE_KEY_GESTURES)
    public void unregisterKeyGestureEventHandler(@NonNull KeyGestureEventHandler handler) {
@@ -1741,7 +1743,7 @@ public final class InputManager {
     * {@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 #registerKeyGestureEventHandler(List, KeyGestureEventHandler)
     * @see #unregisterKeyGestureEventHandler(KeyGestureEventHandler)
     *
     * <p> NOTE: All callbacks will occur on system main and input threads, so the caller needs
@@ -1750,14 +1752,11 @@ public final class InputManager {
     */
    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.
         * Called when a key gesture event starts, is completed, or is cancelled.
         *
         * @param event the gesture event
         */
        boolean handleKeyGestureEvent(@NonNull KeyGestureEvent event,
                @Nullable IBinder focusedToken);
        void handleKeyGestureEvent(@NonNull KeyGestureEvent event, @Nullable IBinder focusedToken);
    }

    /** @hide */
+63 −40
Original line number Diff line number Diff line
@@ -25,8 +25,8 @@ import android.hardware.BatteryState;
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.KeyEventActivityListener;
import android.hardware.input.InputManager.KeyGestureEventHandler;
import android.hardware.input.InputManager.KeyGestureEventListener;
import android.hardware.input.InputManager.KeyboardBacklightListener;
import android.hardware.input.InputManager.OnTabletModeChangedListener;
@@ -49,6 +49,7 @@ import android.os.ServiceManager;
import android.os.VibrationEffect;
import android.os.Vibrator;
import android.os.VibratorManager;
import android.util.IntArray;
import android.util.Log;
import android.util.SparseArray;
import android.view.Display;
@@ -132,13 +133,13 @@ public final class InputManagerGlobal {
    @Nullable
    private IKeyEventActivityListener mKeyEventActivityListener;

    private final Object mKeyGestureEventHandlerLock = new Object();
    @GuardedBy("mKeyGestureEventHandlerLock")
    @Nullable
    private ArrayList<KeyGestureEventHandler> mKeyGestureEventHandlers;
    @GuardedBy("mKeyGestureEventHandlerLock")
    @GuardedBy("mKeyGesturesToHandlerMap")
    @Nullable
    private IKeyGestureHandler mKeyGestureHandler;
    @GuardedBy("mKeyGesturesToHandlerMap")
    private final SparseArray<KeyGestureEventHandler> mKeyGesturesToHandlerMap =
            new SparseArray<>();


    // InputDeviceSensorManager gets notified synchronously from the binder thread when input
    // devices change, so it must be synchronized with the input device listeners.
@@ -1177,50 +1178,69 @@ public final class InputManagerGlobal {

    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;
                    }
        public void handleKeyGesture(@NonNull AidlKeyGestureEvent ev, IBinder focusedToken) {
            synchronized (mKeyGesturesToHandlerMap) {
                KeyGestureEventHandler handler = mKeyGesturesToHandlerMap.get(ev.gestureType);
                if (handler == null) {
                    Log.w(TAG, "Key gesture event " + ev.gestureType
                            + " occurred without a registered handler!");
                    return;
                }
                handler.handleKeyGestureEvent(new KeyGestureEvent(ev), focusedToken);
            }
            return false;
        }
    }

    /**
     * @see InputManager#registerKeyGestureEventHandler(KeyGestureEventHandler)
     * @see InputManager#registerKeyGestureEventHandler(List, KeyGestureEventHandler)
     */
    @RequiresPermission(Manifest.permission.MANAGE_KEY_GESTURES)
    void registerKeyGestureEventHandler(@NonNull KeyGestureEventHandler handler)
            throws IllegalArgumentException {
    void registerKeyGestureEventHandler(List<Integer> keyGesturesToHandle,
            @NonNull KeyGestureEventHandler handler) throws IllegalArgumentException {
        Objects.requireNonNull(keyGesturesToHandle, "List of gestures should not be null");
        Objects.requireNonNull(handler, "handler should not be null");

        synchronized (mKeyGestureEventHandlerLock) {
            if (mKeyGestureHandler == null) {
                mKeyGestureEventHandlers = new ArrayList<>();
                mKeyGestureHandler = new LocalKeyGestureHandler();
        if (keyGesturesToHandle.isEmpty()) {
            throw new IllegalArgumentException("No key gestures provided!");
        }

                try {
                    mIm.registerKeyGestureHandler(mKeyGestureHandler);
                } catch (RemoteException e) {
                    throw e.rethrowFromSystemServer();
        synchronized (mKeyGesturesToHandlerMap) {
            IntArray newKeyGestures = new IntArray(
                    keyGesturesToHandle.size() + mKeyGesturesToHandlerMap.size());

            // Check if the handler already exists
            for (int i = 0; i < mKeyGesturesToHandlerMap.size(); i++) {
                KeyGestureEventHandler h = mKeyGesturesToHandlerMap.valueAt(i);
                if (h == handler) {
                    throw new IllegalArgumentException("Handler has already been registered!");
                }
                newKeyGestures.add(mKeyGesturesToHandlerMap.keyAt(i));
            }
            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!");

            // Check if any of the key gestures are already handled by existing handlers
            for (int gesture : keyGesturesToHandle) {
                if (mKeyGesturesToHandlerMap.contains(gesture)) {
                    throw new IllegalArgumentException("Key gesture " + gesture
                            + " is already registered by another handler!");
                }
                newKeyGestures.add(gesture);
            }

            try {
                // If handler was already registered for this process, we need to unregister and
                // re-register it for the new set of gestures
                if (mKeyGestureHandler != null) {
                    mIm.unregisterKeyGestureHandler(mKeyGestureHandler);
                } else {
                    mKeyGestureHandler = new LocalKeyGestureHandler();
                }
                mIm.registerKeyGestureHandler(newKeyGestures.toArray(), mKeyGestureHandler);
                for (int gesture : keyGesturesToHandle) {
                    mKeyGesturesToHandlerMap.put(gesture, handler);
                }
            } catch (RemoteException e) {
                throw e.rethrowFromSystemServer();
            }
            mKeyGestureEventHandlers.add(handler);
        }
    }

@@ -1231,18 +1251,21 @@ public final class InputManagerGlobal {
    void unregisterKeyGestureEventHandler(@NonNull KeyGestureEventHandler handler) {
        Objects.requireNonNull(handler, "handler should not be null");

        synchronized (mKeyGestureEventHandlerLock) {
            if (mKeyGestureEventHandlers == null) {
        synchronized (mKeyGesturesToHandlerMap) {
            if (mKeyGestureHandler == null) {
                return;
            }
            mKeyGestureEventHandlers.removeIf(existingHandler -> existingHandler == handler);
            if (mKeyGestureEventHandlers.isEmpty()) {
            for (int i = mKeyGesturesToHandlerMap.size() - 1; i >= 0; i--) {
                if (mKeyGesturesToHandlerMap.valueAt(i) == handler) {
                    mKeyGesturesToHandlerMap.removeAt(i);
                }
            }
            if (mKeyGesturesToHandlerMap.size() == 0) {
                try {
                    mIm.unregisterKeyGestureHandler(mKeyGestureHandler);
                } catch (RemoteException e) {
                    throw e.rethrowFromSystemServer();
                }
                mKeyGestureEventHandlers = null;
                mKeyGestureHandler = null;
            }
        }
+12 −17
Original line number Diff line number Diff line
@@ -23,10 +23,7 @@ import android.hardware.input.InputManager
import android.hardware.input.InputManager.KeyGestureEventHandler
import android.hardware.input.KeyGestureEvent
import android.os.IBinder
import android.window.DesktopModeFlags
import com.android.hardware.input.Flags.manageKeyGestures
import com.android.internal.protolog.ProtoLog
import com.android.window.flags.Flags.enableMoveToNextDisplayShortcut
import com.android.wm.shell.ShellTaskOrganizer
import com.android.wm.shell.common.DisplayController
import com.android.wm.shell.common.ShellExecutor
@@ -51,16 +48,20 @@ class DesktopModeKeyGestureHandler(
) : KeyGestureEventHandler {

    init {
        inputManager.registerKeyGestureEventHandler(this)
        if (desktopTasksController.isPresent && desktopModeWindowDecorViewModel.isPresent) {
            val supportedGestures =
                listOf(
                    KeyGestureEvent.KEY_GESTURE_TYPE_MOVE_TO_NEXT_DISPLAY,
                    KeyGestureEvent.KEY_GESTURE_TYPE_SNAP_LEFT_FREEFORM_WINDOW,
                    KeyGestureEvent.KEY_GESTURE_TYPE_SNAP_RIGHT_FREEFORM_WINDOW,
                    KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_MAXIMIZE_FREEFORM_WINDOW,
                    KeyGestureEvent.KEY_GESTURE_TYPE_MINIMIZE_FREEFORM_WINDOW,
                )
            inputManager.registerKeyGestureEventHandler(supportedGestures, this)
        }

    override fun handleKeyGestureEvent(event: KeyGestureEvent, focusedToken: IBinder?): Boolean {
        if (
                !desktopTasksController.isPresent ||
                !desktopModeWindowDecorViewModel.isPresent
        ) {
            return false
    }

    override fun handleKeyGestureEvent(event: KeyGestureEvent, focusedToken: IBinder?) {
        when (event.keyGestureType) {
            KeyGestureEvent.KEY_GESTURE_TYPE_MOVE_TO_NEXT_DISPLAY -> {
                logV("Key gesture MOVE_TO_NEXT_DISPLAY is handled")
@@ -69,7 +70,6 @@ class DesktopModeKeyGestureHandler(
                        desktopTasksController.get().moveToNextDisplay(it.taskId)
                    }
                }
                return true
            }
            KeyGestureEvent.KEY_GESTURE_TYPE_SNAP_LEFT_FREEFORM_WINDOW -> {
                logV("Key gesture SNAP_LEFT_FREEFORM_WINDOW is handled")
@@ -85,7 +85,6 @@ class DesktopModeKeyGestureHandler(
                            )
                    }
                }
                return true
            }
            KeyGestureEvent.KEY_GESTURE_TYPE_SNAP_RIGHT_FREEFORM_WINDOW -> {
                logV("Key gesture SNAP_RIGHT_FREEFORM_WINDOW is handled")
@@ -101,7 +100,6 @@ class DesktopModeKeyGestureHandler(
                            )
                    }
                }
                return true
            }
            KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_MAXIMIZE_FREEFORM_WINDOW -> {
                logV("Key gesture TOGGLE_MAXIMIZE_FREEFORM_WINDOW is handled")
@@ -120,7 +118,6 @@ class DesktopModeKeyGestureHandler(
                            )
                    }
                }
                return true
            }
            KeyGestureEvent.KEY_GESTURE_TYPE_MINIMIZE_FREEFORM_WINDOW -> {
                logV("Key gesture MINIMIZE_FREEFORM_WINDOW is handled")
@@ -129,9 +126,7 @@ class DesktopModeKeyGestureHandler(
                        desktopTasksController.get().minimizeTask(it, MinimizeReason.KEY_GESTURE)
                    }
                }
                return true
            }
            else -> return false
        }
    }

Loading