[flexiglass] Fixes issue where Flexiglass starts blank.
The issue stemmed from a premature switch to the Gone scene because
canSwipeToEnter started off as false while upstream state was being
collected, triggering the move.
Fix: 312526380
Test: unit tests added
Test: manually verified that after restarting the zygote process, the
system correctly starts on the Lockscreen scene and not the Gone scene
Test: manually verified that unlocking via the bouncer and via face
unlock + with and without bypass still works as expected
Flag: ACONFIG com.android.systemui.scene_container DEVELOPMENT
Change-Id: I291a9651d29ed5815e8827edad58dcf0b4506b63
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt
index 715fb17..4cddb9c 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt
@@ -103,8 +103,11 @@
initialValue = false,
)
- // Authenticated by a TrustAgent like trusted device, location, etc or by face auth.
- private val passivelyAuthenticated =
+ /**
+ * Whether the user is currently authenticated by a TrustAgent like trusted device, location,
+ * etc., or by face auth.
+ */
+ private val isPassivelyAuthenticated =
merge(
trustRepository.isCurrentUserTrusted,
deviceEntryFaceAuthRepository.isAuthenticated,
@@ -117,25 +120,31 @@
* mechanism like face or trust manager. This returns `false` whenever the lockscreen has been
* dismissed.
*
+ * A value of `null` is meaningless and is used as placeholder while the actual value is still
+ * being loaded in the background.
+ *
* Note: `true` doesn't mean the lockscreen is visible. It may be occluded or covered by other
* UI.
*/
- val canSwipeToEnter =
+ val canSwipeToEnter: StateFlow<Boolean?> =
combine(
// This is true when the user has chosen to show the lockscreen but has not made it
// secure.
authenticationInteractor.authenticationMethod.map {
it == AuthenticationMethodModel.None && repository.isLockscreenEnabled()
},
- passivelyAuthenticated,
+ isPassivelyAuthenticated,
isDeviceEntered
- ) { isSwipeAuthMethod, passivelyAuthenticated, isDeviceEntered ->
- (isSwipeAuthMethod || passivelyAuthenticated) && !isDeviceEntered
+ ) { isSwipeAuthMethod, isPassivelyAuthenticated, isDeviceEntered ->
+ (isSwipeAuthMethod || isPassivelyAuthenticated) && !isDeviceEntered
}
.stateIn(
scope = applicationScope,
started = SharingStarted.Eagerly,
- initialValue = false,
+ // Starts as null to prevent downstream collectors from falsely assuming that the
+ // user can or cannot swipe to enter the device while the real value is being loaded
+ // from upstream data sources.
+ initialValue = null,
)
/**
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
index f3f9c91..1c5330e 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
@@ -146,19 +146,21 @@
isAnySimLocked -> {
switchToScene(
targetSceneKey = SceneKey.Bouncer,
- loggingReason = "Need to authenticate locked sim card."
+ loggingReason = "Need to authenticate locked SIM card."
)
}
- isUnlocked && !canSwipeToEnter -> {
+ isUnlocked && canSwipeToEnter == false -> {
switchToScene(
targetSceneKey = SceneKey.Gone,
- loggingReason = "Sim cards are unlocked."
+ loggingReason = "All SIM cards unlocked and device already" +
+ " unlocked and lockscreen doesn't require a swipe to dismiss."
)
}
else -> {
switchToScene(
targetSceneKey = SceneKey.Lockscreen,
- loggingReason = "Sim cards are unlocked."
+ loggingReason = "All SIM cards unlocked and device still locked" +
+ " or lockscreen still requires a swipe to dismiss."
)
}
}
@@ -205,11 +207,17 @@
// when the user is passively authenticated, the false value here
// when the unlock state changes indicates this is an active
// authentication attempt.
- if (isBypassEnabled || !canSwipeToEnter)
- SceneKey.Gone to
- "device has been unlocked on lockscreen with either " +
- "bypass enabled or using an active authentication mechanism"
- else null
+ when {
+ isBypassEnabled ->
+ SceneKey.Gone to
+ "device has been unlocked on lockscreen with bypass" +
+ " enabled"
+ canSwipeToEnter == false ->
+ SceneKey.Gone to
+ "device has been unlocked on lockscreen using an active" +
+ " authentication mechanism"
+ else -> null
+ }
// Not on lockscreen or bouncer, so remain in the current scene.
else -> null
}
@@ -232,7 +240,7 @@
} else {
val canSwipeToEnter = deviceEntryInteractor.canSwipeToEnter.value
val isUnlocked = deviceEntryInteractor.isUnlocked.value
- if (isUnlocked && !canSwipeToEnter) {
+ if (isUnlocked && canSwipeToEnter == false) {
switchToScene(
targetSceneKey = SceneKey.Gone,
loggingReason =
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModel.kt b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModel.kt
index 2a071de..0065db3 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModel.kt
@@ -66,10 +66,10 @@
private fun upDestinationSceneKey(
isUnlocked: Boolean,
- canSwipeToDismiss: Boolean,
+ canSwipeToDismiss: Boolean?,
): SceneKey {
return when {
- canSwipeToDismiss -> SceneKey.Lockscreen
+ canSwipeToDismiss == true -> SceneKey.Lockscreen
isUnlocked -> SceneKey.Gone
else -> SceneKey.Lockscreen
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorTest.kt
index 0004f52..910097e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorTest.kt
@@ -21,6 +21,7 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.coroutines.collectValues
import com.android.systemui.deviceentry.data.repository.FakeDeviceEntryRepository
import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFaceAuthRepository
import com.android.systemui.keyguard.data.repository.FakeTrustRepository
@@ -56,6 +57,13 @@
)
@Test
+ fun canSwipeToEnter_startsNull() =
+ testScope.runTest {
+ val values by collectValues(underTest.canSwipeToEnter)
+ assertThat(values[0]).isNull()
+ }
+
+ @Test
fun isUnlocked_whenAuthMethodIsNoneAndLockscreenDisabled_isTrue() =
testScope.runTest {
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.None)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
index c3294ff..c953743 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
@@ -61,7 +61,6 @@
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import com.google.common.truth.Truth.assertWithMessage
-import junit.framework.Assert.assertTrue
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.MutableStateFlow
@@ -273,6 +272,9 @@
}
@Test
+ fun startsInLockscreenScene() = testScope.runTest { assertCurrentScene(SceneKey.Lockscreen) }
+
+ @Test
fun clickLockButtonAndEnterCorrectPin_unlocksDevice() =
testScope.runTest {
emulateUserDrivenTransition(SceneKey.Bouncer)
@@ -336,7 +338,7 @@
testScope.runTest {
val upDestinationSceneKey by collectLastValue(shadeSceneViewModel.upDestinationSceneKey)
setAuthMethod(AuthenticationMethodModel.None, enableLockscreen = true)
- assertTrue(deviceEntryInteractor.canSwipeToEnter.value)
+ assertThat(deviceEntryInteractor.canSwipeToEnter.value).isTrue()
assertCurrentScene(SceneKey.Lockscreen)
// Emulate a user swipe to dismiss the lockscreen.
diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
index c4ec56c..adc1c61 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
@@ -145,6 +145,18 @@
}
@Test
+ fun startsInLockscreenScene() =
+ testScope.runTest {
+ val currentSceneKey by collectLastValue(sceneInteractor.desiredScene.map { it.key })
+ prepareState()
+
+ underTest.start()
+ runCurrent()
+
+ assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen)
+ }
+
+ @Test
fun switchToLockscreenWhenDeviceLocks() =
testScope.runTest {
val currentSceneKey by collectLastValue(sceneInteractor.desiredScene.map { it.key })