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

Commit bf9f5e00 authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Allow search kcm fallbacks" into main

parents d095be44 b7e88c95
Loading
Loading
Loading
Loading
+9 −0
Original line number Original line Diff line number Diff line
@@ -213,3 +213,12 @@ flag {
   is_fixed_read_only: true
   is_fixed_read_only: true
}
}


flag {
    name: "fix_search_modifier_fallbacks"
    namespace: "input"
    description: "Fixes a bug in which fallbacks from Search based key combinations were not activating."
    bug: "384113980"
    metadata {
        purpose: PURPOSE_BUGFIX
    }
}
+17 −3
Original line number Original line Diff line number Diff line
@@ -25,6 +25,7 @@ import static android.view.KeyEvent.KEYCODE_UNKNOWN;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;


import static com.android.hardware.input.Flags.enableCustomizableInputGestures;
import static com.android.hardware.input.Flags.enableCustomizableInputGestures;
import static com.android.hardware.input.Flags.fixSearchModifierFallbacks;
import static com.android.hardware.input.Flags.keyEventActivityDetection;
import static com.android.hardware.input.Flags.keyEventActivityDetection;
import static com.android.hardware.input.Flags.touchpadVisualizer;
import static com.android.hardware.input.Flags.touchpadVisualizer;
import static com.android.hardware.input.Flags.useKeyGestureEventHandler;
import static com.android.hardware.input.Flags.useKeyGestureEventHandler;
@@ -2659,6 +2660,8 @@ public class InputManagerService extends IInputManager.Stub
    @SuppressWarnings("unused")
    @SuppressWarnings("unused")
    @VisibleForTesting
    @VisibleForTesting
    long interceptKeyBeforeDispatching(IBinder focus, KeyEvent event, int policyFlags) {
    long interceptKeyBeforeDispatching(IBinder focus, KeyEvent event, int policyFlags) {
        final long keyNotConsumedGoFallback = -2;
        final long keyConsumed = -1;
        final long keyNotConsumed = 0;
        final long keyNotConsumed = 0;
        long value = keyNotConsumed;
        long value = keyNotConsumed;
        // TODO(b/358569822) Remove below once we have nicer API for listening to shortcuts
        // TODO(b/358569822) Remove below once we have nicer API for listening to shortcuts
@@ -2673,6 +2676,16 @@ public class InputManagerService extends IInputManager.Stub
            value = mWindowManagerCallbacks.interceptKeyBeforeDispatching(focus, event,
            value = mWindowManagerCallbacks.interceptKeyBeforeDispatching(focus, event,
                    policyFlags);
                    policyFlags);
        }
        }
        if (fixSearchModifierFallbacks() && value == keyNotConsumed && event.isMetaPressed()) {
            // If the key has not been consumed and includes the meta key, do not send the event
            // to the app and attempt to generate a fallback.
            final KeyCharacterMap kcm = event.getKeyCharacterMap();
            final KeyCharacterMap.FallbackAction fallbackAction =
                    kcm.getFallbackAction(event.getKeyCode(), event.getMetaState());
            if (fallbackAction != null) {
                return keyNotConsumedGoFallback;
            }
        }
        return value;
        return value;
    }
    }


@@ -3316,9 +3329,10 @@ public class InputManagerService extends IInputManager.Stub
         * @param token the window token that's about to receive this event
         * @param token the window token that's about to receive this event
         * @param event the key event that's being dispatched
         * @param event the key event that's being dispatched
         * @param policyFlags the policy flags
         * @param policyFlags the policy flags
         * @return negative value if the key should be skipped (not sent to the app). 0 if the key
         * @return -1 if the key should be skipped (not sent to the app). -2 if the key should not
         * should proceed getting dispatched to the app. positive value to indicate the additional
         * be sent to the app, but it should still generate a fallback.
         * time delay, in nanoseconds, to wait before sending this key to the app.
         * 0 if the key should proceed getting dispatched to the app. positive value to indicate the
         * additional time delay, in nanoseconds, to wait before sending this key to the app.
         */
         */
        long interceptKeyBeforeDispatching(IBinder token, KeyEvent event, int policyFlags);
        long interceptKeyBeforeDispatching(IBinder token, KeyEvent event, int policyFlags);


+8 −0
Original line number Original line Diff line number Diff line
@@ -86,6 +86,7 @@ import static android.view.contentprotection.flags.Flags.createAccessibilityOver
import static com.android.hardware.input.Flags.enableNew25q2Keycodes;
import static com.android.hardware.input.Flags.enableNew25q2Keycodes;
import static com.android.hardware.input.Flags.enableTalkbackAndMagnifierKeyGestures;
import static com.android.hardware.input.Flags.enableTalkbackAndMagnifierKeyGestures;
import static com.android.hardware.input.Flags.enableVoiceAccessKeyGestures;
import static com.android.hardware.input.Flags.enableVoiceAccessKeyGestures;
import static com.android.hardware.input.Flags.fixSearchModifierFallbacks;
import static com.android.hardware.input.Flags.inputManagerLifecycleSupport;
import static com.android.hardware.input.Flags.inputManagerLifecycleSupport;
import static com.android.hardware.input.Flags.keyboardA11yShortcutControl;
import static com.android.hardware.input.Flags.keyboardA11yShortcutControl;
import static com.android.hardware.input.Flags.modifierShortcutDump;
import static com.android.hardware.input.Flags.modifierShortcutDump;
@@ -4181,6 +4182,13 @@ public class PhoneWindowManager implements WindowManagerPolicy {
            return true;
            return true;
        }
        }


        if (fixSearchModifierFallbacks()) {
            // Pass event as unhandled to give other services, e.g. InputManagerService, the
            // opportunity to determine if the event can be modified, e.g. generating a fallback for
            // meta/search events.
            return false;
        }

        // Reserve all the META modifier combos for system behavior
        // Reserve all the META modifier combos for system behavior
        return (metaState & KeyEvent.META_META_ON) != 0;
        return (metaState & KeyEvent.META_META_ON) != 0;
    }
    }
+5 −0
Original line number Original line Diff line number Diff line
@@ -1978,6 +1978,11 @@ NativeInputManager::interceptKeyBeforeDispatching(const sp<IBinder>& token,
        return inputdispatcher::KeyEntry::InterceptKeyResult::SKIP;
        return inputdispatcher::KeyEntry::InterceptKeyResult::SKIP;
    }
    }


    // -2 : Skip sending even to application and go directly to post processing e.g. fallbacks.
    if (delayMillis == -2) {
        return inputdispatcher::KeyEntry::InterceptKeyResult::FALLBACK;
    }

    return milliseconds_to_nanoseconds(delayMillis);
    return milliseconds_to_nanoseconds(delayMillis);
}
}


+39 −4
Original line number Original line Diff line number Diff line
@@ -53,14 +53,16 @@ import android.app.AppOpsManager;
import android.content.Context;
import android.content.Context;
import android.hardware.input.InputManager;
import android.hardware.input.InputManager;
import android.os.Bundle;
import android.os.Bundle;
import android.os.IBinder;
import android.os.PowerManager;
import android.os.PowerManager;
import android.os.PowerManagerInternal;
import android.os.PowerManagerInternal;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.annotations.Presubmit;
import android.platform.test.annotations.Presubmit;
import android.platform.test.flag.junit.SetFlagsRule;
import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.Settings;
import android.provider.Settings;
import android.service.dreams.DreamManagerInternal;
import android.service.dreams.DreamManagerInternal;
import android.testing.TestableContext;
import android.testing.TestableContext;
import android.view.contentprotection.flags.Flags;
import android.view.KeyEvent;


import androidx.test.filters.SmallTest;
import androidx.test.filters.SmallTest;


@@ -102,6 +104,8 @@ public class PhoneWindowManagerTests {
    public final TestableContext mContext = spy(
    public final TestableContext mContext = spy(
            new TestableContext(getInstrumentation().getContext()));
            new TestableContext(getInstrumentation().getContext()));


    @Mock private IBinder mInputToken;

    PhoneWindowManager mPhoneWindowManager;
    PhoneWindowManager mPhoneWindowManager;
    @Mock
    @Mock
    private ActivityTaskManagerInternal mAtmInternal;
    private ActivityTaskManagerInternal mAtmInternal;
@@ -125,6 +129,8 @@ public class PhoneWindowManagerTests {
    @Mock
    @Mock
    private LockPatternUtils mLockPatternUtils;
    private LockPatternUtils mLockPatternUtils;


    private static final int INTERCEPT_SYSTEM_KEY_NOT_CONSUMED_DELAY = 0;

    @Before
    @Before
    public void setUp() {
    public void setUp() {
        MockitoAnnotations.initMocks(this);
        MockitoAnnotations.initMocks(this);
@@ -216,7 +222,7 @@ public class PhoneWindowManagerTests {


    @Test
    @Test
    public void testCheckAddPermission_withoutAccessibilityOverlay_noAccessibilityAppOpLogged() {
    public void testCheckAddPermission_withoutAccessibilityOverlay_noAccessibilityAppOpLogged() {
        mSetFlagsRule.enableFlags(Flags.FLAG_CREATE_ACCESSIBILITY_OVERLAY_APP_OP_ENABLED);
        mSetFlagsRule.enableFlags(android.view.contentprotection.flags.Flags.FLAG_CREATE_ACCESSIBILITY_OVERLAY_APP_OP_ENABLED);
        int[] outAppOp = new int[1];
        int[] outAppOp = new int[1];
        assertEquals(ADD_OKAY, mPhoneWindowManager.checkAddPermission(TYPE_WALLPAPER,
        assertEquals(ADD_OKAY, mPhoneWindowManager.checkAddPermission(TYPE_WALLPAPER,
                /* isRoundedCornerOverlay= */ false, "test.pkg", outAppOp, DEFAULT_DISPLAY));
                /* isRoundedCornerOverlay= */ false, "test.pkg", outAppOp, DEFAULT_DISPLAY));
@@ -225,7 +231,7 @@ public class PhoneWindowManagerTests {


    @Test
    @Test
    public void testCheckAddPermission_withAccessibilityOverlay() {
    public void testCheckAddPermission_withAccessibilityOverlay() {
        mSetFlagsRule.enableFlags(Flags.FLAG_CREATE_ACCESSIBILITY_OVERLAY_APP_OP_ENABLED);
        mSetFlagsRule.enableFlags(android.view.contentprotection.flags.Flags.FLAG_CREATE_ACCESSIBILITY_OVERLAY_APP_OP_ENABLED);
        int[] outAppOp = new int[1];
        int[] outAppOp = new int[1];
        assertEquals(ADD_OKAY, mPhoneWindowManager.checkAddPermission(TYPE_ACCESSIBILITY_OVERLAY,
        assertEquals(ADD_OKAY, mPhoneWindowManager.checkAddPermission(TYPE_ACCESSIBILITY_OVERLAY,
                /* isRoundedCornerOverlay= */ false, "test.pkg", outAppOp, DEFAULT_DISPLAY));
                /* isRoundedCornerOverlay= */ false, "test.pkg", outAppOp, DEFAULT_DISPLAY));
@@ -234,7 +240,7 @@ public class PhoneWindowManagerTests {


    @Test
    @Test
    public void testCheckAddPermission_withAccessibilityOverlay_flagDisabled() {
    public void testCheckAddPermission_withAccessibilityOverlay_flagDisabled() {
        mSetFlagsRule.disableFlags(Flags.FLAG_CREATE_ACCESSIBILITY_OVERLAY_APP_OP_ENABLED);
        mSetFlagsRule.disableFlags(android.view.contentprotection.flags.Flags.FLAG_CREATE_ACCESSIBILITY_OVERLAY_APP_OP_ENABLED);
        int[] outAppOp = new int[1];
        int[] outAppOp = new int[1];
        assertEquals(ADD_OKAY, mPhoneWindowManager.checkAddPermission(TYPE_ACCESSIBILITY_OVERLAY,
        assertEquals(ADD_OKAY, mPhoneWindowManager.checkAddPermission(TYPE_ACCESSIBILITY_OVERLAY,
                /* isRoundedCornerOverlay= */ false, "test.pkg", outAppOp, DEFAULT_DISPLAY));
                /* isRoundedCornerOverlay= */ false, "test.pkg", outAppOp, DEFAULT_DISPLAY));
@@ -401,6 +407,35 @@ public class PhoneWindowManagerTests {
        verify(mDreamManagerInternal).requestDream();
        verify(mDreamManagerInternal).requestDream();
    }
    }


    @EnableFlags(com.android.hardware.input.Flags.FLAG_FIX_SEARCH_MODIFIER_FALLBACKS)
    public void testInterceptKeyBeforeDispatching() {
        // Handle sub-tasks of init().
        doNothing().when(mPhoneWindowManager).updateSettings(any());
        doNothing().when(mPhoneWindowManager).initializeHdmiState();
        final DisplayPolicy displayPolicy = mock(DisplayPolicy.class);
        mPhoneWindowManager.mDefaultDisplayPolicy = displayPolicy;
        mPhoneWindowManager.mDefaultDisplayRotation = mock(DisplayRotation.class);
        final PowerManager pm = mock(PowerManager.class);
        doReturn(true).when(pm).isInteractive();
        doReturn(pm).when(mContext).getSystemService(eq(Context.POWER_SERVICE));

        mContext.getMainThreadHandler().runWithScissors(() -> mPhoneWindowManager.init(
                new PhoneWindowManager.Injector(mContext,
                        mock(WindowManagerPolicy.WindowManagerFuncs.class))), 0);

        // Case: KeyNotConsumed with meta key.
        KeyEvent keyEvent = new KeyEvent(0, 0, KeyEvent.ACTION_DOWN,
                KeyEvent.KEYCODE_A, 0, KeyEvent.META_META_ON);
        long result = mPhoneWindowManager.interceptKeyBeforeDispatching(mInputToken, keyEvent, 0);
        assertEquals(INTERCEPT_SYSTEM_KEY_NOT_CONSUMED_DELAY, result);

        // Case: KeyNotConsumed without meta key.
        KeyEvent keyEvent1 = new KeyEvent(0, 0, KeyEvent.ACTION_DOWN,
                KeyEvent.KEYCODE_ESCAPE, 0, 0);
        long result1 = mPhoneWindowManager.interceptKeyBeforeDispatching(mInputToken, keyEvent1, 0);
        assertEquals(INTERCEPT_SYSTEM_KEY_NOT_CONSUMED_DELAY, result1);
    }

    private void initPhoneWindowManager() {
    private void initPhoneWindowManager() {
        mPhoneWindowManager.mDefaultDisplayPolicy = mDisplayPolicy;
        mPhoneWindowManager.mDefaultDisplayPolicy = mDisplayPolicy;
        mPhoneWindowManager.mDefaultDisplayRotation = mock(DisplayRotation.class);
        mPhoneWindowManager.mDefaultDisplayRotation = mock(DisplayRotation.class);
Loading