Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 99b6f2bd authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Use more face-acquired frames for msg deferral" into tm-qpr-dev

parents 2573d93f 41cee294
Loading
Loading
Loading
Loading
+27 −0
Original line number Diff line number Diff line
@@ -611,6 +611,33 @@
         2 - Override the setting to never bypass keyguard -->
    <integer name="config_face_unlock_bypass_override">0</integer>

    <!-- Messages that should NOT be shown to the user during face authentication on keyguard.
         This includes both lockscreen and bouncer. This should be used to hide messages that may be
         too chatty or messages that the user can't do much about. Entries are defined in
         android.hardware.biometrics.face@1.0 types.hal.

         Although not visibly shown to the user, these acquired messages (sent per face auth frame)
         are still counted towards the total frames to determine whether a deferred message
         (see config_face_help_msgs_defer_until_timeout) meets the threshold % of frames to show on
         face timeout. -->
     <integer-array name="config_face_acquire_device_entry_ignorelist" translatable="false" >
    </integer-array>

    <!-- Which face help messages to defer until face auth times out. If face auth is cancelled
         or ends on another error, then the message is never surfaced. May also never surface
         if it doesn't meet a threshold % of authentication frames specified by.
         config_face_help_msgs_defer_until_timeout_threshold. -->
    <integer-array name="config_face_help_msgs_defer_until_timeout">
    </integer-array>

    <!-- Percentage of face auth frames received required to show a deferred message at
         FACE_ERROR_TIMEOUT. See config_face_help_msgs_defer_until_timeout for messages
         that are deferred.-->
    <item name="config_face_help_msgs_defer_until_timeout_threshold"
          translatable="false" format="float" type="dimen">
        .75
    </item>

    <!-- 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">
+12 −0
Original line number Diff line number Diff line
@@ -157,6 +157,7 @@ import com.google.android.collect.Lists;
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
@@ -167,6 +168,7 @@ import java.util.Set;
import java.util.TimeZone;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
import java.util.stream.Collectors;

import javax.inject.Inject;
import javax.inject.Provider;
@@ -281,6 +283,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
    private final AuthController mAuthController;
    private final StatusBarStateController mStatusBarStateController;
    private final UiEventLogger mUiEventLogger;
    private final Set<Integer> mFaceAcquiredInfoIgnoreList;
    private int mStatusBarState;
    private final StatusBarStateController.StateListener mStatusBarStateControllerListener =
            new StatusBarStateController.StateListener() {
@@ -1023,6 +1026,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab

    private void handleFaceAuthFailed() {
        Assert.isMainThread();
        mLogger.d("onFaceAuthFailed");
        mFaceCancelSignal = null;
        setFaceRunningState(BIOMETRIC_STATE_STOPPED);
        for (int i = 0; i < mCallbacks.size(); i++) {
@@ -1639,6 +1643,9 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab

                @Override
                public void onAuthenticationHelp(int helpMsgId, CharSequence helpString) {
                    if (mFaceAcquiredInfoIgnoreList.contains(helpMsgId)) {
                        return;
                    }
                    handleFaceHelp(helpMsgId, helpString.toString());
                }

@@ -1931,6 +1938,11 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
        mActiveUnlockConfig.setKeyguardUpdateMonitor(this);
        mWakeOnFingerprintAcquiredStart = context.getResources()
                        .getBoolean(com.android.internal.R.bool.kg_wake_on_acquire_start);
        mFaceAcquiredInfoIgnoreList = Arrays.stream(
                mContext.getResources().getIntArray(
                        R.array.config_face_acquire_device_entry_ignorelist))
                .boxed()
                .collect(Collectors.toSet());

        mHandler = new Handler(mainLooper) {
            @Override
+71 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2022 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.keyguard.logging

import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.LogLevel.DEBUG
import com.android.systemui.log.dagger.BiometricMessagesLog
import javax.inject.Inject

/** Helper class for logging for [com.android.systemui.biometrics.FaceHelpMessageDeferral] */
@SysUISingleton
class FaceMessageDeferralLogger
@Inject
constructor(@BiometricMessagesLog private val logBuffer: LogBuffer) :
    BiometricMessageDeferralLogger(logBuffer, "FaceMessageDeferralLogger")

open class BiometricMessageDeferralLogger(
    private val logBuffer: LogBuffer,
    private val tag: String
) {
    fun reset() {
        logBuffer.log(tag, DEBUG, "reset")
    }

    fun logUpdateMessage(acquiredInfo: Int, helpString: String) {
        logBuffer.log(
            tag,
            DEBUG,
            {
                int1 = acquiredInfo
                str1 = helpString
            },
            { "updateMessage acquiredInfo=$int1 helpString=$str1" }
        )
    }

    fun logFrameProcessed(
        acquiredInfo: Int,
        totalFrames: Int,
        mostFrequentAcquiredInfoToDefer: String? // may not meet the threshold
    ) {
        logBuffer.log(
            tag,
            DEBUG,
            {
                int1 = acquiredInfo
                int2 = totalFrames
                str1 = mostFrequentAcquiredInfoToDefer
            },
            {
                "frameProcessed acquiredInfo=$int1 totalFrames=$int2 " +
                    "messageToShowOnTimeout=$str1"
            }
        )
    }
}
+0 −89
Original line number Diff line number Diff line
/*
 * Copyright (C) 2022 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.biometrics

/**
 * Provides whether an acquired error message should be shown immediately when its received (see
 * [shouldDefer]) or should be shown when the biometric error is received [getDeferredMessage].
 * @property excludedMessages messages that are excluded from counts
 * @property messagesToDefer messages that shouldn't show immediately when received, but may be
 * shown later if the message is the most frequent message processed and meets [THRESHOLD]
 * percentage of all messages (excluding [excludedMessages])
 */
class BiometricMessageDeferral(
    private val excludedMessages: Set<Int>,
    private val messagesToDefer: Set<Int>
) {
    private val msgCounts: MutableMap<Int, Int> = HashMap() // msgId => frequency of msg
    private val msgIdToCharSequence: MutableMap<Int, CharSequence> = HashMap() // msgId => message
    private var totalRelevantMessages = 0
    private var mostFrequentMsgIdToDefer: Int? = null

    /** Reset all saved counts. */
    fun reset() {
        totalRelevantMessages = 0
        msgCounts.clear()
        msgIdToCharSequence.clear()
    }

    /** Whether the given message should be deferred instead of being shown immediately. */
    fun shouldDefer(acquiredMsgId: Int): Boolean {
        return messagesToDefer.contains(acquiredMsgId)
    }

    /**
     * Adds the acquiredMsgId to the counts if it's not in [excludedMessages]. We still count
     * messages that shouldn't be deferred in these counts.
     */
    fun processMessage(acquiredMsgId: Int, helpString: CharSequence) {
        if (excludedMessages.contains(acquiredMsgId)) {
            return
        }

        totalRelevantMessages++
        msgIdToCharSequence[acquiredMsgId] = helpString

        val newAcquiredMsgCount = msgCounts.getOrDefault(acquiredMsgId, 0) + 1
        msgCounts[acquiredMsgId] = newAcquiredMsgCount
        if (
            messagesToDefer.contains(acquiredMsgId) &&
                (mostFrequentMsgIdToDefer == null ||
                    newAcquiredMsgCount > msgCounts.getOrDefault(mostFrequentMsgIdToDefer!!, 0))
        ) {
            mostFrequentMsgIdToDefer = acquiredMsgId
        }
    }

    /**
     * Get the most frequent deferred message that meets the [THRESHOLD] percentage of processed
     * messages excluding [excludedMessages].
     * @return null if no messages have been deferred OR deferred messages didn't meet the
     * [THRESHOLD] percentage of messages to show.
     */
    fun getDeferredMessage(): CharSequence? {
        mostFrequentMsgIdToDefer?.let {
            if (msgCounts.getOrDefault(it, 0) > (THRESHOLD * totalRelevantMessages)) {
                return msgIdToCharSequence[mostFrequentMsgIdToDefer]
            }
        }

        return null
    }
    companion object {
        const val THRESHOLD = .5f
    }
}
+141 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2022 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.biometrics

import android.content.res.Resources
import com.android.keyguard.logging.BiometricMessageDeferralLogger
import com.android.keyguard.logging.FaceMessageDeferralLogger
import com.android.systemui.Dumpable
import com.android.systemui.R
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dump.DumpManager
import java.io.PrintWriter
import java.util.*
import javax.inject.Inject

/**
 * Provides whether a face acquired help message should be shown immediately when its received or
 * should be shown when face auth times out. See [updateMessage] and [getDeferredMessage].
 */
@SysUISingleton
class FaceHelpMessageDeferral
@Inject
constructor(
    @Main resources: Resources,
    logBuffer: FaceMessageDeferralLogger,
    dumpManager: DumpManager
) :
    BiometricMessageDeferral(
        resources.getIntArray(R.array.config_face_help_msgs_defer_until_timeout).toHashSet(),
        resources.getFloat(R.dimen.config_face_help_msgs_defer_until_timeout_threshold),
        logBuffer,
        dumpManager
    )

/**
 * @property messagesToDefer messages that shouldn't show immediately when received, but may be
 * shown later if the message is the most frequent acquiredInfo processed and meets [threshold]
 * percentage of all passed acquired frames.
 */
open class BiometricMessageDeferral(
    private val messagesToDefer: Set<Int>,
    private val threshold: Float,
    private val logBuffer: BiometricMessageDeferralLogger,
    dumpManager: DumpManager
) : Dumpable {
    private val acquiredInfoToFrequency: MutableMap<Int, Int> = HashMap()
    private val acquiredInfoToHelpString: MutableMap<Int, String> = HashMap()
    private var mostFrequentAcquiredInfoToDefer: Int? = null
    private var totalFrames = 0

    init {
        dumpManager.registerDumpable(this.javaClass.name, this)
    }

    override fun dump(pw: PrintWriter, args: Array<out String>) {
        pw.println("messagesToDefer=$messagesToDefer")
        pw.println("totalFrames=$totalFrames")
        pw.println("threshold=$threshold")
    }

    /** Reset all saved counts. */
    fun reset() {
        totalFrames = 0
        mostFrequentAcquiredInfoToDefer = null
        acquiredInfoToFrequency.clear()
        acquiredInfoToHelpString.clear()
        logBuffer.reset()
    }

    /** Updates the message associated with the acquiredInfo if it's a message we may defer. */
    fun updateMessage(acquiredInfo: Int, helpString: String) {
        if (!messagesToDefer.contains(acquiredInfo)) {
            return
        }
        if (!Objects.equals(acquiredInfoToHelpString[acquiredInfo], helpString)) {
            logBuffer.logUpdateMessage(acquiredInfo, helpString)
            acquiredInfoToHelpString[acquiredInfo] = helpString
        }
    }

    /** Whether the given message should be deferred instead of being shown immediately. */
    fun shouldDefer(acquiredMsgId: Int): Boolean {
        return messagesToDefer.contains(acquiredMsgId)
    }

    /** Adds the acquiredInfo frame to the counts. We account for all frames. */
    fun processFrame(acquiredInfo: Int) {
        if (messagesToDefer.isEmpty()) {
            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
        }

        logBuffer.logFrameProcessed(
            acquiredInfo,
            totalFrames,
            mostFrequentAcquiredInfoToDefer?.toString()
        )
    }

    /**
     * Get the most frequent deferred message that meets the [threshold] percentage of processed
     * frames.
     * @return null if no acquiredInfo have been deferred OR deferred messages didn't meet the
     * [threshold] percentage.
     */
    fun getDeferredMessage(): CharSequence? {
        mostFrequentAcquiredInfoToDefer?.let {
            if (acquiredInfoToFrequency.getOrDefault(it, 0) > (threshold * totalFrames)) {
                return acquiredInfoToHelpString[it]
            }
        }
        return null
    }
}
Loading