Merge "Add SideFps AlternateBouncer" into tm-qpr-dev

This commit is contained in:
Beverly Tai
2023-01-26 16:57:51 +00:00
committed by Android (Google) Code Review
9 changed files with 174 additions and 25 deletions

View File

@@ -54,12 +54,18 @@ import com.android.internal.annotations.VisibleForTesting
import com.android.systemui.Dumpable import com.android.systemui.Dumpable
import com.android.systemui.R import com.android.systemui.R
import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dump.DumpManager import com.android.systemui.dump.DumpManager
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor
import com.android.systemui.recents.OverviewProxyService import com.android.systemui.recents.OverviewProxyService
import com.android.systemui.util.concurrency.DelayableExecutor import com.android.systemui.util.concurrency.DelayableExecutor
import java.io.PrintWriter import java.io.PrintWriter
import javax.inject.Inject import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
private const val TAG = "SideFpsController" private const val TAG = "SideFpsController"
@@ -79,6 +85,9 @@ constructor(
displayManager: DisplayManager, displayManager: DisplayManager,
@Main private val mainExecutor: DelayableExecutor, @Main private val mainExecutor: DelayableExecutor,
@Main private val handler: Handler, @Main private val handler: Handler,
private val alternateBouncerInteractor: AlternateBouncerInteractor,
@Application private val scope: CoroutineScope,
private val featureFlags: FeatureFlags,
dumpManager: DumpManager dumpManager: DumpManager
) : Dumpable { ) : Dumpable {
val requests: HashSet<SideFpsUiRequestSource> = HashSet() val requests: HashSet<SideFpsUiRequestSource> = HashSet()
@@ -168,9 +177,26 @@ constructor(
} }
) )
overviewProxyService.addCallback(overviewProxyListener) overviewProxyService.addCallback(overviewProxyListener)
listenForAlternateBouncerVisibility()
dumpManager.registerDumpable(this) dumpManager.registerDumpable(this)
} }
private fun listenForAlternateBouncerVisibility() {
alternateBouncerInteractor.setAlternateBouncerUIAvailable(true)
if (featureFlags.isEnabled(Flags.MODERN_ALTERNATE_BOUNCER)) {
scope.launch {
alternateBouncerInteractor.isVisible.collect { isVisible: Boolean ->
if (isVisible) {
show(SideFpsUiRequestSource.ALTERNATE_BOUNCER)
} else {
hide(SideFpsUiRequestSource.ALTERNATE_BOUNCER)
}
}
}
}
}
/** Shows the side fps overlay if not already shown. */ /** Shows the side fps overlay if not already shown. */
fun show(request: SideFpsUiRequestSource) { fun show(request: SideFpsUiRequestSource) {
requests.add(request) requests.add(request)
@@ -423,4 +449,5 @@ enum class SideFpsUiRequestSource {
AUTO_SHOW, AUTO_SHOW,
/** Pin, pattern or password bouncer */ /** Pin, pattern or password bouncer */
PRIMARY_BOUNCER, PRIMARY_BOUNCER,
ALTERNATE_BOUNCER
} }

View File

@@ -22,14 +22,18 @@ import com.android.keyguard.KeyguardUpdateMonitorCallback
import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import javax.inject.Inject import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.stateIn
/** Encapsulates state about device entry fingerprint auth mechanism. */ /** Encapsulates state about device entry fingerprint auth mechanism. */
interface DeviceEntryFingerprintAuthRepository { interface DeviceEntryFingerprintAuthRepository {
/** Whether the device entry fingerprint auth is locked out. */ /** Whether the device entry fingerprint auth is locked out. */
val isLockedOut: Flow<Boolean> val isLockedOut: StateFlow<Boolean>
} }
/** /**
@@ -44,29 +48,34 @@ class DeviceEntryFingerprintAuthRepositoryImpl
@Inject @Inject
constructor( constructor(
val keyguardUpdateMonitor: KeyguardUpdateMonitor, val keyguardUpdateMonitor: KeyguardUpdateMonitor,
@Application scope: CoroutineScope,
) : DeviceEntryFingerprintAuthRepository { ) : DeviceEntryFingerprintAuthRepository {
override val isLockedOut: Flow<Boolean> = conflatedCallbackFlow { override val isLockedOut: StateFlow<Boolean> =
val sendLockoutUpdate = conflatedCallbackFlow {
fun() { val sendLockoutUpdate =
trySendWithFailureLogging( fun() {
keyguardUpdateMonitor.isFingerprintLockedOut, trySendWithFailureLogging(
TAG, keyguardUpdateMonitor.isFingerprintLockedOut,
"onLockedOutStateChanged" TAG,
) "onLockedOutStateChanged"
} )
val callback =
object : KeyguardUpdateMonitorCallback() {
override fun onLockedOutStateChanged(biometricSourceType: BiometricSourceType?) {
if (biometricSourceType == BiometricSourceType.FINGERPRINT) {
sendLockoutUpdate()
} }
} val callback =
object : KeyguardUpdateMonitorCallback() {
override fun onLockedOutStateChanged(
biometricSourceType: BiometricSourceType?
) {
if (biometricSourceType == BiometricSourceType.FINGERPRINT) {
sendLockoutUpdate()
}
}
}
keyguardUpdateMonitor.registerCallback(callback)
sendLockoutUpdate()
awaitClose { keyguardUpdateMonitor.removeCallback(callback) }
} }
keyguardUpdateMonitor.registerCallback(callback) .stateIn(scope, started = SharingStarted.Eagerly, initialValue = false)
sendLockoutUpdate()
awaitClose { keyguardUpdateMonitor.removeCallback(callback) }
}
companion object { companion object {
const val TAG = "DeviceEntryFingerprintAuthRepositoryImpl" const val TAG = "DeviceEntryFingerprintAuthRepositoryImpl"

View File

@@ -32,4 +32,9 @@ interface KeyguardRepositoryModule {
fun lightRevealScrimRepository(impl: LightRevealScrimRepositoryImpl): LightRevealScrimRepository fun lightRevealScrimRepository(impl: LightRevealScrimRepositoryImpl): LightRevealScrimRepository
@Binds fun biometricRepository(impl: BiometricRepositoryImpl): BiometricRepository @Binds fun biometricRepository(impl: BiometricRepositoryImpl): BiometricRepository
@Binds
fun deviceEntryFingerprintAuthRepository(
impl: DeviceEntryFingerprintAuthRepositoryImpl
): DeviceEntryFingerprintAuthRepository
} }

View File

@@ -21,6 +21,7 @@ import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.flags.FeatureFlags import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.data.repository.BiometricRepository import com.android.systemui.keyguard.data.repository.BiometricRepository
import com.android.systemui.keyguard.data.repository.DeviceEntryFingerprintAuthRepository
import com.android.systemui.keyguard.data.repository.KeyguardBouncerRepository import com.android.systemui.keyguard.data.repository.KeyguardBouncerRepository
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager.LegacyAlternateBouncer import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager.LegacyAlternateBouncer
import com.android.systemui.util.time.SystemClock import com.android.systemui.util.time.SystemClock
@@ -34,6 +35,7 @@ class AlternateBouncerInteractor
constructor( constructor(
private val bouncerRepository: KeyguardBouncerRepository, private val bouncerRepository: KeyguardBouncerRepository,
private val biometricRepository: BiometricRepository, private val biometricRepository: BiometricRepository,
private val deviceEntryFingerprintAuthRepository: DeviceEntryFingerprintAuthRepository,
private val systemClock: SystemClock, private val systemClock: SystemClock,
private val keyguardUpdateMonitor: KeyguardUpdateMonitor, private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
featureFlags: FeatureFlags, featureFlags: FeatureFlags,
@@ -99,7 +101,8 @@ constructor(
bouncerRepository.isAlternateBouncerUIAvailable.value && bouncerRepository.isAlternateBouncerUIAvailable.value &&
biometricRepository.isFingerprintEnrolled.value && biometricRepository.isFingerprintEnrolled.value &&
biometricRepository.isStrongBiometricAllowed.value && biometricRepository.isStrongBiometricAllowed.value &&
biometricRepository.isFingerprintEnabledByDevicePolicy.value biometricRepository.isFingerprintEnabledByDevicePolicy.value &&
!deviceEntryFingerprintAuthRepository.isLockedOut.value
} else { } else {
legacyAlternateBouncer != null && legacyAlternateBouncer != null &&
keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(true) keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(true)

View File

@@ -51,14 +51,24 @@ import android.view.WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG
import android.view.WindowMetrics import android.view.WindowMetrics
import androidx.test.filters.SmallTest import androidx.test.filters.SmallTest
import com.airbnb.lottie.LottieAnimationView import com.airbnb.lottie.LottieAnimationView
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.ViewMediatorCallback
import com.android.systemui.R import com.android.systemui.R
import com.android.systemui.SysuiTestCase import com.android.systemui.SysuiTestCase
import com.android.systemui.SysuiTestableContext import com.android.systemui.SysuiTestableContext
import com.android.systemui.dump.DumpManager import com.android.systemui.dump.DumpManager
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags.MODERN_ALTERNATE_BOUNCER
import com.android.systemui.keyguard.data.repository.FakeBiometricRepository
import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository
import com.android.systemui.keyguard.data.repository.KeyguardBouncerRepository
import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor
import com.android.systemui.log.table.TableLogBuffer
import com.android.systemui.recents.OverviewProxyService import com.android.systemui.recents.OverviewProxyService
import com.android.systemui.util.concurrency.FakeExecutor import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.time.FakeSystemClock import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.TestCoroutineScope
import org.junit.Before import org.junit.Before
import org.junit.Rule import org.junit.Rule
import org.junit.Test import org.junit.Test
@@ -101,6 +111,9 @@ class SideFpsControllerTest : SysuiTestCase() {
@Captor lateinit var overlayCaptor: ArgumentCaptor<View> @Captor lateinit var overlayCaptor: ArgumentCaptor<View>
@Captor lateinit var overlayViewParamsCaptor: ArgumentCaptor<WindowManager.LayoutParams> @Captor lateinit var overlayViewParamsCaptor: ArgumentCaptor<WindowManager.LayoutParams>
private lateinit var keyguardBouncerRepository: KeyguardBouncerRepository
private lateinit var alternateBouncerInteractor: AlternateBouncerInteractor
private val featureFlags = FakeFeatureFlags()
private val executor = FakeExecutor(FakeSystemClock()) private val executor = FakeExecutor(FakeSystemClock())
private lateinit var overlayController: ISidefpsController private lateinit var overlayController: ISidefpsController
private lateinit var sideFpsController: SideFpsController private lateinit var sideFpsController: SideFpsController
@@ -121,6 +134,24 @@ class SideFpsControllerTest : SysuiTestCase() {
@Before @Before
fun setup() { fun setup() {
featureFlags.set(MODERN_ALTERNATE_BOUNCER, true)
keyguardBouncerRepository =
KeyguardBouncerRepository(
mock(ViewMediatorCallback::class.java),
FakeSystemClock(),
TestCoroutineScope(),
mock(TableLogBuffer::class.java),
)
alternateBouncerInteractor =
AlternateBouncerInteractor(
keyguardBouncerRepository,
FakeBiometricRepository(),
FakeDeviceEntryFingerprintAuthRepository(),
FakeSystemClock(),
mock(KeyguardUpdateMonitor::class.java),
featureFlags,
)
context.addMockSystemService(DisplayManager::class.java, displayManager) context.addMockSystemService(DisplayManager::class.java, displayManager)
context.addMockSystemService(WindowManager::class.java, windowManager) context.addMockSystemService(WindowManager::class.java, windowManager)
@@ -217,7 +248,10 @@ class SideFpsControllerTest : SysuiTestCase() {
displayManager, displayManager,
executor, executor,
handler, handler,
dumpManager alternateBouncerInteractor,
TestCoroutineScope(),
featureFlags,
dumpManager,
) )
overlayController = overlayController =
@@ -507,6 +541,26 @@ class SideFpsControllerTest : SysuiTestCase() {
private fun verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible: Boolean) { private fun verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible: Boolean) {
sideFpsController.overlayOffsets = sensorLocation sideFpsController.overlayOffsets = sensorLocation
}
fun alternateBouncerVisibility_showAndHideSideFpsUI() = testWithDisplay {
// WHEN alternate bouncer is visible
keyguardBouncerRepository.setAlternateVisible(true)
executor.runAllReady()
// THEN side fps shows UI
verify(windowManager).addView(any(), any())
verify(windowManager, never()).removeView(any())
// WHEN alternate bouncer is no longer visible
keyguardBouncerRepository.setAlternateVisible(false)
executor.runAllReady()
// THEN side fps UI is hidden
verify(windowManager).removeView(any())
}
private fun hidesWithTaskbar(visible: Boolean) {
overlayController.show(SENSOR_ID, REASON_UNKNOWN) overlayController.show(SENSOR_ID, REASON_UNKNOWN)
executor.runAllReady() executor.runAllReady()
@@ -515,7 +569,7 @@ class SideFpsControllerTest : SysuiTestCase() {
verify(windowManager).addView(any(), any()) verify(windowManager).addView(any(), any())
verify(windowManager, never()).removeView(any()) verify(windowManager, never()).removeView(any())
verify(sideFpsView).visibility = if (sfpsViewVisible) View.VISIBLE else View.GONE verify(sideFpsView).visibility = if (visible) View.VISIBLE else View.GONE
} }
/** /**

View File

@@ -27,6 +27,7 @@ import com.android.systemui.flags.FeatureFlags
import com.android.systemui.keyguard.DismissCallbackRegistry import com.android.systemui.keyguard.DismissCallbackRegistry
import com.android.systemui.keyguard.data.BouncerView import com.android.systemui.keyguard.data.BouncerView
import com.android.systemui.keyguard.data.repository.BiometricRepository import com.android.systemui.keyguard.data.repository.BiometricRepository
import com.android.systemui.keyguard.data.repository.DeviceEntryFingerprintAuthRepository
import com.android.systemui.keyguard.data.repository.KeyguardBouncerRepository import com.android.systemui.keyguard.data.repository.KeyguardBouncerRepository
import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor
import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerCallbackInteractor import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerCallbackInteractor
@@ -91,6 +92,7 @@ class UdfpsKeyguardViewControllerWithCoroutinesTest : UdfpsKeyguardViewControlle
AlternateBouncerInteractor( AlternateBouncerInteractor(
keyguardBouncerRepository, keyguardBouncerRepository,
mock(BiometricRepository::class.java), mock(BiometricRepository::class.java),
mock(DeviceEntryFingerprintAuthRepository::class.java),
mock(SystemClock::class.java), mock(SystemClock::class.java),
mock(KeyguardUpdateMonitor::class.java), mock(KeyguardUpdateMonitor::class.java),
mock(FeatureFlags::class.java) mock(FeatureFlags::class.java)

View File

@@ -55,7 +55,11 @@ class DeviceEntryFingerprintAuthRepositoryTest : SysuiTestCase() {
MockitoAnnotations.initMocks(this) MockitoAnnotations.initMocks(this)
testScope = TestScope() testScope = TestScope()
underTest = DeviceEntryFingerprintAuthRepositoryImpl(keyguardUpdateMonitor) underTest =
DeviceEntryFingerprintAuthRepositoryImpl(
keyguardUpdateMonitor,
testScope.backgroundScope,
)
} }
@After @After

View File

@@ -23,6 +23,7 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.flags.FakeFeatureFlags import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.data.repository.FakeBiometricRepository import com.android.systemui.keyguard.data.repository.FakeBiometricRepository
import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository
import com.android.systemui.keyguard.data.repository.KeyguardBouncerRepository import com.android.systemui.keyguard.data.repository.KeyguardBouncerRepository
import com.android.systemui.log.table.TableLogBuffer import com.android.systemui.log.table.TableLogBuffer
import com.android.systemui.util.time.FakeSystemClock import com.android.systemui.util.time.FakeSystemClock
@@ -46,6 +47,8 @@ class AlternateBouncerInteractorTest : SysuiTestCase() {
private lateinit var underTest: AlternateBouncerInteractor private lateinit var underTest: AlternateBouncerInteractor
private lateinit var bouncerRepository: KeyguardBouncerRepository private lateinit var bouncerRepository: KeyguardBouncerRepository
private lateinit var biometricRepository: FakeBiometricRepository private lateinit var biometricRepository: FakeBiometricRepository
private lateinit var deviceEntryFingerprintAuthRepository:
FakeDeviceEntryFingerprintAuthRepository
@Mock private lateinit var systemClock: SystemClock @Mock private lateinit var systemClock: SystemClock
@Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
@Mock private lateinit var bouncerLogger: TableLogBuffer @Mock private lateinit var bouncerLogger: TableLogBuffer
@@ -62,11 +65,13 @@ class AlternateBouncerInteractorTest : SysuiTestCase() {
bouncerLogger, bouncerLogger,
) )
biometricRepository = FakeBiometricRepository() biometricRepository = FakeBiometricRepository()
deviceEntryFingerprintAuthRepository = FakeDeviceEntryFingerprintAuthRepository()
featureFlags = FakeFeatureFlags().apply { this.set(Flags.MODERN_ALTERNATE_BOUNCER, true) } featureFlags = FakeFeatureFlags().apply { this.set(Flags.MODERN_ALTERNATE_BOUNCER, true) }
underTest = underTest =
AlternateBouncerInteractor( AlternateBouncerInteractor(
bouncerRepository, bouncerRepository,
biometricRepository, biometricRepository,
deviceEntryFingerprintAuthRepository,
systemClock, systemClock,
keyguardUpdateMonitor, keyguardUpdateMonitor,
featureFlags, featureFlags,
@@ -111,6 +116,14 @@ class AlternateBouncerInteractorTest : SysuiTestCase() {
assertFalse(underTest.canShowAlternateBouncerForFingerprint()) assertFalse(underTest.canShowAlternateBouncerForFingerprint())
} }
@Test
fun canShowAlternateBouncerForFingerprint_fingerprintLockedOut() {
givenCanShowAlternateBouncer()
deviceEntryFingerprintAuthRepository.setLockedOut(true)
assertFalse(underTest.canShowAlternateBouncerForFingerprint())
}
@Test @Test
fun show_whenCanShow() { fun show_whenCanShow() {
givenCanShowAlternateBouncer() givenCanShowAlternateBouncer()
@@ -148,6 +161,7 @@ class AlternateBouncerInteractorTest : SysuiTestCase() {
biometricRepository.setFingerprintEnrolled(true) biometricRepository.setFingerprintEnrolled(true)
biometricRepository.setStrongBiometricAllowed(true) biometricRepository.setStrongBiometricAllowed(true)
biometricRepository.setFingerprintEnabledByDevicePolicy(true) biometricRepository.setFingerprintEnabledByDevicePolicy(true)
deviceEntryFingerprintAuthRepository.setLockedOut(false)
} }
private fun givenCannotShowAlternateBouncer() { private fun givenCannotShowAlternateBouncer() {

View File

@@ -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 kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
class FakeDeviceEntryFingerprintAuthRepository : DeviceEntryFingerprintAuthRepository {
private val _isLockedOut = MutableStateFlow<Boolean>(false)
override val isLockedOut: StateFlow<Boolean> = _isLockedOut.asStateFlow()
fun setLockedOut(lockedOut: Boolean) {
_isLockedOut.value = lockedOut
}
}