Add quickpickup gesture

Only registered when:
- udfps enrolled
- AOD is not enabled
- quickpick enabled:
    adb shell settings put secure doze_quick_pickup_gesture 1
Currently by default, quickpickup is not enabled

On gesture trigger, AOD screen will show for 5 seconds

Test: manual
Bug: 176550666
Change-Id: I804a2590c1d95acad62fbfbc85228ecfc8bfa3ed
This commit is contained in:
Beverly
2021-02-25 10:21:30 -05:00
committed by Beverly Tai
parent d6209d4e18
commit c0ddb20d17
16 changed files with 146 additions and 29 deletions

View File

@@ -52,7 +52,8 @@ public class AmbientDisplayConfiguration {
|| wakeDisplayGestureEnabled(user)
|| pickupGestureEnabled(user)
|| tapGestureEnabled(user)
|| doubleTapGestureEnabled(user);
|| doubleTapGestureEnabled(user)
|| quickPickupSensorEnabled(user);
}
/** {@hide} */
@@ -99,6 +100,13 @@ public class AmbientDisplayConfiguration {
return !TextUtils.isEmpty(doubleTapSensorType());
}
/** {@hide} */
public boolean quickPickupSensorEnabled(int user) {
return boolSettingDefaultOff(Settings.Secure.DOZE_QUICK_PICKUP_GESTURE, user)
&& !TextUtils.isEmpty(quickPickupSensorType())
&& !alwaysOnEnabled(user);
}
/** {@hide} */
public boolean wakeScreenGestureAvailable() {
return mContext.getResources()
@@ -142,6 +150,11 @@ public class AmbientDisplayConfiguration {
return mContext.getResources().getString(R.string.config_dozeUdfpsLongPressSensorType);
}
/** {@hide} */
public String quickPickupSensorType() {
return mContext.getResources().getString(R.string.config_quickPickupSensorType);
}
/** {@hide} */
public boolean pulseOnLongPressEnabled(int user) {
return pulseOnLongPressAvailable() && boolSettingDefaultOff(

View File

@@ -8361,6 +8361,14 @@ public final class Settings {
@Readable
public static final String DOZE_WAKE_DISPLAY_GESTURE = "doze_wake_display_gesture";
/**
* Gesture that wakes up the display on quick pickup, toggling between
* {@link Display.STATE_OFF} and {@link Display.STATE_DOZE}.
* @hide
*/
@Readable
public static final String DOZE_QUICK_PICKUP_GESTURE = "doze_quick_pickup_gesture";
/**
* Whether the device should suppress the current doze configuration and disable dozing.
* @hide

View File

@@ -2268,6 +2268,9 @@
<bool name="config_dozeWakeLockScreenSensorAvailable">false</bool>
<integer name="config_dozeWakeLockScreenDebounce">300</integer>
<!-- Type of the quick pickup sensor. Empty if quick pickup is not supported. -->
<string name="config_quickPickupSensorType" translatable="false"></string>
<!-- Control whether the always on display mode is available. This should only be enabled on
devices where the display has been tuned to be power efficient in DOZE and/or DOZE_SUSPEND
states. -->

View File

@@ -3590,6 +3590,7 @@
<java-symbol type="string" name="config_dozeUdfpsLongPressSensorType" />
<java-symbol type="bool" name="config_dozeWakeLockScreenSensorAvailable" />
<java-symbol type="integer" name="config_dozeWakeLockScreenDebounce" />
<java-symbol type="string" name="config_quickPickupSensorType" />
<java-symbol type="array" name="config_allowedGlobalInstantAppSettings" />
<java-symbol type="array" name="config_allowedSystemInstantAppSettings" />

View File

@@ -153,6 +153,7 @@ public class SecureSettingsValidators {
VALIDATORS.put(Secure.DOZE_TAP_SCREEN_GESTURE, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.DOZE_WAKE_LOCK_SCREEN_GESTURE, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.DOZE_WAKE_DISPLAY_GESTURE, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.DOZE_QUICK_PICKUP_GESTURE, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.NFC_PAYMENT_DEFAULT_COMPONENT, COMPONENT_NAME_VALIDATOR);
VALIDATORS.put(
Secure.AUTOMATIC_STORAGE_MANAGER_DAYS_TO_RETAIN, NON_NEGATIVE_INTEGER_VALIDATOR);

View File

@@ -745,6 +745,7 @@ public class SettingsBackupTest {
Settings.Secure.SILENCE_GESTURE,
Settings.Secure.DOZE_WAKE_LOCK_SCREEN_GESTURE,
Settings.Secure.DOZE_WAKE_DISPLAY_GESTURE,
Settings.Secure.DOZE_QUICK_PICKUP_GESTURE,
Settings.Secure.FACE_UNLOCK_RE_ENROLL,
Settings.Secure.TAP_GESTURE,
Settings.Secure.NEARBY_SHARING_COMPONENT, // not user configurable

View File

@@ -180,6 +180,9 @@
<!-- Doze: duration to avoid false pickup gestures triggered by notification vibrations -->
<integer name="doze_pickup_vibration_threshold">2000</integer>
<!-- Doze: quick pickup duration to stay in AOD until the next gesture is triggered -->
<integer name="doze_quick_pickup_aod_duration">5000</integer>
<!-- Type of a sensor that provides a low-power estimate of the desired display
brightness, suitable to listen to while the device is asleep (e.g. during
always-on display) -->

View File

@@ -225,8 +225,8 @@ public class DozeLog implements Dumpable {
* Appends wake-display event to the logs.
* @param wake if we're waking up or sleeping.
*/
public void traceWakeDisplay(boolean wake) {
mLogger.logWakeDisplay(wake);
public void traceWakeDisplay(boolean wake, @Reason int reason) {
mLogger.logWakeDisplay(wake, reason);
}
/**
@@ -380,6 +380,7 @@ public class DozeLog implements Dumpable {
case REASON_SENSOR_WAKE_UP: return "wakeup";
case REASON_SENSOR_TAP: return "tap";
case REASON_SENSOR_UDFPS_LONG_PRESS: return "udfps";
case REASON_SENSOR_QUICK_PICKUP: return "quickPickup";
default: throw new IllegalArgumentException("invalid reason: " + pulseReason);
}
}
@@ -389,7 +390,7 @@ public class DozeLog implements Dumpable {
PULSE_REASON_SENSOR_SIGMOTION, REASON_SENSOR_PICKUP, REASON_SENSOR_DOUBLE_TAP,
PULSE_REASON_SENSOR_LONG_PRESS, PULSE_REASON_DOCKING, REASON_SENSOR_WAKE_UP,
PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN, REASON_SENSOR_TAP,
REASON_SENSOR_UDFPS_LONG_PRESS})
REASON_SENSOR_UDFPS_LONG_PRESS, REASON_SENSOR_QUICK_PICKUP})
public @interface Reason {}
public static final int PULSE_REASON_NONE = -1;
public static final int PULSE_REASON_INTENT = 0;
@@ -403,6 +404,7 @@ public class DozeLog implements Dumpable {
public static final int PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN = 8;
public static final int REASON_SENSOR_TAP = 9;
public static final int REASON_SENSOR_UDFPS_LONG_PRESS = 10;
public static final int REASON_SENSOR_QUICK_PICKUP = 11;
public static final int TOTAL_REASONS = 11;
public static final int TOTAL_REASONS = 12;
}

View File

@@ -16,6 +16,7 @@
package com.android.systemui.doze
import android.view.Display
import com.android.systemui.doze.DozeLog.Reason
import com.android.systemui.doze.DozeLog.reasonToString
import com.android.systemui.log.LogBuffer
@@ -161,17 +162,18 @@ class DozeLogger @Inject constructor(
fun logDisplayStateChanged(displayState: Int) {
buffer.log(TAG, INFO, {
int1 = displayState
str1 = Display.stateToString(displayState)
}, {
"Display state changed to $int1"
"Display state changed to $str1"
})
}
fun logWakeDisplay(isAwake: Boolean) {
fun logWakeDisplay(isAwake: Boolean, @Reason reason: Int) {
buffer.log(TAG, DEBUG, {
bool1 = isAwake
int1 = reason
}, {
"Display wakefulness changed, isAwake=$bool1"
"Display wakefulness changed, isAwake=$bool1, reason=${reasonToString(int1)}"
})
}

View File

@@ -113,6 +113,8 @@ public class DozeSensors {
mCallback = callback;
mProximitySensor = proximitySensor;
boolean udfpsEnrolled =
authController.isUdfpsEnrolled(KeyguardUpdateMonitor.getCurrentUser());
boolean alwaysOn = mConfig.alwaysOnEnabled(UserHandle.USER_CURRENT);
mSensors = new TriggerSensor[] {
new TriggerSensor(
@@ -159,7 +161,7 @@ public class DozeSensors {
findSensorWithType(config.udfpsLongPressSensorType()),
"doze_pulse_on_auth",
true /* settingDef */,
authController.isUdfpsEnrolled(KeyguardUpdateMonitor.getCurrentUser()),
udfpsEnrolled,
DozeLog.REASON_SENSOR_UDFPS_LONG_PRESS,
true /* reports touch coordinates */,
true /* touchscreen */,
@@ -181,6 +183,15 @@ public class DozeSensors {
false /* touchscreen */,
mConfig.getWakeLockScreenDebounce(),
dozeLog),
new TriggerSensor(
findSensorWithType(config.quickPickupSensorType()),
Settings.Secure.DOZE_QUICK_PICKUP_GESTURE,
false /* setting default */,
config.quickPickupSensorEnabled(KeyguardUpdateMonitor.getCurrentUser())
&& udfpsEnrolled,
DozeLog.REASON_SENSOR_QUICK_PICKUP,
false /* touchCoords */,
false /* touchscreen */, dozeLog),
};
setProxListening(false); // Don't immediately start listening when we register.

View File

@@ -42,10 +42,13 @@ import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.Dependency;
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dock.DockManager;
import com.android.systemui.doze.dagger.DozeScope;
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.util.Assert;
import com.android.systemui.util.concurrency.DelayableExecutor;
import com.android.systemui.util.sensors.AsyncSensorManager;
import com.android.systemui.util.sensors.ProximitySensor;
import com.android.systemui.util.settings.SecureSettings;
@@ -76,6 +79,7 @@ public class DozeTriggers implements DozeMachine.Part {
* Assuming that the screen should start on.
*/
private static boolean sWakeDisplaySensorState = true;
private Runnable mQuickPickupDozeCancellable;
private static final int PROXIMITY_TIMEOUT_DELAY_MS = 500;
@@ -96,6 +100,8 @@ public class DozeTriggers implements DozeMachine.Part {
private final ProximitySensor.ProximityCheck mProxCheck;
private final BroadcastDispatcher mBroadcastDispatcher;
private final AuthController mAuthController;
private final DelayableExecutor mMainExecutor;
private final DelayableExecutor mBgExecutor;
private long mNotificationPulseTime;
private boolean mPulsePending;
@@ -135,7 +141,10 @@ public class DozeTriggers implements DozeMachine.Part {
DOZING_UPDATE_SENSOR_TAP(441),
@UiEvent(doc = "Dozing updated because on display auth was triggered from AOD.")
DOZING_UPDATE_AUTH_TRIGGERED(657);
DOZING_UPDATE_AUTH_TRIGGERED(657),
@UiEvent(doc = "Dozing updated because quick pickup sensor woke up.")
DOZING_UPDATE_QUICK_PICKUP(708);
private final int mId;
@@ -160,6 +169,7 @@ public class DozeTriggers implements DozeMachine.Part {
case 8: return DOZING_UPDATE_SENSOR_WAKE_LOCKSCREEN;
case 9: return DOZING_UPDATE_SENSOR_TAP;
case 10: return DOZING_UPDATE_AUTH_TRIGGERED;
case 11: return DOZING_UPDATE_QUICK_PICKUP;
default: return null;
}
}
@@ -172,7 +182,8 @@ public class DozeTriggers implements DozeMachine.Part {
WakeLock wakeLock, DockManager dockManager,
ProximitySensor proximitySensor, ProximitySensor.ProximityCheck proxCheck,
DozeLog dozeLog, BroadcastDispatcher broadcastDispatcher,
SecureSettings secureSettings, AuthController authController) {
SecureSettings secureSettings, AuthController authController,
@Main DelayableExecutor mainExecutor, @Background DelayableExecutor bgExecutor) {
mContext = context;
mDozeHost = dozeHost;
mConfig = config;
@@ -189,6 +200,8 @@ public class DozeTriggers implements DozeMachine.Part {
mDozeLog = dozeLog;
mBroadcastDispatcher = broadcastDispatcher;
mAuthController = authController;
mMainExecutor = mainExecutor;
mBgExecutor = bgExecutor;
}
@Override
@@ -262,18 +275,22 @@ public class DozeTriggers implements DozeMachine.Part {
boolean isTap = pulseReason == DozeLog.REASON_SENSOR_TAP;
boolean isPickup = pulseReason == DozeLog.REASON_SENSOR_PICKUP;
boolean isLongPress = pulseReason == DozeLog.PULSE_REASON_SENSOR_LONG_PRESS;
boolean isWakeDisplay = pulseReason == DozeLog.REASON_SENSOR_WAKE_UP;
boolean isWakeLockScreen = pulseReason == DozeLog.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN;
boolean isWakeOnPresence = pulseReason == DozeLog.REASON_SENSOR_WAKE_UP;
boolean isWakeOnReach = pulseReason == DozeLog.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN;
boolean isUdfpsLongPress = pulseReason == DozeLog.REASON_SENSOR_UDFPS_LONG_PRESS;
boolean wakeEvent = rawValues != null && rawValues.length > 0 && rawValues[0] != 0;
boolean isQuickPickup = pulseReason == DozeLog.REASON_SENSOR_QUICK_PICKUP;
boolean isWakeDisplayEvent = isQuickPickup || ((isWakeOnPresence || isWakeOnReach)
&& rawValues != null && rawValues.length > 0 && rawValues[0] != 0);
if (isWakeDisplay) {
onWakeScreen(wakeEvent, mMachine.isExecutingTransition() ? null : mMachine.getState());
if (isWakeOnPresence || isQuickPickup) {
onWakeScreen(isQuickPickup || isWakeDisplayEvent,
mMachine.isExecutingTransition() ? null : mMachine.getState(),
pulseReason);
} else if (isLongPress) {
requestPulse(pulseReason, true /* alreadyPerformedProxCheck */,
null /* onPulseSuppressedListener */);
} else if (isWakeLockScreen) {
if (wakeEvent) {
} else if (isWakeOnReach) {
if (isWakeDisplayEvent) {
requestPulse(pulseReason, true /* alreadyPerformedProxCheck */,
null /* onPulseSuppressedListener */);
}
@@ -370,13 +387,17 @@ public class DozeTriggers implements DozeMachine.Part {
* @param state The current state, or null if the state could not be determined due to enqueued
* transitions.
*/
private void onWakeScreen(boolean wake, @Nullable DozeMachine.State state) {
mDozeLog.traceWakeDisplay(wake);
sWakeDisplaySensorState = wake;
private void onWakeScreen(boolean wake, @Nullable DozeMachine.State state, int reason) {
mDozeLog.traceWakeDisplay(wake, reason);
final boolean isWakeOnPresence = reason == DozeLog.REASON_SENSOR_WAKE_UP;
final boolean isQuickPickup = reason == DozeLog.REASON_SENSOR_QUICK_PICKUP;
if (isWakeOnPresence) {
sWakeDisplaySensorState = wake;
}
if (wake) {
proximityCheckThenCall((result) -> {
if (result != null && result) {
if (result != null && result) {
// In pocket, drop event.
return;
}
@@ -385,26 +406,51 @@ public class DozeTriggers implements DozeMachine.Part {
// Logs AOD open due to sensor wake up.
mMetricsLogger.write(new LogMaker(MetricsEvent.DOZING)
.setType(MetricsEvent.TYPE_OPEN)
.setSubtype(DozeLog.REASON_SENSOR_WAKE_UP));
.setSubtype(reason));
if (isQuickPickup) {
// schedule runnable to go back to DOZE
onQuickPickup();
}
} else if (state == DozeMachine.State.DOZE_AOD && isQuickPickup) {
// elongate time in DOZE_AOD, schedule new runnable to go back to DOZE
onQuickPickup();
}
}, true /* alreadyPerformedProxCheck */, DozeLog.REASON_SENSOR_WAKE_UP);
}, isQuickPickup /* alreadyPerformedProxCheck */, reason);
} else {
boolean paused = (state == DozeMachine.State.DOZE_AOD_PAUSED);
boolean pausing = (state == DozeMachine.State.DOZE_AOD_PAUSING);
boolean pulse = (state == DozeMachine.State.DOZE_REQUEST_PULSE)
|| (state == DozeMachine.State.DOZE_PULSING)
|| (state == DozeMachine.State.DOZE_PULSING_BRIGHT);
boolean docked = (state == DozeMachine.State.DOZE_AOD_DOCKED);
if (!pausing && !paused) {
if (isQuickPickup && (pulse || docked)) {
return;
}
mMachine.requestState(DozeMachine.State.DOZE);
// Logs AOD close due to sensor wake up.
mMetricsLogger.write(new LogMaker(MetricsEvent.DOZING)
.setType(MetricsEvent.TYPE_CLOSE)
.setSubtype(DozeLog.REASON_SENSOR_WAKE_UP));
.setSubtype(reason));
}
}
}
private void onQuickPickup() {
cancelQuickPickupDelayableDoze();
mQuickPickupDozeCancellable = mMainExecutor.executeDelayed(() -> {
onWakeScreen(false,
mMachine.isExecutingTransition() ? null : mMachine.getState(),
DozeLog.REASON_SENSOR_QUICK_PICKUP);
}, mDozeParameters.getQuickPickupAodDuration());
}
@Override
public void transitionTo(DozeMachine.State oldState, DozeMachine.State newState) {
switch (newState) {
case INITIALIZED:
sWakeDisplaySensorState = true;
mBroadcastReceiver.register(mBroadcastDispatcher);
mDozeHost.addCallback(mHostCallback);
mDockManager.addListener(mDockEventListener);
@@ -417,7 +463,7 @@ public class DozeTriggers implements DozeMachine.Part {
mWantSensors = true;
mWantTouchScreenSensors = true;
if (newState == DozeMachine.State.DOZE_AOD && !sWakeDisplaySensorState) {
onWakeScreen(false, newState);
onWakeScreen(false, newState, DozeLog.REASON_SENSOR_WAKE_UP);
}
break;
case DOZE_AOD_PAUSED:
@@ -437,6 +483,7 @@ public class DozeTriggers implements DozeMachine.Part {
mDozeSensors.requestTemporaryDisable();
break;
case FINISH:
cancelQuickPickupDelayableDoze();
mBroadcastReceiver.unregister(mBroadcastDispatcher);
mDozeHost.removeCallback(mHostCallback);
mDockManager.removeListener(mDockEventListener);
@@ -460,6 +507,17 @@ public class DozeTriggers implements DozeMachine.Part {
mDozeSensors.setListening(mWantSensors, mWantTouchScreenSensors);
}
/**
* Cancels last scheduled Runnable that transitions to STATE_DOZE (blank screen) after
* going into STATE_AOD (AOD screen) from the quick pickup gesture.
*/
private void cancelQuickPickupDelayableDoze() {
if (mQuickPickupDozeCancellable != null) {
mQuickPickupDozeCancellable.run();
mQuickPickupDozeCancellable = null;
}
}
private void checkTriggersAtInit() {
if (mUiModeManager.getCurrentModeType() == Configuration.UI_MODE_TYPE_CAR
|| mDozeHost.isBlockingDoze()

View File

@@ -148,6 +148,11 @@ public class DozeParameters implements TunerService.Tunable,
return getInt("doze.pickup.vibration.threshold", R.integer.doze_pickup_vibration_threshold);
}
public int getQuickPickupAodDuration() {
return getInt("doze.gesture.quickpickup.duration",
R.integer.doze_quick_pickup_aod_duration);
}
/**
* For how long a wallpaper can be visible in AoD before it fades aways.
* @return duration in millis.
@@ -175,6 +180,10 @@ public class DozeParameters implements TunerService.Tunable,
return mDozeAlwaysOn && !mBatteryController.isAodPowerSave();
}
public boolean isQuickPickupEnabled() {
return mAmbientDisplayConfiguration.quickPickupSensorEnabled(UserHandle.USER_CURRENT);
}
/**
* Some screens need to be completely black before changing the display power mode,
* unexpected behavior might happen if this parameter isn't respected.

View File

@@ -140,11 +140,13 @@ public enum ScrimState {
@Override
public void prepare(ScrimState previousState) {
final boolean alwaysOnEnabled = mDozeParameters.getAlwaysOn();
final boolean quickPickupEnabled = mDozeParameters.isQuickPickupEnabled();
final boolean isDocked = mDockManager.isDocked();
mBlankScreen = mDisplayRequiresBlanking;
mFrontTint = Color.BLACK;
mFrontAlpha = (alwaysOnEnabled || isDocked) ? mAodFrontScrimAlpha : 1f;
mFrontAlpha = (alwaysOnEnabled || isDocked || quickPickupEnabled)
? mAodFrontScrimAlpha : 1f;
mBehindTint = Color.BLACK;
mBehindAlpha = ScrimController.TRANSPARENT;

View File

@@ -60,6 +60,7 @@ public class DozeConfigurationUtil {
when(config.tapSensorType()).thenReturn(null);
when(config.longPressSensorType()).thenReturn(null);
when(config.udfpsLongPressSensorType()).thenReturn(null);
when(config.quickPickupSensorType()).thenReturn(null);
when(config.tapGestureEnabled(anyInt())).thenReturn(true);
when(config.tapSensorAvailable()).thenReturn(true);
@@ -67,6 +68,7 @@ public class DozeConfigurationUtil {
when(config.dozePickupSensorAvailable()).thenReturn(false);
when(config.wakeScreenGestureAvailable()).thenReturn(false);
when(config.quickPickupSensorEnabled(anyInt())).thenReturn(false);
doneHolder[0] = true;
return config;

View File

@@ -104,7 +104,7 @@ public class DozeTriggersTest extends SysuiTestCase {
mTriggers = new DozeTriggers(mContext, mHost, mAlarmManager, config, parameters,
asyncSensorManager, wakeLock, mDockManager, mProximitySensor,
mProximityCheck, mock(DozeLog.class), mBroadcastDispatcher, new FakeSettings(),
mAuthController);
mAuthController, mExecutor, mExecutor);
mTriggers.setDozeMachine(mMachine);
waitForSensorManager();
}

View File

@@ -174,6 +174,7 @@ public class DozeServiceHostTest extends SysuiTestCase {
DozeLog.PULSE_REASON_SENSOR_LONG_PRESS,
DozeLog.PULSE_REASON_DOCKING,
DozeLog.REASON_SENSOR_WAKE_UP,
DozeLog.REASON_SENSOR_QUICK_PICKUP,
DozeLog.REASON_SENSOR_TAP));
HashSet<Integer> reasonsThatDontPulse = new HashSet<>(
Arrays.asList(DozeLog.REASON_SENSOR_PICKUP,