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

Commit 2d5b6806 authored by Charles Wang's avatar Charles Wang
Browse files

Add WindowManager flag for capturing the power key event in the foreground.

This is part of a feature to grant Wallet access to the double-click
power button gesture while the app is in the foreground.

Wallet would need to set this flag for specific windows it wishes to
handle the power gesture.

Bug: 357144512
Test: atest WmTests:WindowManagerServiceTests
Flag: com.android.hardware.input.override_power_key_behavior_in_focused_window

Change-Id: I03c97cac407afca81ed193a33524717e0aed92f0
parent eddf0372
Loading
Loading
Loading
Loading
+2 −0
Original line number Original line Diff line number Diff line
@@ -19037,8 +19037,10 @@ package android.view {
  public static class WindowManager.LayoutParams extends android.view.ViewGroup.LayoutParams implements android.os.Parcelable {
  public static class WindowManager.LayoutParams extends android.view.ViewGroup.LayoutParams implements android.os.Parcelable {
    method public final long getUserActivityTimeout();
    method public final long getUserActivityTimeout();
    method @FlaggedApi("com.android.hardware.input.override_power_key_behavior_in_focused_window") @RequiresPermission(android.Manifest.permission.OVERRIDE_SYSTEM_KEY_BEHAVIOR_IN_FOCUSED_WINDOW) public boolean isReceivePowerKeyDoublePressEnabled();
    method public boolean isSystemApplicationOverlay();
    method public boolean isSystemApplicationOverlay();
    method @FlaggedApi("android.companion.virtualdevice.flags.status_bar_and_insets") public void setInsetsParams(@NonNull java.util.List<android.view.WindowManager.InsetsParams>);
    method @FlaggedApi("android.companion.virtualdevice.flags.status_bar_and_insets") public void setInsetsParams(@NonNull java.util.List<android.view.WindowManager.InsetsParams>);
    method @FlaggedApi("com.android.hardware.input.override_power_key_behavior_in_focused_window") @RequiresPermission(android.Manifest.permission.OVERRIDE_SYSTEM_KEY_BEHAVIOR_IN_FOCUSED_WINDOW) public void setReceivePowerKeyDoublePressEnabled(boolean);
    method @RequiresPermission(android.Manifest.permission.SYSTEM_APPLICATION_OVERLAY) public void setSystemApplicationOverlay(boolean);
    method @RequiresPermission(android.Manifest.permission.SYSTEM_APPLICATION_OVERLAY) public void setSystemApplicationOverlay(boolean);
    method public final void setUserActivityTimeout(long);
    method public final void setUserActivityTimeout(long);
    field @RequiresPermission(android.Manifest.permission.HIDE_NON_SYSTEM_OVERLAY_WINDOWS) public static final int SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS = 524288; // 0x80000
    field @RequiresPermission(android.Manifest.permission.HIDE_NON_SYSTEM_OVERLAY_WINDOWS) public static final int SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS = 524288; // 0x80000
+75 −0
Original line number Original line Diff line number Diff line
@@ -80,6 +80,9 @@ import static android.view.WindowLayoutParamsProto.WINDOW_ANIMATIONS;
import static android.view.WindowLayoutParamsProto.X;
import static android.view.WindowLayoutParamsProto.X;
import static android.view.WindowLayoutParamsProto.Y;
import static android.view.WindowLayoutParamsProto.Y;


import static com.android.hardware.input.Flags.FLAG_OVERRIDE_POWER_KEY_BEHAVIOR_IN_FOCUSED_WINDOW;
import static com.android.hardware.input.Flags.overridePowerKeyBehaviorInFocusedWindow;

import android.Manifest.permission;
import android.Manifest.permission;
import android.annotation.CallbackExecutor;
import android.annotation.CallbackExecutor;
import android.annotation.FlaggedApi;
import android.annotation.FlaggedApi;
@@ -4464,6 +4467,29 @@ public interface WindowManager extends ViewManager {
         */
         */
        public static final int INPUT_FEATURE_SENSITIVE_FOR_PRIVACY = 1 << 3;
        public static final int INPUT_FEATURE_SENSITIVE_FOR_PRIVACY = 1 << 3;


        /**
         * Input feature used to indicate that the system should send power key events to this
         * window when it's in the foreground. The window can override the double press power key
         * gesture behavior.
         *
         * A double press gesture is defined as two
         * {@link KeyEvent.Callback#onKeyDown(int, KeyEvent)} events within a time span defined by
         *  {@link ViewConfiguration#getMultiPressTimeout()}.
         *
         * Note: While the window may receive all power key {@link KeyEvent}s, it can only
         * override the double press gesture behavior. The system will perform default behavior for
         * single, long-press and other multi-press gestures, regardless of if the app handles the
         * key or not.
         *
         * To override the default behavior for double press, the app must return true for the
         * second {@link KeyEvent.Callback#onKeyDown(int, KeyEvent)}. If the app returns false, the
         * system behavior will be performed for double press.
         * @hide
         */
        @RequiresPermission(permission.OVERRIDE_SYSTEM_KEY_BEHAVIOR_IN_FOCUSED_WINDOW)
        public static final int
                INPUT_FEATURE_RECEIVE_POWER_KEY_DOUBLE_PRESS = 1 << 4;

        /**
        /**
         * An internal annotation for flags that can be specified to {@link #inputFeatures}.
         * An internal annotation for flags that can be specified to {@link #inputFeatures}.
         *
         *
@@ -4477,6 +4503,7 @@ public interface WindowManager extends ViewManager {
                INPUT_FEATURE_DISABLE_USER_ACTIVITY,
                INPUT_FEATURE_DISABLE_USER_ACTIVITY,
                INPUT_FEATURE_SPY,
                INPUT_FEATURE_SPY,
                INPUT_FEATURE_SENSITIVE_FOR_PRIVACY,
                INPUT_FEATURE_SENSITIVE_FOR_PRIVACY,
                INPUT_FEATURE_RECEIVE_POWER_KEY_DOUBLE_PRESS
        })
        })
        public @interface InputFeatureFlags {
        public @interface InputFeatureFlags {
        }
        }
@@ -4765,6 +4792,44 @@ public interface WindowManager extends ViewManager {
            privateFlags |= PRIVATE_FLAG_FIT_INSETS_CONTROLLED;
            privateFlags |= PRIVATE_FLAG_FIT_INSETS_CONTROLLED;
        }
        }


        /**
         * Specifies if the system should send power key events to this window when it's in the
         * foreground, with only the double tap gesture behavior being overrideable.
         *
         * @param enabled if true, the system should send power key events to this window when it's
         *              in the foreground, with only the power key double tap gesture being
         *              overrideable.
         * @hide
         */
        @SystemApi
        @RequiresPermission(permission.OVERRIDE_SYSTEM_KEY_BEHAVIOR_IN_FOCUSED_WINDOW)
        @FlaggedApi(FLAG_OVERRIDE_POWER_KEY_BEHAVIOR_IN_FOCUSED_WINDOW)
        public void setReceivePowerKeyDoublePressEnabled(boolean enabled) {
            if (enabled) {
                inputFeatures
                        |= INPUT_FEATURE_RECEIVE_POWER_KEY_DOUBLE_PRESS;
            } else {
                inputFeatures
                        &= ~INPUT_FEATURE_RECEIVE_POWER_KEY_DOUBLE_PRESS;
            }
        }

        /**
         * Returns whether or not the system should send power key events to this window when it's
         * in the foreground, with only the double tap gesture being overrideable.
         *
         * @return if the system should send power key events to this window when it's in the
         * foreground, with only the double tap gesture being overrideable.
         * @hide
         */
        @SystemApi
        @RequiresPermission(permission.OVERRIDE_SYSTEM_KEY_BEHAVIOR_IN_FOCUSED_WINDOW)
        @FlaggedApi(FLAG_OVERRIDE_POWER_KEY_BEHAVIOR_IN_FOCUSED_WINDOW)
        public boolean isReceivePowerKeyDoublePressEnabled() {
            return (inputFeatures
                    & INPUT_FEATURE_RECEIVE_POWER_KEY_DOUBLE_PRESS) != 0;
        }

        /**
        /**
         * Specifies that the window should be considered a trusted system overlay. Trusted system
         * Specifies that the window should be considered a trusted system overlay. Trusted system
         * overlays are ignored when considering whether windows are obscured during input
         * overlays are ignored when considering whether windows are obscured during input
@@ -6157,6 +6222,16 @@ public interface WindowManager extends ViewManager {
                inputFeatures &= ~INPUT_FEATURE_SPY;
                inputFeatures &= ~INPUT_FEATURE_SPY;
                features.add("INPUT_FEATURE_SPY");
                features.add("INPUT_FEATURE_SPY");
            }
            }
            if (overridePowerKeyBehaviorInFocusedWindow()) {
                if ((inputFeatures
                        & INPUT_FEATURE_RECEIVE_POWER_KEY_DOUBLE_PRESS)
                        != 0) {
                    inputFeatures
                            &=
                            ~INPUT_FEATURE_RECEIVE_POWER_KEY_DOUBLE_PRESS;
                    features.add("INPUT_FEATURE_RECEIVE_POWER_KEY_DOUBLE_PRESS");
                }
            }
            if (inputFeatures != 0) {
            if (inputFeatures != 0) {
                features.add(Integer.toHexString(inputFeatures));
                features.add(Integer.toHexString(inputFeatures));
            }
            }
+22 −1
Original line number Original line Diff line number Diff line
@@ -69,6 +69,7 @@ import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
import static android.view.WindowManager.LayoutParams.FLAG_SLIPPERY;
import static android.view.WindowManager.LayoutParams.FLAG_SLIPPERY;
import static android.view.WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL;
import static android.view.WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL;
import static android.view.WindowManager.LayoutParams.INPUT_FEATURE_RECEIVE_POWER_KEY_DOUBLE_PRESS;
import static android.view.WindowManager.LayoutParams.INPUT_FEATURE_SENSITIVE_FOR_PRIVACY;
import static android.view.WindowManager.LayoutParams.INPUT_FEATURE_SENSITIVE_FOR_PRIVACY;
import static android.view.WindowManager.LayoutParams.INPUT_FEATURE_SPY;
import static android.view.WindowManager.LayoutParams.INPUT_FEATURE_SPY;
import static android.view.WindowManager.LayoutParams.INVALID_WINDOW_TYPE;
import static android.view.WindowManager.LayoutParams.INVALID_WINDOW_TYPE;
@@ -101,6 +102,7 @@ import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ER
import static android.view.flags.Flags.sensitiveContentAppProtection;
import static android.view.flags.Flags.sensitiveContentAppProtection;
import static android.window.WindowProviderService.isWindowProviderService;
import static android.window.WindowProviderService.isWindowProviderService;


import static com.android.hardware.input.Flags.overridePowerKeyBehaviorInFocusedWindow;
import static com.android.internal.protolog.WmProtoLogGroups.WM_DEBUG_ADD_REMOVE;
import static com.android.internal.protolog.WmProtoLogGroups.WM_DEBUG_ADD_REMOVE;
import static com.android.internal.protolog.WmProtoLogGroups.WM_DEBUG_ANIM;
import static com.android.internal.protolog.WmProtoLogGroups.WM_DEBUG_ANIM;
import static com.android.internal.protolog.WmProtoLogGroups.WM_DEBUG_BOOT;
import static com.android.internal.protolog.WmProtoLogGroups.WM_DEBUG_BOOT;
@@ -157,9 +159,9 @@ import static com.android.server.wm.WindowManagerServiceDumpProto.INPUT_METHOD_W
import static com.android.server.wm.WindowManagerServiceDumpProto.POLICY;
import static com.android.server.wm.WindowManagerServiceDumpProto.POLICY;
import static com.android.server.wm.WindowManagerServiceDumpProto.ROOT_WINDOW_CONTAINER;
import static com.android.server.wm.WindowManagerServiceDumpProto.ROOT_WINDOW_CONTAINER;
import static com.android.server.wm.WindowManagerServiceDumpProto.WINDOW_FRAMES_VALID;
import static com.android.server.wm.WindowManagerServiceDumpProto.WINDOW_FRAMES_VALID;
import static com.android.window.flags.Flags.enableDisplayFocusInShellTransitions;
import static com.android.window.flags.Flags.multiCrop;
import static com.android.window.flags.Flags.multiCrop;
import static com.android.window.flags.Flags.setScPropertiesInClient;
import static com.android.window.flags.Flags.setScPropertiesInClient;
import static com.android.window.flags.Flags.enableDisplayFocusInShellTransitions;


import android.Manifest;
import android.Manifest;
import android.Manifest.permission;
import android.Manifest.permission;
@@ -9217,6 +9219,25 @@ public class WindowManagerService extends IWindowManager.Stub
                    + "' because it isn't a trusted overlay");
                    + "' because it isn't a trusted overlay");
            return inputFeatures & ~INPUT_FEATURE_SENSITIVE_FOR_PRIVACY;
            return inputFeatures & ~INPUT_FEATURE_SENSITIVE_FOR_PRIVACY;
        }
        }

        // You need OVERRIDE_SYSTEM_KEY_BEHAVIOR_IN_FOCUSED_WINDOW permission to be able
        // to set INPUT_FEATURE_RECEIVE_POWER_KEY_DOUBLE_PRESS.
        if (overridePowerKeyBehaviorInFocusedWindow()
                && (inputFeatures
                & INPUT_FEATURE_RECEIVE_POWER_KEY_DOUBLE_PRESS)
                != 0) {
            final int powerPermissionResult =
                    mContext.checkPermission(
                            permission.OVERRIDE_SYSTEM_KEY_BEHAVIOR_IN_FOCUSED_WINDOW,
                            callingPid,
                            callingUid);
            if (powerPermissionResult != PackageManager.PERMISSION_GRANTED) {
                throw new IllegalArgumentException(
                        "Cannot use INPUT_FEATURE_RECEIVE_POWER_KEY_DOUBLE_PRESS from" + windowName
                                + " because it doesn't have the"
                                + " OVERRIDE_SYSTEM_KEY_BEHAVIOR_IN_FOCUSED_WINDOW permission");
            }
        }
        return inputFeatures;
        return inputFeatures;
    }
    }


+50 −0
Original line number Original line Diff line number Diff line
@@ -29,6 +29,7 @@ import static android.view.Display.FLAG_OWN_FOCUS;
import static android.view.Display.INVALID_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
import static android.view.WindowManager.LayoutParams.FLAG_SECURE;
import static android.view.WindowManager.LayoutParams.FLAG_SECURE;
import static android.view.WindowManager.LayoutParams.INPUT_FEATURE_RECEIVE_POWER_KEY_DOUBLE_PRESS;
import static android.view.WindowManager.LayoutParams.INPUT_FEATURE_SENSITIVE_FOR_PRIVACY;
import static android.view.WindowManager.LayoutParams.INPUT_FEATURE_SENSITIVE_FOR_PRIVACY;
import static android.view.WindowManager.LayoutParams.INPUT_FEATURE_SPY;
import static android.view.WindowManager.LayoutParams.INPUT_FEATURE_SPY;
import static android.view.WindowManager.LayoutParams.INVALID_WINDOW_TYPE;
import static android.view.WindowManager.LayoutParams.INVALID_WINDOW_TYPE;
@@ -47,6 +48,7 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.hardware.input.Flags.FLAG_OVERRIDE_POWER_KEY_BEHAVIOR_IN_FOCUSED_WINDOW;
import static com.android.server.wm.AppCompatConfiguration.LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND;
import static com.android.server.wm.AppCompatConfiguration.LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND;
import static com.android.server.wm.AppCompatConfiguration.LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING;
import static com.android.server.wm.AppCompatConfiguration.LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING;
import static com.android.server.wm.AppCompatConfiguration.LETTERBOX_BACKGROUND_SOLID_COLOR;
import static com.android.server.wm.AppCompatConfiguration.LETTERBOX_BACKGROUND_SOLID_COLOR;
@@ -76,6 +78,7 @@ import static org.mockito.Mockito.when;
import android.app.ActivityThread;
import android.app.ActivityThread;
import android.app.IApplicationThread;
import android.app.IApplicationThread;
import android.content.pm.ActivityInfo;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.graphics.Rect;
import android.graphics.Rect;
import android.os.Binder;
import android.os.Binder;
import android.os.IBinder;
import android.os.IBinder;
@@ -1138,6 +1141,53 @@ public class WindowManagerServiceTests extends WindowTestsBase {
                        null /* region */));
                        null /* region */));
    }
    }


    @Test
    @RequiresFlagsEnabled(FLAG_OVERRIDE_POWER_KEY_BEHAVIOR_IN_FOCUSED_WINDOW)
    public void testUpdateInputChannel_sanitizeWithoutPermission_ThrowsError() {
        final Session session = mock(Session.class);
        final int callingUid = Process.FIRST_APPLICATION_UID;
        final int callingPid = 1234;
        final SurfaceControl surfaceControl = mock(SurfaceControl.class);
        final IBinder window = new Binder();
        final InputTransferToken inputTransferToken = mock(InputTransferToken.class);


        final InputChannel inputChannel = new InputChannel();

        assertThrows(IllegalArgumentException.class, () ->
                mWm.grantInputChannel(session, callingUid, callingPid, DEFAULT_DISPLAY,
                        surfaceControl, window, null /* hostInputToken */, FLAG_NOT_FOCUSABLE,
                        0 /* privateFlags */,
                        INPUT_FEATURE_RECEIVE_POWER_KEY_DOUBLE_PRESS,
                        TYPE_APPLICATION, null /* windowToken */, inputTransferToken,
                        "TestInputChannel", inputChannel));
    }


    @Test
    @RequiresFlagsEnabled(FLAG_OVERRIDE_POWER_KEY_BEHAVIOR_IN_FOCUSED_WINDOW)
    public void testUpdateInputChannel_sanitizeWithPermission_doesNotThrowError() {
        final Session session = mock(Session.class);
        final int callingUid = Process.FIRST_APPLICATION_UID;
        final int callingPid = 1234;
        final SurfaceControl surfaceControl = mock(SurfaceControl.class);
        final IBinder window = new Binder();
        final InputTransferToken inputTransferToken = mock(InputTransferToken.class);

        doReturn(PackageManager.PERMISSION_GRANTED).when(mWm.mContext).checkPermission(
                android.Manifest.permission.OVERRIDE_SYSTEM_KEY_BEHAVIOR_IN_FOCUSED_WINDOW,
                callingPid,
                callingUid);

        final InputChannel inputChannel = new InputChannel();

        mWm.grantInputChannel(session, callingUid, callingPid, DEFAULT_DISPLAY, surfaceControl,
                window, null /* hostInputToken */, FLAG_NOT_FOCUSABLE, 0 /* privateFlags */,
                INPUT_FEATURE_RECEIVE_POWER_KEY_DOUBLE_PRESS,
                TYPE_APPLICATION, null /* windowToken */, inputTransferToken, "TestInputChannel",
                inputChannel);
    }

    @Test
    @Test
    public void testUpdateInputChannel_allowSpyWindowForInputMonitorPermission() {
    public void testUpdateInputChannel_allowSpyWindowForInputMonitorPermission() {
        final Session session = mock(Session.class);
        final Session session = mock(Session.class);