Loading packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/SideFpsSensorInteractor.kt +26 −9 Original line number Diff line number Diff line Loading @@ -19,6 +19,7 @@ package com.android.systemui.biometrics.domain.interactor import android.content.Context import android.hardware.biometrics.SensorLocationInternal import android.view.WindowManager import com.android.systemui.biometrics.FingerprintInteractiveToAuthProvider import com.android.systemui.biometrics.data.repository.FingerprintPropertyRepository import com.android.systemui.biometrics.domain.model.SideFpsSensorLocation import com.android.systemui.biometrics.shared.model.DisplayRotation Loading @@ -27,17 +28,16 @@ import com.android.systemui.biometrics.shared.model.isDefaultOrientation import com.android.systemui.dagger.SysUISingleton import com.android.systemui.flags.FeatureFlagsClassic import com.android.systemui.flags.Flags import com.android.systemui.log.SideFpsLogger import com.android.systemui.res.R import java.util.Optional import javax.inject.Inject import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.map @OptIn(ExperimentalCoroutinesApi::class) @SysUISingleton class SideFpsSensorInteractor @Inject Loading @@ -47,6 +47,8 @@ constructor( windowManager: WindowManager, displayStateInteractor: DisplayStateInteractor, featureFlags: FeatureFlagsClassic, fingerprintInteractiveToAuthProvider: Optional<FingerprintInteractiveToAuthProvider>, private val logger: SideFpsLogger, ) { private val sensorForCurrentDisplay = Loading @@ -65,12 +67,18 @@ constructor( flowOf(context.resources?.getInteger(R.integer.config_restToUnlockDuration)?.toLong() ?: 0L) val isProlongedTouchRequiredForAuthentication: Flow<Boolean> = isAvailable.flatMapLatest { sfpsAvailable -> if (sfpsAvailable) { // todo (b/305236201) also add the settings check here. flowOf(featureFlags.isEnabled(Flags.REST_TO_UNLOCK)) } else { if ( fingerprintInteractiveToAuthProvider.isEmpty || !featureFlags.isEnabled(Flags.REST_TO_UNLOCK) ) { flowOf(false) } else { combine( isAvailable, fingerprintInteractiveToAuthProvider.get().enabledForCurrentUser ) { sfpsAvailable, isSettingEnabled -> logger.logStateChange(sfpsAvailable, isSettingEnabled) sfpsAvailable && isSettingEnabled } } Loading Loading @@ -126,6 +134,15 @@ constructor( } } logger.sensorLocationStateChanged( size, rotation, displayWidth, displayHeight, sensorWidth, isSensorVerticalInDefaultOrientation ) SideFpsSensorLocation( left = sensorLeft, top = sensorTop, Loading packages/SystemUI/src/com/android/systemui/log/SideFpsLogger.kt 0 → 100644 +132 −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.log import android.graphics.Point import android.graphics.Rect import com.android.systemui.biometrics.shared.model.DisplayRotation import com.android.systemui.dagger.SysUISingleton import com.android.systemui.log.core.LogLevel import com.android.systemui.log.dagger.BouncerLog import javax.inject.Inject private const val TAG = "SideFpsLogger" /** * Helper class for logging for SFPS related functionality * * To enable logcat echoing for an entire buffer: * ``` * adb shell settings put global systemui/buffer/BouncerLog <logLevel> * * ``` */ @SysUISingleton class SideFpsLogger @Inject constructor(@BouncerLog private val buffer: LogBuffer) { fun sfpsProgressBarStateChanged( visible: Boolean, location: Point, shouldRotate: Boolean, fpDetectRunning: Boolean, sensorWidth: Int ) { buffer.log( TAG, LogLevel.DEBUG, { bool1 = visible int1 = location.x int2 = location.y bool2 = shouldRotate bool3 = fpDetectRunning long1 = sensorWidth.toLong() }, { "SFPS progress bar state changed: visible: $bool1, " + "sensorLocation (x, y): ($int1, $int2), " + "shouldRotate = $bool2, " + "fpDetectRunning: $bool3, " + "sensorWidth: $long1" } ) } fun hidingSfpsIndicator() { buffer.log(TAG, LogLevel.DEBUG, "hiding SFPS indicator to show progress bar") } fun showingSfpsIndicator() { buffer.log( TAG, LogLevel.DEBUG, "Requesting show SFPS indicator because progress bar " + "is being hidden and FP detect is currently running" ) } fun isProlongedTouchRequiredForAuthenticationChanged(enabled: Boolean) { buffer.log( TAG, LogLevel.DEBUG, { bool1 = enabled }, { "isProlongedTouchRequiredForAuthentication: $bool1" } ) } fun logStateChange(sfpsAvailable: Boolean, settingEnabled: Boolean) { buffer.log( TAG, LogLevel.DEBUG, { bool1 = sfpsAvailable bool2 = settingEnabled }, { "SFPS rest to unlock state changed: sfpsAvailable: $bool1, settingEnabled: $bool2" } ) } fun sensorLocationStateChanged( windowSize: Rect?, rotation: DisplayRotation, displayWidth: Int, displayHeight: Int, sensorWidth: Int, sensorVerticalInDefaultOrientation: Boolean ) { buffer.log( TAG, LogLevel.DEBUG, { str1 = "$windowSize" str2 = rotation.name int1 = displayWidth int2 = displayHeight long1 = sensorWidth.toLong() bool1 = sensorVerticalInDefaultOrientation }, { "sensorLocation state changed: " + "windowSize: $str1, " + "rotation: $str2, " + "widthInRotation0: $int1, " + "heightInRotation0: $int2, " + "sensorWidth: $long1, " + "sensorVerticalInDefaultOrientation: $bool1" } ) } } packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/SideFpsSensorInteractorTest.kt +32 −7 Original line number Diff line number Diff line Loading @@ -26,6 +26,7 @@ import android.view.WindowManager import android.view.WindowMetrics import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.biometrics.FingerprintInteractiveToAuthProvider import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepository import com.android.systemui.biometrics.shared.model.DisplayRotation import com.android.systemui.biometrics.shared.model.DisplayRotation.ROTATION_0 Loading @@ -35,11 +36,14 @@ import com.android.systemui.biometrics.shared.model.DisplayRotation.ROTATION_90 import com.android.systemui.biometrics.shared.model.FingerprintSensorType import com.android.systemui.biometrics.shared.model.SensorStrength import com.android.systemui.coroutines.collectLastValue import com.android.systemui.dump.logcatLogBuffer import com.android.systemui.flags.FakeFeatureFlagsClassic import com.android.systemui.flags.Flags.REST_TO_UNLOCK import com.android.systemui.log.SideFpsLogger import com.android.systemui.res.R import com.android.systemui.util.mockito.whenever import com.google.common.truth.Truth.assertThat import java.util.Optional import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.test.StandardTestDispatcher Loading @@ -62,7 +66,7 @@ import org.mockito.junit.MockitoJUnit class SideFpsSensorInteractorTest : SysuiTestCase() { @JvmField @Rule var mockitoRule = MockitoJUnit.rule() private lateinit var testScope: TestScope private val testScope = TestScope(StandardTestDispatcher()) private val fingerprintRepository = FakeFingerprintPropertyRepository() Loading @@ -70,32 +74,38 @@ class SideFpsSensorInteractorTest : SysuiTestCase() { @Mock private lateinit var windowManager: WindowManager @Mock private lateinit var displayStateInteractor: DisplayStateInteractor @Mock private lateinit var fingerprintInteractiveToAuthProvider: FingerprintInteractiveToAuthProvider private val isRestToUnlockEnabled = MutableStateFlow(false) private val contextDisplayInfo = DisplayInfo() private val displayChangeEvent = MutableStateFlow(0) private val currentRotation = MutableStateFlow(ROTATION_0) @Before fun setup() { testScope = TestScope(StandardTestDispatcher()) mContext = spy(mContext) val displayManager = mock(DisplayManagerGlobal::class.java) val resources = mContext.resources whenever(mContext.display) .thenReturn(Display(displayManager, 1, contextDisplayInfo, resources)) .thenReturn( Display(mock(DisplayManagerGlobal::class.java), 1, contextDisplayInfo, resources) ) whenever(displayStateInteractor.displayChanges).thenReturn(displayChangeEvent) whenever(displayStateInteractor.currentRotation).thenReturn(currentRotation) contextDisplayInfo.uniqueId = "current-display" val featureFlags = FakeFeatureFlagsClassic().apply { set(REST_TO_UNLOCK, true) } whenever(fingerprintInteractiveToAuthProvider.enabledForCurrentUser) .thenReturn(isRestToUnlockEnabled) underTest = SideFpsSensorInteractor( mContext, fingerprintRepository, windowManager, displayStateInteractor, FakeFeatureFlagsClassic().apply { set(REST_TO_UNLOCK, true) } featureFlags, Optional.of(fingerprintInteractiveToAuthProvider), SideFpsLogger(logcatLogBuffer("SfpsLogger")) ) } Loading Loading @@ -348,6 +358,21 @@ class SideFpsSensorInteractorTest : SysuiTestCase() { assertThat(sensorLocation!!.width).isEqualTo(100) } @Test fun isProlongedTouchRequiredForAuthentication_dependsOnSettingsToggle() = testScope.runTest { val isEnabled by collectLastValue(underTest.isProlongedTouchRequiredForAuthentication) setupFingerprint(FingerprintSensorType.POWER_BUTTON) isRestToUnlockEnabled.value = true runCurrent() assertThat(isEnabled).isTrue() isRestToUnlockEnabled.value = false runCurrent() assertThat(isEnabled).isFalse() } private suspend fun TestScope.setupFPLocationAndDisplaySize( width: Int, height: Int, Loading Loading
packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/SideFpsSensorInteractor.kt +26 −9 Original line number Diff line number Diff line Loading @@ -19,6 +19,7 @@ package com.android.systemui.biometrics.domain.interactor import android.content.Context import android.hardware.biometrics.SensorLocationInternal import android.view.WindowManager import com.android.systemui.biometrics.FingerprintInteractiveToAuthProvider import com.android.systemui.biometrics.data.repository.FingerprintPropertyRepository import com.android.systemui.biometrics.domain.model.SideFpsSensorLocation import com.android.systemui.biometrics.shared.model.DisplayRotation Loading @@ -27,17 +28,16 @@ import com.android.systemui.biometrics.shared.model.isDefaultOrientation import com.android.systemui.dagger.SysUISingleton import com.android.systemui.flags.FeatureFlagsClassic import com.android.systemui.flags.Flags import com.android.systemui.log.SideFpsLogger import com.android.systemui.res.R import java.util.Optional import javax.inject.Inject import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.map @OptIn(ExperimentalCoroutinesApi::class) @SysUISingleton class SideFpsSensorInteractor @Inject Loading @@ -47,6 +47,8 @@ constructor( windowManager: WindowManager, displayStateInteractor: DisplayStateInteractor, featureFlags: FeatureFlagsClassic, fingerprintInteractiveToAuthProvider: Optional<FingerprintInteractiveToAuthProvider>, private val logger: SideFpsLogger, ) { private val sensorForCurrentDisplay = Loading @@ -65,12 +67,18 @@ constructor( flowOf(context.resources?.getInteger(R.integer.config_restToUnlockDuration)?.toLong() ?: 0L) val isProlongedTouchRequiredForAuthentication: Flow<Boolean> = isAvailable.flatMapLatest { sfpsAvailable -> if (sfpsAvailable) { // todo (b/305236201) also add the settings check here. flowOf(featureFlags.isEnabled(Flags.REST_TO_UNLOCK)) } else { if ( fingerprintInteractiveToAuthProvider.isEmpty || !featureFlags.isEnabled(Flags.REST_TO_UNLOCK) ) { flowOf(false) } else { combine( isAvailable, fingerprintInteractiveToAuthProvider.get().enabledForCurrentUser ) { sfpsAvailable, isSettingEnabled -> logger.logStateChange(sfpsAvailable, isSettingEnabled) sfpsAvailable && isSettingEnabled } } Loading Loading @@ -126,6 +134,15 @@ constructor( } } logger.sensorLocationStateChanged( size, rotation, displayWidth, displayHeight, sensorWidth, isSensorVerticalInDefaultOrientation ) SideFpsSensorLocation( left = sensorLeft, top = sensorTop, Loading
packages/SystemUI/src/com/android/systemui/log/SideFpsLogger.kt 0 → 100644 +132 −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.log import android.graphics.Point import android.graphics.Rect import com.android.systemui.biometrics.shared.model.DisplayRotation import com.android.systemui.dagger.SysUISingleton import com.android.systemui.log.core.LogLevel import com.android.systemui.log.dagger.BouncerLog import javax.inject.Inject private const val TAG = "SideFpsLogger" /** * Helper class for logging for SFPS related functionality * * To enable logcat echoing for an entire buffer: * ``` * adb shell settings put global systemui/buffer/BouncerLog <logLevel> * * ``` */ @SysUISingleton class SideFpsLogger @Inject constructor(@BouncerLog private val buffer: LogBuffer) { fun sfpsProgressBarStateChanged( visible: Boolean, location: Point, shouldRotate: Boolean, fpDetectRunning: Boolean, sensorWidth: Int ) { buffer.log( TAG, LogLevel.DEBUG, { bool1 = visible int1 = location.x int2 = location.y bool2 = shouldRotate bool3 = fpDetectRunning long1 = sensorWidth.toLong() }, { "SFPS progress bar state changed: visible: $bool1, " + "sensorLocation (x, y): ($int1, $int2), " + "shouldRotate = $bool2, " + "fpDetectRunning: $bool3, " + "sensorWidth: $long1" } ) } fun hidingSfpsIndicator() { buffer.log(TAG, LogLevel.DEBUG, "hiding SFPS indicator to show progress bar") } fun showingSfpsIndicator() { buffer.log( TAG, LogLevel.DEBUG, "Requesting show SFPS indicator because progress bar " + "is being hidden and FP detect is currently running" ) } fun isProlongedTouchRequiredForAuthenticationChanged(enabled: Boolean) { buffer.log( TAG, LogLevel.DEBUG, { bool1 = enabled }, { "isProlongedTouchRequiredForAuthentication: $bool1" } ) } fun logStateChange(sfpsAvailable: Boolean, settingEnabled: Boolean) { buffer.log( TAG, LogLevel.DEBUG, { bool1 = sfpsAvailable bool2 = settingEnabled }, { "SFPS rest to unlock state changed: sfpsAvailable: $bool1, settingEnabled: $bool2" } ) } fun sensorLocationStateChanged( windowSize: Rect?, rotation: DisplayRotation, displayWidth: Int, displayHeight: Int, sensorWidth: Int, sensorVerticalInDefaultOrientation: Boolean ) { buffer.log( TAG, LogLevel.DEBUG, { str1 = "$windowSize" str2 = rotation.name int1 = displayWidth int2 = displayHeight long1 = sensorWidth.toLong() bool1 = sensorVerticalInDefaultOrientation }, { "sensorLocation state changed: " + "windowSize: $str1, " + "rotation: $str2, " + "widthInRotation0: $int1, " + "heightInRotation0: $int2, " + "sensorWidth: $long1, " + "sensorVerticalInDefaultOrientation: $bool1" } ) } }
packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/SideFpsSensorInteractorTest.kt +32 −7 Original line number Diff line number Diff line Loading @@ -26,6 +26,7 @@ import android.view.WindowManager import android.view.WindowMetrics import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.biometrics.FingerprintInteractiveToAuthProvider import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepository import com.android.systemui.biometrics.shared.model.DisplayRotation import com.android.systemui.biometrics.shared.model.DisplayRotation.ROTATION_0 Loading @@ -35,11 +36,14 @@ import com.android.systemui.biometrics.shared.model.DisplayRotation.ROTATION_90 import com.android.systemui.biometrics.shared.model.FingerprintSensorType import com.android.systemui.biometrics.shared.model.SensorStrength import com.android.systemui.coroutines.collectLastValue import com.android.systemui.dump.logcatLogBuffer import com.android.systemui.flags.FakeFeatureFlagsClassic import com.android.systemui.flags.Flags.REST_TO_UNLOCK import com.android.systemui.log.SideFpsLogger import com.android.systemui.res.R import com.android.systemui.util.mockito.whenever import com.google.common.truth.Truth.assertThat import java.util.Optional import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.test.StandardTestDispatcher Loading @@ -62,7 +66,7 @@ import org.mockito.junit.MockitoJUnit class SideFpsSensorInteractorTest : SysuiTestCase() { @JvmField @Rule var mockitoRule = MockitoJUnit.rule() private lateinit var testScope: TestScope private val testScope = TestScope(StandardTestDispatcher()) private val fingerprintRepository = FakeFingerprintPropertyRepository() Loading @@ -70,32 +74,38 @@ class SideFpsSensorInteractorTest : SysuiTestCase() { @Mock private lateinit var windowManager: WindowManager @Mock private lateinit var displayStateInteractor: DisplayStateInteractor @Mock private lateinit var fingerprintInteractiveToAuthProvider: FingerprintInteractiveToAuthProvider private val isRestToUnlockEnabled = MutableStateFlow(false) private val contextDisplayInfo = DisplayInfo() private val displayChangeEvent = MutableStateFlow(0) private val currentRotation = MutableStateFlow(ROTATION_0) @Before fun setup() { testScope = TestScope(StandardTestDispatcher()) mContext = spy(mContext) val displayManager = mock(DisplayManagerGlobal::class.java) val resources = mContext.resources whenever(mContext.display) .thenReturn(Display(displayManager, 1, contextDisplayInfo, resources)) .thenReturn( Display(mock(DisplayManagerGlobal::class.java), 1, contextDisplayInfo, resources) ) whenever(displayStateInteractor.displayChanges).thenReturn(displayChangeEvent) whenever(displayStateInteractor.currentRotation).thenReturn(currentRotation) contextDisplayInfo.uniqueId = "current-display" val featureFlags = FakeFeatureFlagsClassic().apply { set(REST_TO_UNLOCK, true) } whenever(fingerprintInteractiveToAuthProvider.enabledForCurrentUser) .thenReturn(isRestToUnlockEnabled) underTest = SideFpsSensorInteractor( mContext, fingerprintRepository, windowManager, displayStateInteractor, FakeFeatureFlagsClassic().apply { set(REST_TO_UNLOCK, true) } featureFlags, Optional.of(fingerprintInteractiveToAuthProvider), SideFpsLogger(logcatLogBuffer("SfpsLogger")) ) } Loading Loading @@ -348,6 +358,21 @@ class SideFpsSensorInteractorTest : SysuiTestCase() { assertThat(sensorLocation!!.width).isEqualTo(100) } @Test fun isProlongedTouchRequiredForAuthentication_dependsOnSettingsToggle() = testScope.runTest { val isEnabled by collectLastValue(underTest.isProlongedTouchRequiredForAuthentication) setupFingerprint(FingerprintSensorType.POWER_BUTTON) isRestToUnlockEnabled.value = true runCurrent() assertThat(isEnabled).isTrue() isRestToUnlockEnabled.value = false runCurrent() assertThat(isEnabled).isFalse() } private suspend fun TestScope.setupFPLocationAndDisplaySize( width: Int, height: Int, Loading