diff options
| author | 2022-09-08 15:34:15 +0000 | |
|---|---|---|
| committer | 2022-09-08 15:34:15 +0000 | |
| commit | a12fe48a22b73e7927e769a27543bc8de54e6979 (patch) | |
| tree | cf137256f24bade07c1dea7dd18163f52b1ca1db | |
| parent | e04e312e984a3678ee8369d99b7191f49f9b6bb6 (diff) | |
| parent | 59ce3eac823e3fdf7c7f44861dbf81554b5e56e6 (diff) | |
Merge changes from topic "too_dark_face_acquisition_message_enable" into tm-qpr-dev
* changes:
Show low light soft-error on FACE_TIMEOUT
[Co-ex] Show 'too_dark' face acquired messages
6 files changed, 378 insertions, 41 deletions
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index aad32b118b8f..f0ba5ff2ecf4 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -1804,7 +1804,7 @@ <!-- Message shown during face acquisition when the image is too bright [CHAR LIMIT=50] --> <string name="face_acquired_too_bright">Too bright. Try gentler lighting.</string> <!-- Message shown during face acquisition when the image is too dark [CHAR LIMIT=50] --> - <string name="face_acquired_too_dark">Try brighter lighting</string> + <string name="face_acquired_too_dark">Not enough light</string> <!-- Message shown during face acquisition when the user is too close to sensor [CHAR LIMIT=50] --> <string name="face_acquired_too_close">Move phone farther away</string> <!-- Message shown during face acquisition when the user is too far from sensor [CHAR LIMIT=50] --> diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index ec22c609b0b9..ae9ebbacea05 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -617,8 +617,9 @@ <!-- 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"> - <item>25</item> - <item>26</item> + <item>3</item> <!-- TOO_DARK --> + <item>25</item> <!-- DARK_GLASSES --> + <item>26</item> <!-- MOUTH_COVERING_DETECTED --> </integer-array> <!-- Whether the communal service should be enabled --> diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricMessageDeferral.kt b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricMessageDeferral.kt new file mode 100644 index 000000000000..f2d8aaa30f21 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricMessageDeferral.kt @@ -0,0 +1,89 @@ +/* + * 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 + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java index 47dc5c2a5513..2251ab5460d5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java @@ -19,6 +19,10 @@ package com.android.systemui.statusbar; import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_FINANCED; import static android.app.admin.DevicePolicyResources.Strings.SystemUi.KEYGUARD_MANAGEMENT_DISCLOSURE; import static android.app.admin.DevicePolicyResources.Strings.SystemUi.KEYGUARD_NAMED_MANAGEMENT_DISCLOSURE; +import static android.hardware.biometrics.BiometricFaceConstants.FACE_ACQUIRED_FIRST_FRAME_RECEIVED; +import static android.hardware.biometrics.BiometricFaceConstants.FACE_ACQUIRED_GOOD; +import static android.hardware.biometrics.BiometricFaceConstants.FACE_ACQUIRED_START; +import static android.hardware.biometrics.BiometricFaceConstants.FACE_ACQUIRED_TOO_DARK; import static android.hardware.biometrics.BiometricSourceType.FACE; import static android.view.View.GONE; import static android.view.View.VISIBLE; @@ -78,6 +82,7 @@ import com.android.keyguard.KeyguardUpdateMonitorCallback; import com.android.settingslib.Utils; import com.android.settingslib.fuelgauge.BatteryStatus; import com.android.systemui.R; +import com.android.systemui.biometrics.BiometricMessageDeferral; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Background; @@ -178,7 +183,7 @@ public class KeyguardIndicationController { private boolean mBatteryPresent = true; private long mChargingTimeRemaining; private String mBiometricErrorMessageToShowOnScreenOn; - private final Set<Integer> mCoExFaceHelpMsgIdsToShow; + private final Set<Integer> mCoExFaceAcquisitionMsgIdsToShow; private boolean mInited; private KeyguardUpdateMonitorCallback mUpdateMonitorCallback; @@ -249,11 +254,11 @@ public class KeyguardIndicationController { mScreenLifecycle = screenLifecycle; mScreenLifecycle.addObserver(mScreenObserver); - mCoExFaceHelpMsgIdsToShow = new HashSet<>(); + mCoExFaceAcquisitionMsgIdsToShow = new HashSet<>(); int[] msgIds = context.getResources().getIntArray( com.android.systemui.R.array.config_face_help_msgs_when_fingerprint_enrolled); for (int msgId : msgIds) { - mCoExFaceHelpMsgIdsToShow.add(msgId); + mCoExFaceAcquisitionMsgIdsToShow.add(msgId); } mHandler = new Handler(mainLooper) { @@ -990,7 +995,7 @@ public class KeyguardIndicationController { mTopIndicationView == null ? null : mTopIndicationView.getText())); pw.println(" computePowerIndication(): " + computePowerIndication()); pw.println(" trustGrantedIndication: " + getTrustGrantedIndication()); - pw.println(" mCoExFaceHelpMsgIdsToShow=" + mCoExFaceHelpMsgIdsToShow); + pw.println(" mCoExFaceHelpMsgIdsToShow=" + mCoExFaceAcquisitionMsgIdsToShow); mRotateTextViewController.dump(pw, args); } @@ -1048,6 +1053,17 @@ public class KeyguardIndicationController { return; } + if (biometricSourceType == FACE) { + if (msgId == KeyguardUpdateMonitor.BIOMETRIC_HELP_FACE_NOT_RECOGNIZED) { + mFaceAcquiredMessageDeferral.reset(); + } else { + mFaceAcquiredMessageDeferral.processMessage(msgId, helpString); + if (mFaceAcquiredMessageDeferral.shouldDefer(msgId)) { + return; + } + } + } + final boolean faceAuthSoftError = biometricSourceType == FACE && msgId != BIOMETRIC_HELP_FACE_NOT_RECOGNIZED; final boolean faceAuthFailed = biometricSourceType == FACE @@ -1055,9 +1071,9 @@ public class KeyguardIndicationController { final boolean isUnlockWithFingerprintPossible = mKeyguardUpdateMonitor.getCachedIsUnlockWithFingerprintPossible( getCurrentUser()); - if (faceAuthSoftError - && isUnlockWithFingerprintPossible - && !mCoExFaceHelpMsgIdsToShow.contains(msgId)) { + final boolean isCoExFaceAcquisitionMessage = + faceAuthSoftError && isUnlockWithFingerprintPossible; + if (isCoExFaceAcquisitionMessage && !mCoExFaceAcquisitionMsgIdsToShow.contains(msgId)) { if (DEBUG) { Log.d(TAG, "skip showing msgId=" + msgId + " helpString=" + helpString + ", due to co-ex logic"); @@ -1067,7 +1083,12 @@ public class KeyguardIndicationController { mStatusBarKeyguardViewManager.showBouncerMessage(helpString, mInitialTextColorState); } else if (mScreenLifecycle.getScreenState() == SCREEN_ON) { - if (faceAuthFailed && isUnlockWithFingerprintPossible) { + if (isCoExFaceAcquisitionMessage && msgId == FACE_ACQUIRED_TOO_DARK) { + showBiometricMessage( + helpString, + mContext.getString(R.string.keyguard_suggest_fingerprint) + ); + } else if (faceAuthFailed && isUnlockWithFingerprintPossible) { showBiometricMessage( mContext.getString(R.string.keyguard_face_failed), mContext.getString(R.string.keyguard_suggest_fingerprint) @@ -1090,34 +1111,44 @@ public class KeyguardIndicationController { @Override public void onBiometricError(int msgId, String errString, BiometricSourceType biometricSourceType) { - if (shouldSuppressBiometricError(msgId, biometricSourceType, mKeyguardUpdateMonitor)) { - return; + CharSequence deferredFaceMessage = null; + if (biometricSourceType == FACE) { + deferredFaceMessage = mFaceAcquiredMessageDeferral.getDeferredMessage(); + mFaceAcquiredMessageDeferral.reset(); } - if (biometricSourceType == FACE - && msgId == FaceManager.FACE_ERROR_UNABLE_TO_PROCESS) { - // suppress all face UNABLE_TO_PROCESS errors + if (shouldSuppressBiometricError(msgId, biometricSourceType, mKeyguardUpdateMonitor)) { if (DEBUG) { - Log.d(TAG, "skip showing FACE_ERROR_UNABLE_TO_PROCESS errString=" - + errString); + Log.d(TAG, "suppressingBiometricError msgId=" + msgId + + " source=" + biometricSourceType); } - } else if (biometricSourceType == FACE - && msgId == FaceManager.FACE_ERROR_TIMEOUT) { + } else if (biometricSourceType == FACE && msgId == FaceManager.FACE_ERROR_TIMEOUT) { + // Co-ex: show deferred message OR nothing if (mKeyguardUpdateMonitor.getCachedIsUnlockWithFingerprintPossible( - getCurrentUser())) { - // no message if fingerprint is also enrolled + KeyguardUpdateMonitor.getCurrentUser())) { + // if we're on the lock screen (bouncer isn't showing), show the deferred msg + if (deferredFaceMessage != null + && !mStatusBarKeyguardViewManager.isBouncerShowing()) { + showBiometricMessage( + deferredFaceMessage, + mContext.getString(R.string.keyguard_suggest_fingerprint) + ); + return; + } + + // otherwise, don't show any message if (DEBUG) { Log.d(TAG, "skip showing FACE_ERROR_TIMEOUT due to co-ex logic"); } return; } - // The face timeout message is not very actionable, let's ask the user to + // Face-only: The face timeout message is not very actionable, let's ask the user to // manually retry. - if (mStatusBarKeyguardViewManager.isShowingAlternateAuth()) { - mStatusBarKeyguardViewManager.showBouncerMessage( - mContext.getResources().getString(R.string.keyguard_try_fingerprint), - mInitialTextColorState + if (deferredFaceMessage != null) { + showBiometricMessage( + deferredFaceMessage, + mContext.getString(R.string.keyguard_unlock) ); } else { // suggest swiping up to unlock (try face auth again or swipe up to bouncer) @@ -1134,8 +1165,9 @@ public class KeyguardIndicationController { private boolean shouldSuppressBiometricError(int msgId, BiometricSourceType biometricSourceType, KeyguardUpdateMonitor updateMonitor) { - if (biometricSourceType == BiometricSourceType.FINGERPRINT) + if (biometricSourceType == BiometricSourceType.FINGERPRINT) { return shouldSuppressFingerprintError(msgId, updateMonitor); + } if (biometricSourceType == FACE) { return shouldSuppressFaceError(msgId, updateMonitor); } @@ -1161,7 +1193,8 @@ public class KeyguardIndicationController { // check of whether non-strong biometric is allowed return ((!updateMonitor.isUnlockingWithBiometricAllowed(true /* isStrongBiometric */) && msgId != FaceManager.FACE_ERROR_LOCKOUT_PERMANENT) - || msgId == FaceManager.FACE_ERROR_CANCELED); + || msgId == FaceManager.FACE_ERROR_CANCELED + || msgId == FaceManager.FACE_ERROR_UNABLE_TO_PROCESS); } @@ -1200,10 +1233,11 @@ public class KeyguardIndicationController { boolean isStrongBiometric) { super.onBiometricAuthenticated(userId, biometricSourceType, isStrongBiometric); hideBiometricMessage(); - - if (biometricSourceType == FACE - && !mKeyguardBypassController.canBypass()) { - showActionToUnlock(); + if (biometricSourceType == FACE) { + mFaceAcquiredMessageDeferral.reset(); + if (!mKeyguardBypassController.canBypass()) { + showActionToUnlock(); + } } } @@ -1274,4 +1308,14 @@ public class KeyguardIndicationController { } } }; + + private final BiometricMessageDeferral mFaceAcquiredMessageDeferral = + new BiometricMessageDeferral( + Set.of( + FACE_ACQUIRED_GOOD, + FACE_ACQUIRED_START, + FACE_ACQUIRED_FIRST_FRAME_RECEIVED + ), + Set.of(FACE_ACQUIRED_TOO_DARK) + ); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricMessageDeferralTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricMessageDeferralTest.kt new file mode 100644 index 000000000000..419fedf99c15 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricMessageDeferralTest.kt @@ -0,0 +1,147 @@ +/* + * 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.testing.AndroidTestingRunner +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Assert.assertNull +import org.junit.Assert.assertTrue +import org.junit.Test +import org.junit.runner.RunWith + +@SmallTest +@RunWith(AndroidTestingRunner::class) +class BiometricMessageDeferralTest : SysuiTestCase() { + + @Test + fun testProcessNoMessages_noDeferredMessage() { + val biometricMessageDeferral = BiometricMessageDeferral(setOf(), setOf()) + + assertNull(biometricMessageDeferral.getDeferredMessage()) + } + + @Test + fun testProcessNonDeferredMessages_noDeferredMessage() { + val biometricMessageDeferral = BiometricMessageDeferral(setOf(), setOf(1, 2)) + + // WHEN there are no deferred messages processed + for (i in 0..3) { + biometricMessageDeferral.processMessage(4, "test") + } + + // THEN getDeferredMessage is null + assertNull(biometricMessageDeferral.getDeferredMessage()) + } + + @Test + fun testAllProcessedMessagesWereDeferred() { + val biometricMessageDeferral = BiometricMessageDeferral(setOf(), setOf(1)) + + // WHEN all the processed messages are a deferred message + for (i in 0..3) { + biometricMessageDeferral.processMessage(1, "test") + } + + // THEN deferredMessage will return the string associated with the deferred msgId + assertEquals("test", biometricMessageDeferral.getDeferredMessage()) + } + + @Test + fun testReturnsMostFrequentDeferredMessage() { + val biometricMessageDeferral = BiometricMessageDeferral(setOf(), setOf(1, 2)) + + // WHEN there's two msgId=1 processed and one msgId=2 processed + biometricMessageDeferral.processMessage(1, "msgId-1") + biometricMessageDeferral.processMessage(1, "msgId-1") + biometricMessageDeferral.processMessage(1, "msgId-1") + biometricMessageDeferral.processMessage(2, "msgId-2") + + // THEN the most frequent deferred message is that meets the threshold is returned + assertEquals("msgId-1", biometricMessageDeferral.getDeferredMessage()) + } + + @Test + fun testDeferredMessage_mustMeetThreshold() { + val biometricMessageDeferral = BiometricMessageDeferral(setOf(), setOf(1)) + + // WHEN more nonDeferredMessages are shown than the deferred message + val totalMessages = 10 + val nonDeferredMessagesCount = + (totalMessages * BiometricMessageDeferral.THRESHOLD).toInt() + 1 + for (i in 0 until nonDeferredMessagesCount) { + biometricMessageDeferral.processMessage(4, "non-deferred-msg") + } + for (i in nonDeferredMessagesCount until totalMessages) { + biometricMessageDeferral.processMessage(1, "msgId-1") + } + + // THEN there's no deferred message because it didn't meet the threshold + assertNull(biometricMessageDeferral.getDeferredMessage()) + } + + @Test + fun testDeferredMessage_manyExcludedMessages_getDeferredMessage() { + val biometricMessageDeferral = BiometricMessageDeferral(setOf(3), setOf(1)) + + // WHEN more excludedMessages are shown than the deferred message + val totalMessages = 10 + val excludedMessagesCount = (totalMessages * BiometricMessageDeferral.THRESHOLD).toInt() + 1 + for (i in 0 until excludedMessagesCount) { + biometricMessageDeferral.processMessage(3, "excluded-msg") + } + for (i in excludedMessagesCount until totalMessages) { + biometricMessageDeferral.processMessage(1, "msgId-1") + } + + // THEN there IS a deferred message because the deferred msg meets the threshold amongst the + // non-excluded messages + assertEquals("msgId-1", biometricMessageDeferral.getDeferredMessage()) + } + + @Test + fun testResetClearsOutCounts() { + val biometricMessageDeferral = BiometricMessageDeferral(setOf(), setOf(1, 2)) + + // GIVEN two msgId=1 events processed + biometricMessageDeferral.processMessage(1, "msgId-1") + biometricMessageDeferral.processMessage(1, "msgId-1") + + // WHEN counts are reset and then a single deferred message is processed (msgId=2) + biometricMessageDeferral.reset() + biometricMessageDeferral.processMessage(2, "msgId-2") + + // THEN msgId-2 is the deferred message since the two msgId=1 events were reset + assertEquals("msgId-2", biometricMessageDeferral.getDeferredMessage()) + } + + @Test + fun testShouldDefer() { + // GIVEN should defer msgIds 1 and 2 + val biometricMessageDeferral = BiometricMessageDeferral(setOf(3), setOf(1, 2)) + + // THEN shouldDefer returns true for ids 1 & 2 + assertTrue(biometricMessageDeferral.shouldDefer(1)) + assertTrue(biometricMessageDeferral.shouldDefer(2)) + + // THEN should defer returns false for ids 3 & 4 + assertFalse(biometricMessageDeferral.shouldDefer(3)) + assertFalse(biometricMessageDeferral.shouldDefer(4)) + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java index 798f47d2d6cb..d73d07ca1423 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java @@ -19,6 +19,7 @@ package com.android.systemui.statusbar; import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_DEFAULT; import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_FINANCED; import static android.content.pm.UserInfo.FLAG_MANAGED_PROFILE; +import static android.hardware.biometrics.BiometricFaceConstants.FACE_ERROR_TIMEOUT; import static com.android.keyguard.KeyguardUpdateMonitor.BIOMETRIC_HELP_FINGERPRINT_NOT_RECOGNIZED; import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_ALIGNMENT; @@ -65,7 +66,6 @@ import android.content.pm.UserInfo; import android.graphics.Color; import android.hardware.biometrics.BiometricFaceConstants; import android.hardware.biometrics.BiometricSourceType; -import android.hardware.face.FaceManager; import android.hardware.fingerprint.FingerprintManager; import android.os.BatteryManager; import android.os.Looper; @@ -602,7 +602,7 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase { String message = mContext.getString(R.string.keyguard_unlock); mController.setVisible(true); - mController.getKeyguardCallback().onBiometricError(FaceManager.FACE_ERROR_TIMEOUT, + mController.getKeyguardCallback().onBiometricError(FACE_ERROR_TIMEOUT, "A message", BiometricSourceType.FACE); verifyIndicationMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE, message); @@ -636,7 +636,7 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase { when(mKeyguardUpdateMonitor.isFaceEnrolled()).thenReturn(true); mController.setVisible(true); - mController.getKeyguardCallback().onBiometricError(FaceManager.FACE_ERROR_TIMEOUT, + mController.getKeyguardCallback().onBiometricError(FACE_ERROR_TIMEOUT, "A message", BiometricSourceType.FACE); verify(mStatusBarKeyguardViewManager).showBouncerMessage(eq(message), any()); @@ -651,7 +651,7 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase { mController.setVisible(true); mController.getKeyguardCallback().onBiometricError( - FaceManager.FACE_ERROR_TIMEOUT, message, BiometricSourceType.FACE); + FACE_ERROR_TIMEOUT, message, BiometricSourceType.FACE); verifyNoMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE); } @@ -698,8 +698,7 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase { BiometricFaceConstants.FACE_ACQUIRED_TOO_LEFT, BiometricFaceConstants.FACE_ACQUIRED_TOO_HIGH, BiometricFaceConstants.FACE_ACQUIRED_TOO_LOW, - BiometricFaceConstants.FACE_ACQUIRED_TOO_BRIGHT, - BiometricFaceConstants.FACE_ACQUIRED_TOO_DARK + BiometricFaceConstants.FACE_ACQUIRED_TOO_BRIGHT }; for (int msgId : msgIds) { mKeyguardUpdateMonitorCallback.onBiometricHelp( @@ -728,8 +727,7 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase { BiometricFaceConstants.FACE_ACQUIRED_TOO_LEFT, BiometricFaceConstants.FACE_ACQUIRED_TOO_HIGH, BiometricFaceConstants.FACE_ACQUIRED_TOO_LOW, - BiometricFaceConstants.FACE_ACQUIRED_TOO_BRIGHT, - BiometricFaceConstants.FACE_ACQUIRED_TOO_DARK + BiometricFaceConstants.FACE_ACQUIRED_TOO_BRIGHT }; for (int msgId : msgIds) { final String numberedHelpString = helpString + msgId; @@ -743,6 +741,64 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase { } @Test + public void sendTooDarkFaceHelpMessages_onTimeout_noFpEnrolled() { + createController(); + + // GIVEN fingerprint NOT enrolled + when(mKeyguardUpdateMonitor.getCachedIsUnlockWithFingerprintPossible( + 0)).thenReturn(false); + + // WHEN help message received + final String helpString = "helpMsg"; + mKeyguardUpdateMonitorCallback.onBiometricHelp( + BiometricFaceConstants.FACE_ACQUIRED_TOO_DARK, + helpString, + BiometricSourceType.FACE + ); + + // THEN help message not shown yet + verifyNoMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE); + + // WHEN face timeout error received + mKeyguardUpdateMonitorCallback.onBiometricError(FACE_ERROR_TIMEOUT, "face timeout", + BiometricSourceType.FACE); + + // THEN the low light message shows with suggestion to swipe up to unlock + verifyIndicationMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE, helpString); + verifyIndicationMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE_FOLLOW_UP, + mContext.getString(R.string.keyguard_unlock)); + } + + @Test + public void sendTooDarkFaceHelpMessages_onTimeout_fingerprintEnrolled() { + createController(); + + // GIVEN fingerprint enrolled + when(mKeyguardUpdateMonitor.getCachedIsUnlockWithFingerprintPossible( + 0)).thenReturn(true); + + // WHEN help message received + final String helpString = "helpMsg"; + mKeyguardUpdateMonitorCallback.onBiometricHelp( + BiometricFaceConstants.FACE_ACQUIRED_TOO_DARK, + helpString, + BiometricSourceType.FACE + ); + + // THEN help message not shown yet + verifyNoMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE); + + // WHEN face timeout error received + mKeyguardUpdateMonitorCallback.onBiometricError(FACE_ERROR_TIMEOUT, "face timeout", + BiometricSourceType.FACE); + + // THEN the low light message shows and suggests trying fingerprint + verifyIndicationMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE, helpString); + verifyIndicationMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE_FOLLOW_UP, + mContext.getString(R.string.keyguard_suggest_fingerprint)); + } + + @Test public void updateMonitor_listenerUpdatesIndication() { createController(); String restingIndication = "Resting indication"; |