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

Commit b519f3be authored by Beverly's avatar Beverly
Browse files

Show directional face auth feedback when unfolded

Bug: 319377982
Flag: NONE bugfix
Test: atest KeyguardIndicationControllerTest
Change-Id: I10c6d938ae0124663997477b7ddc8e369e69ed42
parent 5dd37854
Loading
Loading
Loading
Loading
+14 −0
Original line number Original line Diff line number Diff line
@@ -720,6 +720,20 @@
        <item>26</item> <!-- MOUTH_COVERING_DETECTED -->
        <item>26</item> <!-- MOUTH_COVERING_DETECTED -->
    </integer-array>
    </integer-array>


    <!-- Which face help messages to surface when fingerprint is enrolled and device is unfolded.
     Message ids correspond with the acquired ids in BiometricFaceConstants -->
    <integer-array name="config_face_help_msgs_when_fingerprint_enrolled_unfolded">
        <item>3</item> <!-- TOO_DARK -->
        <item>4</item> <!-- TOO_CLOSE -->
        <item>5</item> <!-- TOO_FAR -->
        <item>6</item> <!-- TOO_HIGH -->
        <item>7</item> <!-- TOO_LOW -->
        <item>8</item> <!-- TOO_RIGHT -->
        <item>9</item> <!-- TOO_LEFT -->
        <item>25</item> <!-- DARK_GLASSES -->
        <item>26</item> <!-- MOUTH_COVERING_DETECTED -->
    </integer-array>

    <!-- Which device wake-ups will trigger passive auth. These values correspond with
    <!-- Which device wake-ups will trigger passive auth. These values correspond with
         PowerManager#WakeReason. -->
         PowerManager#WakeReason. -->
    <integer-array name="config_face_auth_wake_up_triggers">
    <integer-array name="config_face_auth_wake_up_triggers">
+52 −15
Original line number Original line Diff line number Diff line
@@ -30,17 +30,20 @@ import com.android.systemui.deviceentry.shared.model.FingerprintFailureMessage
import com.android.systemui.deviceentry.shared.model.FingerprintLockoutMessage
import com.android.systemui.deviceentry.shared.model.FingerprintLockoutMessage
import com.android.systemui.deviceentry.shared.model.FingerprintMessage
import com.android.systemui.deviceentry.shared.model.FingerprintMessage
import com.android.systemui.deviceentry.shared.model.HelpFaceAuthenticationStatus
import com.android.systemui.deviceentry.shared.model.HelpFaceAuthenticationStatus
import com.android.systemui.keyguard.domain.interactor.DevicePostureInteractor
import com.android.systemui.keyguard.shared.model.DevicePosture
import com.android.systemui.keyguard.shared.model.ErrorFingerprintAuthenticationStatus
import com.android.systemui.keyguard.shared.model.ErrorFingerprintAuthenticationStatus
import com.android.systemui.res.R
import com.android.systemui.res.R
import com.android.systemui.util.kotlin.Utils.Companion.toTriple
import com.android.systemui.util.kotlin.sample
import com.android.systemui.util.kotlin.sample
import javax.inject.Inject
import javax.inject.Inject
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.filterIsInstance
import kotlinx.coroutines.flow.filterIsInstance
import kotlinx.coroutines.flow.filterNot
import kotlinx.coroutines.flow.filterNot
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.merge
import kotlinx.coroutines.flow.merge


@@ -59,6 +62,7 @@ constructor(
    faceAuthInteractor: DeviceEntryFaceAuthInteractor,
    faceAuthInteractor: DeviceEntryFaceAuthInteractor,
    private val biometricSettingsInteractor: DeviceEntryBiometricSettingsInteractor,
    private val biometricSettingsInteractor: DeviceEntryBiometricSettingsInteractor,
    faceHelpMessageDeferralInteractor: FaceHelpMessageDeferralInteractor,
    faceHelpMessageDeferralInteractor: FaceHelpMessageDeferralInteractor,
    devicePostureInteractor: DevicePostureInteractor,
) {
) {
    private val faceHelp: Flow<HelpFaceAuthenticationStatus> =
    private val faceHelp: Flow<HelpFaceAuthenticationStatus> =
        faceAuthInteractor.authenticationStatus.filterIsInstance<HelpFaceAuthenticationStatus>()
        faceAuthInteractor.authenticationStatus.filterIsInstance<HelpFaceAuthenticationStatus>()
@@ -71,9 +75,18 @@ constructor(
     * The acquisition message ids to show message when both fingerprint and face are enrolled and
     * The acquisition message ids to show message when both fingerprint and face are enrolled and
     * enabled for device entry.
     * enabled for device entry.
     */
     */
    private val coExFaceAcquisitionMsgIdsToShow: Set<Int> =
    private val coExFaceAcquisitionMsgIdsToShowDefault: Set<Int> =
        resources.getIntArray(R.array.config_face_help_msgs_when_fingerprint_enrolled).toSet()
        resources.getIntArray(R.array.config_face_help_msgs_when_fingerprint_enrolled).toSet()


    /**
     * The acquisition message ids to show message when both fingerprint and face are enrolled and
     * enabled for device entry and the device is unfolded.
     */
    private val coExFaceAcquisitionMsgIdsToShowUnfolded: Set<Int> =
        resources
            .getIntArray(R.array.config_face_help_msgs_when_fingerprint_enrolled_unfolded)
            .toSet()

    private fun ErrorFingerprintAuthenticationStatus.shouldSuppressError(): Boolean {
    private fun ErrorFingerprintAuthenticationStatus.shouldSuppressError(): Boolean {
        return isCancellationError() || isPowerPressedError()
        return isCancellationError() || isPowerPressedError()
    }
    }
@@ -122,6 +135,17 @@ constructor(
                }
                }
        }
        }


    val coExFaceAcquisitionMsgIdsToShow: Flow<Set<Int>> =
        devicePostureInteractor.posture.map { devicePosture ->
            when (devicePosture) {
                DevicePosture.OPENED -> coExFaceAcquisitionMsgIdsToShowUnfolded
                DevicePosture.UNKNOWN, // Devices without posture support (non-foldable) use UNKNOWN
                DevicePosture.CLOSED,
                DevicePosture.HALF_OPENED,
                DevicePosture.FLIPPED -> coExFaceAcquisitionMsgIdsToShowDefault
            }
        }

    val fingerprintMessage: Flow<FingerprintMessage> =
    val fingerprintMessage: Flow<FingerprintMessage> =
        merge(
        merge(
            fingerprintErrorMessage,
            fingerprintErrorMessage,
@@ -129,25 +153,38 @@ constructor(
            fingerprintHelpMessage,
            fingerprintHelpMessage,
        )
        )


    private val filterConditionForFaceHelpMessages:
        Flow<(HelpFaceAuthenticationStatus) -> Boolean> =
        combine(
                biometricSettingsInteractor.isFingerprintAuthEnrolledAndEnabled,
                biometricSettingsInteractor.faceAuthCurrentlyAllowed,
                ::Pair
            )
            .flatMapLatest { (fingerprintEnrolled, faceAuthCurrentlyAllowed) ->
                if (fingerprintEnrolled && faceAuthCurrentlyAllowed) {
                    // Show only some face help messages if fingerprint is also enrolled
                    coExFaceAcquisitionMsgIdsToShow.map { msgIdsToShow ->
                        { helpStatus: HelpFaceAuthenticationStatus ->
                            msgIdsToShow.contains(helpStatus.msgId)
                        }
                    }
                } else if (faceAuthCurrentlyAllowed) {
                    // Show all face help messages if only face is enrolled and currently allowed
                    flowOf { _: HelpFaceAuthenticationStatus -> true }
                } else {
                    flowOf { _: HelpFaceAuthenticationStatus -> false }
                }
            }

    private val faceHelpMessage: Flow<FaceMessage> =
    private val faceHelpMessage: Flow<FaceMessage> =
        faceHelp
        faceHelp
            .filterNot {
            .filterNot {
                // Message deferred to potentially show at face timeout error instead
                // Message deferred to potentially show at face timeout error instead
                faceHelpMessageDeferralInteractor.shouldDefer(it.msgId)
                faceHelpMessageDeferralInteractor.shouldDefer(it.msgId)
            }
            }
            .sample(biometricSettingsInteractor.fingerprintAndFaceEnrolledAndEnabled, ::Pair)
            .sample(filterConditionForFaceHelpMessages, ::Pair)
            .filter { (faceAuthHelpStatus, fingerprintAndFaceEnrolledAndEnabled) ->
            .filter { (helpMessage, filterCondition) -> filterCondition(helpMessage) }
                if (fingerprintAndFaceEnrolledAndEnabled) {
            .map { (status, _) -> FaceMessage(status.msg) }
                    // Show only some face help messages if fingerprint is also enrolled
                    coExFaceAcquisitionMsgIdsToShow.contains(faceAuthHelpStatus.msgId)
                } else {
                    // Show all face help messages if only face is enrolled
                    true
                }
            }
            .sample(biometricSettingsInteractor.faceAuthCurrentlyAllowed, ::toTriple)
            .filter { (_, _, faceAuthCurrentlyAllowed) -> faceAuthCurrentlyAllowed }
            .map { (status, _, _) -> FaceMessage(status.msg) }


    private val faceFailureMessage: Flow<FaceMessage> =
    private val faceFailureMessage: Flow<FaceMessage> =
        faceFailure
        faceFailure
+31 −0
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2024 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.keyguard.domain.interactor

import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.data.repository.DevicePostureRepository
import javax.inject.Inject
import kotlinx.coroutines.ExperimentalCoroutinesApi

/** DevicePosture business logic. */
@ExperimentalCoroutinesApi
@SysUISingleton
class DevicePostureInteractor
@Inject
constructor(devicePostureRepository: DevicePostureRepository) {
    val posture = devicePostureRepository.currentDevicePosture
}
+15 −10
Original line number Original line Diff line number Diff line
@@ -97,6 +97,7 @@ import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.deviceentry.domain.interactor.BiometricMessageInteractor;
import com.android.systemui.dock.DockManager;
import com.android.systemui.dock.DockManager;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.keyguard.KeyguardIndication;
import com.android.systemui.keyguard.KeyguardIndication;
@@ -120,7 +121,6 @@ import com.android.systemui.util.wakelock.WakeLock;


import java.io.PrintWriter;
import java.io.PrintWriter;
import java.text.NumberFormat;
import java.text.NumberFormat;
import java.util.HashSet;
import java.util.Set;
import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Consumer;


@@ -182,6 +182,7 @@ public class KeyguardIndicationController {
    private BroadcastReceiver mBroadcastReceiver;
    private BroadcastReceiver mBroadcastReceiver;
    private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
    private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
    private KeyguardInteractor mKeyguardInteractor;
    private KeyguardInteractor mKeyguardInteractor;
    private final BiometricMessageInteractor mBiometricMessageInteractor;
    private String mPersistentUnlockMessage;
    private String mPersistentUnlockMessage;
    private String mAlignmentIndication;
    private String mAlignmentIndication;
    private boolean mForceIsDismissible;
    private boolean mForceIsDismissible;
@@ -212,7 +213,7 @@ public class KeyguardIndicationController {
    private boolean mBatteryPresent = true;
    private boolean mBatteryPresent = true;
    protected long mChargingTimeRemaining;
    protected long mChargingTimeRemaining;
    private Pair<String, BiometricSourceType> mBiometricErrorMessageToShowOnScreenOn;
    private Pair<String, BiometricSourceType> mBiometricErrorMessageToShowOnScreenOn;
    private final Set<Integer> mCoExFaceAcquisitionMsgIdsToShow;
    private Set<Integer> mCoExFaceAcquisitionMsgIdsToShow;
    private final FaceHelpMessageDeferral mFaceAcquiredMessageDeferral;
    private final FaceHelpMessageDeferral mFaceAcquiredMessageDeferral;
    private boolean mInited;
    private boolean mInited;


@@ -230,6 +231,10 @@ public class KeyguardIndicationController {
                mIsActiveDreamLockscreenHosted = isLockscreenHosted;
                mIsActiveDreamLockscreenHosted = isLockscreenHosted;
                updateDeviceEntryIndication(false);
                updateDeviceEntryIndication(false);
            };
            };
    @VisibleForTesting
    final Consumer<Set<Integer>> mCoExAcquisitionMsgIdsToShowCallback =
            (Set<Integer> coExFaceAcquisitionMsgIdsToShow) -> mCoExFaceAcquisitionMsgIdsToShow =
                    coExFaceAcquisitionMsgIdsToShow;
    private final ScreenLifecycle.Observer mScreenObserver = new ScreenLifecycle.Observer() {
    private final ScreenLifecycle.Observer mScreenObserver = new ScreenLifecycle.Observer() {
        @Override
        @Override
        public void onScreenTurnedOn() {
        public void onScreenTurnedOn() {
@@ -289,7 +294,8 @@ public class KeyguardIndicationController {
            BouncerMessageInteractor bouncerMessageInteractor,
            BouncerMessageInteractor bouncerMessageInteractor,
            FeatureFlags flags,
            FeatureFlags flags,
            IndicationHelper indicationHelper,
            IndicationHelper indicationHelper,
            KeyguardInteractor keyguardInteractor
            KeyguardInteractor keyguardInteractor,
            BiometricMessageInteractor biometricMessageInteractor
    ) {
    ) {
        mContext = context;
        mContext = context;
        mBroadcastDispatcher = broadcastDispatcher;
        mBroadcastDispatcher = broadcastDispatcher;
@@ -318,14 +324,9 @@ public class KeyguardIndicationController {
        mFeatureFlags = flags;
        mFeatureFlags = flags;
        mIndicationHelper = indicationHelper;
        mIndicationHelper = indicationHelper;
        mKeyguardInteractor = keyguardInteractor;
        mKeyguardInteractor = keyguardInteractor;
        mBiometricMessageInteractor = biometricMessageInteractor;


        mFaceAcquiredMessageDeferral = faceHelpMessageDeferral.create();
        mFaceAcquiredMessageDeferral = faceHelpMessageDeferral.create();
        mCoExFaceAcquisitionMsgIdsToShow = new HashSet<>();
        int[] msgIds = context.getResources().getIntArray(
                com.android.systemui.res.R.array.config_face_help_msgs_when_fingerprint_enrolled);
        for (int msgId : msgIds) {
            mCoExFaceAcquisitionMsgIdsToShow.add(msgId);
        }


        mHandler = new Handler(mainLooper) {
        mHandler = new Handler(mainLooper) {
            @Override
            @Override
@@ -404,6 +405,10 @@ public class KeyguardIndicationController {
            collectFlow(mIndicationArea, mKeyguardInteractor.isActiveDreamLockscreenHosted(),
            collectFlow(mIndicationArea, mKeyguardInteractor.isActiveDreamLockscreenHosted(),
                    mIsActiveDreamLockscreenHostedCallback);
                    mIsActiveDreamLockscreenHostedCallback);
        }
        }

        collectFlow(mIndicationArea,
                mBiometricMessageInteractor.getCoExFaceAcquisitionMsgIdsToShow(),
                mCoExAcquisitionMsgIdsToShowCallback);
    }
    }


    /**
    /**
+2 −0
Original line number Original line Diff line number Diff line
@@ -28,6 +28,8 @@ class Utils {
        fun <A, B, C, D> toQuad(a: A, b: B, c: C, d: D) = Quad(a, b, c, d)
        fun <A, B, C, D> toQuad(a: A, b: B, c: C, d: D) = Quad(a, b, c, d)
        fun <A, B, C, D> toQuad(a: A, bcd: Triple<B, C, D>) =
        fun <A, B, C, D> toQuad(a: A, bcd: Triple<B, C, D>) =
            Quad(a, bcd.first, bcd.second, bcd.third)
            Quad(a, bcd.first, bcd.second, bcd.third)
        fun <A, B, C, D> toQuad(abc: Triple<A, B, C>, d: D) =
            Quad(abc.first, abc.second, abc.third, d)


        fun <A, B, C, D, E> toQuint(a: A, b: B, c: C, d: D, e: E) = Quint(a, b, c, d, e)
        fun <A, B, C, D, E> toQuint(a: A, b: B, c: C, d: D, e: E) = Quint(a, b, c, d, e)
        fun <A, B, C, D, E> toQuint(a: A, bcde: Quad<B, C, D, E>) =
        fun <A, B, C, D, E> toQuint(a: A, bcde: Quad<B, C, D, E>) =
Loading