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

Commit 53dac827 authored by Zhengping Jiang's avatar Zhengping Jiang
Browse files

Power: Set policy to wake up when HID reconnect

Bluetoth HID host broadcasts the profile connection state. The window
policy listens to the broadcast and wake up the screen.

Bug: 385357349
Bug: 376121154
Test: wake up with one keyboard click
Test: atest PhoneWindowManagerTests
Flag: com.android.hardware.input.hid_bluetooth_wakeup
Change-Id: I104f903d8dc25fa0321061f35ab2c8febff569ec
parent 6656eb4c
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -190,3 +190,10 @@ flag {
        purpose: PURPOSE_BUGFIX
    }
}

flag {
    name: "hid_bluetooth_wakeup"
    namespace: "desktop_pnp"
    description: "Allow Bluetooth HID profile connection to fully wake up the device."
    bug: "385357349"
}
+23 −0
Original line number Diff line number Diff line
@@ -24,6 +24,8 @@ import static android.Manifest.permission.SYSTEM_APPLICATION_OVERLAY;
import static android.app.AppOpsManager.OP_CREATE_ACCESSIBILITY_OVERLAY;
import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW;
import static android.app.AppOpsManager.OP_TOAST_WINDOW;
import static android.bluetooth.BluetoothHidHost.ACTION_CONNECTION_STATE_CHANGED;
import static android.bluetooth.BluetoothProfile.STATE_CONNECTED;
import static android.content.PermissionChecker.PID_UNKNOWN;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
@@ -79,6 +81,7 @@ import static android.view.WindowManagerGlobal.ADD_PERMISSION_DENIED;
import static android.view.contentprotection.flags.Flags.createAccessibilityOverlayAppOpEnabled;

import static com.android.hardware.input.Flags.enableNew25q2Keycodes;
import static com.android.hardware.input.Flags.hidBluetoothWakeup;
import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_COVERED;
import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_COVER_ABSENT;
import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_UNCOVERED;
@@ -118,6 +121,7 @@ import android.app.NotificationManager;
import android.app.ProgressDialog;
import android.app.SearchManager;
import android.app.UiModeManager;
import android.bluetooth.BluetoothProfile;
import android.content.ActivityNotFoundException;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -2398,6 +2402,12 @@ public class PhoneWindowManager implements WindowManagerPolicy {
        filter = new IntentFilter(Intent.ACTION_USER_SWITCHED);
        mContext.registerReceiver(mMultiuserReceiver, filter);

        // register for Bluetooth HID profile broadcasts.
        if (hidBluetoothWakeup()) {
            filter = new IntentFilter(ACTION_CONNECTION_STATE_CHANGED);
            mContext.registerReceiver(mBluetoothHidReceiver, filter);
        }

        mVibrator = (Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE);

        mGlobalKeyManager = new GlobalKeyManager(mContext);
@@ -5153,6 +5163,19 @@ public class PhoneWindowManager implements WindowManagerPolicy {
        }
    };

    BroadcastReceiver mBluetoothHidReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            if (ACTION_CONNECTION_STATE_CHANGED.equals(intent.getAction())) {
                Integer state = (Integer) intent.getExtra(BluetoothProfile.EXTRA_STATE);
                final boolean interactive = mDefaultDisplayPolicy.isAwake();
                if (state != null && !interactive && state == STATE_CONNECTED) {
                    mWindowWakeUpPolicy.wakeUpFromBluetooth();
                }
            }
        }
    };

    @Override
    public void startedWakingUpGlobal(@WakeReason int reason) {

+20 −0
Original line number Diff line number Diff line
@@ -89,6 +89,7 @@ class WindowWakeUpPolicy {
                com.android.internal.R.bool.config_allowTheaterModeWakeFromLidSwitch);
        mAllowTheaterModeWakeFromWakeGesture = res.getBoolean(
                com.android.internal.R.bool.config_allowTheaterModeWakeFromGesture);

        if (supportInputWakeupDelegate()) {
            LocalServices.addService(WindowWakeUpPolicyInternal.class, new LocalService());
        }
@@ -236,6 +237,25 @@ class WindowWakeUpPolicy {
        return true;
    }

    /**
     * Wakes up due to a Bluetooth HID profile connection.
     *
     * The policy at the theater mode is the same as the motion, because Bluetooth HID
     * connection is caused by user motions.
     *
     * @return {@code true} if the policy allows the requested wake up and the request has been
     *      executed; {@code false} otherwise.
     */
    boolean wakeUpFromBluetooth() {
        if (!canWakeUp(mAllowTheaterModeWakeFromMotion)) {
            if (DEBUG) Slog.d(TAG, "Unable to wake up from Bluetooth.");
            return false;
        }
        wakeUp(mClock.uptimeMillis(), WAKE_REASON_WAKE_MOTION, "BLUETOOTH_DEVICE_CONNECTED");
        return true;
    }


    private boolean canWakeUp(boolean wakeInTheaterMode) {
        if (supportInputWakeupDelegate() && isDefaultDisplayOn()) {
            // If the default display is on, theater mode should not influence whether or not
+38 −2
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package com.android.server.policy;

import static android.bluetooth.BluetoothHidHost.ACTION_CONNECTION_STATE_CHANGED;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
@@ -34,6 +35,7 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
import static com.android.hardware.input.Flags.FLAG_HID_BLUETOOTH_WAKEUP;
import static com.android.server.policy.PhoneWindowManager.EXTRA_TRIGGER_HUB;
import static com.android.server.policy.PhoneWindowManager.SHORT_PRESS_POWER_DREAM_OR_AWAKE_OR_SLEEP;
import static com.android.server.policy.PhoneWindowManager.SHORT_PRESS_POWER_HUB_OR_DREAM_OR_SLEEP;
@@ -46,11 +48,16 @@ import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyList;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;

import android.app.ActivityManager;
import android.app.AppOpsManager;
import android.bluetooth.BluetoothProfile;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.hardware.input.InputManager;
import android.hardware.input.KeyGestureEvent;
import android.os.Bundle;
@@ -110,6 +117,7 @@ public class PhoneWindowManagerTests {

    @Mock private IBinder mInputToken;

    PhoneWindowManager mNonSpyPhoneWindowManager;
    PhoneWindowManager mPhoneWindowManager;

    @Mock
@@ -135,6 +143,10 @@ public class PhoneWindowManagerTests {
    private KeyguardServiceDelegate mKeyguardServiceDelegate;
    @Mock
    private LockPatternUtils mLockPatternUtils;
    @Mock
    private WindowWakeUpPolicy mWindowWakeUpPolicy;
    @Mock
    private PackageManager mPackageManager;

    private static final int INTERCEPT_SYSTEM_KEY_NOT_CONSUMED_DELAY = 0;

@@ -143,7 +155,8 @@ public class PhoneWindowManagerTests {
        MockitoAnnotations.initMocks(this);
        when(mContext.getSystemService(Context.POWER_SERVICE)).thenReturn(mPowerManager);

        mPhoneWindowManager = spy(new PhoneWindowManager());
        mNonSpyPhoneWindowManager = new PhoneWindowManager();
        mPhoneWindowManager = spy(mNonSpyPhoneWindowManager);
        spyOn(ActivityManager.getService());

        mLocalServiceKeeperRule.overrideLocalService(ActivityTaskManagerInternal.class,
@@ -488,6 +501,29 @@ public class PhoneWindowManagerTests {
                KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL);
    }

    @Test
    @EnableFlags(FLAG_HID_BLUETOOTH_WAKEUP)
    public void testBluetoothHidConnectionBroadcastCanWakeup() {
        when(mContext.getPackageManager()).thenReturn(mPackageManager);
        when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_PC)).thenReturn(true);
        initNonSpyPhoneWindowManager();

        final Intent intent = new Intent(ACTION_CONNECTION_STATE_CHANGED);
        intent.putExtra(BluetoothProfile.EXTRA_STATE, BluetoothProfile.STATE_CONNECTED);
        ArgumentCaptor<BroadcastReceiver> captor = ArgumentCaptor.forClass(BroadcastReceiver.class);
        verify(mContext).registerReceiver(captor.capture(), argThat(intentFilter ->
                                intentFilter.matchAction(ACTION_CONNECTION_STATE_CHANGED)));
        captor.getValue().onReceive(mContext, intent);
        verify(mWindowWakeUpPolicy).wakeUpFromBluetooth();
    }

    private void initNonSpyPhoneWindowManager() {
        mNonSpyPhoneWindowManager.mDefaultDisplayPolicy = mDisplayPolicy;
        mNonSpyPhoneWindowManager.mDefaultDisplayRotation = mock(DisplayRotation.class);
        mContext.getMainThreadHandler().runWithScissors(() -> mNonSpyPhoneWindowManager.init(
                new TestInjector(mContext, mock(WindowManagerPolicy.WindowManagerFuncs.class))), 0);
    }

    private void initPhoneWindowManager() {
        mPhoneWindowManager.mDefaultDisplayPolicy = mDisplayPolicy;
        mPhoneWindowManager.mDefaultDisplayRotation = mock(DisplayRotation.class);
@@ -521,7 +557,7 @@ public class PhoneWindowManagerTests {
         * mock it out so we don't have to unregister it after every test.
         */
        WindowWakeUpPolicy getWindowWakeUpPolicy() {
            return mock(WindowWakeUpPolicy.class);
            return mWindowWakeUpPolicy;
        }
    }
}