diff options
4 files changed, 265 insertions, 51 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/util/IndicationHelper.kt b/packages/SystemUI/src/com/android/systemui/keyguard/util/IndicationHelper.kt new file mode 100644 index 000000000000..712900161d48 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyguard/util/IndicationHelper.kt @@ -0,0 +1,65 @@ +/* + * 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 */) + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java index 795bcadc8272..96924821cc1d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java @@ -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; @@ -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; @@ -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; @@ -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. @@ -259,7 +260,8 @@ public class KeyguardIndicationController { AlarmManager alarmManager, UserTracker userTracker, BouncerMessageInteractor bouncerMessageInteractor, - FeatureFlags flags + FeatureFlags flags, + IndicationHelper indicationHelper ) { mContext = context; mBroadcastDispatcher = broadcastDispatcher; @@ -286,6 +288,7 @@ public class KeyguardIndicationController { mUserTracker = userTracker; mBouncerMessageInteractor = bouncerMessageInteractor; mFeatureFlags = flags; + mIndicationHelper = indicationHelper; mFaceAcquiredMessageDeferral = faceHelpMessageDeferral; mCoExFaceAcquisitionMsgIdsToShow = new HashSet<>(); @@ -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); @@ -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 { @@ -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; @@ -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)); diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/util/IndicationHelperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/util/IndicationHelperTest.kt new file mode 100644 index 000000000000..fd0ff9b38eec --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/util/IndicationHelperTest.kt @@ -0,0 +1,173 @@ +/* + * 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) + } +} 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 48c3e2d85574..b1f5dde278be 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java @@ -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; @@ -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; @@ -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; @@ -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); @@ -304,7 +308,8 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase { mAlarmManager, mUserTracker, mock(BouncerMessageInteractor.class), - flags + flags, + mIndicationHelper ); mController.init(); mController.setIndicationArea(mIndicationArea); @@ -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 |