Merge "Add repository to be the source of truth for device posture" into tm-qpr-dev
This commit is contained in:
@@ -24,8 +24,10 @@ 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.systemui.Dumpable
|
||||
import com.android.systemui.R
|
||||
import com.android.systemui.biometrics.AuthController
|
||||
import com.android.systemui.broadcast.BroadcastDispatcher
|
||||
import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
|
||||
@@ -35,6 +37,7 @@ 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.shared.model.DevicePosture
|
||||
import com.android.systemui.user.data.repository.UserRepository
|
||||
import java.io.PrintWriter
|
||||
import javax.inject.Inject
|
||||
@@ -47,8 +50,10 @@ import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.flatMapLatest
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.flow.flowOn
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.flow.onStart
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import kotlinx.coroutines.flow.transformLatest
|
||||
@@ -82,6 +87,12 @@ interface BiometricSettingsRepository {
|
||||
|
||||
/** Whether fingerprint feature is enabled for the current user by the DevicePolicy */
|
||||
val isFingerprintEnabledByDevicePolicy: StateFlow<Boolean>
|
||||
|
||||
/**
|
||||
* Whether face authentication is supported for the current device posture. Face auth can be
|
||||
* restricted to specific postures using [R.integer.config_face_auth_supported_posture]
|
||||
*/
|
||||
val isFaceAuthSupportedInCurrentPosture: Flow<Boolean>
|
||||
}
|
||||
|
||||
@SysUISingleton
|
||||
@@ -98,11 +109,27 @@ constructor(
|
||||
@Background backgroundDispatcher: CoroutineDispatcher,
|
||||
biometricManager: BiometricManager?,
|
||||
@Main looper: Looper,
|
||||
devicePostureRepository: DevicePostureRepository,
|
||||
dumpManager: DumpManager,
|
||||
) : BiometricSettingsRepository, Dumpable {
|
||||
|
||||
override val isFaceAuthSupportedInCurrentPosture: Flow<Boolean>
|
||||
|
||||
init {
|
||||
dumpManager.registerDumpable(this)
|
||||
val configFaceAuthSupportedPosture =
|
||||
DevicePosture.toPosture(
|
||||
context.resources.getInteger(R.integer.config_face_auth_supported_posture)
|
||||
)
|
||||
isFaceAuthSupportedInCurrentPosture =
|
||||
if (configFaceAuthSupportedPosture == DevicePosture.UNKNOWN) {
|
||||
flowOf(true)
|
||||
} else {
|
||||
devicePostureRepository.currentDevicePosture.map {
|
||||
it == configFaceAuthSupportedPosture
|
||||
}
|
||||
}
|
||||
.onEach { Log.d(TAG, "isFaceAuthSupportedInCurrentPosture value changed to: $it") }
|
||||
}
|
||||
|
||||
override fun dump(pw: PrintWriter, args: Array<String?>) {
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright (C) 2023 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.systemui.keyguard.data.repository
|
||||
|
||||
import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
|
||||
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
|
||||
import com.android.systemui.dagger.SysUISingleton
|
||||
import com.android.systemui.keyguard.shared.model.DevicePosture
|
||||
import com.android.systemui.statusbar.policy.DevicePostureController
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.channels.awaitClose
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
/** Provide current device posture state. */
|
||||
interface DevicePostureRepository {
|
||||
/** Provides the current device posture. */
|
||||
val currentDevicePosture: Flow<DevicePosture>
|
||||
}
|
||||
|
||||
@SysUISingleton
|
||||
class DevicePostureRepositoryImpl
|
||||
@Inject
|
||||
constructor(private val postureController: DevicePostureController) : DevicePostureRepository {
|
||||
override val currentDevicePosture: Flow<DevicePosture>
|
||||
get() = conflatedCallbackFlow {
|
||||
val sendPostureUpdate = { posture: Int ->
|
||||
val currentDevicePosture = DevicePosture.toPosture(posture)
|
||||
trySendWithFailureLogging(
|
||||
currentDevicePosture,
|
||||
TAG,
|
||||
"Error sending posture update to $currentDevicePosture"
|
||||
)
|
||||
}
|
||||
val callback = DevicePostureController.Callback { sendPostureUpdate(it) }
|
||||
postureController.addCallback(callback)
|
||||
sendPostureUpdate(postureController.devicePosture)
|
||||
|
||||
awaitClose { postureController.removeCallback(callback) }
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val TAG = "PostureRepositoryImpl"
|
||||
}
|
||||
}
|
||||
@@ -31,6 +31,8 @@ interface KeyguardRepositoryModule {
|
||||
@Binds
|
||||
fun lightRevealScrimRepository(impl: LightRevealScrimRepositoryImpl): LightRevealScrimRepository
|
||||
|
||||
@Binds fun devicePostureRepository(impl: DevicePostureRepositoryImpl): DevicePostureRepository
|
||||
|
||||
@Binds
|
||||
fun biometricSettingsRepository(
|
||||
impl: BiometricSettingsRepositoryImpl
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright (C) 2023 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.systemui.keyguard.shared.model
|
||||
|
||||
import com.android.systemui.statusbar.policy.DevicePostureController
|
||||
|
||||
/** Represents the possible posture states of the device. */
|
||||
enum class DevicePosture {
|
||||
UNKNOWN,
|
||||
CLOSED,
|
||||
HALF_OPENED,
|
||||
OPENED,
|
||||
FLIPPED;
|
||||
|
||||
companion object {
|
||||
fun toPosture(@DevicePostureController.DevicePostureInt posture: Int): DevicePosture {
|
||||
return when (posture) {
|
||||
DevicePostureController.DEVICE_POSTURE_CLOSED -> CLOSED
|
||||
DevicePostureController.DEVICE_POSTURE_HALF_OPENED -> HALF_OPENED
|
||||
DevicePostureController.DEVICE_POSTURE_OPENED -> OPENED
|
||||
DevicePostureController.DEVICE_POSTURE_FLIPPED -> FLIPPED
|
||||
DevicePostureController.DEVICE_POSTURE_UNKNOWN -> UNKNOWN
|
||||
else -> UNKNOWN
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -30,6 +30,7 @@ import androidx.test.filters.SmallTest
|
||||
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.systemui.R
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import com.android.systemui.biometrics.AuthController
|
||||
import com.android.systemui.coroutines.collectLastValue
|
||||
@@ -38,11 +39,14 @@ import com.android.systemui.keyguard.data.repository.BiometricType.FACE
|
||||
import com.android.systemui.keyguard.data.repository.BiometricType.REAR_FINGERPRINT
|
||||
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.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
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.test.StandardTestDispatcher
|
||||
import kotlinx.coroutines.test.TestDispatcher
|
||||
import kotlinx.coroutines.test.TestScope
|
||||
@@ -62,6 +66,7 @@ import org.mockito.Mockito.times
|
||||
import org.mockito.Mockito.verify
|
||||
import org.mockito.MockitoAnnotations
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
@SmallTest
|
||||
@TestableLooper.RunWithLooper(setAsMainLooper = true)
|
||||
@RunWith(AndroidTestingRunner::class)
|
||||
@@ -78,6 +83,7 @@ class BiometricSettingsRepositoryTest : SysuiTestCase() {
|
||||
private lateinit var biometricManagerCallback:
|
||||
ArgumentCaptor<IBiometricEnabledOnKeyguardCallback.Stub>
|
||||
private lateinit var userRepository: FakeUserRepository
|
||||
private lateinit var devicePostureRepository: FakeDevicePostureRepository
|
||||
|
||||
private lateinit var testDispatcher: TestDispatcher
|
||||
private lateinit var testScope: TestScope
|
||||
@@ -90,6 +96,7 @@ class BiometricSettingsRepositoryTest : SysuiTestCase() {
|
||||
testDispatcher = StandardTestDispatcher()
|
||||
testScope = TestScope(testDispatcher)
|
||||
userRepository = FakeUserRepository()
|
||||
devicePostureRepository = FakeDevicePostureRepository()
|
||||
}
|
||||
|
||||
private suspend fun createBiometricSettingsRepository() {
|
||||
@@ -108,6 +115,7 @@ class BiometricSettingsRepositoryTest : SysuiTestCase() {
|
||||
looper = testableLooper!!.looper,
|
||||
dumpManager = dumpManager,
|
||||
biometricManager = biometricManager,
|
||||
devicePostureRepository = devicePostureRepository,
|
||||
)
|
||||
testScope.runCurrent()
|
||||
}
|
||||
@@ -299,6 +307,50 @@ class BiometricSettingsRepositoryTest : SysuiTestCase() {
|
||||
verify(biometricManager, times(1)).registerEnabledOnKeyguardCallback(any())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun faceAuthIsAlwaysSupportedIfSpecificPostureIsNotConfigured() =
|
||||
testScope.runTest {
|
||||
overrideResource(
|
||||
R.integer.config_face_auth_supported_posture,
|
||||
DevicePostureController.DEVICE_POSTURE_UNKNOWN
|
||||
)
|
||||
|
||||
createBiometricSettingsRepository()
|
||||
|
||||
assertThat(collectLastValue(underTest.isFaceAuthSupportedInCurrentPosture)()).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun faceAuthIsSupportedOnlyWhenDevicePostureMatchesConfigValue() =
|
||||
testScope.runTest {
|
||||
overrideResource(
|
||||
R.integer.config_face_auth_supported_posture,
|
||||
DevicePostureController.DEVICE_POSTURE_FLIPPED
|
||||
)
|
||||
|
||||
createBiometricSettingsRepository()
|
||||
|
||||
val isFaceAuthSupported =
|
||||
collectLastValue(underTest.isFaceAuthSupportedInCurrentPosture)
|
||||
|
||||
assertThat(isFaceAuthSupported()).isFalse()
|
||||
|
||||
devicePostureRepository.setCurrentPosture(DevicePosture.CLOSED)
|
||||
assertThat(isFaceAuthSupported()).isFalse()
|
||||
|
||||
devicePostureRepository.setCurrentPosture(DevicePosture.HALF_OPENED)
|
||||
assertThat(isFaceAuthSupported()).isFalse()
|
||||
|
||||
devicePostureRepository.setCurrentPosture(DevicePosture.OPENED)
|
||||
assertThat(isFaceAuthSupported()).isFalse()
|
||||
|
||||
devicePostureRepository.setCurrentPosture(DevicePosture.UNKNOWN)
|
||||
assertThat(isFaceAuthSupported()).isFalse()
|
||||
|
||||
devicePostureRepository.setCurrentPosture(DevicePosture.FLIPPED)
|
||||
assertThat(isFaceAuthSupported()).isTrue()
|
||||
}
|
||||
|
||||
private fun enrollmentChange(biometricType: BiometricType, userId: Int, enabled: Boolean) {
|
||||
authControllerCallback.value.onEnrollmentsChanged(biometricType, userId, enabled)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright (C) 2023 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.systemui.keyguard.data.repository
|
||||
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import com.android.systemui.coroutines.collectLastValue
|
||||
import com.android.systemui.keyguard.shared.model.DevicePosture
|
||||
import com.android.systemui.statusbar.policy.DevicePostureController
|
||||
import com.android.systemui.util.mockito.whenever
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.test.TestScope
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.junit.runners.JUnit4
|
||||
import org.mockito.ArgumentCaptor
|
||||
import org.mockito.Captor
|
||||
import org.mockito.Mock
|
||||
import org.mockito.Mockito.verify
|
||||
import org.mockito.MockitoAnnotations
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
@SmallTest
|
||||
@RunWith(JUnit4::class)
|
||||
class DevicePostureRepositoryTest : SysuiTestCase() {
|
||||
private lateinit var underTest: DevicePostureRepository
|
||||
private lateinit var testScope: TestScope
|
||||
@Mock private lateinit var devicePostureController: DevicePostureController
|
||||
@Captor private lateinit var callback: ArgumentCaptor<DevicePostureController.Callback>
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
MockitoAnnotations.initMocks(this)
|
||||
testScope = TestScope()
|
||||
underTest = DevicePostureRepositoryImpl(postureController = devicePostureController)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun postureChangesArePropagated() =
|
||||
testScope.runTest {
|
||||
whenever(devicePostureController.devicePosture)
|
||||
.thenReturn(DevicePostureController.DEVICE_POSTURE_FLIPPED)
|
||||
val currentPosture = collectLastValue(underTest.currentDevicePosture)
|
||||
assertThat(currentPosture()).isEqualTo(DevicePosture.FLIPPED)
|
||||
|
||||
verify(devicePostureController).addCallback(callback.capture())
|
||||
|
||||
callback.value.onPostureChanged(DevicePostureController.DEVICE_POSTURE_UNKNOWN)
|
||||
assertThat(currentPosture()).isEqualTo(DevicePosture.UNKNOWN)
|
||||
|
||||
callback.value.onPostureChanged(DevicePostureController.DEVICE_POSTURE_CLOSED)
|
||||
assertThat(currentPosture()).isEqualTo(DevicePosture.CLOSED)
|
||||
|
||||
callback.value.onPostureChanged(DevicePostureController.DEVICE_POSTURE_HALF_OPENED)
|
||||
assertThat(currentPosture()).isEqualTo(DevicePosture.HALF_OPENED)
|
||||
|
||||
callback.value.onPostureChanged(DevicePostureController.DEVICE_POSTURE_OPENED)
|
||||
assertThat(currentPosture()).isEqualTo(DevicePosture.OPENED)
|
||||
|
||||
callback.value.onPostureChanged(DevicePostureController.DEVICE_POSTURE_FLIPPED)
|
||||
assertThat(currentPosture()).isEqualTo(DevicePosture.FLIPPED)
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,7 @@ import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
|
||||
class FakeBiometricSettingsRepository : BiometricSettingsRepository {
|
||||
|
||||
@@ -42,6 +43,9 @@ class FakeBiometricSettingsRepository : BiometricSettingsRepository {
|
||||
override val isFingerprintEnabledByDevicePolicy =
|
||||
_isFingerprintEnabledByDevicePolicy.asStateFlow()
|
||||
|
||||
override val isFaceAuthSupportedInCurrentPosture: Flow<Boolean>
|
||||
get() = flowOf(true)
|
||||
|
||||
fun setFingerprintEnrolled(isFingerprintEnrolled: Boolean) {
|
||||
_isFingerprintEnrolled.value = isFingerprintEnrolled
|
||||
}
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright (C) 2023 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.systemui.keyguard.data.repository
|
||||
|
||||
import com.android.systemui.keyguard.shared.model.DevicePosture
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
|
||||
class FakeDevicePostureRepository : DevicePostureRepository {
|
||||
private val _currentDevicePosture = MutableStateFlow(DevicePosture.UNKNOWN)
|
||||
override val currentDevicePosture: Flow<DevicePosture>
|
||||
get() = _currentDevicePosture
|
||||
|
||||
fun setCurrentPosture(posture: DevicePosture) {
|
||||
_currentDevicePosture.value = posture
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user