Loading packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/FaceHelpMessageDebouncerTest.kt +56 −0 Original line number Diff line number Diff line Loading @@ -218,4 +218,60 @@ class FaceHelpMessageDebouncerTest : SysuiTestCase() { assertThat(underTest.getMessageToShow(startWindow)?.msgId) .isEqualTo(BiometricFaceConstants.FACE_ACQUIRED_TOO_CLOSE) } @Test fun messageMustMeetThreshold() { underTest = FaceHelpMessageDebouncer( window = window, startWindow = 0, shownFaceMessageFrequencyBoost = 0, threshold = .8f, ) underTest.addMessage( HelpFaceAuthenticationStatus( BiometricFaceConstants.FACE_ACQUIRED_TOO_CLOSE, "tooClose", 0 ) ) underTest.addMessage( HelpFaceAuthenticationStatus( BiometricFaceConstants.FACE_ACQUIRED_TOO_CLOSE, "tooClose", 0 ) ) underTest.addMessage( HelpFaceAuthenticationStatus( BiometricFaceConstants.FACE_ACQUIRED_TOO_BRIGHT, "tooBright", 0 ) ) // although tooClose message is the majority, it doesn't meet the 80% threshold assertThat(underTest.getMessageToShow(startWindow)).isNull() underTest.addMessage( HelpFaceAuthenticationStatus( BiometricFaceConstants.FACE_ACQUIRED_TOO_CLOSE, "tooClose", 0 ) ) underTest.addMessage( HelpFaceAuthenticationStatus( BiometricFaceConstants.FACE_ACQUIRED_TOO_CLOSE, "tooClose", 0 ) ) // message shows once it meets the threshold assertThat(underTest.getMessageToShow(startWindow)?.msgId) .isEqualTo(BiometricFaceConstants.FACE_ACQUIRED_TOO_CLOSE) assertThat(underTest.getMessageToShow(startWindow)?.msg).isEqualTo("tooClose") } } packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/FaceHelpMessageDeferralTest.kt +68 −11 Original line number Diff line number Diff line Loading @@ -16,11 +16,15 @@ package com.android.systemui.biometrics import androidx.test.ext.junit.runners.AndroidJUnit4 import android.platform.test.annotations.DisableFlags import android.platform.test.annotations.EnableFlags import android.platform.test.flag.junit.FlagsParameterization import androidx.test.filters.SmallTest import com.android.keyguard.logging.BiometricMessageDeferralLogger import com.android.systemui.Flags import com.android.systemui.SysuiTestCase import com.android.systemui.dump.DumpManager import com.android.systemui.util.time.FakeSystemClock import org.junit.Assert.assertEquals import org.junit.Assert.assertFalse import org.junit.Assert.assertNull Loading @@ -31,14 +35,29 @@ import org.junit.runner.RunWith import org.mockito.Mock import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations import platform.test.runner.parameterized.ParameterizedAndroidJunit4 import platform.test.runner.parameterized.Parameters @SmallTest @RunWith(AndroidJUnit4::class) @RunWith(ParameterizedAndroidJunit4::class) @android.platform.test.annotations.EnabledOnRavenwood class FaceHelpMessageDeferralTest : SysuiTestCase() { class FaceHelpMessageDeferralTest(flags: FlagsParameterization) : SysuiTestCase() { val threshold = .75f @Mock lateinit var logger: BiometricMessageDeferralLogger @Mock lateinit var dumpManager: DumpManager val systemClock = FakeSystemClock() companion object { @JvmStatic @Parameters(name = "{0}") fun getParams(): List<FlagsParameterization> { return FlagsParameterization.allCombinationsOf(Flags.FLAG_FACE_MESSAGE_DEFER_UPDATE) } } init { mSetFlagsRule.setFlagsParameterization(flags) } @Before fun setUp() { Loading Loading @@ -111,6 +130,7 @@ class FaceHelpMessageDeferralTest : SysuiTestCase() { } @Test @DisableFlags(Flags.FLAG_FACE_MESSAGE_DEFER_UPDATE) fun testReturnsMostFrequentDeferredMessage() { val biometricMessageDeferral = createMsgDeferral(setOf(1, 2)) Loading @@ -124,7 +144,41 @@ class FaceHelpMessageDeferralTest : SysuiTestCase() { biometricMessageDeferral.processFrame(2) biometricMessageDeferral.updateMessage(2, "msgId-2") // THEN the most frequent deferred message is that meets the threshold is returned // THEN the most frequent deferred message that meets the threshold is returned assertEquals("msgId-1", biometricMessageDeferral.getDeferredMessage()) } @Test @EnableFlags(Flags.FLAG_FACE_MESSAGE_DEFER_UPDATE) fun testReturnsMostFrequentDeferredMessage_onlyAnalyzesLastNWindow() { val biometricMessageDeferral = createMsgDeferral(setOf(1, 2)) // WHEN there's 80% of the messages are msgId=1 and 20% is msgId=2, but the last // N window only contains messages with msgId=2 repeat(80) { biometricMessageDeferral.processFrame(1) } biometricMessageDeferral.updateMessage(1, "msgId-1") systemClock.setElapsedRealtime(systemClock.elapsedRealtime() + 501L) repeat(20) { biometricMessageDeferral.processFrame(2) } biometricMessageDeferral.updateMessage(2, "msgId-2") // THEN the most frequent deferred message in the last N window (500L) is returned assertEquals("msgId-2", biometricMessageDeferral.getDeferredMessage()) } @Test @DisableFlags(Flags.FLAG_FACE_MESSAGE_DEFER_UPDATE) fun testReturnsMostFrequentDeferredMessage_analyzesAllFrames() { val biometricMessageDeferral = createMsgDeferral(setOf(1, 2)) // WHEN there's 80% of the messages are msgId=1 and 20% is msgId=2, but the last // N window only contains messages with msgId=2 repeat(80) { biometricMessageDeferral.processFrame(1) } biometricMessageDeferral.updateMessage(1, "msgId-1") systemClock.setElapsedRealtime(systemClock.elapsedRealtime() + 501L) repeat(20) { biometricMessageDeferral.processFrame(2) } biometricMessageDeferral.updateMessage(2, "msgId-2") // THEN the most frequent deferred message is returned assertEquals("msgId-1", biometricMessageDeferral.getDeferredMessage()) } Loading Loading @@ -213,14 +267,17 @@ class FaceHelpMessageDeferralTest : SysuiTestCase() { private fun createMsgDeferral( messagesToDefer: Set<Int>, acquiredInfoToIgnore: Set<Int> = emptySet(), windowToAnalyzeLastNFrames: Long = 500L, ): BiometricMessageDeferral { return BiometricMessageDeferral( messagesToDefer, acquiredInfoToIgnore, threshold, logger, dumpManager, "0", messagesToDefer = messagesToDefer, acquiredInfoToIgnore = acquiredInfoToIgnore, threshold = threshold, windowToAnalyzeLastNFrames = windowToAnalyzeLastNFrames, logBuffer = logger, dumpManager = dumpManager, id = "0", systemClock = { systemClock }, ) } } packages/SystemUI/res/values/config.xml +4 −0 Original line number Diff line number Diff line Loading @@ -727,6 +727,10 @@ .75 </item> <!-- The last x ms of face acquired info messages to analyze to determine whether to show a deferred face auth help message. --> <integer name="config_face_help_msgs_defer_analyze_timeframe">500</integer> <!-- Which face help messages to surface when fingerprint is also enrolled. Message ids correspond with the acquired ids in BiometricFaceConstants --> <integer-array name="config_face_help_msgs_when_fingerprint_enrolled"> Loading packages/SystemUI/src/com/android/systemui/biometrics/FaceHelpMessageDebouncer.kt +25 −4 Original line number Diff line number Diff line Loading @@ -25,11 +25,13 @@ import com.android.systemui.deviceentry.shared.model.HelpFaceAuthenticationStatu * - startWindow: Window of time on start required before showing the first help message * - shownFaceMessageFrequencyBoost: Frequency boost given to messages that are currently shown to * the user * - threshold: minimum percentage of frames a message must appear in order to show it */ class FaceHelpMessageDebouncer( private val window: Long = DEFAULT_WINDOW_MS, private val startWindow: Long = window, private val shownFaceMessageFrequencyBoost: Int = 4, private val threshold: Float = 0f, ) { private val TAG = "FaceHelpMessageDebouncer" private var startTime = 0L Loading @@ -56,7 +58,7 @@ class FaceHelpMessageDebouncer( } } private fun getMostFrequentHelpMessage(): HelpFaceAuthenticationStatus? { private fun getMostFrequentHelpMessageSurpassingThreshold(): HelpFaceAuthenticationStatus? { // freqMap: msgId => frequency val freqMap = helpFaceAuthStatuses.groupingBy { it.msgId }.eachCount().toMutableMap() Loading @@ -83,7 +85,25 @@ class FaceHelpMessageDebouncer( } } ?.key return helpFaceAuthStatuses.findLast { it.msgId == msgIdWithHighestFrequency } if (msgIdWithHighestFrequency == null) { return null } val freq = if (msgIdWithHighestFrequency == lastMessageIdShown) { freqMap[msgIdWithHighestFrequency]!! - shownFaceMessageFrequencyBoost } else { freqMap[msgIdWithHighestFrequency]!! } .toFloat() return if ((freq / helpFaceAuthStatuses.size.toFloat()) >= threshold) { helpFaceAuthStatuses.findLast { it.msgId == msgIdWithHighestFrequency } } else { Log.v(TAG, "most frequent helpFaceAuthStatus didn't make the threshold: $threshold") null } } fun addMessage(helpFaceAuthStatus: HelpFaceAuthenticationStatus) { Loading @@ -98,14 +118,15 @@ class FaceHelpMessageDebouncer( return null } removeOldMessages(atTimestamp) val messageToShow = getMostFrequentHelpMessage() val messageToShow = getMostFrequentHelpMessageSurpassingThreshold() if (lastMessageIdShown != messageToShow?.msgId) { Log.v( TAG, "showMessage previousLastMessageId=$lastMessageIdShown" + "\n\tmessageToShow=$messageToShow " + "\n\thelpFaceAuthStatusesSize=${helpFaceAuthStatuses.size}" + "\n\thelpFaceAuthStatuses=$helpFaceAuthStatuses" "\n\thelpFaceAuthStatuses=$helpFaceAuthStatuses" + "\n\tthreshold=$threshold" ) lastMessageIdShown = messageToShow?.msgId } Loading packages/SystemUI/src/com/android/systemui/biometrics/FaceHelpMessageDeferral.kt +79 −17 Original line number Diff line number Diff line Loading @@ -17,14 +17,19 @@ package com.android.systemui.biometrics import android.content.res.Resources import android.os.SystemClock.elapsedRealtime import com.android.keyguard.logging.BiometricMessageDeferralLogger import com.android.systemui.Dumpable import com.android.systemui.Flags.faceMessageDeferUpdate import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.deviceentry.shared.model.HelpFaceAuthenticationStatus import com.android.systemui.dump.DumpManager import com.android.systemui.log.LogBuffer import com.android.systemui.log.dagger.BiometricLog import com.android.systemui.res.R import com.android.systemui.util.time.SystemClock import dagger.Lazy import java.io.PrintWriter import java.util.Objects import java.util.UUID Loading @@ -36,7 +41,8 @@ class FaceHelpMessageDeferralFactory constructor( @Main private val resources: Resources, @BiometricLog private val logBuffer: LogBuffer, private val dumpManager: DumpManager private val dumpManager: DumpManager, private val systemClock: Lazy<SystemClock>, ) { fun create(): FaceHelpMessageDeferral { val id = UUID.randomUUID().toString() Loading @@ -45,6 +51,7 @@ constructor( logBuffer = BiometricMessageDeferralLogger(logBuffer, "FaceHelpMessageDeferral[$id]"), dumpManager = dumpManager, id = id, systemClock, ) } } Loading @@ -58,14 +65,17 @@ class FaceHelpMessageDeferral( logBuffer: BiometricMessageDeferralLogger, dumpManager: DumpManager, val id: String, val systemClock: Lazy<SystemClock>, ) : BiometricMessageDeferral( resources.getIntArray(R.array.config_face_help_msgs_defer_until_timeout).toHashSet(), resources.getIntArray(R.array.config_face_help_msgs_ignore).toHashSet(), resources.getFloat(R.dimen.config_face_help_msgs_defer_until_timeout_threshold), resources.getInteger(R.integer.config_face_help_msgs_defer_analyze_timeframe).toLong(), logBuffer, dumpManager, id, systemClock, ) /** Loading @@ -77,10 +87,24 @@ open class BiometricMessageDeferral( private val messagesToDefer: Set<Int>, private val acquiredInfoToIgnore: Set<Int>, private val threshold: Float, private val windowToAnalyzeLastNFrames: Long, private val logBuffer: BiometricMessageDeferralLogger, dumpManager: DumpManager, id: String, private val systemClock: Lazy<SystemClock>, ) : Dumpable { private val faceHelpMessageDebouncer: FaceHelpMessageDebouncer? = if (faceMessageDeferUpdate()) { FaceHelpMessageDebouncer( window = windowToAnalyzeLastNFrames, startWindow = 0L, shownFaceMessageFrequencyBoost = 0, threshold = threshold, ) } else { null } private val acquiredInfoToFrequency: MutableMap<Int, Int> = HashMap() private val acquiredInfoToHelpString: MutableMap<Int, String> = HashMap() private var mostFrequentAcquiredInfoToDefer: Int? = null Loading @@ -97,13 +121,20 @@ open class BiometricMessageDeferral( pw.println("messagesToDefer=$messagesToDefer") pw.println("totalFrames=$totalFrames") pw.println("threshold=$threshold") pw.println("faceMessageDeferUpdateFlagEnabled=${faceMessageDeferUpdate()}") if (faceMessageDeferUpdate()) { pw.println("windowToAnalyzeLastNFrames(ms)=$windowToAnalyzeLastNFrames") } } /** Reset all saved counts. */ fun reset() { totalFrames = 0 if (!faceMessageDeferUpdate()) { mostFrequentAcquiredInfoToDefer = null acquiredInfoToFrequency.clear() } acquiredInfoToHelpString.clear() logBuffer.reset() } Loading Loading @@ -137,24 +168,48 @@ open class BiometricMessageDeferral( logBuffer.logFrameIgnored(acquiredInfo) return } totalFrames++ if (faceMessageDeferUpdate()) { faceHelpMessageDebouncer?.let { val helpFaceAuthStatus = HelpFaceAuthenticationStatus( msgId = acquiredInfo, msg = null, systemClock.get().elapsedRealtime() ) if (totalFrames == 1) { // first frame it.startNewFaceAuthSession(helpFaceAuthStatus.createdAt) } it.addMessage(helpFaceAuthStatus) } } else { val newAcquiredInfoCount = acquiredInfoToFrequency.getOrDefault(acquiredInfo, 0) + 1 acquiredInfoToFrequency[acquiredInfo] = newAcquiredInfoCount if ( messagesToDefer.contains(acquiredInfo) && (mostFrequentAcquiredInfoToDefer == null || newAcquiredInfoCount > acquiredInfoToFrequency.getOrDefault(mostFrequentAcquiredInfoToDefer!!, 0)) acquiredInfoToFrequency.getOrDefault( mostFrequentAcquiredInfoToDefer!!, 0 )) ) { mostFrequentAcquiredInfoToDefer = acquiredInfo } } logBuffer.logFrameProcessed( acquiredInfo, totalFrames, if (faceMessageDeferUpdate()) { faceHelpMessageDebouncer ?.getMessageToShow(systemClock.get().elapsedRealtime()) ?.msgId .toString() } else { mostFrequentAcquiredInfoToDefer?.toString() } ) } Loading @@ -166,11 +221,18 @@ open class BiometricMessageDeferral( * [threshold] percentage. */ fun getDeferredMessage(): CharSequence? { if (faceMessageDeferUpdate()) { faceHelpMessageDebouncer?.let { val helpFaceAuthStatus = it.getMessageToShow(systemClock.get().elapsedRealtime()) return acquiredInfoToHelpString[helpFaceAuthStatus?.msgId] } } else { mostFrequentAcquiredInfoToDefer?.let { if (acquiredInfoToFrequency.getOrDefault(it, 0) > (threshold * totalFrames)) { return acquiredInfoToHelpString[it] } } } return null } } Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/FaceHelpMessageDebouncerTest.kt +56 −0 Original line number Diff line number Diff line Loading @@ -218,4 +218,60 @@ class FaceHelpMessageDebouncerTest : SysuiTestCase() { assertThat(underTest.getMessageToShow(startWindow)?.msgId) .isEqualTo(BiometricFaceConstants.FACE_ACQUIRED_TOO_CLOSE) } @Test fun messageMustMeetThreshold() { underTest = FaceHelpMessageDebouncer( window = window, startWindow = 0, shownFaceMessageFrequencyBoost = 0, threshold = .8f, ) underTest.addMessage( HelpFaceAuthenticationStatus( BiometricFaceConstants.FACE_ACQUIRED_TOO_CLOSE, "tooClose", 0 ) ) underTest.addMessage( HelpFaceAuthenticationStatus( BiometricFaceConstants.FACE_ACQUIRED_TOO_CLOSE, "tooClose", 0 ) ) underTest.addMessage( HelpFaceAuthenticationStatus( BiometricFaceConstants.FACE_ACQUIRED_TOO_BRIGHT, "tooBright", 0 ) ) // although tooClose message is the majority, it doesn't meet the 80% threshold assertThat(underTest.getMessageToShow(startWindow)).isNull() underTest.addMessage( HelpFaceAuthenticationStatus( BiometricFaceConstants.FACE_ACQUIRED_TOO_CLOSE, "tooClose", 0 ) ) underTest.addMessage( HelpFaceAuthenticationStatus( BiometricFaceConstants.FACE_ACQUIRED_TOO_CLOSE, "tooClose", 0 ) ) // message shows once it meets the threshold assertThat(underTest.getMessageToShow(startWindow)?.msgId) .isEqualTo(BiometricFaceConstants.FACE_ACQUIRED_TOO_CLOSE) assertThat(underTest.getMessageToShow(startWindow)?.msg).isEqualTo("tooClose") } }
packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/FaceHelpMessageDeferralTest.kt +68 −11 Original line number Diff line number Diff line Loading @@ -16,11 +16,15 @@ package com.android.systemui.biometrics import androidx.test.ext.junit.runners.AndroidJUnit4 import android.platform.test.annotations.DisableFlags import android.platform.test.annotations.EnableFlags import android.platform.test.flag.junit.FlagsParameterization import androidx.test.filters.SmallTest import com.android.keyguard.logging.BiometricMessageDeferralLogger import com.android.systemui.Flags import com.android.systemui.SysuiTestCase import com.android.systemui.dump.DumpManager import com.android.systemui.util.time.FakeSystemClock import org.junit.Assert.assertEquals import org.junit.Assert.assertFalse import org.junit.Assert.assertNull Loading @@ -31,14 +35,29 @@ import org.junit.runner.RunWith import org.mockito.Mock import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations import platform.test.runner.parameterized.ParameterizedAndroidJunit4 import platform.test.runner.parameterized.Parameters @SmallTest @RunWith(AndroidJUnit4::class) @RunWith(ParameterizedAndroidJunit4::class) @android.platform.test.annotations.EnabledOnRavenwood class FaceHelpMessageDeferralTest : SysuiTestCase() { class FaceHelpMessageDeferralTest(flags: FlagsParameterization) : SysuiTestCase() { val threshold = .75f @Mock lateinit var logger: BiometricMessageDeferralLogger @Mock lateinit var dumpManager: DumpManager val systemClock = FakeSystemClock() companion object { @JvmStatic @Parameters(name = "{0}") fun getParams(): List<FlagsParameterization> { return FlagsParameterization.allCombinationsOf(Flags.FLAG_FACE_MESSAGE_DEFER_UPDATE) } } init { mSetFlagsRule.setFlagsParameterization(flags) } @Before fun setUp() { Loading Loading @@ -111,6 +130,7 @@ class FaceHelpMessageDeferralTest : SysuiTestCase() { } @Test @DisableFlags(Flags.FLAG_FACE_MESSAGE_DEFER_UPDATE) fun testReturnsMostFrequentDeferredMessage() { val biometricMessageDeferral = createMsgDeferral(setOf(1, 2)) Loading @@ -124,7 +144,41 @@ class FaceHelpMessageDeferralTest : SysuiTestCase() { biometricMessageDeferral.processFrame(2) biometricMessageDeferral.updateMessage(2, "msgId-2") // THEN the most frequent deferred message is that meets the threshold is returned // THEN the most frequent deferred message that meets the threshold is returned assertEquals("msgId-1", biometricMessageDeferral.getDeferredMessage()) } @Test @EnableFlags(Flags.FLAG_FACE_MESSAGE_DEFER_UPDATE) fun testReturnsMostFrequentDeferredMessage_onlyAnalyzesLastNWindow() { val biometricMessageDeferral = createMsgDeferral(setOf(1, 2)) // WHEN there's 80% of the messages are msgId=1 and 20% is msgId=2, but the last // N window only contains messages with msgId=2 repeat(80) { biometricMessageDeferral.processFrame(1) } biometricMessageDeferral.updateMessage(1, "msgId-1") systemClock.setElapsedRealtime(systemClock.elapsedRealtime() + 501L) repeat(20) { biometricMessageDeferral.processFrame(2) } biometricMessageDeferral.updateMessage(2, "msgId-2") // THEN the most frequent deferred message in the last N window (500L) is returned assertEquals("msgId-2", biometricMessageDeferral.getDeferredMessage()) } @Test @DisableFlags(Flags.FLAG_FACE_MESSAGE_DEFER_UPDATE) fun testReturnsMostFrequentDeferredMessage_analyzesAllFrames() { val biometricMessageDeferral = createMsgDeferral(setOf(1, 2)) // WHEN there's 80% of the messages are msgId=1 and 20% is msgId=2, but the last // N window only contains messages with msgId=2 repeat(80) { biometricMessageDeferral.processFrame(1) } biometricMessageDeferral.updateMessage(1, "msgId-1") systemClock.setElapsedRealtime(systemClock.elapsedRealtime() + 501L) repeat(20) { biometricMessageDeferral.processFrame(2) } biometricMessageDeferral.updateMessage(2, "msgId-2") // THEN the most frequent deferred message is returned assertEquals("msgId-1", biometricMessageDeferral.getDeferredMessage()) } Loading Loading @@ -213,14 +267,17 @@ class FaceHelpMessageDeferralTest : SysuiTestCase() { private fun createMsgDeferral( messagesToDefer: Set<Int>, acquiredInfoToIgnore: Set<Int> = emptySet(), windowToAnalyzeLastNFrames: Long = 500L, ): BiometricMessageDeferral { return BiometricMessageDeferral( messagesToDefer, acquiredInfoToIgnore, threshold, logger, dumpManager, "0", messagesToDefer = messagesToDefer, acquiredInfoToIgnore = acquiredInfoToIgnore, threshold = threshold, windowToAnalyzeLastNFrames = windowToAnalyzeLastNFrames, logBuffer = logger, dumpManager = dumpManager, id = "0", systemClock = { systemClock }, ) } }
packages/SystemUI/res/values/config.xml +4 −0 Original line number Diff line number Diff line Loading @@ -727,6 +727,10 @@ .75 </item> <!-- The last x ms of face acquired info messages to analyze to determine whether to show a deferred face auth help message. --> <integer name="config_face_help_msgs_defer_analyze_timeframe">500</integer> <!-- Which face help messages to surface when fingerprint is also enrolled. Message ids correspond with the acquired ids in BiometricFaceConstants --> <integer-array name="config_face_help_msgs_when_fingerprint_enrolled"> Loading
packages/SystemUI/src/com/android/systemui/biometrics/FaceHelpMessageDebouncer.kt +25 −4 Original line number Diff line number Diff line Loading @@ -25,11 +25,13 @@ import com.android.systemui.deviceentry.shared.model.HelpFaceAuthenticationStatu * - startWindow: Window of time on start required before showing the first help message * - shownFaceMessageFrequencyBoost: Frequency boost given to messages that are currently shown to * the user * - threshold: minimum percentage of frames a message must appear in order to show it */ class FaceHelpMessageDebouncer( private val window: Long = DEFAULT_WINDOW_MS, private val startWindow: Long = window, private val shownFaceMessageFrequencyBoost: Int = 4, private val threshold: Float = 0f, ) { private val TAG = "FaceHelpMessageDebouncer" private var startTime = 0L Loading @@ -56,7 +58,7 @@ class FaceHelpMessageDebouncer( } } private fun getMostFrequentHelpMessage(): HelpFaceAuthenticationStatus? { private fun getMostFrequentHelpMessageSurpassingThreshold(): HelpFaceAuthenticationStatus? { // freqMap: msgId => frequency val freqMap = helpFaceAuthStatuses.groupingBy { it.msgId }.eachCount().toMutableMap() Loading @@ -83,7 +85,25 @@ class FaceHelpMessageDebouncer( } } ?.key return helpFaceAuthStatuses.findLast { it.msgId == msgIdWithHighestFrequency } if (msgIdWithHighestFrequency == null) { return null } val freq = if (msgIdWithHighestFrequency == lastMessageIdShown) { freqMap[msgIdWithHighestFrequency]!! - shownFaceMessageFrequencyBoost } else { freqMap[msgIdWithHighestFrequency]!! } .toFloat() return if ((freq / helpFaceAuthStatuses.size.toFloat()) >= threshold) { helpFaceAuthStatuses.findLast { it.msgId == msgIdWithHighestFrequency } } else { Log.v(TAG, "most frequent helpFaceAuthStatus didn't make the threshold: $threshold") null } } fun addMessage(helpFaceAuthStatus: HelpFaceAuthenticationStatus) { Loading @@ -98,14 +118,15 @@ class FaceHelpMessageDebouncer( return null } removeOldMessages(atTimestamp) val messageToShow = getMostFrequentHelpMessage() val messageToShow = getMostFrequentHelpMessageSurpassingThreshold() if (lastMessageIdShown != messageToShow?.msgId) { Log.v( TAG, "showMessage previousLastMessageId=$lastMessageIdShown" + "\n\tmessageToShow=$messageToShow " + "\n\thelpFaceAuthStatusesSize=${helpFaceAuthStatuses.size}" + "\n\thelpFaceAuthStatuses=$helpFaceAuthStatuses" "\n\thelpFaceAuthStatuses=$helpFaceAuthStatuses" + "\n\tthreshold=$threshold" ) lastMessageIdShown = messageToShow?.msgId } Loading
packages/SystemUI/src/com/android/systemui/biometrics/FaceHelpMessageDeferral.kt +79 −17 Original line number Diff line number Diff line Loading @@ -17,14 +17,19 @@ package com.android.systemui.biometrics import android.content.res.Resources import android.os.SystemClock.elapsedRealtime import com.android.keyguard.logging.BiometricMessageDeferralLogger import com.android.systemui.Dumpable import com.android.systemui.Flags.faceMessageDeferUpdate import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.deviceentry.shared.model.HelpFaceAuthenticationStatus import com.android.systemui.dump.DumpManager import com.android.systemui.log.LogBuffer import com.android.systemui.log.dagger.BiometricLog import com.android.systemui.res.R import com.android.systemui.util.time.SystemClock import dagger.Lazy import java.io.PrintWriter import java.util.Objects import java.util.UUID Loading @@ -36,7 +41,8 @@ class FaceHelpMessageDeferralFactory constructor( @Main private val resources: Resources, @BiometricLog private val logBuffer: LogBuffer, private val dumpManager: DumpManager private val dumpManager: DumpManager, private val systemClock: Lazy<SystemClock>, ) { fun create(): FaceHelpMessageDeferral { val id = UUID.randomUUID().toString() Loading @@ -45,6 +51,7 @@ constructor( logBuffer = BiometricMessageDeferralLogger(logBuffer, "FaceHelpMessageDeferral[$id]"), dumpManager = dumpManager, id = id, systemClock, ) } } Loading @@ -58,14 +65,17 @@ class FaceHelpMessageDeferral( logBuffer: BiometricMessageDeferralLogger, dumpManager: DumpManager, val id: String, val systemClock: Lazy<SystemClock>, ) : BiometricMessageDeferral( resources.getIntArray(R.array.config_face_help_msgs_defer_until_timeout).toHashSet(), resources.getIntArray(R.array.config_face_help_msgs_ignore).toHashSet(), resources.getFloat(R.dimen.config_face_help_msgs_defer_until_timeout_threshold), resources.getInteger(R.integer.config_face_help_msgs_defer_analyze_timeframe).toLong(), logBuffer, dumpManager, id, systemClock, ) /** Loading @@ -77,10 +87,24 @@ open class BiometricMessageDeferral( private val messagesToDefer: Set<Int>, private val acquiredInfoToIgnore: Set<Int>, private val threshold: Float, private val windowToAnalyzeLastNFrames: Long, private val logBuffer: BiometricMessageDeferralLogger, dumpManager: DumpManager, id: String, private val systemClock: Lazy<SystemClock>, ) : Dumpable { private val faceHelpMessageDebouncer: FaceHelpMessageDebouncer? = if (faceMessageDeferUpdate()) { FaceHelpMessageDebouncer( window = windowToAnalyzeLastNFrames, startWindow = 0L, shownFaceMessageFrequencyBoost = 0, threshold = threshold, ) } else { null } private val acquiredInfoToFrequency: MutableMap<Int, Int> = HashMap() private val acquiredInfoToHelpString: MutableMap<Int, String> = HashMap() private var mostFrequentAcquiredInfoToDefer: Int? = null Loading @@ -97,13 +121,20 @@ open class BiometricMessageDeferral( pw.println("messagesToDefer=$messagesToDefer") pw.println("totalFrames=$totalFrames") pw.println("threshold=$threshold") pw.println("faceMessageDeferUpdateFlagEnabled=${faceMessageDeferUpdate()}") if (faceMessageDeferUpdate()) { pw.println("windowToAnalyzeLastNFrames(ms)=$windowToAnalyzeLastNFrames") } } /** Reset all saved counts. */ fun reset() { totalFrames = 0 if (!faceMessageDeferUpdate()) { mostFrequentAcquiredInfoToDefer = null acquiredInfoToFrequency.clear() } acquiredInfoToHelpString.clear() logBuffer.reset() } Loading Loading @@ -137,24 +168,48 @@ open class BiometricMessageDeferral( logBuffer.logFrameIgnored(acquiredInfo) return } totalFrames++ if (faceMessageDeferUpdate()) { faceHelpMessageDebouncer?.let { val helpFaceAuthStatus = HelpFaceAuthenticationStatus( msgId = acquiredInfo, msg = null, systemClock.get().elapsedRealtime() ) if (totalFrames == 1) { // first frame it.startNewFaceAuthSession(helpFaceAuthStatus.createdAt) } it.addMessage(helpFaceAuthStatus) } } else { val newAcquiredInfoCount = acquiredInfoToFrequency.getOrDefault(acquiredInfo, 0) + 1 acquiredInfoToFrequency[acquiredInfo] = newAcquiredInfoCount if ( messagesToDefer.contains(acquiredInfo) && (mostFrequentAcquiredInfoToDefer == null || newAcquiredInfoCount > acquiredInfoToFrequency.getOrDefault(mostFrequentAcquiredInfoToDefer!!, 0)) acquiredInfoToFrequency.getOrDefault( mostFrequentAcquiredInfoToDefer!!, 0 )) ) { mostFrequentAcquiredInfoToDefer = acquiredInfo } } logBuffer.logFrameProcessed( acquiredInfo, totalFrames, if (faceMessageDeferUpdate()) { faceHelpMessageDebouncer ?.getMessageToShow(systemClock.get().elapsedRealtime()) ?.msgId .toString() } else { mostFrequentAcquiredInfoToDefer?.toString() } ) } Loading @@ -166,11 +221,18 @@ open class BiometricMessageDeferral( * [threshold] percentage. */ fun getDeferredMessage(): CharSequence? { if (faceMessageDeferUpdate()) { faceHelpMessageDebouncer?.let { val helpFaceAuthStatus = it.getMessageToShow(systemClock.get().elapsedRealtime()) return acquiredInfoToHelpString[helpFaceAuthStatus?.msgId] } } else { mostFrequentAcquiredInfoToDefer?.let { if (acquiredInfoToFrequency.getOrDefault(it, 0) > (threshold * totalFrames)) { return acquiredInfoToHelpString[it] } } } return null } }