Merge "Disable face auth if any SIM is in secure state." into main
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index 5e7e044..104f3d2 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -235,6 +235,9 @@
srcs: [
/* Status bar fakes */
"tests/src/com/android/systemui/statusbar/pipeline/airplane/data/repository/FakeAirplaneModeRepository.kt",
+ "tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionRepository.kt",
+ "tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionsRepository.kt",
+ "tests/src/com/android/systemui/statusbar/pipeline/mobile/util/FakeMobileMappingsProxy.kt",
"tests/src/com/android/systemui/statusbar/pipeline/shared/data/repository/FakeConnectivityRepository.kt",
"tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/FakeWifiRepository.kt",
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 06cf723..e8740a4 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
@@ -26,7 +26,6 @@
import android.util.Log
import com.android.internal.widget.LockPatternUtils
import com.android.systemui.Dumpable
-import com.android.systemui.res.R
import com.android.systemui.biometrics.AuthController
import com.android.systemui.biometrics.data.repository.FacePropertyRepository
import com.android.systemui.biometrics.data.repository.FingerprintPropertyRepository
@@ -40,6 +39,8 @@
import com.android.systemui.dump.DumpManager
import com.android.systemui.keyguard.shared.model.AuthenticationFlags
import com.android.systemui.keyguard.shared.model.DevicePosture
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionsRepository
import com.android.systemui.user.data.repository.UserRepository
import java.io.PrintWriter
import javax.inject.Inject
@@ -133,6 +134,7 @@
devicePostureRepository: DevicePostureRepository,
facePropertyRepository: FacePropertyRepository,
fingerprintPropertyRepository: FingerprintPropertyRepository,
+ mobileConnectionsRepository: MobileConnectionsRepository,
dumpManager: DumpManager,
) : BiometricSettingsRepository, Dumpable {
@@ -346,14 +348,15 @@
.and(isFingerprintBiometricAllowed)
.stateIn(scope, SharingStarted.Eagerly, false)
- override val isFaceAuthEnrolledAndEnabled: Flow<Boolean>
- get() = isFaceAuthenticationEnabled.and(isFaceEnrolled)
+ override val isFaceAuthEnrolledAndEnabled: Flow<Boolean> =
+ isFaceAuthenticationEnabled
+ .and(isFaceEnrolled)
+ .and(mobileConnectionsRepository.isAnySimSecure.isFalse())
- override val isFaceAuthCurrentlyAllowed: Flow<Boolean>
- get() =
- isFaceAuthEnrolledAndEnabled
- .and(isFaceBiometricsAllowed)
- .and(isFaceAuthSupportedInCurrentPosture)
+ override val isFaceAuthCurrentlyAllowed: Flow<Boolean> =
+ isFaceAuthEnrolledAndEnabled
+ .and(isFaceBiometricsAllowed)
+ .and(isFaceAuthSupportedInCurrentPosture)
}
@OptIn(ExperimentalCoroutinesApi::class)
@@ -426,3 +429,5 @@
private fun Flow<Boolean>.and(anotherFlow: Flow<Boolean>): Flow<Boolean> =
this.combine(anotherFlow) { a, b -> a && b }
+
+private fun Flow<Boolean>.isFalse(): Flow<Boolean> = this.map { !it }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/MobileInputLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/MobileInputLogger.kt
index 02473f2..aacdc63 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/MobileInputLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/MobileInputLogger.kt
@@ -186,6 +186,10 @@
}
)
}
+
+ fun logOnSimStateChanged() {
+ buffer.log(TAG, LogLevel.INFO, "onSimStateChanged")
+ }
}
private const val TAG = "MobileInputLog"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionsRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionsRepository.kt
index ea77163..cf1c97c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionsRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionsRepository.kt
@@ -90,4 +90,12 @@
/** Fallback [MobileIconGroup] in the case where there is no icon in the mapping */
val defaultMobileIconGroup: Flow<MobileIconGroup>
+
+ /**
+ * If any active SIM on the device is in
+ * [android.telephony.TelephonyManager.SIM_STATE_PIN_REQUIRED] or
+ * [android.telephony.TelephonyManager.SIM_STATE_PUK_REQUIRED] or
+ * [android.telephony.TelephonyManager.SIM_STATE_PERM_DISABLED]
+ */
+ val isAnySimSecure: Flow<Boolean>
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcher.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcher.kt
index 991ff56..2291631 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcher.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcher.kt
@@ -151,6 +151,8 @@
override val defaultMobileIconGroup: Flow<SignalIcon.MobileIconGroup> =
activeRepo.flatMapLatest { it.defaultMobileIconGroup }
+ override val isAnySimSecure: Flow<Boolean> = activeRepo.flatMapLatest { it.isAnySimSecure }
+
override val defaultDataSubId: StateFlow<Int> =
activeRepo
.flatMapLatest { it.defaultDataSubId }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepository.kt
index ee13d93..c7987e2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepository.kt
@@ -134,6 +134,8 @@
override val defaultMobileIconGroup = flowOf(TelephonyIcons.THREE_G)
+ override val isAnySimSecure: Flow<Boolean> = flowOf(false)
+
override val defaultMobileIconMapping = MutableStateFlow(TelephonyIcons.ICON_NAME_TO_ICON)
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt
index ec54f08..ecb80f2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt
@@ -28,9 +28,10 @@
import android.telephony.TelephonyManager
import androidx.annotation.VisibleForTesting
import com.android.internal.telephony.PhoneConstants
+import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.keyguard.KeyguardUpdateMonitorCallback
import com.android.settingslib.SignalIcon.MobileIconGroup
import com.android.settingslib.mobile.MobileMappings.Config
-import com.android.systemui.res.R
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
import com.android.systemui.dagger.SysUISingleton
@@ -38,6 +39,7 @@
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.log.table.TableLogBuffer
import com.android.systemui.log.table.logDiffsForTable
+import com.android.systemui.res.R
import com.android.systemui.statusbar.pipeline.airplane.data.repository.AirplaneModeRepository
import com.android.systemui.statusbar.pipeline.dagger.MobileSummaryLog
import com.android.systemui.statusbar.pipeline.mobile.data.MobileInputLogger
@@ -94,6 +96,7 @@
// See [CarrierMergedConnectionRepository] for details.
wifiRepository: WifiRepository,
private val fullMobileRepoFactory: FullMobileConnectionRepository.Factory,
+ private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
) : MobileConnectionsRepository {
private var subIdRepositoryCache: MutableMap<Int, FullMobileConnectionRepository> =
mutableMapOf()
@@ -253,6 +256,27 @@
.distinctUntilChanged()
.onEach { logger.logDefaultMobileIconGroup(it) }
+ override val isAnySimSecure: Flow<Boolean> =
+ conflatedCallbackFlow {
+ val callback =
+ object : KeyguardUpdateMonitorCallback() {
+ override fun onSimStateChanged(subId: Int, slotId: Int, simState: Int) {
+ logger.logOnSimStateChanged()
+ trySend(keyguardUpdateMonitor.isSimPinSecure)
+ }
+ }
+ keyguardUpdateMonitor.registerCallback(callback)
+ trySend(false)
+ awaitClose { keyguardUpdateMonitor.removeCallback(callback) }
+ }
+ .logDiffsForTable(
+ tableLogger,
+ LOGGING_PREFIX,
+ columnName = "isAnySimSecure",
+ initialValue = false,
+ )
+ .distinctUntilChanged()
+
override fun getRepoForSubId(subId: Int): FullMobileConnectionRepository =
getOrCreateRepoForSubId(subId)
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 360fa56..944b059 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
@@ -32,7 +32,6 @@
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.res.R
import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.biometrics.AuthController
@@ -48,6 +47,10 @@
import com.android.systemui.keyguard.data.repository.BiometricType.SIDE_FINGERPRINT
import com.android.systemui.keyguard.data.repository.BiometricType.UNDER_DISPLAY_FINGERPRINT
import com.android.systemui.keyguard.shared.model.DevicePosture
+import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionsRepository
+import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
import com.android.systemui.statusbar.policy.DevicePostureController
import com.android.systemui.user.data.repository.FakeUserRepository
import com.android.systemui.util.mockito.eq
@@ -87,6 +90,7 @@
@Mock private lateinit var devicePolicyManager: DevicePolicyManager
@Mock private lateinit var dumpManager: DumpManager
@Mock private lateinit var biometricManager: BiometricManager
+ @Mock private lateinit var tableLogger: TableLogBuffer
@Captor
private lateinit var strongAuthTracker: ArgumentCaptor<LockPatternUtils.StrongAuthTracker>
@Captor private lateinit var authControllerCallback: ArgumentCaptor<AuthController.Callback>
@@ -97,6 +101,7 @@
private lateinit var devicePostureRepository: FakeDevicePostureRepository
private lateinit var facePropertyRepository: FakeFacePropertyRepository
private lateinit var fingerprintPropertyRepository: FakeFingerprintPropertyRepository
+ private lateinit var mobileConnectionsRepository: FakeMobileConnectionsRepository
private lateinit var testDispatcher: TestDispatcher
private lateinit var testScope: TestScope
@@ -112,6 +117,8 @@
devicePostureRepository = FakeDevicePostureRepository()
facePropertyRepository = FakeFacePropertyRepository()
fingerprintPropertyRepository = FakeFingerprintPropertyRepository()
+ mobileConnectionsRepository =
+ FakeMobileConnectionsRepository(FakeMobileMappingsProxy(), tableLogger)
}
private suspend fun createBiometricSettingsRepository() {
@@ -132,6 +139,7 @@
dumpManager = dumpManager,
facePropertyRepository = facePropertyRepository,
fingerprintPropertyRepository = fingerprintPropertyRepository,
+ mobileConnectionsRepository = mobileConnectionsRepository,
)
testScope.runCurrent()
fingerprintPropertyRepository.setProperties(
@@ -421,6 +429,50 @@
}
@Test
+ fun anySimSecure_disablesFaceAuth() =
+ testScope.runTest {
+ faceAuthIsEnrolled()
+ createBiometricSettingsRepository()
+
+ faceAuthIsEnabledByBiometricManager()
+ doNotDisableKeyguardAuthFeatures()
+ mobileConnectionsRepository.isAnySimSecure.value = false
+ runCurrent()
+
+ val isFaceAuthEnabledAndEnrolled by
+ collectLastValue(underTest.isFaceAuthEnrolledAndEnabled)
+
+ assertThat(isFaceAuthEnabledAndEnrolled).isTrue()
+
+ mobileConnectionsRepository.isAnySimSecure.value = true
+ runCurrent()
+
+ assertThat(isFaceAuthEnabledAndEnrolled).isFalse()
+ }
+
+ @Test
+ fun anySimSecure_disablesFaceAuthToNotCurrentlyRun() =
+ testScope.runTest {
+ faceAuthIsEnrolled()
+
+ createBiometricSettingsRepository()
+ val isFaceAuthCurrentlyAllowed by collectLastValue(underTest.isFaceAuthCurrentlyAllowed)
+
+ deviceIsInPostureThatSupportsFaceAuth()
+ doNotDisableKeyguardAuthFeatures()
+ faceAuthIsStrongBiometric()
+ faceAuthIsEnabledByBiometricManager()
+ mobileConnectionsRepository.isAnySimSecure.value = false
+
+ onStrongAuthChanged(STRONG_AUTH_NOT_REQUIRED, PRIMARY_USER_ID)
+ onNonStrongAuthChanged(false, PRIMARY_USER_ID)
+ assertThat(isFaceAuthCurrentlyAllowed).isTrue()
+
+ mobileConnectionsRepository.isAnySimSecure.value = true
+ assertThat(isFaceAuthCurrentlyAllowed).isFalse()
+ }
+
+ @Test
fun biometricManagerControlsFaceAuthenticationEnabledStatus() =
testScope.runTest {
faceAuthIsEnrolled()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionsRepository.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionsRepository.kt
index 99e4030..b54fbd3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionsRepository.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionsRepository.kt
@@ -90,6 +90,8 @@
private val _defaultMobileIconGroup = MutableStateFlow(DEFAULT_ICON)
override val defaultMobileIconGroup = _defaultMobileIconGroup
+ override val isAnySimSecure = MutableStateFlow(false)
+
fun setSubscriptions(subs: List<SubscriptionModel>) {
_subscriptions.value = subs
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcherTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcherTest.kt
index d005972..4d4f33b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcherTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcherTest.kt
@@ -135,6 +135,7 @@
FakeAirplaneModeRepository(),
wifiRepository,
mock(),
+ mock(),
)
demoRepo =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt
index 6f9764a..9148c75 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt
@@ -37,6 +37,8 @@
import android.testing.TestableLooper
import androidx.test.filters.SmallTest
import com.android.internal.telephony.PhoneConstants
+import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.keyguard.KeyguardUpdateMonitorCallback
import com.android.settingslib.R
import com.android.settingslib.mobile.MobileMappings
import com.android.systemui.SysuiTestCase
@@ -104,6 +106,7 @@
@Mock private lateinit var logger: MobileInputLogger
@Mock private lateinit var summaryLogger: TableLogBuffer
@Mock private lateinit var logBufferFactory: TableLogBufferFactory
+ @Mock private lateinit var updateMonitor: KeyguardUpdateMonitor
private val mobileMappings = FakeMobileMappingsProxy()
private val subscriptionManagerProxy = FakeSubscriptionManagerProxy()
@@ -214,6 +217,7 @@
airplaneModeRepository,
wifiRepository,
fullConnectionFactory,
+ updateMonitor,
)
testScope.runCurrent()
@@ -1048,6 +1052,7 @@
airplaneModeRepository,
wifiRepository,
fullConnectionFactory,
+ updateMonitor
)
val latest by collectLastValue(underTest.defaultDataSubRatConfig)
@@ -1103,7 +1108,6 @@
@Test
fun carrierConfig_initialValueIsFetched() =
testScope.runTest {
-
// Value starts out false
assertThat(underTest.defaultDataSubRatConfig.value.showAtLeast3G).isFalse()
@@ -1151,6 +1155,26 @@
assertThat(latest).isEqualTo(null)
}
+ @Test
+ fun anySimSecure_propagatesStateFromKeyguardUpdateMonitor() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.isAnySimSecure)
+ assertThat(latest).isFalse()
+
+ val updateMonitorCallback = argumentCaptor<KeyguardUpdateMonitorCallback>()
+ verify(updateMonitor).registerCallback(updateMonitorCallback.capture())
+
+ whenever(updateMonitor.isSimPinSecure).thenReturn(true)
+ updateMonitorCallback.value.onSimStateChanged(0, 0, 0)
+
+ assertThat(latest).isTrue()
+
+ whenever(updateMonitor.isSimPinSecure).thenReturn(false)
+ updateMonitorCallback.value.onSimStateChanged(0, 0, 0)
+
+ assertThat(latest).isFalse()
+ }
+
private fun TestScope.getDefaultNetworkCallback(): ConnectivityManager.NetworkCallback {
runCurrent()
val callbackCaptor = argumentCaptor<ConnectivityManager.NetworkCallback>()