Loading core/java/android/hardware/input/input_framework.aconfig +7 −0 Original line number Diff line number Diff line Loading @@ -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" } services/core/java/com/android/server/policy/PhoneWindowManager.java +23 −0 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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); Loading Loading @@ -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) { Loading services/core/java/com/android/server/policy/WindowWakeUpPolicy.java +20 −0 Original line number Diff line number Diff line Loading @@ -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()); } Loading Loading @@ -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 Loading services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerTests.java +38 −2 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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; Loading Loading @@ -110,6 +117,7 @@ public class PhoneWindowManagerTests { @Mock private IBinder mInputToken; PhoneWindowManager mNonSpyPhoneWindowManager; PhoneWindowManager mPhoneWindowManager; @Mock Loading @@ -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; Loading @@ -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, Loading Loading @@ -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); Loading Loading @@ -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; } } } Loading
core/java/android/hardware/input/input_framework.aconfig +7 −0 Original line number Diff line number Diff line Loading @@ -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" }
services/core/java/com/android/server/policy/PhoneWindowManager.java +23 −0 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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); Loading Loading @@ -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) { Loading
services/core/java/com/android/server/policy/WindowWakeUpPolicy.java +20 −0 Original line number Diff line number Diff line Loading @@ -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()); } Loading Loading @@ -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 Loading
services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerTests.java +38 −2 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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; Loading Loading @@ -110,6 +117,7 @@ public class PhoneWindowManagerTests { @Mock private IBinder mInputToken; PhoneWindowManager mNonSpyPhoneWindowManager; PhoneWindowManager mPhoneWindowManager; @Mock Loading @@ -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; Loading @@ -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, Loading Loading @@ -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); Loading Loading @@ -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; } } }