summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/FaceHelpMessageDebouncerTest.kt56
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/FaceHelpMessageDeferralTest.kt79
-rw-r--r--packages/SystemUI/res/values/config.xml4
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/FaceHelpMessageDebouncer.kt29
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/FaceHelpMessageDeferral.kt96
5 files changed, 232 insertions, 32 deletions
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/FaceHelpMessageDebouncerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/FaceHelpMessageDebouncerTest.kt
index baef620ad556..a36b0bca42b5 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/FaceHelpMessageDebouncerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/FaceHelpMessageDebouncerTest.kt
@@ -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")
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/FaceHelpMessageDeferralTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/FaceHelpMessageDeferralTest.kt
index b31f6f5b096b..add7a7fbb8a1 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/FaceHelpMessageDeferralTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/FaceHelpMessageDeferralTest.kt
@@ -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
@@ -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() {
@@ -111,10 +130,11 @@ class FaceHelpMessageDeferralTest : SysuiTestCase() {
}
@Test
+ @DisableFlags(Flags.FLAG_FACE_MESSAGE_DEFER_UPDATE)
fun testReturnsMostFrequentDeferredMessage() {
val biometricMessageDeferral = createMsgDeferral(setOf(1, 2))
- // WHEN there's 80%of the messages are msgId=1 and 20% is msgId=2
+ // WHEN there's 80% of the messages are msgId=1 and 20% is msgId=2
biometricMessageDeferral.processFrame(1)
biometricMessageDeferral.processFrame(1)
biometricMessageDeferral.processFrame(1)
@@ -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())
}
@@ -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 },
)
}
}
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 823ff9f54be3..e8fd2ef6eafa 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -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">
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/FaceHelpMessageDebouncer.kt b/packages/SystemUI/src/com/android/systemui/biometrics/FaceHelpMessageDebouncer.kt
index 1685f49e4f3e..4731ebba7124 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/FaceHelpMessageDebouncer.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/FaceHelpMessageDebouncer.kt
@@ -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
@@ -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()
@@ -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) {
@@ -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
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/FaceHelpMessageDeferral.kt b/packages/SystemUI/src/com/android/systemui/biometrics/FaceHelpMessageDeferral.kt
index 90d06fb0bec1..d382adaff955 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/FaceHelpMessageDeferral.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/FaceHelpMessageDeferral.kt
@@ -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
@@ -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()
@@ -45,6 +51,7 @@ constructor(
logBuffer = BiometricMessageDeferralLogger(logBuffer, "FaceHelpMessageDeferral[$id]"),
dumpManager = dumpManager,
id = id,
+ systemClock,
)
}
}
@@ -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,
)
/**
@@ -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
@@ -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
- mostFrequentAcquiredInfoToDefer = null
- acquiredInfoToFrequency.clear()
+ if (!faceMessageDeferUpdate()) {
+ mostFrequentAcquiredInfoToDefer = null
+ acquiredInfoToFrequency.clear()
+ }
+
acquiredInfoToHelpString.clear()
logBuffer.reset()
}
@@ -137,24 +168,48 @@ open class BiometricMessageDeferral(
logBuffer.logFrameIgnored(acquiredInfo)
return
}
-
totalFrames++
- val newAcquiredInfoCount = acquiredInfoToFrequency.getOrDefault(acquiredInfo, 0) + 1
- acquiredInfoToFrequency[acquiredInfo] = newAcquiredInfoCount
- if (
- messagesToDefer.contains(acquiredInfo) &&
- (mostFrequentAcquiredInfoToDefer == null ||
- newAcquiredInfoCount >
- acquiredInfoToFrequency.getOrDefault(mostFrequentAcquiredInfoToDefer!!, 0))
- ) {
- mostFrequentAcquiredInfoToDefer = acquiredInfo
+ 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
+ ))
+ ) {
+ mostFrequentAcquiredInfoToDefer = acquiredInfo
+ }
}
logBuffer.logFrameProcessed(
acquiredInfo,
totalFrames,
- mostFrequentAcquiredInfoToDefer?.toString()
+ if (faceMessageDeferUpdate()) {
+ faceHelpMessageDebouncer
+ ?.getMessageToShow(systemClock.get().elapsedRealtime())
+ ?.msgId
+ .toString()
+ } else {
+ mostFrequentAcquiredInfoToDefer?.toString()
+ }
)
}
@@ -166,9 +221,16 @@ open class BiometricMessageDeferral(
* [threshold] percentage.
*/
fun getDeferredMessage(): CharSequence? {
- mostFrequentAcquiredInfoToDefer?.let {
- if (acquiredInfoToFrequency.getOrDefault(it, 0) > (threshold * totalFrames)) {
- return acquiredInfoToHelpString[it]
+ 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