• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2022 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.systemui.biometrics
18 
19 import android.content.res.Resources
20 import com.android.keyguard.logging.BiometricMessageDeferralLogger
21 import com.android.systemui.Dumpable
22 import com.android.systemui.dagger.SysUISingleton
23 import com.android.systemui.dagger.qualifiers.Main
24 import com.android.systemui.dump.DumpManager
25 import com.android.systemui.log.LogBuffer
26 import com.android.systemui.log.dagger.BiometricLog
27 import com.android.systemui.res.R
28 import java.io.PrintWriter
29 import java.util.Objects
30 import java.util.UUID
31 import javax.inject.Inject
32 
33 @SysUISingleton
34 class FaceHelpMessageDeferralFactory
35 @Inject
36 constructor(
37     @Main private val resources: Resources,
38     @BiometricLog private val logBuffer: LogBuffer,
39     private val dumpManager: DumpManager
40 ) {
createnull41     fun create(): FaceHelpMessageDeferral {
42         val id = UUID.randomUUID().toString()
43         return FaceHelpMessageDeferral(
44             resources = resources,
45             logBuffer = BiometricMessageDeferralLogger(logBuffer, "FaceHelpMessageDeferral[$id]"),
46             dumpManager = dumpManager,
47             id = id,
48         )
49     }
50 }
51 
52 /**
53  * Provides whether a face acquired help message should be shown immediately when its received or
54  * should be shown when face auth times out. See [updateMessage] and [getDeferredMessage].
55  */
56 class FaceHelpMessageDeferral(
57     resources: Resources,
58     logBuffer: BiometricMessageDeferralLogger,
59     dumpManager: DumpManager,
60     val id: String,
61 ) :
62     BiometricMessageDeferral(
63         resources.getIntArray(R.array.config_face_help_msgs_defer_until_timeout).toHashSet(),
64         resources.getIntArray(R.array.config_face_help_msgs_ignore).toHashSet(),
65         resources.getFloat(R.dimen.config_face_help_msgs_defer_until_timeout_threshold),
66         logBuffer,
67         dumpManager,
68         id,
69     )
70 
71 /**
72  * @property messagesToDefer messages that shouldn't show immediately when received, but may be
73  *   shown later if the message is the most frequent acquiredInfo processed and meets [threshold]
74  *   percentage of all acquired frames, excluding [acquiredInfoToIgnore].
75  */
76 open class BiometricMessageDeferral(
77     private val messagesToDefer: Set<Int>,
78     private val acquiredInfoToIgnore: Set<Int>,
79     private val threshold: Float,
80     private val logBuffer: BiometricMessageDeferralLogger,
81     dumpManager: DumpManager,
82     id: String,
83 ) : Dumpable {
84     private val acquiredInfoToFrequency: MutableMap<Int, Int> = HashMap()
85     private val acquiredInfoToHelpString: MutableMap<Int, String> = HashMap()
86     private var mostFrequentAcquiredInfoToDefer: Int? = null
87     private var totalFrames = 0
88 
89     init {
90         dumpManager.registerNormalDumpable(
91             "${this.javaClass.name}[$id]",
92             this,
93         )
94     }
95 
dumpnull96     override fun dump(pw: PrintWriter, args: Array<out String>) {
97         pw.println("messagesToDefer=$messagesToDefer")
98         pw.println("totalFrames=$totalFrames")
99         pw.println("threshold=$threshold")
100     }
101 
102     /** Reset all saved counts. */
resetnull103     fun reset() {
104         totalFrames = 0
105         mostFrequentAcquiredInfoToDefer = null
106         acquiredInfoToFrequency.clear()
107         acquiredInfoToHelpString.clear()
108         logBuffer.reset()
109     }
110 
111     /** Updates the message associated with the acquiredInfo if it's a message we may defer. */
updateMessagenull112     fun updateMessage(acquiredInfo: Int, helpString: String) {
113         if (!messagesToDefer.contains(acquiredInfo)) {
114             return
115         }
116         if (!Objects.equals(acquiredInfoToHelpString[acquiredInfo], helpString)) {
117             logBuffer.logUpdateMessage(acquiredInfo, helpString)
118             acquiredInfoToHelpString[acquiredInfo] = helpString
119         }
120     }
121 
122     /** Whether the given message should be deferred instead of being shown immediately. */
shouldDefernull123     fun shouldDefer(acquiredMsgId: Int): Boolean {
124         return messagesToDefer.contains(acquiredMsgId)
125     }
126 
127     /**
128      * Adds the acquiredInfo frame to the counts. We account for frames not included in
129      * acquiredInfoToIgnore.
130      */
processFramenull131     fun processFrame(acquiredInfo: Int) {
132         if (messagesToDefer.isEmpty()) {
133             return
134         }
135 
136         if (acquiredInfoToIgnore.contains(acquiredInfo)) {
137             logBuffer.logFrameIgnored(acquiredInfo)
138             return
139         }
140 
141         totalFrames++
142 
143         val newAcquiredInfoCount = acquiredInfoToFrequency.getOrDefault(acquiredInfo, 0) + 1
144         acquiredInfoToFrequency[acquiredInfo] = newAcquiredInfoCount
145         if (
146             messagesToDefer.contains(acquiredInfo) &&
147                 (mostFrequentAcquiredInfoToDefer == null ||
148                     newAcquiredInfoCount >
149                         acquiredInfoToFrequency.getOrDefault(mostFrequentAcquiredInfoToDefer!!, 0))
150         ) {
151             mostFrequentAcquiredInfoToDefer = acquiredInfo
152         }
153 
154         logBuffer.logFrameProcessed(
155             acquiredInfo,
156             totalFrames,
157             mostFrequentAcquiredInfoToDefer?.toString()
158         )
159     }
160 
161     /**
162      * Get the most frequent deferred message that meets the [threshold] percentage of processed
163      * frames.
164      *
165      * @return null if no acquiredInfo have been deferred OR deferred messages didn't meet the
166      *   [threshold] percentage.
167      */
getDeferredMessagenull168     fun getDeferredMessage(): CharSequence? {
169         mostFrequentAcquiredInfoToDefer?.let {
170             if (acquiredInfoToFrequency.getOrDefault(it, 0) > (threshold * totalFrames)) {
171                 return acquiredInfoToHelpString[it]
172             }
173         }
174         return null
175     }
176 }
177