Merge "Add power button cooldown after emergency gesture is triggered." into sc-v2-dev

This commit is contained in:
Patrick Huang
2022-01-28 18:51:04 +00:00
committed by Android (Google) Code Review
4 changed files with 358 additions and 0 deletions

View File

@@ -9194,6 +9194,16 @@ public final class Settings {
public static final String EMERGENCY_GESTURE_SOUND_ENABLED =
"emergency_gesture_sound_enabled";
/**
* The power button "cooldown" period in milliseconds after the Emergency gesture is
* triggered, during which single-key actions on the power button are suppressed. Cooldown
* period is disabled if set to zero.
*
* @hide
*/
public static final String EMERGENCY_GESTURE_POWER_BUTTON_COOLDOWN_PERIOD_MS =
"emergency_gesture_power_button_cooldown_period_ms";
/**
* Whether the camera launch gesture to double tap the power button when the screen is off
* should be disabled.

View File

@@ -272,6 +272,8 @@ public class SecureSettingsValidators {
VALIDATORS.put(Secure.SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.EMERGENCY_GESTURE_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.EMERGENCY_GESTURE_SOUND_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.EMERGENCY_GESTURE_POWER_BUTTON_COOLDOWN_PERIOD_MS,
NON_NEGATIVE_INTEGER_VALIDATOR);
VALIDATORS.put(Secure.ADAPTIVE_CONNECTIVITY_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(
Secure.ASSIST_HANDLES_LEARNING_TIME_ELAPSED_MILLIS, NONE_NEGATIVE_LONG_VALIDATOR);

View File

@@ -82,6 +82,20 @@ public class GestureLauncherService extends SystemService {
*/
private static final int EMERGENCY_GESTURE_POWER_TAP_COUNT_THRESHOLD = 5;
/**
* Default value of the power button "cooldown" period after the Emergency gesture is triggered.
* See {@link Settings.Secure#EMERGENCY_GESTURE_POWER_BUTTON_COOLDOWN_PERIOD_MS}
*/
private static final int EMERGENCY_GESTURE_POWER_BUTTON_COOLDOWN_PERIOD_MS_DEFAULT = 3000;
/**
* Maximum value of the power button "cooldown" period after the Emergency gesture is triggered.
* The value read from {@link Settings.Secure#EMERGENCY_GESTURE_POWER_BUTTON_COOLDOWN_PERIOD_MS}
* is capped at this maximum.
*/
@VisibleForTesting
static final int EMERGENCY_GESTURE_POWER_BUTTON_COOLDOWN_PERIOD_MS_MAX = 5000;
/**
* Number of taps required to launch camera shortcut.
*/
@@ -145,7 +159,14 @@ public class GestureLauncherService extends SystemService {
*/
private boolean mEmergencyGestureEnabled;
/**
* Power button cooldown period in milliseconds, after emergency gesture is triggered. A zero
* value means the cooldown period is disabled.
*/
private int mEmergencyGesturePowerButtonCooldownPeriodMs;
private long mLastPowerDown;
private long mLastEmergencyGestureTriggered;
private int mPowerButtonConsecutiveTaps;
private int mPowerButtonSlowConsecutiveTaps;
private final UiEventLogger mUiEventLogger;
@@ -210,6 +231,7 @@ public class GestureLauncherService extends SystemService {
updateCameraRegistered();
updateCameraDoubleTapPowerEnabled();
updateEmergencyGestureEnabled();
updateEmergencyGesturePowerButtonCooldownPeriodMs();
mUserId = ActivityManager.getCurrentUser();
mContext.registerReceiver(mUserReceiver, new IntentFilter(Intent.ACTION_USER_SWITCHED));
@@ -230,6 +252,10 @@ public class GestureLauncherService extends SystemService {
mContext.getContentResolver().registerContentObserver(
Settings.Secure.getUriFor(Settings.Secure.EMERGENCY_GESTURE_ENABLED),
false, mSettingObserver, mUserId);
mContext.getContentResolver().registerContentObserver(
Settings.Secure.getUriFor(
Settings.Secure.EMERGENCY_GESTURE_POWER_BUTTON_COOLDOWN_PERIOD_MS),
false, mSettingObserver, mUserId);
}
private void updateCameraRegistered() {
@@ -263,6 +289,14 @@ public class GestureLauncherService extends SystemService {
}
}
@VisibleForTesting
void updateEmergencyGesturePowerButtonCooldownPeriodMs() {
int cooldownPeriodMs = getEmergencyGesturePowerButtonCooldownPeriodMs(mContext, mUserId);
synchronized (this) {
mEmergencyGesturePowerButtonCooldownPeriodMs = cooldownPeriodMs;
}
}
private void unregisterCameraLaunchGesture() {
if (mCameraLaunchRegistered) {
mCameraLaunchRegistered = false;
@@ -397,6 +431,20 @@ public class GestureLauncherService extends SystemService {
Settings.Secure.EMERGENCY_GESTURE_ENABLED, 1, userId) != 0;
}
/**
* Gets power button cooldown period in milliseconds after emergency gesture is triggered. The
* value is capped at a maximum
* {@link GestureLauncherService#EMERGENCY_GESTURE_POWER_BUTTON_COOLDOWN_PERIOD_MS_MAX}. If the
* value is zero, it means the cooldown period is disabled.
*/
@VisibleForTesting
static int getEmergencyGesturePowerButtonCooldownPeriodMs(Context context, int userId) {
int cooldown = Settings.Secure.getIntForUser(context.getContentResolver(),
Settings.Secure.EMERGENCY_GESTURE_POWER_BUTTON_COOLDOWN_PERIOD_MS,
EMERGENCY_GESTURE_POWER_BUTTON_COOLDOWN_PERIOD_MS_DEFAULT, userId);
return Math.min(cooldown, EMERGENCY_GESTURE_POWER_BUTTON_COOLDOWN_PERIOD_MS_MAX);
}
/**
* Whether to enable the camera launch gesture.
*/
@@ -445,10 +493,24 @@ public class GestureLauncherService extends SystemService {
*/
public boolean interceptPowerKeyDown(KeyEvent event, boolean interactive,
MutableBoolean outLaunched) {
if (mEmergencyGestureEnabled && mEmergencyGesturePowerButtonCooldownPeriodMs >= 0
&& event.getEventTime() - mLastEmergencyGestureTriggered
< mEmergencyGesturePowerButtonCooldownPeriodMs) {
Slog.i(TAG, String.format(
"Suppressing power button: within %dms cooldown period after Emergency "
+ "Gesture. Begin=%dms, end=%dms.",
mEmergencyGesturePowerButtonCooldownPeriodMs,
mLastEmergencyGestureTriggered,
mLastEmergencyGestureTriggered + mEmergencyGesturePowerButtonCooldownPeriodMs));
outLaunched.value = false;
return true;
}
if (event.isLongPress()) {
// Long presses are sent as a second key down. If the long press threshold is set lower
// than the double tap of sequence interval thresholds, this could cause false double
// taps or consecutive taps, so we want to ignore the long press event.
outLaunched.value = false;
return false;
}
boolean launchCamera = false;
@@ -509,6 +571,12 @@ public class GestureLauncherService extends SystemService {
Slog.i(TAG, "Emergency gesture detected, launching.");
launchEmergencyGesture = handleEmergencyGesture();
mUiEventLogger.log(GestureLauncherEvent.GESTURE_EMERGENCY_TAP_POWER);
// Record emergency trigger time if emergency UI was launched
if (launchEmergencyGesture) {
synchronized (this) {
mLastEmergencyGestureTriggered = event.getEventTime();
}
}
}
mMetricsLogger.histogram("power_consecutive_short_tap_count",
mPowerButtonSlowConsecutiveTaps);
@@ -600,6 +668,7 @@ public class GestureLauncherService extends SystemService {
updateCameraRegistered();
updateCameraDoubleTapPowerEnabled();
updateEmergencyGestureEnabled();
updateEmergencyGesturePowerButtonCooldownPeriodMs();
}
}
};
@@ -610,6 +679,7 @@ public class GestureLauncherService extends SystemService {
updateCameraRegistered();
updateCameraDoubleTapPowerEnabled();
updateEmergencyGestureEnabled();
updateEmergencyGesturePowerButtonCooldownPeriodMs();
}
}
};

View File

@@ -186,6 +186,30 @@ public class GestureLauncherServiceTest {
mContext, FAKE_USER_ID));
}
@Test
public void testGetEmergencyGesturePowerButtonCooldownPeriodMs_enabled() {
withEmergencyGesturePowerButtonCooldownPeriodMsValue(4000);
assertEquals(4000,
mGestureLauncherService.getEmergencyGesturePowerButtonCooldownPeriodMs(mContext,
FAKE_USER_ID));
}
@Test
public void testGetEmergencyGesturePowerButtonCooldownPeriodMs_disabled() {
withEmergencyGesturePowerButtonCooldownPeriodMsValue(0);
assertEquals(0,
mGestureLauncherService.getEmergencyGesturePowerButtonCooldownPeriodMs(mContext,
FAKE_USER_ID));
}
@Test
public void testGetEmergencyGesturePowerButtonCooldownPeriodMs_cappedAtMaximum() {
withEmergencyGesturePowerButtonCooldownPeriodMsValue(10000);
assertEquals(GestureLauncherService.EMERGENCY_GESTURE_POWER_BUTTON_COOLDOWN_PERIOD_MS_MAX,
mGestureLauncherService.getEmergencyGesturePowerButtonCooldownPeriodMs(mContext,
FAKE_USER_ID));
}
@Test
public void testHandleCameraLaunchGesture_userSetupComplete() {
withUserSetupCompleteValue(true);
@@ -644,6 +668,211 @@ public class GestureLauncherServiceTest {
}
}
@Test
public void testInterceptPowerKeyDown_triggerEmergency_singleTaps_cooldownTriggered() {
// Enable power button cooldown
withEmergencyGesturePowerButtonCooldownPeriodMsValue(3000);
mGestureLauncherService.updateEmergencyGesturePowerButtonCooldownPeriodMs();
// Trigger emergency by tapping button 5 times
long eventTime = triggerEmergencyGesture();
// Add enough interval to reset consecutive tap count
long interval = GestureLauncherService.POWER_SHORT_TAP_SEQUENCE_MAX_INTERVAL_MS + 1;
eventTime += interval;
// Subsequent single tap is intercepted, but should not trigger any gesture
KeyEvent keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
IGNORED_REPEAT);
boolean interactive = true;
MutableBoolean outLaunched = new MutableBoolean(true);
boolean intercepted = mGestureLauncherService.interceptPowerKeyDown(keyEvent, interactive,
outLaunched);
assertTrue(intercepted);
assertFalse(outLaunched.value);
// Add enough interval to reset consecutive tap count
interval = GestureLauncherService.POWER_SHORT_TAP_SEQUENCE_MAX_INTERVAL_MS + 1;
eventTime += interval;
// Another single tap should be the same (intercepted but should not trigger gesture)
keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
IGNORED_REPEAT);
interactive = true;
outLaunched = new MutableBoolean(true);
intercepted = mGestureLauncherService.interceptPowerKeyDown(keyEvent, interactive,
outLaunched);
assertTrue(intercepted);
assertFalse(outLaunched.value);
}
@Test
public void
testInterceptPowerKeyDown_triggerEmergency_cameraGestureEnabled_doubleTap_cooldownTriggered() {
// Enable camera double tap gesture
withCameraDoubleTapPowerEnableConfigValue(true);
withCameraDoubleTapPowerDisableSettingValue(0);
mGestureLauncherService.updateCameraDoubleTapPowerEnabled();
// Enable power button cooldown
withEmergencyGesturePowerButtonCooldownPeriodMsValue(3000);
mGestureLauncherService.updateEmergencyGesturePowerButtonCooldownPeriodMs();
// Trigger emergency by tapping button 5 times
long eventTime = triggerEmergencyGesture();
// Add enough interval to reset consecutive tap count
long interval = GestureLauncherService.POWER_SHORT_TAP_SEQUENCE_MAX_INTERVAL_MS + 1;
eventTime += interval;
// Subsequent double tap is intercepted, but should not trigger any gesture
for (int i = 0; i < 2; i++) {
KeyEvent keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION,
IGNORED_CODE, IGNORED_REPEAT);
boolean interactive = true;
MutableBoolean outLaunched = new MutableBoolean(true);
boolean intercepted = mGestureLauncherService.interceptPowerKeyDown(keyEvent,
interactive, outLaunched);
assertTrue(intercepted);
assertFalse(outLaunched.value);
interval = GestureLauncherService.CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
eventTime += interval;
}
}
@Test
public void testInterceptPowerKeyDown_triggerEmergency_fiveTaps_cooldownTriggered() {
// Enable power button cooldown
withEmergencyGesturePowerButtonCooldownPeriodMsValue(3000);
mGestureLauncherService.updateEmergencyGesturePowerButtonCooldownPeriodMs();
// Trigger emergency by tapping button 5 times
long eventTime = triggerEmergencyGesture();
// Add enough interval to reset consecutive tap count
long interval = GestureLauncherService.POWER_SHORT_TAP_SEQUENCE_MAX_INTERVAL_MS + 1;
eventTime += interval;
// Subsequent 5 taps are intercepted, but should not trigger any gesture
for (int i = 0; i < 5; i++) {
KeyEvent keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION,
IGNORED_CODE, IGNORED_REPEAT);
boolean interactive = true;
MutableBoolean outLaunched = new MutableBoolean(true);
boolean intercepted = mGestureLauncherService.interceptPowerKeyDown(keyEvent,
interactive, outLaunched);
assertTrue(intercepted);
assertFalse(outLaunched.value);
interval = GestureLauncherService.CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
eventTime += interval;
}
}
@Test
public void testInterceptPowerKeyDown_triggerEmergency_longPress_cooldownTriggered() {
// Enable power button cooldown
withEmergencyGesturePowerButtonCooldownPeriodMsValue(3000);
mGestureLauncherService.updateEmergencyGesturePowerButtonCooldownPeriodMs();
// Trigger emergency by tapping button 5 times
long eventTime = triggerEmergencyGesture();
// Add enough interval to reset consecutive tap count
long interval = GestureLauncherService.POWER_SHORT_TAP_SEQUENCE_MAX_INTERVAL_MS + 1;
eventTime += interval;
// Subsequent long press is intercepted, but should not trigger any gesture
KeyEvent keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
IGNORED_REPEAT, IGNORED_META_STATE, IGNORED_DEVICE_ID, IGNORED_SCANCODE,
KeyEvent.FLAG_LONG_PRESS);
boolean interactive = true;
MutableBoolean outLaunched = new MutableBoolean(true);
boolean intercepted = mGestureLauncherService.interceptPowerKeyDown(keyEvent, interactive,
outLaunched);
assertTrue(intercepted);
assertFalse(outLaunched.value);
}
@Test
public void testInterceptPowerKeyDown_triggerEmergency_cooldownDisabled_cooldownNotTriggered() {
// Disable power button cooldown by setting cooldown period to 0
withEmergencyGesturePowerButtonCooldownPeriodMsValue(0);
mGestureLauncherService.updateEmergencyGesturePowerButtonCooldownPeriodMs();
// Trigger emergency by tapping button 5 times
long eventTime = triggerEmergencyGesture();
// Add enough interval to reset consecutive tap count
long interval = GestureLauncherService.POWER_SHORT_TAP_SEQUENCE_MAX_INTERVAL_MS + 1;
eventTime += interval;
// Subsequent single tap is NOT intercepted
KeyEvent keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
IGNORED_REPEAT);
boolean interactive = true;
MutableBoolean outLaunched = new MutableBoolean(true);
boolean intercepted = mGestureLauncherService.interceptPowerKeyDown(keyEvent, interactive,
outLaunched);
assertFalse(intercepted);
assertFalse(outLaunched.value);
// Add enough interval to reset consecutive tap count
interval = GestureLauncherService.POWER_SHORT_TAP_SEQUENCE_MAX_INTERVAL_MS + 1;
eventTime += interval;
// Long press also NOT intercepted
keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
IGNORED_REPEAT, IGNORED_META_STATE, IGNORED_DEVICE_ID, IGNORED_SCANCODE,
KeyEvent.FLAG_LONG_PRESS);
interactive = true;
outLaunched = new MutableBoolean(true);
intercepted = mGestureLauncherService.interceptPowerKeyDown(keyEvent, interactive,
outLaunched);
assertFalse(intercepted);
assertFalse(outLaunched.value);
}
@Test
public void
testInterceptPowerKeyDown_triggerEmergency_outsideCooldownPeriod_cooldownNotTriggered() {
// Enable power button cooldown
withEmergencyGesturePowerButtonCooldownPeriodMsValue(5000);
mGestureLauncherService.updateEmergencyGesturePowerButtonCooldownPeriodMs();
// Trigger emergency by tapping button 5 times
long eventTime = triggerEmergencyGesture();
// Add enough interval to be outside of cooldown period
long interval = 5001;
eventTime += interval;
// Subsequent single tap is NOT intercepted
KeyEvent keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
IGNORED_REPEAT);
boolean interactive = true;
MutableBoolean outLaunched = new MutableBoolean(true);
boolean intercepted = mGestureLauncherService.interceptPowerKeyDown(keyEvent, interactive,
outLaunched);
assertFalse(intercepted);
assertFalse(outLaunched.value);
// Add enough interval to reset consecutive tap count
interval = GestureLauncherService.POWER_SHORT_TAP_SEQUENCE_MAX_INTERVAL_MS + 1;
eventTime += interval;
// Long press also NOT intercepted
keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
IGNORED_REPEAT, IGNORED_META_STATE, IGNORED_DEVICE_ID, IGNORED_SCANCODE,
KeyEvent.FLAG_LONG_PRESS);
interactive = true;
outLaunched = new MutableBoolean(true);
intercepted = mGestureLauncherService.interceptPowerKeyDown(keyEvent, interactive,
outLaunched);
assertFalse(intercepted);
assertFalse(outLaunched.value);
}
@Test
public void testInterceptPowerKeyDown_longpress() {
withCameraDoubleTapPowerEnableConfigValue(true);
@@ -1153,6 +1382,45 @@ public class GestureLauncherServiceTest {
assertEquals(1, tapCounts.get(1).intValue());
}
/**
* Helper method to trigger emergency gesture by pressing button for 5 times.
* @return last event time.
*/
private long triggerEmergencyGesture() {
// Enable emergency power gesture
withEmergencyGestureEnabledConfigValue(true);
withEmergencyGestureEnabledSettingValue(true);
mGestureLauncherService.updateEmergencyGestureEnabled();
withUserSetupCompleteValue(true);
// 4 button presses
long eventTime = INITIAL_EVENT_TIME_MILLIS;
boolean interactive = true;
KeyEvent keyEvent;
MutableBoolean outLaunched = new MutableBoolean(false);
for (int i = 0; i < 4; i++) {
keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
IGNORED_REPEAT);
mGestureLauncherService.interceptPowerKeyDown(keyEvent, interactive, outLaunched);
final long interval = GestureLauncherService.CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
eventTime += interval;
}
// 5th button press should trigger the emergency flow
keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
IGNORED_REPEAT);
outLaunched.value = false;
boolean intercepted = mGestureLauncherService.interceptPowerKeyDown(keyEvent, interactive,
outLaunched);
assertTrue(outLaunched.value);
assertTrue(intercepted);
verify(mUiEventLogger, times(1))
.log(GestureLauncherService.GestureLauncherEvent.GESTURE_EMERGENCY_TAP_POWER);
verify(mStatusBarManagerInternal).onEmergencyActionLaunchGestureDetected();
return eventTime;
}
private void withCameraDoubleTapPowerEnableConfigValue(boolean enableConfigValue) {
when(mResources.getBoolean(
com.android.internal.R.bool.config_cameraDoubleTapPowerGestureEnabled))
@@ -1181,6 +1449,14 @@ public class GestureLauncherServiceTest {
UserHandle.USER_CURRENT);
}
private void withEmergencyGesturePowerButtonCooldownPeriodMsValue(int period) {
Settings.Secure.putIntForUser(
mContentResolver,
Settings.Secure.EMERGENCY_GESTURE_POWER_BUTTON_COOLDOWN_PERIOD_MS,
period,
UserHandle.USER_CURRENT);
}
private void withUserSetupCompleteValue(boolean userSetupComplete) {
int userSetupCompleteValue = userSetupComplete ? 1 : 0;
Settings.Secure.putIntForUser(