Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e03541b1 authored by Chandru S's avatar Chandru S Committed by Automerger Merge Worker
Browse files

Merge "Add repository to be the source of truth for device posture" into...

Merge "Add repository to be the source of truth for device posture" into tm-qpr-dev am: 6d21c1dc am: 99333024

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



Change-Id: Ib02577674993c64012b8129e483f0c34193d987f
Signed-off-by: default avatarAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
parents a30e5718 99333024
Loading
Loading
Loading
Loading
+27 −0
Original line number Diff line number Diff line
@@ -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?>) {
+58 −0
Original line number Diff line number Diff line
/*
 * 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"
    }
}
+2 −0
Original line number Diff line number Diff line
@@ -31,6 +31,8 @@ interface KeyguardRepositoryModule {
    @Binds
    fun lightRevealScrimRepository(impl: LightRevealScrimRepositoryImpl): LightRevealScrimRepository

    @Binds fun devicePostureRepository(impl: DevicePostureRepositoryImpl): DevicePostureRepository

    @Binds
    fun biometricSettingsRepository(
        impl: BiometricSettingsRepositoryImpl
+41 −0
Original line number Diff line number Diff line
/*
 * 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
            }
        }
    }
}
+52 −0
Original line number Diff line number Diff line
@@ -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)
    }
Loading