• 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.keyguard.logging.FaceMessageDeferralLogger
22 import com.android.systemui.Dumpable
23 import com.android.systemui.R
24 import com.android.systemui.dagger.SysUISingleton
25 import com.android.systemui.dagger.qualifiers.Main
26 import com.android.systemui.dump.DumpManager
27 import java.io.PrintWriter
28 import java.util.*
29 import javax.inject.Inject
30 
31 /**
32  * Provides whether a face acquired help message should be shown immediately when its received or
33  * should be shown when face auth times out. See [updateMessage] and [getDeferredMessage].
34  */
35 @SysUISingleton
36 class FaceHelpMessageDeferral
37 @Inject
38 constructor(
39     @Main resources: Resources,
40     logBuffer: FaceMessageDeferralLogger,
41     dumpManager: DumpManager
42 ) :
43     BiometricMessageDeferral(
44         resources.getIntArray(R.array.config_face_help_msgs_defer_until_timeout).toHashSet(),
45         resources.getFloat(R.dimen.config_face_help_msgs_defer_until_timeout_threshold),
46         logBuffer,
47         dumpManager
48     )
49 
50 /**
51  * @property messagesToDefer messages that shouldn't show immediately when received, but may be
52  *   shown later if the message is the most frequent acquiredInfo processed and meets [threshold]
53  *   percentage of all passed acquired frames.
54  */
55 open class BiometricMessageDeferral(
56     private val messagesToDefer: Set<Int>,
57     private val threshold: Float,
58     private val logBuffer: BiometricMessageDeferralLogger,
59     dumpManager: DumpManager
60 ) : Dumpable {
61     private val acquiredInfoToFrequency: MutableMap<Int, Int> = HashMap()
62     private val acquiredInfoToHelpString: MutableMap<Int, String> = HashMap()
63     private var mostFrequentAcquiredInfoToDefer: Int? = null
64     private var totalFrames = 0
65 
66     init {
67         dumpManager.registerDumpable(this.javaClass.name, this)
68     }
69 
dumpnull70     override fun dump(pw: PrintWriter, args: Array<out String>) {
71         pw.println("messagesToDefer=$messagesToDefer")
72         pw.println("totalFrames=$totalFrames")
73         pw.println("threshold=$threshold")
74     }
75 
76     /** Reset all saved counts. */
resetnull77     fun reset() {
78         totalFrames = 0
79         mostFrequentAcquiredInfoToDefer = null
80         acquiredInfoToFrequency.clear()
81         acquiredInfoToHelpString.clear()
82         logBuffer.reset()
83     }
84 
85     /** Updates the message associated with the acquiredInfo if it's a message we may defer. */
updateMessagenull86     fun updateMessage(acquiredInfo: Int, helpString: String) {
87         if (!messagesToDefer.contains(acquiredInfo)) {
88             return
89         }
90         if (!Objects.equals(acquiredInfoToHelpString[acquiredInfo], helpString)) {
91             logBuffer.logUpdateMessage(acquiredInfo, helpString)
92             acquiredInfoToHelpString[acquiredInfo] = helpString
93         }
94     }
95 
96     /** Whether the given message should be deferred instead of being shown immediately. */
shouldDefernull97     fun shouldDefer(acquiredMsgId: Int): Boolean {
98         return messagesToDefer.contains(acquiredMsgId)
99     }
100 
101     /** Adds the acquiredInfo frame to the counts. We account for all frames. */
processFramenull102     fun processFrame(acquiredInfo: Int) {
103         if (messagesToDefer.isEmpty()) {
104             return
105         }
106 
107         totalFrames++
108 
109         val newAcquiredInfoCount = acquiredInfoToFrequency.getOrDefault(acquiredInfo, 0) + 1
110         acquiredInfoToFrequency[acquiredInfo] = newAcquiredInfoCount
111         if (
112             messagesToDefer.contains(acquiredInfo) &&
113                 (mostFrequentAcquiredInfoToDefer == null ||
114                     newAcquiredInfoCount >
115                         acquiredInfoToFrequency.getOrDefault(mostFrequentAcquiredInfoToDefer!!, 0))
116         ) {
117             mostFrequentAcquiredInfoToDefer = acquiredInfo
118         }
119 
120         logBuffer.logFrameProcessed(
121             acquiredInfo,
122             totalFrames,
123             mostFrequentAcquiredInfoToDefer?.toString()
124         )
125     }
126 
127     /**
128      * Get the most frequent deferred message that meets the [threshold] percentage of processed
129      * frames.
130      *
131      * @return null if no acquiredInfo have been deferred OR deferred messages didn't meet the
132      *   [threshold] percentage.
133      */
getDeferredMessagenull134     fun getDeferredMessage(): CharSequence? {
135         mostFrequentAcquiredInfoToDefer?.let {
136             if (acquiredInfoToFrequency.getOrDefault(it, 0) > (threshold * totalFrames)) {
137                 return acquiredInfoToHelpString[it]
138             }
139         }
140         return null
141     }
142 }
143