Loading packages/SystemUI/src/com/android/systemui/keyguard/util/IndicationHelper.kt 0 → 100644 +65 −0 Original line number Diff line number Diff line /* * Copyright (C) 2023 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.util import android.hardware.biometrics.BiometricFaceConstants import android.hardware.biometrics.BiometricFingerprintConstants import android.hardware.biometrics.BiometricSourceType import com.android.keyguard.KeyguardUpdateMonitor import com.android.systemui.dagger.SysUISingleton import javax.inject.Inject @SysUISingleton class IndicationHelper @Inject constructor( val keyguardUpdateMonitor: KeyguardUpdateMonitor, ) { fun shouldSuppressErrorMsg(biometricSource: BiometricSourceType, msgId: Int): Boolean { return when (biometricSource) { BiometricSourceType.FINGERPRINT -> (isPrimaryAuthRequired() && !isFingerprintLockoutErrorMsg(msgId)) || msgId == BiometricFingerprintConstants.FINGERPRINT_ERROR_CANCELED || msgId == BiometricFingerprintConstants.FINGERPRINT_ERROR_USER_CANCELED || msgId == BiometricFingerprintConstants.BIOMETRIC_ERROR_POWER_PRESSED BiometricSourceType.FACE -> (isPrimaryAuthRequired() && !isFaceLockoutErrorMsg(msgId)) || msgId == BiometricFaceConstants.FACE_ERROR_CANCELED || msgId == BiometricFaceConstants.FACE_ERROR_UNABLE_TO_PROCESS else -> false } } private fun isFingerprintLockoutErrorMsg(msgId: Int): Boolean { return msgId == BiometricFingerprintConstants.FINGERPRINT_ERROR_LOCKOUT || msgId == BiometricFingerprintConstants.FINGERPRINT_ERROR_LOCKOUT_PERMANENT } fun isFaceLockoutErrorMsg(msgId: Int): Boolean { return msgId == BiometricFaceConstants.FACE_ERROR_LOCKOUT || msgId == BiometricFaceConstants.FACE_ERROR_LOCKOUT_PERMANENT } private fun isPrimaryAuthRequired(): Boolean { // Only checking if unlocking with Biometric is allowed (no matter strong or non-strong // as long as primary auth, i.e. PIN/pattern/password, is required), so it's ok to // pass true for isStrongBiometric to isUnlockingWithBiometricAllowed() to bypass the // check of whether non-strong biometric is allowed since strong biometrics can still be // used. return !keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(true /* isStrongBiometric */) } } packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java +13 −28 Original line number Diff line number Diff line Loading @@ -56,7 +56,6 @@ import android.content.res.Resources; import android.graphics.Color; import android.hardware.biometrics.BiometricSourceType; import android.hardware.face.FaceManager; import android.hardware.fingerprint.FingerprintManager; import android.os.BatteryManager; import android.os.Handler; import android.os.Looper; Loading Loading @@ -86,6 +85,8 @@ import com.android.settingslib.fuelgauge.BatteryStatus; import com.android.systemui.R; import com.android.systemui.biometrics.AuthController; import com.android.systemui.biometrics.FaceHelpMessageDeferral; import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor; import com.android.systemui.bouncer.domain.interactor.BouncerMessageInteractor; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Background; Loading @@ -95,8 +96,7 @@ import com.android.systemui.flags.FeatureFlags; import com.android.systemui.keyguard.KeyguardIndication; import com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController; import com.android.systemui.keyguard.ScreenLifecycle; import com.android.systemui.bouncer.domain.interactor.BouncerMessageInteractor; import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor; import com.android.systemui.keyguard.util.IndicationHelper; import com.android.systemui.log.LogLevel; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; Loading Loading @@ -227,7 +227,8 @@ public class KeyguardIndicationController { // triggered while the device is asleep private final AlarmTimeout mHideTransientMessageHandler; private final AlarmTimeout mHideBiometricMessageHandler; private FeatureFlags mFeatureFlags; private final FeatureFlags mFeatureFlags; private final IndicationHelper mIndicationHelper; /** * Creates a new KeyguardIndicationController and registers callbacks. Loading Loading @@ -259,7 +260,8 @@ public class KeyguardIndicationController { AlarmManager alarmManager, UserTracker userTracker, BouncerMessageInteractor bouncerMessageInteractor, FeatureFlags flags FeatureFlags flags, IndicationHelper indicationHelper ) { mContext = context; mBroadcastDispatcher = broadcastDispatcher; Loading @@ -286,6 +288,7 @@ public class KeyguardIndicationController { mUserTracker = userTracker; mBouncerMessageInteractor = bouncerMessageInteractor; mFeatureFlags = flags; mIndicationHelper = indicationHelper; mFaceAcquiredMessageDeferral = faceHelpMessageDeferral; mCoExFaceAcquisitionMsgIdsToShow = new HashSet<>(); Loading Loading @@ -1249,13 +1252,13 @@ public class KeyguardIndicationController { private void onFaceAuthError(int msgId, String errString) { CharSequence deferredFaceMessage = mFaceAcquiredMessageDeferral.getDeferredMessage(); mFaceAcquiredMessageDeferral.reset(); if (shouldSuppressFaceError(msgId)) { mKeyguardLogger.logBiometricMessage("suppressingFaceError", msgId, errString); if (mIndicationHelper.shouldSuppressErrorMsg(FACE, msgId)) { mKeyguardLogger.logBiometricMessage("KIC suppressingFaceError", msgId, errString); return; } if (msgId == FaceManager.FACE_ERROR_TIMEOUT) { handleFaceAuthTimeoutError(deferredFaceMessage); } else if (isLockoutError(msgId)) { } else if (mIndicationHelper.isFaceLockoutErrorMsg(msgId)) { handleFaceLockoutError(errString); } else { showErrorMessageNowOrLater(errString, null); Loading @@ -1263,8 +1266,8 @@ public class KeyguardIndicationController { } private void onFingerprintAuthError(int msgId, String errString) { if (shouldSuppressFingerprintError(msgId)) { mKeyguardLogger.logBiometricMessage("suppressingFingerprintError", if (mIndicationHelper.shouldSuppressErrorMsg(FINGERPRINT, msgId)) { mKeyguardLogger.logBiometricMessage("KIC suppressingFingerprintError", msgId, errString); } else { Loading @@ -1272,19 +1275,6 @@ public class KeyguardIndicationController { } } private boolean shouldSuppressFingerprintError(int msgId) { return ((isPrimaryAuthRequired() && !isLockoutError(msgId)) || msgId == FingerprintManager.FINGERPRINT_ERROR_CANCELED || msgId == FingerprintManager.FINGERPRINT_ERROR_USER_CANCELED || msgId == FingerprintManager.BIOMETRIC_ERROR_POWER_PRESSED); } private boolean shouldSuppressFaceError(int msgId) { return ((isPrimaryAuthRequired() && msgId != FaceManager.FACE_ERROR_LOCKOUT_PERMANENT) || msgId == FaceManager.FACE_ERROR_CANCELED || msgId == FaceManager.FACE_ERROR_UNABLE_TO_PROCESS); } @Override public void onTrustChanged(int userId) { if (!isCurrentUser(userId)) return; Loading Loading @@ -1408,11 +1398,6 @@ public class KeyguardIndicationController { return mContext.getString(followupMsgId); } private static boolean isLockoutError(int msgId) { return msgId == FaceManager.FACE_ERROR_LOCKOUT_PERMANENT || msgId == FaceManager.FACE_ERROR_LOCKOUT; } private void handleFaceAuthTimeoutError(@Nullable CharSequence deferredFaceMessage) { mKeyguardLogger.logBiometricMessage("deferred message after face auth timeout", null, String.valueOf(deferredFaceMessage)); Loading packages/SystemUI/tests/src/com/android/systemui/keyguard/util/IndicationHelperTest.kt 0 → 100644 +173 −0 Original line number Diff line number Diff line /* * Copyright (C) 2023 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.util import android.hardware.biometrics.BiometricFaceConstants.BIOMETRIC_ERROR_POWER_PRESSED import android.hardware.biometrics.BiometricFaceConstants.FACE_ERROR_CANCELED import android.hardware.biometrics.BiometricFaceConstants.FACE_ERROR_LOCKOUT import android.hardware.biometrics.BiometricFaceConstants.FACE_ERROR_LOCKOUT_PERMANENT import android.hardware.biometrics.BiometricFaceConstants.FACE_ERROR_TIMEOUT import android.hardware.biometrics.BiometricFaceConstants.FACE_ERROR_UNABLE_TO_PROCESS import android.hardware.biometrics.BiometricFaceConstants.FACE_ERROR_VENDOR import android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ERROR_CANCELED import android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ERROR_LOCKOUT import android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ERROR_LOCKOUT_PERMANENT import android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ERROR_TIMEOUT import android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ERROR_USER_CANCELED import android.hardware.biometrics.BiometricSourceType import android.testing.TestableLooper import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.keyguard.KeyguardUpdateMonitor import com.android.systemui.SysuiTestCase import com.android.systemui.util.mockito.whenever import junit.framework.Assert.assertFalse import junit.framework.Assert.assertTrue import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.mockito.ArgumentMatchers.anyBoolean import org.mockito.Mock import org.mockito.junit.MockitoJUnit @SmallTest @RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper class IndicationHelperTest : SysuiTestCase() { @JvmField @Rule var mockitoRule = MockitoJUnit.rule() @Mock lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor private lateinit var underTest: IndicationHelper @Before fun setup() { underTest = IndicationHelper( keyguardUpdateMonitor, ) } @Test fun suppressErrorMsg_faceErrorCancelled() { givenPrimaryAuthNotRequired() assertTrue(underTest.shouldSuppressErrorMsg(BiometricSourceType.FACE, FACE_ERROR_CANCELED)) } @Test fun suppressErrorMsg_faceErrorUnableToProcess() { givenPrimaryAuthNotRequired() assertTrue( underTest.shouldSuppressErrorMsg(BiometricSourceType.FACE, FACE_ERROR_UNABLE_TO_PROCESS) ) } @Test fun suppressErrorMsg_facePrimaryAuthRequired() { givenPrimaryAuthRequired() assertTrue(underTest.shouldSuppressErrorMsg(BiometricSourceType.FACE, FACE_ERROR_TIMEOUT)) } @Test fun doNotSuppressErrorMsg_facePrimaryAuthRequired_faceLockout() { givenPrimaryAuthRequired() assertFalse(underTest.shouldSuppressErrorMsg(BiometricSourceType.FACE, FACE_ERROR_LOCKOUT)) assertFalse( underTest.shouldSuppressErrorMsg(BiometricSourceType.FACE, FACE_ERROR_LOCKOUT_PERMANENT) ) } @Test fun suppressErrorMsg_fingerprintErrorCancelled() { givenPrimaryAuthNotRequired() assertTrue( underTest.shouldSuppressErrorMsg( BiometricSourceType.FINGERPRINT, FINGERPRINT_ERROR_CANCELED ) ) } @Test fun suppressErrorMsg_fingerprintErrorUserCancelled() { givenPrimaryAuthNotRequired() assertTrue( underTest.shouldSuppressErrorMsg( BiometricSourceType.FINGERPRINT, FINGERPRINT_ERROR_USER_CANCELED ) ) } @Test fun suppressErrorMsg_fingerprintErrorPowerPressed() { givenPrimaryAuthNotRequired() assertTrue( underTest.shouldSuppressErrorMsg( BiometricSourceType.FINGERPRINT, BIOMETRIC_ERROR_POWER_PRESSED ) ) } @Test fun suppressErrorMsg_fingerprintPrimaryAuthRequired() { givenPrimaryAuthRequired() assertTrue( underTest.shouldSuppressErrorMsg(BiometricSourceType.FACE, FINGERPRINT_ERROR_TIMEOUT) ) } @Test fun doNotSuppressErrorMsg_fingerprintPrimaryAuthRequired_fingerprintLockout() { givenPrimaryAuthRequired() assertFalse( underTest.shouldSuppressErrorMsg( BiometricSourceType.FINGERPRINT, FINGERPRINT_ERROR_LOCKOUT ) ) assertFalse( underTest.shouldSuppressErrorMsg( BiometricSourceType.FACE, FINGERPRINT_ERROR_LOCKOUT_PERMANENT ) ) } @Test fun isFaceLockoutErrorMsgId() { givenPrimaryAuthRequired() assertTrue(underTest.isFaceLockoutErrorMsg(FACE_ERROR_LOCKOUT)) assertTrue(underTest.isFaceLockoutErrorMsg(FACE_ERROR_LOCKOUT_PERMANENT)) assertFalse(underTest.isFaceLockoutErrorMsg(FACE_ERROR_TIMEOUT)) assertFalse(underTest.isFaceLockoutErrorMsg(FACE_ERROR_CANCELED)) assertFalse(underTest.isFaceLockoutErrorMsg(FACE_ERROR_UNABLE_TO_PROCESS)) assertFalse(underTest.isFaceLockoutErrorMsg(FACE_ERROR_VENDOR)) } private fun givenPrimaryAuthNotRequired() { whenever(keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(anyBoolean())) .thenReturn(true) } private fun givenPrimaryAuthRequired() { whenever(keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(anyBoolean())) .thenReturn(false) } } packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java +14 −23 Original line number Diff line number Diff line Loading @@ -72,6 +72,7 @@ import android.content.Intent; import android.content.pm.UserInfo; import android.graphics.Color; import android.hardware.biometrics.BiometricFaceConstants; import android.hardware.biometrics.BiometricFingerprintConstants; import android.hardware.biometrics.BiometricSourceType; import android.hardware.fingerprint.FingerprintManager; import android.os.BatteryManager; Loading @@ -98,14 +99,15 @@ import com.android.systemui.R; import com.android.systemui.SysuiTestCase; import com.android.systemui.biometrics.AuthController; import com.android.systemui.biometrics.FaceHelpMessageDeferral; import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor; import com.android.systemui.bouncer.domain.interactor.BouncerMessageInteractor; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.dock.DockManager; import com.android.systemui.flags.FakeFeatureFlags; import com.android.systemui.keyguard.KeyguardIndication; import com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController; import com.android.systemui.keyguard.ScreenLifecycle; import com.android.systemui.bouncer.domain.interactor.BouncerMessageInteractor; import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor; import com.android.systemui.keyguard.util.IndicationHelper; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.settings.UserTracker; Loading Loading @@ -213,6 +215,7 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase { private StatusBarStateController.StateListener mStatusBarStateListener; private ScreenLifecycle.Observer mScreenObserver; private BroadcastReceiver mBroadcastReceiver; private IndicationHelper mIndicationHelper; private FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock()); private TestableLooper mTestableLooper; private final int mCurrentUserId = 1; Loading Loading @@ -262,13 +265,14 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase { when(mDevicePolicyManager.getDeviceOwnerType(DEVICE_OWNER_COMPONENT)) .thenReturn(DEVICE_OWNER_TYPE_DEFAULT); when(mDevicePolicyResourcesManager.getString(anyString(), any())) .thenReturn(mDisclosureGeneric); when(mDevicePolicyResourcesManager.getString(anyString(), any(), anyString())) .thenReturn(mDisclosureWithOrganization); when(mUserTracker.getUserId()).thenReturn(mCurrentUserId); mIndicationHelper = new IndicationHelper(mKeyguardUpdateMonitor); mWakeLock = new WakeLockFake(); mWakeLockBuilder = new WakeLockFake.Builder(mContext); mWakeLockBuilder.setWakeLock(mWakeLock); Loading Loading @@ -304,7 +308,8 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase { mAlarmManager, mUserTracker, mock(BouncerMessageInteractor.class), flags flags, mIndicationHelper ); mController.init(); mController.setIndicationArea(mIndicationArea); Loading Loading @@ -805,33 +810,19 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase { } @Test public void transientIndication_visibleWhenDozing_ignoresFingerprintCancellation() { public void transientIndication_visibleWhenDozing_ignoresFingerprintErrorMsg() { createController(); mController.setVisible(true); reset(mRotateTextViewController); mController.getKeyguardCallback().onBiometricError( FingerprintManager.FINGERPRINT_ERROR_USER_CANCELED, "foo", BiometricSourceType.FINGERPRINT); mController.getKeyguardCallback().onBiometricError( FingerprintManager.FINGERPRINT_ERROR_CANCELED, "bar", BiometricSourceType.FINGERPRINT); verifyNoMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE); verifyNoMessage(INDICATION_TYPE_TRANSIENT); } @Test public void transientIndication_visibleWhenDozing_ignoresPowerPressed() { createController(); mController.setVisible(true); reset(mRotateTextViewController); // WHEN a fingerprint error user cancelled message is received mController.getKeyguardCallback().onBiometricError( FingerprintManager.BIOMETRIC_ERROR_POWER_PRESSED, "foo", BiometricFingerprintConstants.FINGERPRINT_ERROR_USER_CANCELED, "foo", BiometricSourceType.FINGERPRINT); // THEN no message is shown verifyNoMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE); verifyNoMessage(INDICATION_TYPE_TRANSIENT); } @Test Loading Loading
packages/SystemUI/src/com/android/systemui/keyguard/util/IndicationHelper.kt 0 → 100644 +65 −0 Original line number Diff line number Diff line /* * Copyright (C) 2023 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.util import android.hardware.biometrics.BiometricFaceConstants import android.hardware.biometrics.BiometricFingerprintConstants import android.hardware.biometrics.BiometricSourceType import com.android.keyguard.KeyguardUpdateMonitor import com.android.systemui.dagger.SysUISingleton import javax.inject.Inject @SysUISingleton class IndicationHelper @Inject constructor( val keyguardUpdateMonitor: KeyguardUpdateMonitor, ) { fun shouldSuppressErrorMsg(biometricSource: BiometricSourceType, msgId: Int): Boolean { return when (biometricSource) { BiometricSourceType.FINGERPRINT -> (isPrimaryAuthRequired() && !isFingerprintLockoutErrorMsg(msgId)) || msgId == BiometricFingerprintConstants.FINGERPRINT_ERROR_CANCELED || msgId == BiometricFingerprintConstants.FINGERPRINT_ERROR_USER_CANCELED || msgId == BiometricFingerprintConstants.BIOMETRIC_ERROR_POWER_PRESSED BiometricSourceType.FACE -> (isPrimaryAuthRequired() && !isFaceLockoutErrorMsg(msgId)) || msgId == BiometricFaceConstants.FACE_ERROR_CANCELED || msgId == BiometricFaceConstants.FACE_ERROR_UNABLE_TO_PROCESS else -> false } } private fun isFingerprintLockoutErrorMsg(msgId: Int): Boolean { return msgId == BiometricFingerprintConstants.FINGERPRINT_ERROR_LOCKOUT || msgId == BiometricFingerprintConstants.FINGERPRINT_ERROR_LOCKOUT_PERMANENT } fun isFaceLockoutErrorMsg(msgId: Int): Boolean { return msgId == BiometricFaceConstants.FACE_ERROR_LOCKOUT || msgId == BiometricFaceConstants.FACE_ERROR_LOCKOUT_PERMANENT } private fun isPrimaryAuthRequired(): Boolean { // Only checking if unlocking with Biometric is allowed (no matter strong or non-strong // as long as primary auth, i.e. PIN/pattern/password, is required), so it's ok to // pass true for isStrongBiometric to isUnlockingWithBiometricAllowed() to bypass the // check of whether non-strong biometric is allowed since strong biometrics can still be // used. return !keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(true /* isStrongBiometric */) } }
packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java +13 −28 Original line number Diff line number Diff line Loading @@ -56,7 +56,6 @@ import android.content.res.Resources; import android.graphics.Color; import android.hardware.biometrics.BiometricSourceType; import android.hardware.face.FaceManager; import android.hardware.fingerprint.FingerprintManager; import android.os.BatteryManager; import android.os.Handler; import android.os.Looper; Loading Loading @@ -86,6 +85,8 @@ import com.android.settingslib.fuelgauge.BatteryStatus; import com.android.systemui.R; import com.android.systemui.biometrics.AuthController; import com.android.systemui.biometrics.FaceHelpMessageDeferral; import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor; import com.android.systemui.bouncer.domain.interactor.BouncerMessageInteractor; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Background; Loading @@ -95,8 +96,7 @@ import com.android.systemui.flags.FeatureFlags; import com.android.systemui.keyguard.KeyguardIndication; import com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController; import com.android.systemui.keyguard.ScreenLifecycle; import com.android.systemui.bouncer.domain.interactor.BouncerMessageInteractor; import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor; import com.android.systemui.keyguard.util.IndicationHelper; import com.android.systemui.log.LogLevel; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; Loading Loading @@ -227,7 +227,8 @@ public class KeyguardIndicationController { // triggered while the device is asleep private final AlarmTimeout mHideTransientMessageHandler; private final AlarmTimeout mHideBiometricMessageHandler; private FeatureFlags mFeatureFlags; private final FeatureFlags mFeatureFlags; private final IndicationHelper mIndicationHelper; /** * Creates a new KeyguardIndicationController and registers callbacks. Loading Loading @@ -259,7 +260,8 @@ public class KeyguardIndicationController { AlarmManager alarmManager, UserTracker userTracker, BouncerMessageInteractor bouncerMessageInteractor, FeatureFlags flags FeatureFlags flags, IndicationHelper indicationHelper ) { mContext = context; mBroadcastDispatcher = broadcastDispatcher; Loading @@ -286,6 +288,7 @@ public class KeyguardIndicationController { mUserTracker = userTracker; mBouncerMessageInteractor = bouncerMessageInteractor; mFeatureFlags = flags; mIndicationHelper = indicationHelper; mFaceAcquiredMessageDeferral = faceHelpMessageDeferral; mCoExFaceAcquisitionMsgIdsToShow = new HashSet<>(); Loading Loading @@ -1249,13 +1252,13 @@ public class KeyguardIndicationController { private void onFaceAuthError(int msgId, String errString) { CharSequence deferredFaceMessage = mFaceAcquiredMessageDeferral.getDeferredMessage(); mFaceAcquiredMessageDeferral.reset(); if (shouldSuppressFaceError(msgId)) { mKeyguardLogger.logBiometricMessage("suppressingFaceError", msgId, errString); if (mIndicationHelper.shouldSuppressErrorMsg(FACE, msgId)) { mKeyguardLogger.logBiometricMessage("KIC suppressingFaceError", msgId, errString); return; } if (msgId == FaceManager.FACE_ERROR_TIMEOUT) { handleFaceAuthTimeoutError(deferredFaceMessage); } else if (isLockoutError(msgId)) { } else if (mIndicationHelper.isFaceLockoutErrorMsg(msgId)) { handleFaceLockoutError(errString); } else { showErrorMessageNowOrLater(errString, null); Loading @@ -1263,8 +1266,8 @@ public class KeyguardIndicationController { } private void onFingerprintAuthError(int msgId, String errString) { if (shouldSuppressFingerprintError(msgId)) { mKeyguardLogger.logBiometricMessage("suppressingFingerprintError", if (mIndicationHelper.shouldSuppressErrorMsg(FINGERPRINT, msgId)) { mKeyguardLogger.logBiometricMessage("KIC suppressingFingerprintError", msgId, errString); } else { Loading @@ -1272,19 +1275,6 @@ public class KeyguardIndicationController { } } private boolean shouldSuppressFingerprintError(int msgId) { return ((isPrimaryAuthRequired() && !isLockoutError(msgId)) || msgId == FingerprintManager.FINGERPRINT_ERROR_CANCELED || msgId == FingerprintManager.FINGERPRINT_ERROR_USER_CANCELED || msgId == FingerprintManager.BIOMETRIC_ERROR_POWER_PRESSED); } private boolean shouldSuppressFaceError(int msgId) { return ((isPrimaryAuthRequired() && msgId != FaceManager.FACE_ERROR_LOCKOUT_PERMANENT) || msgId == FaceManager.FACE_ERROR_CANCELED || msgId == FaceManager.FACE_ERROR_UNABLE_TO_PROCESS); } @Override public void onTrustChanged(int userId) { if (!isCurrentUser(userId)) return; Loading Loading @@ -1408,11 +1398,6 @@ public class KeyguardIndicationController { return mContext.getString(followupMsgId); } private static boolean isLockoutError(int msgId) { return msgId == FaceManager.FACE_ERROR_LOCKOUT_PERMANENT || msgId == FaceManager.FACE_ERROR_LOCKOUT; } private void handleFaceAuthTimeoutError(@Nullable CharSequence deferredFaceMessage) { mKeyguardLogger.logBiometricMessage("deferred message after face auth timeout", null, String.valueOf(deferredFaceMessage)); Loading
packages/SystemUI/tests/src/com/android/systemui/keyguard/util/IndicationHelperTest.kt 0 → 100644 +173 −0 Original line number Diff line number Diff line /* * Copyright (C) 2023 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.util import android.hardware.biometrics.BiometricFaceConstants.BIOMETRIC_ERROR_POWER_PRESSED import android.hardware.biometrics.BiometricFaceConstants.FACE_ERROR_CANCELED import android.hardware.biometrics.BiometricFaceConstants.FACE_ERROR_LOCKOUT import android.hardware.biometrics.BiometricFaceConstants.FACE_ERROR_LOCKOUT_PERMANENT import android.hardware.biometrics.BiometricFaceConstants.FACE_ERROR_TIMEOUT import android.hardware.biometrics.BiometricFaceConstants.FACE_ERROR_UNABLE_TO_PROCESS import android.hardware.biometrics.BiometricFaceConstants.FACE_ERROR_VENDOR import android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ERROR_CANCELED import android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ERROR_LOCKOUT import android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ERROR_LOCKOUT_PERMANENT import android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ERROR_TIMEOUT import android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ERROR_USER_CANCELED import android.hardware.biometrics.BiometricSourceType import android.testing.TestableLooper import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.keyguard.KeyguardUpdateMonitor import com.android.systemui.SysuiTestCase import com.android.systemui.util.mockito.whenever import junit.framework.Assert.assertFalse import junit.framework.Assert.assertTrue import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.mockito.ArgumentMatchers.anyBoolean import org.mockito.Mock import org.mockito.junit.MockitoJUnit @SmallTest @RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper class IndicationHelperTest : SysuiTestCase() { @JvmField @Rule var mockitoRule = MockitoJUnit.rule() @Mock lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor private lateinit var underTest: IndicationHelper @Before fun setup() { underTest = IndicationHelper( keyguardUpdateMonitor, ) } @Test fun suppressErrorMsg_faceErrorCancelled() { givenPrimaryAuthNotRequired() assertTrue(underTest.shouldSuppressErrorMsg(BiometricSourceType.FACE, FACE_ERROR_CANCELED)) } @Test fun suppressErrorMsg_faceErrorUnableToProcess() { givenPrimaryAuthNotRequired() assertTrue( underTest.shouldSuppressErrorMsg(BiometricSourceType.FACE, FACE_ERROR_UNABLE_TO_PROCESS) ) } @Test fun suppressErrorMsg_facePrimaryAuthRequired() { givenPrimaryAuthRequired() assertTrue(underTest.shouldSuppressErrorMsg(BiometricSourceType.FACE, FACE_ERROR_TIMEOUT)) } @Test fun doNotSuppressErrorMsg_facePrimaryAuthRequired_faceLockout() { givenPrimaryAuthRequired() assertFalse(underTest.shouldSuppressErrorMsg(BiometricSourceType.FACE, FACE_ERROR_LOCKOUT)) assertFalse( underTest.shouldSuppressErrorMsg(BiometricSourceType.FACE, FACE_ERROR_LOCKOUT_PERMANENT) ) } @Test fun suppressErrorMsg_fingerprintErrorCancelled() { givenPrimaryAuthNotRequired() assertTrue( underTest.shouldSuppressErrorMsg( BiometricSourceType.FINGERPRINT, FINGERPRINT_ERROR_CANCELED ) ) } @Test fun suppressErrorMsg_fingerprintErrorUserCancelled() { givenPrimaryAuthNotRequired() assertTrue( underTest.shouldSuppressErrorMsg( BiometricSourceType.FINGERPRINT, FINGERPRINT_ERROR_USER_CANCELED ) ) } @Test fun suppressErrorMsg_fingerprintErrorPowerPressed() { givenPrimaryAuthNotRequired() assertTrue( underTest.shouldSuppressErrorMsg( BiometricSourceType.FINGERPRINT, BIOMETRIC_ERROR_POWER_PRESSED ) ) } @Test fun suppressErrorMsg_fingerprintPrimaryAuthRequired() { givenPrimaryAuthRequired() assertTrue( underTest.shouldSuppressErrorMsg(BiometricSourceType.FACE, FINGERPRINT_ERROR_TIMEOUT) ) } @Test fun doNotSuppressErrorMsg_fingerprintPrimaryAuthRequired_fingerprintLockout() { givenPrimaryAuthRequired() assertFalse( underTest.shouldSuppressErrorMsg( BiometricSourceType.FINGERPRINT, FINGERPRINT_ERROR_LOCKOUT ) ) assertFalse( underTest.shouldSuppressErrorMsg( BiometricSourceType.FACE, FINGERPRINT_ERROR_LOCKOUT_PERMANENT ) ) } @Test fun isFaceLockoutErrorMsgId() { givenPrimaryAuthRequired() assertTrue(underTest.isFaceLockoutErrorMsg(FACE_ERROR_LOCKOUT)) assertTrue(underTest.isFaceLockoutErrorMsg(FACE_ERROR_LOCKOUT_PERMANENT)) assertFalse(underTest.isFaceLockoutErrorMsg(FACE_ERROR_TIMEOUT)) assertFalse(underTest.isFaceLockoutErrorMsg(FACE_ERROR_CANCELED)) assertFalse(underTest.isFaceLockoutErrorMsg(FACE_ERROR_UNABLE_TO_PROCESS)) assertFalse(underTest.isFaceLockoutErrorMsg(FACE_ERROR_VENDOR)) } private fun givenPrimaryAuthNotRequired() { whenever(keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(anyBoolean())) .thenReturn(true) } private fun givenPrimaryAuthRequired() { whenever(keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(anyBoolean())) .thenReturn(false) } }
packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java +14 −23 Original line number Diff line number Diff line Loading @@ -72,6 +72,7 @@ import android.content.Intent; import android.content.pm.UserInfo; import android.graphics.Color; import android.hardware.biometrics.BiometricFaceConstants; import android.hardware.biometrics.BiometricFingerprintConstants; import android.hardware.biometrics.BiometricSourceType; import android.hardware.fingerprint.FingerprintManager; import android.os.BatteryManager; Loading @@ -98,14 +99,15 @@ import com.android.systemui.R; import com.android.systemui.SysuiTestCase; import com.android.systemui.biometrics.AuthController; import com.android.systemui.biometrics.FaceHelpMessageDeferral; import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor; import com.android.systemui.bouncer.domain.interactor.BouncerMessageInteractor; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.dock.DockManager; import com.android.systemui.flags.FakeFeatureFlags; import com.android.systemui.keyguard.KeyguardIndication; import com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController; import com.android.systemui.keyguard.ScreenLifecycle; import com.android.systemui.bouncer.domain.interactor.BouncerMessageInteractor; import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor; import com.android.systemui.keyguard.util.IndicationHelper; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.settings.UserTracker; Loading Loading @@ -213,6 +215,7 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase { private StatusBarStateController.StateListener mStatusBarStateListener; private ScreenLifecycle.Observer mScreenObserver; private BroadcastReceiver mBroadcastReceiver; private IndicationHelper mIndicationHelper; private FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock()); private TestableLooper mTestableLooper; private final int mCurrentUserId = 1; Loading Loading @@ -262,13 +265,14 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase { when(mDevicePolicyManager.getDeviceOwnerType(DEVICE_OWNER_COMPONENT)) .thenReturn(DEVICE_OWNER_TYPE_DEFAULT); when(mDevicePolicyResourcesManager.getString(anyString(), any())) .thenReturn(mDisclosureGeneric); when(mDevicePolicyResourcesManager.getString(anyString(), any(), anyString())) .thenReturn(mDisclosureWithOrganization); when(mUserTracker.getUserId()).thenReturn(mCurrentUserId); mIndicationHelper = new IndicationHelper(mKeyguardUpdateMonitor); mWakeLock = new WakeLockFake(); mWakeLockBuilder = new WakeLockFake.Builder(mContext); mWakeLockBuilder.setWakeLock(mWakeLock); Loading Loading @@ -304,7 +308,8 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase { mAlarmManager, mUserTracker, mock(BouncerMessageInteractor.class), flags flags, mIndicationHelper ); mController.init(); mController.setIndicationArea(mIndicationArea); Loading Loading @@ -805,33 +810,19 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase { } @Test public void transientIndication_visibleWhenDozing_ignoresFingerprintCancellation() { public void transientIndication_visibleWhenDozing_ignoresFingerprintErrorMsg() { createController(); mController.setVisible(true); reset(mRotateTextViewController); mController.getKeyguardCallback().onBiometricError( FingerprintManager.FINGERPRINT_ERROR_USER_CANCELED, "foo", BiometricSourceType.FINGERPRINT); mController.getKeyguardCallback().onBiometricError( FingerprintManager.FINGERPRINT_ERROR_CANCELED, "bar", BiometricSourceType.FINGERPRINT); verifyNoMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE); verifyNoMessage(INDICATION_TYPE_TRANSIENT); } @Test public void transientIndication_visibleWhenDozing_ignoresPowerPressed() { createController(); mController.setVisible(true); reset(mRotateTextViewController); // WHEN a fingerprint error user cancelled message is received mController.getKeyguardCallback().onBiometricError( FingerprintManager.BIOMETRIC_ERROR_POWER_PRESSED, "foo", BiometricFingerprintConstants.FINGERPRINT_ERROR_USER_CANCELED, "foo", BiometricSourceType.FINGERPRINT); // THEN no message is shown verifyNoMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE); verifyNoMessage(INDICATION_TYPE_TRANSIENT); } @Test Loading