Merge "Provide isUserInLockdown through BiometricSettingsRepository" into udc-dev am: 597021432e

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/22202725

Change-Id: I67d19fc460f24d935d6f6cc09c9805f314596fed
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepository.kt
index 84abf57..d5129a6 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepository.kt
@@ -22,10 +22,10 @@
 import android.content.IntentFilter
 import android.hardware.biometrics.BiometricManager
 import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback
-import android.os.Looper
 import android.os.UserHandle
 import android.util.Log
 import com.android.internal.widget.LockPatternUtils
+import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN
 import com.android.systemui.Dumpable
 import com.android.systemui.R
 import com.android.systemui.biometrics.AuthController
@@ -35,8 +35,8 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Background
-import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.dump.DumpManager
+import com.android.systemui.keyguard.TAG
 import com.android.systemui.keyguard.shared.model.DevicePosture
 import com.android.systemui.user.data.repository.UserRepository
 import java.io.PrintWriter
@@ -45,10 +45,12 @@
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.channels.awaitClose
 import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.filter
 import kotlinx.coroutines.flow.flatMapLatest
 import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.flow.flowOn
@@ -93,8 +95,16 @@
      * restricted to specific postures using [R.integer.config_face_auth_supported_posture]
      */
     val isFaceAuthSupportedInCurrentPosture: Flow<Boolean>
+
+    /**
+     * Whether the user manually locked down the device. This doesn't include device policy manager
+     * lockdown.
+     */
+    val isCurrentUserInLockdown: Flow<Boolean>
 }
 
+const val TAG = "BiometricsRepositoryImpl"
+
 @SysUISingleton
 class BiometricSettingsRepositoryImpl
 @Inject
@@ -103,19 +113,25 @@
     lockPatternUtils: LockPatternUtils,
     broadcastDispatcher: BroadcastDispatcher,
     authController: AuthController,
-    userRepository: UserRepository,
+    private val userRepository: UserRepository,
     devicePolicyManager: DevicePolicyManager,
     @Application scope: CoroutineScope,
     @Background backgroundDispatcher: CoroutineDispatcher,
     biometricManager: BiometricManager?,
-    @Main looper: Looper,
     devicePostureRepository: DevicePostureRepository,
     dumpManager: DumpManager,
 ) : BiometricSettingsRepository, Dumpable {
 
     override val isFaceAuthSupportedInCurrentPosture: Flow<Boolean>
 
+    private val strongAuthTracker = StrongAuthTracker(userRepository, context)
+
+    override val isCurrentUserInLockdown: Flow<Boolean> =
+        strongAuthTracker.currentUserAuthFlags.map { it.isInUserLockdown }
+
     init {
+        Log.d(TAG, "Registering StrongAuthTracker")
+        lockPatternUtils.registerStrongAuthTracker(strongAuthTracker)
         dumpManager.registerDumpable(this)
         val configFaceAuthSupportedPosture =
             DevicePosture.toPosture(
@@ -251,38 +267,14 @@
             .stateIn(scope, SharingStarted.Eagerly, false)
 
     override val isStrongBiometricAllowed: StateFlow<Boolean> =
-        selectedUserId
-            .flatMapLatest { currUserId ->
-                conflatedCallbackFlow {
-                    val callback =
-                        object : LockPatternUtils.StrongAuthTracker(context, looper) {
-                            override fun onStrongAuthRequiredChanged(userId: Int) {
-                                if (currUserId != userId) {
-                                    return
-                                }
-
-                                trySendWithFailureLogging(
-                                    isBiometricAllowedForUser(true, currUserId),
-                                    TAG
-                                )
-                            }
-
-                            override fun onIsNonStrongBiometricAllowedChanged(userId: Int) {
-                                // no-op
-                            }
-                        }
-                    lockPatternUtils.registerStrongAuthTracker(callback)
-                    awaitClose { lockPatternUtils.unregisterStrongAuthTracker(callback) }
-                }
-            }
-            .stateIn(
-                scope,
-                started = SharingStarted.Eagerly,
-                initialValue =
-                    lockPatternUtils.isBiometricAllowedForUser(
-                        userRepository.getSelectedUserInfo().id
-                    )
+        strongAuthTracker.isStrongBiometricAllowed.stateIn(
+            scope,
+            SharingStarted.Eagerly,
+            strongAuthTracker.isBiometricAllowedForUser(
+                true,
+                userRepository.getSelectedUserInfo().id
             )
+        )
 
     override val isFingerprintEnabledByDevicePolicy: StateFlow<Boolean> =
         selectedUserId
@@ -300,9 +292,44 @@
                         userRepository.getSelectedUserInfo().id
                     )
             )
+}
 
-    companion object {
-        private const val TAG = "BiometricsRepositoryImpl"
+private class StrongAuthTracker(private val userRepository: UserRepository, context: Context?) :
+    LockPatternUtils.StrongAuthTracker(context) {
+
+    private val _authFlags =
+        MutableStateFlow(
+            StrongAuthenticationFlags(currentUserId, getStrongAuthForUser(currentUserId))
+        )
+
+    val currentUserAuthFlags: Flow<StrongAuthenticationFlags> =
+        userRepository.selectedUserInfo
+            .map { it.id }
+            .distinctUntilChanged()
+            .flatMapLatest { currUserId ->
+                _authFlags
+                    .filter { it.userId == currUserId }
+                    .onEach { Log.d(TAG, "currentUser authFlags changed, new value: $it") }
+                    .onStart {
+                        emit(
+                            StrongAuthenticationFlags(
+                                currentUserId,
+                                getStrongAuthForUser(currentUserId)
+                            )
+                        )
+                    }
+            }
+
+    val isStrongBiometricAllowed: Flow<Boolean> =
+        currentUserAuthFlags.map { isBiometricAllowedForUser(true, it.userId) }
+
+    private val currentUserId
+        get() = userRepository.getSelectedUserInfo().id
+
+    override fun onStrongAuthRequiredChanged(userId: Int) {
+        val newFlags = getStrongAuthForUser(userId)
+        _authFlags.value = StrongAuthenticationFlags(userId, newFlags)
+        Log.d(TAG, "onStrongAuthRequiredChanged for userId: $userId, flag value: $newFlags")
     }
 }
 
@@ -314,3 +341,11 @@
 
 private fun DevicePolicyManager.isNotActive(userId: Int, policy: Int): Boolean =
     (getKeyguardDisabledFeatures(null, userId) and policy) == 0
+
+private data class StrongAuthenticationFlags(val userId: Int, val flag: Int) {
+    val isInUserLockdown = containsFlag(flag, STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN)
+}
+
+private fun containsFlag(haystack: Int, needle: Int): Boolean {
+    return haystack and needle != 0
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt
index 5dc04f7..fb7d379 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt
@@ -30,6 +30,8 @@
 import com.android.internal.widget.LockPatternUtils
 import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED
 import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT
+import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_TIMEOUT
+import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN
 import com.android.systemui.R
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.biometrics.AuthController
@@ -42,7 +44,6 @@
 import com.android.systemui.keyguard.shared.model.DevicePosture
 import com.android.systemui.statusbar.policy.DevicePostureController
 import com.android.systemui.user.data.repository.FakeUserRepository
-import com.android.systemui.util.mockito.argumentCaptor
 import com.android.systemui.util.mockito.eq
 import com.android.systemui.util.mockito.whenever
 import com.google.common.truth.Truth.assertThat
@@ -78,6 +79,8 @@
     @Mock private lateinit var devicePolicyManager: DevicePolicyManager
     @Mock private lateinit var dumpManager: DumpManager
     @Mock private lateinit var biometricManager: BiometricManager
+    @Captor
+    private lateinit var strongAuthTracker: ArgumentCaptor<LockPatternUtils.StrongAuthTracker>
     @Captor private lateinit var authControllerCallback: ArgumentCaptor<AuthController.Callback>
     @Captor
     private lateinit var biometricManagerCallback:
@@ -112,12 +115,12 @@
                 devicePolicyManager = devicePolicyManager,
                 scope = testScope.backgroundScope,
                 backgroundDispatcher = testDispatcher,
-                looper = testableLooper!!.looper,
-                dumpManager = dumpManager,
                 biometricManager = biometricManager,
                 devicePostureRepository = devicePostureRepository,
+                dumpManager = dumpManager,
             )
         testScope.runCurrent()
+        verify(lockPatternUtils).registerStrongAuthTracker(strongAuthTracker.capture())
     }
 
     @Test
@@ -147,21 +150,18 @@
             val strongBiometricAllowed = collectLastValue(underTest.isStrongBiometricAllowed)
             runCurrent()
 
-            val captor = argumentCaptor<LockPatternUtils.StrongAuthTracker>()
-            verify(lockPatternUtils).registerStrongAuthTracker(captor.capture())
-
-            captor.value.stub.onStrongAuthRequiredChanged(STRONG_AUTH_NOT_REQUIRED, PRIMARY_USER_ID)
-            testableLooper?.processAllMessages() // StrongAuthTracker uses the TestableLooper
+            onStrongAuthChanged(STRONG_AUTH_NOT_REQUIRED, PRIMARY_USER_ID)
             assertThat(strongBiometricAllowed()).isTrue()
 
-            captor.value.stub.onStrongAuthRequiredChanged(
-                STRONG_AUTH_REQUIRED_AFTER_BOOT,
-                PRIMARY_USER_ID
-            )
-            testableLooper?.processAllMessages() // StrongAuthTracker uses the TestableLooper
+            onStrongAuthChanged(STRONG_AUTH_REQUIRED_AFTER_BOOT, PRIMARY_USER_ID)
             assertThat(strongBiometricAllowed()).isFalse()
         }
 
+    private fun onStrongAuthChanged(flags: Int, userId: Int) {
+        strongAuthTracker.value.stub.onStrongAuthRequiredChanged(flags, userId)
+        testableLooper?.processAllMessages() // StrongAuthTracker uses the TestableLooper
+    }
+
     @Test
     fun fingerprintDisabledByDpmChange() =
         testScope.runTest {
@@ -351,6 +351,30 @@
             assertThat(isFaceAuthSupported()).isTrue()
         }
 
+    @Test
+    fun userInLockdownUsesStrongAuthFlagsToDetermineValue() =
+        testScope.runTest {
+            createBiometricSettingsRepository()
+
+            val isUserInLockdown = collectLastValue(underTest.isCurrentUserInLockdown)
+            // has default value.
+            assertThat(isUserInLockdown()).isFalse()
+
+            // change strong auth flags for another user.
+            // Combine with one more flag to check if we do the bitwise and
+            val inLockdown =
+                STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN or STRONG_AUTH_REQUIRED_AFTER_TIMEOUT
+            onStrongAuthChanged(inLockdown, ANOTHER_USER_ID)
+
+            // Still false.
+            assertThat(isUserInLockdown()).isFalse()
+
+            // change strong auth flags for current user.
+            onStrongAuthChanged(inLockdown, PRIMARY_USER_ID)
+
+            assertThat(isUserInLockdown()).isTrue()
+        }
+
     private fun enrollmentChange(biometricType: BiometricType, userId: Int, enabled: Boolean) {
         authControllerCallback.value.onEnrollmentsChanged(biometricType, userId, enabled)
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeBiometricSettingsRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeBiometricSettingsRepository.kt
index d4b1701..d8b3270 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeBiometricSettingsRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeBiometricSettingsRepository.kt
@@ -46,6 +46,10 @@
     override val isFaceAuthSupportedInCurrentPosture: Flow<Boolean>
         get() = flowOf(true)
 
+    private val _isCurrentUserInLockdown = MutableStateFlow(false)
+    override val isCurrentUserInLockdown: Flow<Boolean>
+        get() = _isCurrentUserInLockdown
+
     fun setFingerprintEnrolled(isFingerprintEnrolled: Boolean) {
         _isFingerprintEnrolled.value = isFingerprintEnrolled
     }