Merge "[flexiglass] Implement lockscreen bypass." into udc-qpr-dev

This commit is contained in:
Danny Burakov
2023-07-14 19:18:02 +00:00
committed by Android (Google) Code Review
11 changed files with 65 additions and 81 deletions

View File

@@ -63,14 +63,6 @@ interface AuthenticationRepository {
*/
val isUnlocked: StateFlow<Boolean>
/**
* Whether lock screen bypass is enabled. When enabled, the lock screen will be automatically
* dismisses once the authentication challenge is completed. For example, completing a biometric
* authentication challenge via face unlock or fingerprint sensor can automatically bypass the
* lock screen.
*/
val isBypassEnabled: StateFlow<Boolean>
/**
* Whether the auto confirm feature is enabled for the currently-selected user.
*
@@ -113,9 +105,6 @@ interface AuthenticationRepository {
*/
suspend fun isLockscreenEnabled(): Boolean
/** See [isBypassEnabled]. */
fun setBypassEnabled(isBypassEnabled: Boolean)
/** Reports an authentication attempt. */
suspend fun reportAuthenticationAttempt(isSuccessful: Boolean)
@@ -157,7 +146,7 @@ constructor(
private val lockPatternUtils: LockPatternUtils,
) : AuthenticationRepository {
override val isUnlocked: StateFlow<Boolean> = keyguardRepository.isKeyguardUnlocked
override val isUnlocked = keyguardRepository.isKeyguardUnlocked
override suspend fun isLockscreenEnabled(): Boolean {
return withContext(backgroundDispatcher) {
@@ -166,9 +155,6 @@ constructor(
}
}
private val _isBypassEnabled = MutableStateFlow(false)
override val isBypassEnabled: StateFlow<Boolean> = _isBypassEnabled.asStateFlow()
override val isAutoConfirmEnabled: StateFlow<Boolean> =
userRepository.selectedUserInfo
.map { it.id }
@@ -225,10 +211,6 @@ constructor(
}
}
override fun setBypassEnabled(isBypassEnabled: Boolean) {
_isBypassEnabled.value = isBypassEnabled
}
override suspend fun reportAuthenticationAttempt(isSuccessful: Boolean) {
val selectedUserId = userRepository.selectedUserId
withContext(backgroundDispatcher) {

View File

@@ -24,6 +24,7 @@ import com.android.systemui.authentication.shared.model.AuthenticationThrottling
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.keyguard.data.repository.KeyguardRepository
import com.android.systemui.user.data.repository.UserRepository
import com.android.systemui.util.time.SystemClock
import javax.inject.Inject
@@ -51,6 +52,7 @@ constructor(
private val repository: AuthenticationRepository,
@Background private val backgroundDispatcher: CoroutineDispatcher,
private val userRepository: UserRepository,
private val keyguardRepository: KeyguardRepository,
private val clock: SystemClock,
) {
/**
@@ -76,14 +78,6 @@ constructor(
initialValue = true,
)
/**
* Whether lock screen bypass is enabled. When enabled, the lock screen will be automatically
* dismisses once the authentication challenge is completed. For example, completing a biometric
* authentication challenge via face unlock or fingerprint sensor can automatically bypass the
* lock screen.
*/
val isBypassEnabled: StateFlow<Boolean> = repository.isBypassEnabled
/** The current authentication throttling state, only meaningful if [isThrottled] is `true`. */
val throttling: StateFlow<AuthenticationThrottlingModel> = repository.throttling
@@ -155,6 +149,16 @@ constructor(
return !isUnlocked.value && getAuthenticationMethod().isSecure
}
/**
* Whether lock screen bypass is enabled. When enabled, the lock screen will be automatically
* dismisses once the authentication challenge is completed. For example, completing a biometric
* authentication challenge via face unlock or fingerprint sensor can automatically bypass the
* lock screen.
*/
fun isBypassEnabled(): Boolean {
return keyguardRepository.isBypassEnabled()
}
/**
* Attempts to authenticate the user and unlock the device.
*
@@ -218,11 +222,6 @@ constructor(
return authenticationResult.isSuccessful
}
/** See [isBypassEnabled]. */
fun toggleBypassEnabled() {
repository.setBypassEnabled(!repository.isBypassEnabled.value)
}
/** Starts refreshing the throttling state every second. */
private suspend fun startThrottlingCountdown() {
cancelCountdown()

View File

@@ -42,6 +42,7 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.phone.BiometricUnlockController
import com.android.systemui.statusbar.phone.BiometricUnlockController.WakeAndUnlockMode
import com.android.systemui.statusbar.phone.DozeParameters
import com.android.systemui.statusbar.phone.KeyguardBypassController
import com.android.systemui.statusbar.policy.KeyguardStateController
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
@@ -171,6 +172,14 @@ interface KeyguardRepository {
*/
fun isKeyguardShowing(): Boolean
/**
* Whether lock screen bypass is enabled. When enabled, the lock screen will be automatically
* dismissed once the authentication challenge is completed. For example, completing a biometric
* authentication challenge via face unlock or fingerprint sensor can automatically bypass the
* lock screen.
*/
fun isBypassEnabled(): Boolean
/** Sets whether the bottom area UI should animate the transition out of doze state. */
fun setAnimateDozingTransitions(animate: Boolean)
@@ -206,6 +215,7 @@ constructor(
wakefulnessLifecycle: WakefulnessLifecycle,
biometricUnlockController: BiometricUnlockController,
private val keyguardStateController: KeyguardStateController,
private val keyguardBypassController: KeyguardBypassController,
private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
private val dozeTransitionListener: DozeTransitionListener,
private val dozeParameters: DozeParameters,
@@ -252,23 +262,17 @@ constructor(
override val isAodAvailable: Flow<Boolean> =
conflatedCallbackFlow {
val callback =
object : DozeParameters.Callback {
override fun onAlwaysOnChange() {
trySendWithFailureLogging(
dozeParameters.getAlwaysOn(),
TAG,
"updated isAodAvailable"
)
}
DozeParameters.Callback {
trySendWithFailureLogging(
dozeParameters.alwaysOn,
TAG,
"updated isAodAvailable"
)
}
dozeParameters.addCallback(callback)
// Adding the callback does not send an initial update.
trySendWithFailureLogging(
dozeParameters.getAlwaysOn(),
TAG,
"initial isAodAvailable"
)
trySendWithFailureLogging(dozeParameters.alwaysOn, TAG, "initial isAodAvailable")
awaitClose { dozeParameters.removeCallback(callback) }
}
@@ -464,6 +468,10 @@ constructor(
return keyguardStateController.isShowing
}
override fun isBypassEnabled(): Boolean {
return keyguardBypassController.bypassEnabled
}
override val statusBarState: Flow<StatusBarState> = conflatedCallbackFlow {
val callback =
object : StatusBarStateController.StateListener {

View File

@@ -77,7 +77,7 @@ constructor(
authenticationInteractor.isUnlocked
.map { isUnlocked ->
val currentSceneKey = sceneInteractor.currentScene(CONTAINER_NAME).value.key
val isBypassEnabled = authenticationInteractor.isBypassEnabled.value
val isBypassEnabled = authenticationInteractor.isBypassEnabled()
when {
isUnlocked ->
when (currentSceneKey) {

View File

@@ -50,12 +50,6 @@ open class KeyguardBypassController : Dumpable, StackScrollAlgorithm.BypassContr
@DevicePostureInt private var postureState: Int = DEVICE_POSTURE_UNKNOWN
private var pendingUnlock: PendingUnlock? = null
private val listeners = mutableListOf<OnBypassStateChangedListener>()
private val postureCallback = DevicePostureController.Callback { posture ->
if (postureState != posture) {
postureState = posture
notifyListeners()
}
}
private val faceAuthEnabledChangedCallback = object : KeyguardStateController.Callback {
override fun onFaceAuthEnabledChanged() = notifyListeners()
}
@@ -162,10 +156,8 @@ open class KeyguardBypassController : Dumpable, StackScrollAlgorithm.BypassContr
val dismissByDefault = if (context.resources.getBoolean(
com.android.internal.R.bool.config_faceAuthDismissesKeyguard)) 1 else 0
tunerService.addTunable(object : TunerService.Tunable {
override fun onTuningChanged(key: String?, newValue: String?) {
bypassEnabled = tunerService.getValue(key, dismissByDefault) != 0
}
tunerService.addTunable({ key, _ ->
bypassEnabled = tunerService.getValue(key, dismissByDefault) != 0
}, Settings.Secure.FACE_UNLOCK_DISMISSES_KEYGUARD)
lockscreenUserManager.addUserChangedListener(
object : NotificationLockscreenUserManager.UserChangedListener {
@@ -281,8 +273,6 @@ open class KeyguardBypassController : Dumpable, StackScrollAlgorithm.BypassContr
}
companion object {
const val BYPASS_FADE_DURATION = 67
private const val FACE_UNLOCK_BYPASS_NO_OVERRIDE = 0
private const val FACE_UNLOCK_BYPASS_ALWAYS = 1
private const val FACE_UNLOCK_BYPASS_NEVER = 2

View File

@@ -102,19 +102,6 @@ class AuthenticationInteractorTest : SysuiTestCase() {
assertThat(isUnlocked).isFalse()
}
@Test
fun toggleBypassEnabled() =
testScope.runTest {
val isBypassEnabled by collectLastValue(underTest.isBypassEnabled)
assertThat(isBypassEnabled).isFalse()
underTest.toggleBypassEnabled()
assertThat(isBypassEnabled).isTrue()
underTest.toggleBypassEnabled()
assertThat(isBypassEnabled).isFalse()
}
@Test
fun isAuthenticationRequired_lockedAndSecured_true() =
testScope.runTest {

View File

@@ -41,6 +41,7 @@ import com.android.systemui.keyguard.shared.model.WakefulnessState
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.phone.BiometricUnlockController
import com.android.systemui.statusbar.phone.DozeParameters
import com.android.systemui.statusbar.phone.KeyguardBypassController
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.whenever
@@ -73,6 +74,7 @@ class KeyguardRepositoryImplTest : SysuiTestCase() {
@Mock private lateinit var biometricUnlockController: BiometricUnlockController
@Mock private lateinit var dozeTransitionListener: DozeTransitionListener
@Mock private lateinit var authController: AuthController
@Mock private lateinit var keyguardBypassController: KeyguardBypassController
@Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
@Mock private lateinit var dreamOverlayCallbackController: DreamOverlayCallbackController
@Mock private lateinit var dozeParameters: DozeParameters
@@ -92,6 +94,7 @@ class KeyguardRepositoryImplTest : SysuiTestCase() {
wakefulnessLifecycle,
biometricUnlockController,
keyguardStateController,
keyguardBypassController,
keyguardUpdateMonitor,
dozeTransitionListener,
dozeParameters,
@@ -185,6 +188,20 @@ class KeyguardRepositoryImplTest : SysuiTestCase() {
job.cancel()
}
@Test
fun isBypassEnabled_disabledInController() {
whenever(keyguardBypassController.isBypassEnabled).thenReturn(false)
whenever(keyguardBypassController.bypassEnabled).thenReturn(false)
assertThat(underTest.isBypassEnabled()).isFalse()
}
@Test
fun isBypassEnabled_enabledInController() {
whenever(keyguardBypassController.isBypassEnabled).thenReturn(true)
whenever(keyguardBypassController.bypassEnabled).thenReturn(true)
assertThat(underTest.isBypassEnabled()).isTrue()
}
@Test
fun isAodAvailable() = runTest {
val flow = underTest.isAodAvailable

View File

@@ -28,7 +28,6 @@ import com.android.systemui.scene.shared.model.SceneContainerNames
import com.android.systemui.scene.shared.model.SceneKey
import com.android.systemui.scene.shared.model.SceneModel
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.test.runTest
import org.junit.Before
@@ -36,7 +35,6 @@ import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(JUnit4::class)
class SystemUiDefaultSceneContainerStartableTest : SysuiTestCase() {
@@ -385,7 +383,7 @@ class SystemUiDefaultSceneContainerStartableTest : SysuiTestCase() {
) {
featureFlags.set(Flags.SCENE_CONTAINER, isFeatureEnabled)
authenticationRepository.setUnlocked(isDeviceUnlocked)
authenticationRepository.setBypassEnabled(isBypassEnabled)
keyguardRepository.setBypassEnabled(isBypassEnabled)
initialSceneKey?.let {
sceneInteractor.setCurrentScene(SceneContainerNames.SYSTEM_UI_DEFAULT, SceneModel(it))
}

View File

@@ -31,9 +31,6 @@ class FakeAuthenticationRepository(
private val currentTime: () -> Long,
) : AuthenticationRepository {
private val _isBypassEnabled = MutableStateFlow(false)
override val isBypassEnabled: StateFlow<Boolean> = _isBypassEnabled
private val _isAutoConfirmEnabled = MutableStateFlow(false)
override val isAutoConfirmEnabled: StateFlow<Boolean> = _isAutoConfirmEnabled.asStateFlow()
@@ -85,10 +82,6 @@ class FakeAuthenticationRepository(
return (credentialOverride ?: DEFAULT_PIN).size
}
override fun setBypassEnabled(isBypassEnabled: Boolean) {
_isBypassEnabled.value = isBypassEnabled
}
override suspend fun getFailedAuthenticationAttemptCount(): Int {
return failedAttemptCount
}

View File

@@ -114,6 +114,11 @@ class FakeKeyguardRepository : KeyguardRepository {
return _isKeyguardShowing.value
}
private var _isBypassEnabled = false
override fun isBypassEnabled(): Boolean {
return _isBypassEnabled
}
override fun setAnimateDozingTransitions(animate: Boolean) {
_animateBottomAreaDozingTransitions.tryEmit(animate)
}
@@ -198,6 +203,10 @@ class FakeKeyguardRepository : KeyguardRepository {
_isKeyguardUnlocked.value = isUnlocked
}
fun setBypassEnabled(isEnabled: Boolean) {
_isBypassEnabled = isEnabled
}
override fun isUdfpsSupported(): Boolean {
return _isUdfpsSupported.value
}

View File

@@ -142,6 +142,7 @@ class SceneTestUtils(
repository = repository,
backgroundDispatcher = testDispatcher,
userRepository = userRepository,
keyguardRepository = keyguardRepository,
clock = mock { whenever(elapsedRealtime()).thenAnswer { testScope.currentTime } }
)
}