diff options
39 files changed, 1122 insertions, 415 deletions
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt index 54aa3516d867..a450d3af334b 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt @@ -366,7 +366,7 @@ constructor( val dialog = animatedDialog.dialog // Don't animate if the dialog is not showing or if we are locked and going to show the - // bouncer. + // primary bouncer. if ( !dialog.isShowing || (!callback.isUnlocked() && !callback.isShowingAlternateAuthOnUnlock()) diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java index 93027c1914ee..007b956d21b4 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -716,6 +716,13 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab } /** + * Whether keyguard is going away due to screen off or device entry. + */ + public boolean isKeyguardGoingAway() { + return mKeyguardGoingAway; + } + + /** * Updates KeyguardUpdateMonitor's internal state to know if keyguard is showing and if * its occluded. The keyguard is considered visible if its showing and NOT occluded. */ diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java index 092339a74e3f..2dc0cd34adf4 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java @@ -18,6 +18,7 @@ package com.android.systemui.biometrics; import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE; import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT; +import static android.hardware.fingerprint.FingerprintSensorProperties.TYPE_REAR; import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_AWAKE; import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_GOING_TO_SLEEP; @@ -76,6 +77,7 @@ import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.doze.DozeReceiver; import com.android.systemui.keyguard.WakefulnessLifecycle; +import com.android.systemui.keyguard.data.repository.BiometricType; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.VibratorHelper; @@ -85,8 +87,10 @@ import com.android.systemui.util.concurrency.Execution; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; +import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Objects; import java.util.Set; @@ -150,6 +154,7 @@ public class AuthController implements CoreStartable, CommandQueue.Callbacks, @Nullable private List<FingerprintSensorPropertiesInternal> mUdfpsProps; @Nullable private List<FingerprintSensorPropertiesInternal> mSidefpsProps; + @NonNull private final Map<Integer, Boolean> mFpEnrolledForUser = new HashMap<>(); @NonNull private final SparseBooleanArray mUdfpsEnrolledForUser; @NonNull private final SparseBooleanArray mSfpsEnrolledForUser; @NonNull private final SensorPrivacyManager mSensorPrivacyManager; @@ -161,7 +166,6 @@ public class AuthController implements CoreStartable, CommandQueue.Callbacks, private final @Background DelayableExecutor mBackgroundExecutor; private final DisplayInfo mCachedDisplayInfo = new DisplayInfo(); - private final VibratorHelper mVibratorHelper; private void vibrateSuccess(int modality) { @@ -331,27 +335,35 @@ public class AuthController implements CoreStartable, CommandQueue.Callbacks, mExecution.assertIsMainThread(); Log.d(TAG, "handleEnrollmentsChanged, userId: " + userId + ", sensorId: " + sensorId + ", hasEnrollments: " + hasEnrollments); - if (mUdfpsProps == null) { - Log.d(TAG, "handleEnrollmentsChanged, mUdfpsProps is null"); - } else { - for (FingerprintSensorPropertiesInternal prop : mUdfpsProps) { + BiometricType sensorBiometricType = BiometricType.UNKNOWN; + if (mFpProps != null) { + for (FingerprintSensorPropertiesInternal prop: mFpProps) { if (prop.sensorId == sensorId) { - mUdfpsEnrolledForUser.put(userId, hasEnrollments); + mFpEnrolledForUser.put(userId, hasEnrollments); + if (prop.isAnyUdfpsType()) { + sensorBiometricType = BiometricType.UNDER_DISPLAY_FINGERPRINT; + mUdfpsEnrolledForUser.put(userId, hasEnrollments); + } else if (prop.isAnySidefpsType()) { + sensorBiometricType = BiometricType.SIDE_FINGERPRINT; + mSfpsEnrolledForUser.put(userId, hasEnrollments); + } else if (prop.sensorType == TYPE_REAR) { + sensorBiometricType = BiometricType.REAR_FINGERPRINT; + } + break; } } } - - if (mSidefpsProps == null) { - Log.d(TAG, "handleEnrollmentsChanged, mSidefpsProps is null"); - } else { - for (FingerprintSensorPropertiesInternal prop : mSidefpsProps) { + if (mFaceProps != null && sensorBiometricType == BiometricType.UNKNOWN) { + for (FaceSensorPropertiesInternal prop : mFaceProps) { if (prop.sensorId == sensorId) { - mSfpsEnrolledForUser.put(userId, hasEnrollments); + sensorBiometricType = BiometricType.FACE; + break; } } } for (Callback cb : mCallbacks) { cb.onEnrollmentsChanged(); + cb.onEnrollmentsChanged(sensorBiometricType, userId, hasEnrollments); } } @@ -604,6 +616,11 @@ public class AuthController implements CoreStartable, CommandQueue.Callbacks, } } + /** Get FP sensor properties */ + public @Nullable List<FingerprintSensorPropertiesInternal> getFingerprintProperties() { + return mFpProps; + } + /** * @return where the face sensor exists in pixels in the current device orientation. Returns * null if no face sensor exists. @@ -828,7 +845,7 @@ public class AuthController implements CoreStartable, CommandQueue.Callbacks, } @Override - public void setBiometicContextListener(IBiometricContextListener listener) { + public void setBiometricContextListener(IBiometricContextListener listener) { mBiometricContextListener = listener; notifyDozeChanged(mStatusBarStateController.isDozing(), mWakefulnessLifecycle.getWakefulness()); @@ -1081,6 +1098,13 @@ public class AuthController implements CoreStartable, CommandQueue.Callbacks, return mSfpsEnrolledForUser.get(userId); } + /** + * Whether the passed userId has enrolled at least one fingerprint. + */ + public boolean isFingerprintEnrolled(int userId) { + return mFpEnrolledForUser.getOrDefault(userId, false); + } + private void showDialog(SomeArgs args, boolean skipAnimation, Bundle savedState) { mCurrentDialogArgs = args; @@ -1263,6 +1287,16 @@ public class AuthController implements CoreStartable, CommandQueue.Callbacks, default void onEnrollmentsChanged() {} /** + * Called when UDFPS enrollments have changed. This is called after boot and on changes to + * enrollment. + */ + default void onEnrollmentsChanged( + @NonNull BiometricType biometricType, + int userId, + boolean hasEnrollments + ) {} + + /** * Called when the biometric prompt starts showing. */ default void onBiometricPromptShown() {} diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.kt index 4130cf589310..ef7dcb7aac93 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.kt @@ -190,11 +190,6 @@ abstract class UdfpsAnimationViewController<T : UdfpsAnimationView>( open fun listenForTouchesOutsideView(): Boolean = false /** - * Called on touches outside of the view if listenForTouchesOutsideView returns true - */ - open fun onTouchOutsideView() {} - - /** * Called when a view should announce an accessibility event. */ open fun doAnnounceForAccessibility(str: String) {} diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java index f3136babada6..cea1779e39b8 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java @@ -73,6 +73,7 @@ import com.android.systemui.dump.DumpManager; import com.android.systemui.flags.FeatureFlags; import com.android.systemui.flags.Flags; import com.android.systemui.keyguard.ScreenLifecycle; +import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor; import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerInteractor; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; @@ -149,6 +150,7 @@ public class UdfpsController implements DozeReceiver, Dumpable { @NonNull private final ActivityLaunchAnimator mActivityLaunchAnimator; @NonNull private final PrimaryBouncerInteractor mPrimaryBouncerInteractor; @Nullable private final TouchProcessor mTouchProcessor; + @NonNull private final AlternateBouncerInteractor mAlternateBouncerInteractor; // Currently the UdfpsController supports a single UDFPS sensor. If devices have multiple // sensors, this, in addition to a lot of the code here, will be updated. @@ -232,12 +234,12 @@ public class UdfpsController implements DozeReceiver, Dumpable { mShadeExpansionStateManager, mKeyguardViewManager, mKeyguardUpdateMonitor, mDialogManager, mDumpManager, mLockscreenShadeTransitionController, mConfigurationController, - mSystemClock, mKeyguardStateController, + mKeyguardStateController, mUnlockedScreenOffAnimationController, mUdfpsDisplayMode, requestId, reason, callback, (view, event, fromUdfpsView) -> onTouch(requestId, event, fromUdfpsView), mActivityLaunchAnimator, mFeatureFlags, - mPrimaryBouncerInteractor))); + mPrimaryBouncerInteractor, mAlternateBouncerInteractor))); } @Override @@ -329,13 +331,13 @@ public class UdfpsController implements DozeReceiver, Dumpable { if (!mOverlayParams.equals(overlayParams)) { mOverlayParams = overlayParams; - final boolean wasShowingAltAuth = mKeyguardViewManager.isShowingAlternateBouncer(); + final boolean wasShowingAlternateBouncer = mAlternateBouncerInteractor.isVisibleState(); // When the bounds change it's always necessary to re-create the overlay's window with // new LayoutParams. If the overlay needs to be shown, this will re-create and show the // overlay with the updated LayoutParams. Otherwise, the overlay will remain hidden. redrawOverlay(); - if (wasShowingAltAuth) { + if (wasShowingAlternateBouncer) { mKeyguardViewManager.showBouncer(true); } } @@ -543,9 +545,6 @@ public class UdfpsController implements DozeReceiver, Dumpable { final UdfpsView udfpsView = mOverlay.getOverlayView(); boolean handled = false; switch (event.getActionMasked()) { - case MotionEvent.ACTION_OUTSIDE: - udfpsView.onTouchOutsideView(); - return true; case MotionEvent.ACTION_DOWN: case MotionEvent.ACTION_HOVER_ENTER: Trace.beginSection("UdfpsController.onTouch.ACTION_DOWN"); @@ -719,7 +718,8 @@ public class UdfpsController implements DozeReceiver, Dumpable { @NonNull Optional<Provider<AlternateUdfpsTouchProvider>> alternateTouchProvider, @NonNull @BiometricsBackground Executor biometricsExecutor, @NonNull PrimaryBouncerInteractor primaryBouncerInteractor, - @NonNull SinglePointerTouchProcessor singlePointerTouchProcessor) { + @NonNull SinglePointerTouchProcessor singlePointerTouchProcessor, + @NonNull AlternateBouncerInteractor alternateBouncerInteractor) { mContext = context; mExecution = execution; mVibrator = vibrator; @@ -759,6 +759,7 @@ public class UdfpsController implements DozeReceiver, Dumpable { mBiometricExecutor = biometricsExecutor; mPrimaryBouncerInteractor = primaryBouncerInteractor; + mAlternateBouncerInteractor = alternateBouncerInteractor; mTouchProcessor = mFeatureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION) ? singlePointerTouchProcessor : null; @@ -853,9 +854,7 @@ public class UdfpsController implements DozeReceiver, Dumpable { onFingerUp(mOverlay.getRequestId(), oldView); } final boolean removed = mOverlay.hide(); - if (mKeyguardViewManager.isShowingAlternateBouncer()) { - mKeyguardViewManager.hideAlternateBouncer(true); - } + mKeyguardViewManager.hideAlternateBouncer(true); Log.v(TAG, "hideUdfpsOverlay | removing window: " + removed); } else { Log.v(TAG, "hideUdfpsOverlay | the overlay is already hidden"); diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt index 8db4927ee059..a3c4985fd5cc 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt @@ -50,6 +50,7 @@ import com.android.systemui.animation.ActivityLaunchAnimator import com.android.systemui.dump.DumpManager import com.android.systemui.flags.FeatureFlags import com.android.systemui.flags.Flags +import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerInteractor import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.shade.ShadeExpansionStateManager @@ -59,7 +60,6 @@ import com.android.systemui.statusbar.phone.SystemUIDialogManager import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController import com.android.systemui.statusbar.policy.ConfigurationController import com.android.systemui.statusbar.policy.KeyguardStateController -import com.android.systemui.util.time.SystemClock private const val TAG = "UdfpsControllerOverlay" @@ -86,7 +86,6 @@ class UdfpsControllerOverlay @JvmOverloads constructor( private val dumpManager: DumpManager, private val transitionController: LockscreenShadeTransitionController, private val configurationController: ConfigurationController, - private val systemClock: SystemClock, private val keyguardStateController: KeyguardStateController, private val unlockedScreenOffAnimationController: UnlockedScreenOffAnimationController, private var udfpsDisplayModeProvider: UdfpsDisplayModeProvider, @@ -97,7 +96,8 @@ class UdfpsControllerOverlay @JvmOverloads constructor( private val activityLaunchAnimator: ActivityLaunchAnimator, private val featureFlags: FeatureFlags, private val primaryBouncerInteractor: PrimaryBouncerInteractor, - private val isDebuggable: Boolean = Build.IS_DEBUGGABLE + private val alternateBouncerInteractor: AlternateBouncerInteractor, + private val isDebuggable: Boolean = Build.IS_DEBUGGABLE, ) { /** The view, when [isShowing], or null. */ var overlayView: UdfpsView? = null @@ -255,14 +255,14 @@ class UdfpsControllerOverlay @JvmOverloads constructor( dumpManager, transitionController, configurationController, - systemClock, keyguardStateController, unlockedScreenOffAnimationController, dialogManager, controller, activityLaunchAnimator, featureFlags, - primaryBouncerInteractor + primaryBouncerInteractor, + alternateBouncerInteractor, ) } REASON_AUTH_BP -> { diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.kt index 63144fcea761..583ee3ac8e60 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.kt @@ -31,6 +31,7 @@ import com.android.systemui.animation.Interpolators import com.android.systemui.dump.DumpManager import com.android.systemui.flags.FeatureFlags import com.android.systemui.flags.Flags +import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerInteractor import com.android.systemui.lifecycle.repeatWhenAttached import com.android.systemui.plugins.statusbar.StatusBarStateController @@ -42,13 +43,13 @@ import com.android.systemui.statusbar.notification.stack.StackStateAnimator import com.android.systemui.statusbar.phone.KeyguardBouncer import com.android.systemui.statusbar.phone.KeyguardBouncer.PrimaryBouncerExpansionCallback import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager -import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager.AlternateBouncer import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager.KeyguardViewManagerCallback +import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager.LegacyAlternateBouncer +import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager.OccludingAppBiometricUI import com.android.systemui.statusbar.phone.SystemUIDialogManager import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController import com.android.systemui.statusbar.policy.ConfigurationController import com.android.systemui.statusbar.policy.KeyguardStateController -import com.android.systemui.util.time.SystemClock import java.io.PrintWriter import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job @@ -65,25 +66,27 @@ constructor( dumpManager: DumpManager, private val lockScreenShadeTransitionController: LockscreenShadeTransitionController, private val configurationController: ConfigurationController, - private val systemClock: SystemClock, private val keyguardStateController: KeyguardStateController, private val unlockedScreenOffAnimationController: UnlockedScreenOffAnimationController, systemUIDialogManager: SystemUIDialogManager, private val udfpsController: UdfpsController, private val activityLaunchAnimator: ActivityLaunchAnimator, featureFlags: FeatureFlags, - private val primaryBouncerInteractor: PrimaryBouncerInteractor + private val primaryBouncerInteractor: PrimaryBouncerInteractor, + private val alternateBouncerInteractor: AlternateBouncerInteractor, ) : UdfpsAnimationViewController<UdfpsKeyguardView>( view, statusBarStateController, shadeExpansionStateManager, systemUIDialogManager, - dumpManager + dumpManager, ) { private val useExpandedOverlay: Boolean = featureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION) private val isModernBouncerEnabled: Boolean = featureFlags.isEnabled(Flags.MODERN_BOUNCER) + private val isModernAlternateBouncerEnabled: Boolean = + featureFlags.isEnabled(Flags.MODERN_ALTERNATE_BOUNCER) private var showingUdfpsBouncer = false private var udfpsRequested = false private var qsExpansion = 0f @@ -91,7 +94,6 @@ constructor( private var statusBarState = 0 private var transitionToFullShadeProgress = 0f private var lastDozeAmount = 0f - private var lastUdfpsBouncerShowTime: Long = -1 private var panelExpansionFraction = 0f private var launchTransitionFadingAway = false private var isLaunchingActivity = false @@ -244,20 +246,8 @@ constructor( } } - private val mAlternateBouncer: AlternateBouncer = - object : AlternateBouncer { - override fun showAlternateBouncer(): Boolean { - return showUdfpsBouncer(true) - } - - override fun hideAlternateBouncer(): Boolean { - return showUdfpsBouncer(false) - } - - override fun isShowingAlternateBouncer(): Boolean { - return showingUdfpsBouncer - } - + private val occludingAppBiometricUI: OccludingAppBiometricUI = + object : OccludingAppBiometricUI { override fun requestUdfps(request: Boolean, color: Int) { udfpsRequested = request view.requestUdfps(request, color) @@ -275,16 +265,19 @@ constructor( override fun onInit() { super.onInit() - keyguardViewManager.setAlternateBouncer(mAlternateBouncer) + keyguardViewManager.setOccludingAppBiometricUI(occludingAppBiometricUI) } init { - if (isModernBouncerEnabled) { + if (isModernBouncerEnabled || isModernAlternateBouncerEnabled) { view.repeatWhenAttached { // repeatOnLifecycle CREATED (as opposed to STARTED) because the Bouncer expansion // can make the view not visible; and we still want to listen for events // that may make the view visible again. - repeatOnLifecycle(Lifecycle.State.CREATED) { listenForBouncerExpansion(this) } + repeatOnLifecycle(Lifecycle.State.CREATED) { + if (isModernBouncerEnabled) listenForBouncerExpansion(this) + if (isModernAlternateBouncerEnabled) listenForAlternateBouncerVisibility(this) + } } } } @@ -300,8 +293,18 @@ constructor( } } + @VisibleForTesting + internal suspend fun listenForAlternateBouncerVisibility(scope: CoroutineScope): Job { + return scope.launch { + alternateBouncerInteractor.isVisible.collect { isVisible: Boolean -> + showUdfpsBouncer(isVisible) + } + } + } + public override fun onViewAttached() { super.onViewAttached() + alternateBouncerInteractor.setAlternateBouncerUIAvailable(true) val dozeAmount = statusBarStateController.dozeAmount lastDozeAmount = dozeAmount stateListener.onDozeAmountChanged(dozeAmount, dozeAmount) @@ -326,7 +329,8 @@ constructor( view.updatePadding() updateAlpha() updatePauseAuth() - keyguardViewManager.setAlternateBouncer(mAlternateBouncer) + keyguardViewManager.setLegacyAlternateBouncer(legacyAlternateBouncer) + keyguardViewManager.setOccludingAppBiometricUI(occludingAppBiometricUI) lockScreenShadeTransitionController.udfpsKeyguardViewController = this activityLaunchAnimator.addListener(activityLaunchAnimatorListener) view.mUseExpandedOverlay = useExpandedOverlay @@ -334,10 +338,12 @@ constructor( override fun onViewDetached() { super.onViewDetached() + alternateBouncerInteractor.setAlternateBouncerUIAvailable(false) faceDetectRunning = false keyguardStateController.removeCallback(keyguardStateControllerCallback) statusBarStateController.removeCallback(stateListener) - keyguardViewManager.removeAlternateAuthInterceptor(mAlternateBouncer) + keyguardViewManager.removeLegacyAlternateBouncer(legacyAlternateBouncer) + keyguardViewManager.removeOccludingAppBiometricUI(occludingAppBiometricUI) keyguardUpdateMonitor.requestFaceAuthOnOccludingApp(false) configurationController.removeCallback(configurationListener) shadeExpansionStateManager.removeExpansionListener(shadeExpansionListener) @@ -356,7 +362,16 @@ constructor( override fun dump(pw: PrintWriter, args: Array<String>) { super.dump(pw, args) pw.println("isModernBouncerEnabled=$isModernBouncerEnabled") + pw.println("isModernAlternateBouncerEnabled=$isModernAlternateBouncerEnabled") pw.println("showingUdfpsAltBouncer=$showingUdfpsBouncer") + pw.println( + "altBouncerInteractor#isAlternateBouncerVisible=" + + "${alternateBouncerInteractor.isVisibleState()}" + ) + pw.println( + "altBouncerInteractor#canShowAlternateBouncerForFingerprint=" + + "${alternateBouncerInteractor.canShowAlternateBouncerForFingerprint()}" + ) pw.println("faceDetectRunning=$faceDetectRunning") pw.println("statusBarState=" + StatusBarState.toString(statusBarState)) pw.println("transitionToFullShadeProgress=$transitionToFullShadeProgress") @@ -385,9 +400,6 @@ constructor( val udfpsAffordanceWasNotShowing = shouldPauseAuth() showingUdfpsBouncer = show if (showingUdfpsBouncer) { - lastUdfpsBouncerShowTime = systemClock.uptimeMillis() - } - if (showingUdfpsBouncer) { if (udfpsAffordanceWasNotShowing) { view.animateInUdfpsBouncer(null) } @@ -452,7 +464,7 @@ constructor( return if (isModernBouncerEnabled) { inputBouncerExpansion == 1f } else { - keyguardViewManager.isBouncerShowing && !keyguardViewManager.isShowingAlternateBouncer + keyguardViewManager.isBouncerShowing && !alternateBouncerInteractor.isVisibleState() } } @@ -460,30 +472,6 @@ constructor( return true } - override fun onTouchOutsideView() { - maybeShowInputBouncer() - } - - /** - * If we were previously showing the udfps bouncer, hide it and instead show the regular - * (pin/pattern/password) bouncer. - * - * Does nothing if we weren't previously showing the UDFPS bouncer. - */ - private fun maybeShowInputBouncer() { - if (showingUdfpsBouncer && hasUdfpsBouncerShownWithMinTime()) { - keyguardViewManager.showPrimaryBouncer(true) - } - } - - /** - * Whether the udfps bouncer has shown for at least 200ms before allowing touches outside of the - * udfps icon area to dismiss the udfps bouncer and show the pin/pattern/password bouncer. - */ - private fun hasUdfpsBouncerShownWithMinTime(): Boolean { - return systemClock.uptimeMillis() - lastUdfpsBouncerShowTime > 200 - } - /** * Set the progress we're currently transitioning to the full shade. 0.0f means we're not * transitioning yet, while 1.0f means we've fully dragged down. For example, start swiping down @@ -545,7 +533,7 @@ constructor( if (isModernBouncerEnabled) { return } - val altBouncerShowing = keyguardViewManager.isShowingAlternateBouncer + val altBouncerShowing = alternateBouncerInteractor.isVisibleState() if (altBouncerShowing || !keyguardViewManager.primaryBouncerIsOrWillBeShowing()) { inputBouncerHiddenAmount = 1f } else if (keyguardViewManager.isBouncerShowing) { @@ -554,6 +542,21 @@ constructor( } } + private val legacyAlternateBouncer: LegacyAlternateBouncer = + object : LegacyAlternateBouncer { + override fun showAlternateBouncer(): Boolean { + return showUdfpsBouncer(true) + } + + override fun hideAlternateBouncer(): Boolean { + return showUdfpsBouncer(false) + } + + override fun isShowingAlternateBouncer(): Boolean { + return showingUdfpsBouncer + } + } + companion object { const val TAG = "UdfpsKeyguardViewController" } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.kt index 4a8877edfa53..e61c614f0292 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.kt @@ -111,10 +111,6 @@ class UdfpsView( } } - fun onTouchOutsideView() { - animationViewController?.onTouchOutsideView() - } - override fun onAttachedToWindow() { super.onAttachedToWindow() Log.v(TAG, "onAttachedToWindow") diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt index ae8caadd7561..d015b1580385 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt +++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt @@ -180,6 +180,13 @@ object Flags { @JvmField val LIGHT_REVEAL_MIGRATION = unreleasedFlag(218, "light_reveal_migration", teamfood = false) + /** + * Whether to use the new alternate bouncer architecture, a refactor of and eventual replacement + * of the Alternate/Authentication Bouncer. No visual UI changes. + */ + // TODO(b/260619425): Tracking Bug + @JvmField val MODERN_ALTERNATE_BOUNCER = unreleasedFlag(219, "modern_alternate_bouncer") + /** Flag to control the migration of face auth to modern architecture. */ // TODO(b/262838215): Tracking bug @JvmField val FACE_AUTH_REFACTOR = unreleasedFlag(220, "face_auth_refactor") diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/BiometricRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/BiometricRepository.kt new file mode 100644 index 000000000000..25d8f4021f87 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/BiometricRepository.kt @@ -0,0 +1,184 @@ +/* + * 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.keyguard.data.repository + +import android.app.admin.DevicePolicyManager +import android.app.admin.DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED +import android.content.Context +import android.content.IntentFilter +import android.os.Looper +import android.os.UserHandle +import com.android.internal.widget.LockPatternUtils +import com.android.systemui.biometrics.AuthController +import com.android.systemui.broadcast.BroadcastDispatcher +import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging +import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.dagger.qualifiers.Background +import com.android.systemui.dagger.qualifiers.Main +import com.android.systemui.user.data.repository.UserRepository +import javax.inject.Inject +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.channels.awaitClose +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.flatMapLatest +import kotlinx.coroutines.flow.flowOn +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.stateIn +import kotlinx.coroutines.flow.transformLatest + +/** + * Acts as source of truth for biometric features. + * + * Abstracts-away data sources and their schemas so the rest of the app doesn't need to worry about + * upstream changes. + */ +interface BiometricRepository { + /** Whether any fingerprints are enrolled for the current user. */ + val isFingerprintEnrolled: StateFlow<Boolean> + + /** + * Whether the current user is allowed to use a strong biometric for device entry based on + * Android Security policies. If false, the user may be able to use primary authentication for + * device entry. + */ + val isStrongBiometricAllowed: StateFlow<Boolean> + + /** Whether fingerprint feature is enabled for the current user by the DevicePolicy */ + val isFingerprintEnabledByDevicePolicy: StateFlow<Boolean> +} + +@SysUISingleton +class BiometricRepositoryImpl +@Inject +constructor( + context: Context, + lockPatternUtils: LockPatternUtils, + broadcastDispatcher: BroadcastDispatcher, + authController: AuthController, + userRepository: UserRepository, + devicePolicyManager: DevicePolicyManager, + @Application scope: CoroutineScope, + @Background backgroundDispatcher: CoroutineDispatcher, + @Main looper: Looper, +) : BiometricRepository { + + /** UserId of the current selected user. */ + private val selectedUserId: Flow<Int> = + userRepository.selectedUserInfo.map { it.id }.distinctUntilChanged() + + override val isFingerprintEnrolled: StateFlow<Boolean> = + selectedUserId + .flatMapLatest { userId -> + conflatedCallbackFlow { + val callback = + object : AuthController.Callback { + override fun onEnrollmentsChanged( + sensorBiometricType: BiometricType, + userId: Int, + hasEnrollments: Boolean + ) { + if (sensorBiometricType.isFingerprint) { + trySendWithFailureLogging( + hasEnrollments, + TAG, + "update fpEnrollment" + ) + } + } + } + authController.addCallback(callback) + awaitClose { authController.removeCallback(callback) } + } + } + .stateIn( + scope, + started = SharingStarted.Eagerly, + initialValue = + authController.isFingerprintEnrolled(userRepository.getSelectedUserInfo().id) + ) + + override val isStrongBiometricAllowed: StateFlow<Boolean> = + selectedUserId + .flatMapLatest { currUserId -> + conflatedCallbackFlow { + val callback = + object : LockPatternUtils.StrongAuthTracker(context, looper) { + override fun onStrongAuthRequiredChanged(userId: Int) { + if (currUserId != userId) { + return + } + + trySendWithFailureLogging( + isBiometricAllowedForUser(true, currUserId), + TAG + ) + } + + override fun onIsNonStrongBiometricAllowedChanged(userId: Int) { + // no-op + } + } + lockPatternUtils.registerStrongAuthTracker(callback) + awaitClose { lockPatternUtils.unregisterStrongAuthTracker(callback) } + } + } + .stateIn( + scope, + started = SharingStarted.Eagerly, + initialValue = + lockPatternUtils.isBiometricAllowedForUser( + userRepository.getSelectedUserInfo().id + ) + ) + + override val isFingerprintEnabledByDevicePolicy: StateFlow<Boolean> = + selectedUserId + .flatMapLatest { userId -> + broadcastDispatcher + .broadcastFlow( + filter = IntentFilter(ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED), + user = UserHandle.ALL + ) + .transformLatest { + emit( + (devicePolicyManager.getKeyguardDisabledFeatures(null, userId) and + DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT) == 0 + ) + } + .flowOn(backgroundDispatcher) + .distinctUntilChanged() + } + .stateIn( + scope, + started = SharingStarted.Eagerly, + initialValue = + devicePolicyManager.getKeyguardDisabledFeatures( + null, + userRepository.getSelectedUserInfo().id + ) and DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT == 0 + ) + + companion object { + private const val TAG = "BiometricsRepositoryImpl" + } +} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/BiometricType.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/BiometricType.kt new file mode 100644 index 000000000000..93c97813d8e5 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/BiometricType.kt @@ -0,0 +1,33 @@ +/* + * 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.keyguard.data.repository + +enum class BiometricType(val isFingerprint: Boolean) { + // An unsupported biometric type + UNKNOWN(false), + + // Fingerprint sensor that is located on the back (opposite side of the display) of the device + REAR_FINGERPRINT(true), + + // Fingerprint sensor that is located under the display + UNDER_DISPLAY_FINGERPRINT(true), + + // Fingerprint sensor that is located on the side of the device, typically on the power button + SIDE_FINGERPRINT(true), + FACE(false), +} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBouncerRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBouncerRepository.kt index 90f3c7d88c8f..2e34e9a93dff 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBouncerRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBouncerRepository.kt @@ -26,9 +26,11 @@ import com.android.systemui.log.dagger.BouncerLog import com.android.systemui.log.table.TableLogBuffer import com.android.systemui.log.table.logDiffsForTable import com.android.systemui.statusbar.phone.KeyguardBouncer +import com.android.systemui.util.time.SystemClock import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.launchIn @@ -44,6 +46,7 @@ class KeyguardBouncerRepository @Inject constructor( private val viewMediatorCallback: ViewMediatorCallback, + private val clock: SystemClock, @Application private val applicationScope: CoroutineScope, @BouncerLog private val buffer: TableLogBuffer, ) { @@ -94,6 +97,14 @@ constructor( setUpLogging() } + /** Values associated with the AlternateBouncer */ + private val _isAlternateBouncerVisible = MutableStateFlow(false) + val isAlternateBouncerVisible = _isAlternateBouncerVisible.asStateFlow() + var lastAlternateBouncerVisibleTime: Long = NOT_VISIBLE + private val _isAlternateBouncerUIAvailable = MutableStateFlow<Boolean>(false) + val isAlternateBouncerUIAvailable: StateFlow<Boolean> = + _isAlternateBouncerUIAvailable.asStateFlow() + fun setPrimaryScrimmed(isScrimmed: Boolean) { _primaryBouncerScrimmed.value = isScrimmed } @@ -102,6 +113,19 @@ constructor( _primaryBouncerVisible.value = isVisible } + fun setAlternateVisible(isVisible: Boolean) { + if (isVisible && !_isAlternateBouncerVisible.value) { + lastAlternateBouncerVisibleTime = clock.uptimeMillis() + } else if (!isVisible) { + lastAlternateBouncerVisibleTime = NOT_VISIBLE + } + _isAlternateBouncerVisible.value = isVisible + } + + fun setAlternateBouncerUIAvailable(isAvailable: Boolean) { + _isAlternateBouncerUIAvailable.value = isAvailable + } + fun setPrimaryShow(keyguardBouncerModel: KeyguardBouncerModel?) { _primaryBouncerShow.value = keyguardBouncerModel } @@ -202,4 +226,8 @@ constructor( .logDiffsForTable(buffer, "", "ResourceUpdateRequests", false) .launchIn(applicationScope) } + + companion object { + private const val NOT_VISIBLE = -1L + } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryModule.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryModule.kt index 26f853f3ad1c..4639597a9b8c 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryModule.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryModule.kt @@ -30,4 +30,6 @@ interface KeyguardRepositoryModule { @Binds fun lightRevealScrimRepository(impl: LightRevealScrimRepositoryImpl): LightRevealScrimRepository + + @Binds fun biometricRepository(impl: BiometricRepositoryImpl): BiometricRepository } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/AlternateBouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/AlternateBouncerInteractor.kt new file mode 100644 index 000000000000..28c0b288147b --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/AlternateBouncerInteractor.kt @@ -0,0 +1,126 @@ +/* + * 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.keyguard.domain.interactor + +import com.android.keyguard.KeyguardUpdateMonitor +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.flags.FeatureFlags +import com.android.systemui.flags.Flags +import com.android.systemui.keyguard.data.repository.BiometricRepository +import com.android.systemui.keyguard.data.repository.KeyguardBouncerRepository +import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager.LegacyAlternateBouncer +import com.android.systemui.util.time.SystemClock +import javax.inject.Inject +import kotlinx.coroutines.flow.Flow + +/** Encapsulates business logic for interacting with the lock-screen alternate bouncer. */ +@SysUISingleton +class AlternateBouncerInteractor +@Inject +constructor( + private val bouncerRepository: KeyguardBouncerRepository, + private val biometricRepository: BiometricRepository, + private val systemClock: SystemClock, + private val keyguardUpdateMonitor: KeyguardUpdateMonitor, + featureFlags: FeatureFlags, +) { + val isModernAlternateBouncerEnabled = featureFlags.isEnabled(Flags.MODERN_ALTERNATE_BOUNCER) + var legacyAlternateBouncer: LegacyAlternateBouncer? = null + var legacyAlternateBouncerVisibleTime: Long = NOT_VISIBLE + + val isVisible: Flow<Boolean> = bouncerRepository.isAlternateBouncerVisible + + /** + * Sets the correct bouncer states to show the alternate bouncer if it can show. + * @return whether alternateBouncer is visible + */ + fun show(): Boolean { + return when { + isModernAlternateBouncerEnabled -> { + bouncerRepository.setAlternateVisible(canShowAlternateBouncerForFingerprint()) + isVisibleState() + } + canShowAlternateBouncerForFingerprint() -> { + if (legacyAlternateBouncer?.showAlternateBouncer() == true) { + legacyAlternateBouncerVisibleTime = systemClock.uptimeMillis() + true + } else { + false + } + } + else -> false + } + } + + /** + * Sets the correct bouncer states to hide the bouncer. Should only be called through + * StatusBarKeyguardViewManager until ScrimController is refactored to use + * alternateBouncerInteractor. + * @return true if the alternate bouncer was newly hidden, else false. + */ + fun hide(): Boolean { + return if (isModernAlternateBouncerEnabled) { + val wasAlternateBouncerVisible = isVisibleState() + bouncerRepository.setAlternateVisible(false) + wasAlternateBouncerVisible && !isVisibleState() + } else { + legacyAlternateBouncer?.hideAlternateBouncer() ?: false + } + } + + fun isVisibleState(): Boolean { + return if (isModernAlternateBouncerEnabled) { + bouncerRepository.isAlternateBouncerVisible.value + } else { + legacyAlternateBouncer?.isShowingAlternateBouncer ?: false + } + } + + fun setAlternateBouncerUIAvailable(isAvailable: Boolean) { + bouncerRepository.setAlternateBouncerUIAvailable(isAvailable) + } + + fun canShowAlternateBouncerForFingerprint(): Boolean { + return if (isModernAlternateBouncerEnabled) { + bouncerRepository.isAlternateBouncerUIAvailable.value && + biometricRepository.isFingerprintEnrolled.value && + biometricRepository.isStrongBiometricAllowed.value && + biometricRepository.isFingerprintEnabledByDevicePolicy.value + } else { + legacyAlternateBouncer != null && + keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(true) + } + } + + /** + * Whether the alt bouncer has shown for a minimum time before allowing touches to dismiss the + * alternate bouncer and show the primary bouncer. + */ + fun hasAlternateBouncerShownWithMinTime(): Boolean { + return if (isModernAlternateBouncerEnabled) { + (systemClock.uptimeMillis() - bouncerRepository.lastAlternateBouncerVisibleTime) > + MIN_VISIBILITY_DURATION_UNTIL_TOUCHES_DISMISS_ALTERNATE_BOUNCER_MS + } else { + systemClock.uptimeMillis() - legacyAlternateBouncerVisibleTime > 200 + } + } + + companion object { + private const val MIN_VISIBILITY_DURATION_UNTIL_TOUCHES_DISMISS_ALTERNATE_BOUNCER_MS = 200L + private const val NOT_VISIBLE = -1L + } +} diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java index ecaabce70f8a..80d7bbc04440 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java @@ -138,6 +138,7 @@ import com.android.systemui.flags.Flags; import com.android.systemui.fragments.FragmentHostManager.FragmentListener; import com.android.systemui.fragments.FragmentService; import com.android.systemui.keyguard.KeyguardUnlockAnimationController; +import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor; import com.android.systemui.keyguard.domain.interactor.KeyguardBottomAreaInteractor; import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor; import com.android.systemui.keyguard.shared.model.TransitionState; @@ -362,6 +363,7 @@ public final class NotificationPanelViewController implements Dumpable { private final FragmentListener mQsFragmentListener = new QsFragmentListener(); private final AccessibilityDelegate mAccessibilityDelegate = new ShadeAccessibilityDelegate(); private final NotificationGutsManager mGutsManager; + private final AlternateBouncerInteractor mAlternateBouncerInteractor; private long mDownTime; private boolean mTouchSlopExceededBeforeDown; @@ -807,6 +809,7 @@ public final class NotificationPanelViewController implements Dumpable { SystemClock systemClock, KeyguardBottomAreaViewModel keyguardBottomAreaViewModel, KeyguardBottomAreaInteractor keyguardBottomAreaInteractor, + AlternateBouncerInteractor alternateBouncerInteractor, DreamingToLockscreenTransitionViewModel dreamingToLockscreenTransitionViewModel, OccludedToLockscreenTransitionViewModel occludedToLockscreenTransitionViewModel, LockscreenToDreamingTransitionViewModel lockscreenToDreamingTransitionViewModel, @@ -1002,6 +1005,7 @@ public final class NotificationPanelViewController implements Dumpable { unlockAnimationStarted(playingCannedAnimation, isWakeAndUnlock, startDelay); } }); + mAlternateBouncerInteractor = alternateBouncerInteractor; dumpManager.registerDumpable(this); } @@ -4950,7 +4954,7 @@ public final class NotificationPanelViewController implements Dumpable { mUpdateFlingVelocity = vel; } } else if (!mCentralSurfaces.isBouncerShowing() - && !mStatusBarKeyguardViewManager.isShowingAlternateBouncer() + && !mAlternateBouncerInteractor.isVisibleState() && !mKeyguardStateController.isKeyguardGoingAway()) { onEmptySpaceClick(); onTrackingStopped(true); diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java index 77307f3de19d..7ed6e3e55623 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java @@ -41,6 +41,7 @@ import com.android.systemui.dock.DockManager; import com.android.systemui.flags.FeatureFlags; import com.android.systemui.flags.Flags; import com.android.systemui.keyguard.KeyguardUnlockAnimationController; +import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor; import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor; import com.android.systemui.keyguard.shared.model.TransitionState; import com.android.systemui.keyguard.shared.model.TransitionStep; @@ -85,6 +86,7 @@ public class NotificationShadeWindowViewController { private final AmbientState mAmbientState; private final PulsingGestureListener mPulsingGestureListener; private final NotificationInsetsController mNotificationInsetsController; + private final AlternateBouncerInteractor mAlternateBouncerInteractor; private GestureDetector mPulsingWakeupGestureHandler; private View mBrightnessMirror; @@ -132,8 +134,9 @@ public class NotificationShadeWindowViewController { PulsingGestureListener pulsingGestureListener, FeatureFlags featureFlags, KeyguardBouncerViewModel keyguardBouncerViewModel, - KeyguardTransitionInteractor keyguardTransitionInteractor, - KeyguardBouncerComponent.Factory keyguardBouncerComponentFactory + KeyguardBouncerComponent.Factory keyguardBouncerComponentFactory, + AlternateBouncerInteractor alternateBouncerInteractor, + KeyguardTransitionInteractor keyguardTransitionInteractor ) { mLockscreenShadeTransitionController = transitionController; mFalsingCollector = falsingCollector; @@ -153,6 +156,7 @@ public class NotificationShadeWindowViewController { mAmbientState = ambientState; mPulsingGestureListener = pulsingGestureListener; mNotificationInsetsController = notificationInsetsController; + mAlternateBouncerInteractor = alternateBouncerInteractor; // This view is not part of the newly inflated expanded status bar. mBrightnessMirror = mView.findViewById(R.id.brightness_mirror_container); @@ -315,7 +319,7 @@ public class NotificationShadeWindowViewController { return true; } - if (mStatusBarKeyguardViewManager.isShowingAlternateBouncer()) { + if (mAlternateBouncerInteractor.isVisibleState()) { // capture all touches if the alt auth bouncer is showing return true; } @@ -353,7 +357,7 @@ public class NotificationShadeWindowViewController { handled = !mService.isPulsing(); } - if (mStatusBarKeyguardViewManager.isShowingAlternateBouncer()) { + if (mAlternateBouncerInteractor.isVisibleState()) { // eat the touch handled = true; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java index 750d00466a8d..584a382184f3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java @@ -334,7 +334,7 @@ public class CommandQueue extends IStatusBar.Stub implements /** * @see IStatusBar#setBiometicContextListener(IBiometricContextListener) */ - default void setBiometicContextListener(IBiometricContextListener listener) { + default void setBiometricContextListener(IBiometricContextListener listener) { } /** @@ -1583,7 +1583,7 @@ public class CommandQueue extends IStatusBar.Stub implements } case MSG_SET_BIOMETRICS_LISTENER: for (int i = 0; i < mCallbacks.size(); i++) { - mCallbacks.get(i).setBiometicContextListener( + mCallbacks.get(i).setBiometricContextListener( (IBiometricContextListener) msg.obj); } break; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java index 37d26c29d6f0..006b5528e7e9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java @@ -91,6 +91,7 @@ import com.android.systemui.dock.DockManager; import com.android.systemui.keyguard.KeyguardIndication; import com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController; import com.android.systemui.keyguard.ScreenLifecycle; +import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.phone.KeyguardBypassController; @@ -156,6 +157,7 @@ public class KeyguardIndicationController { private final KeyguardBypassController mKeyguardBypassController; private final AccessibilityManager mAccessibilityManager; private final Handler mHandler; + private final AlternateBouncerInteractor mAlternateBouncerInteractor; @VisibleForTesting public KeyguardIndicationRotateTextViewController mRotateTextViewController; @@ -235,7 +237,8 @@ public class KeyguardIndicationController { KeyguardBypassController keyguardBypassController, AccessibilityManager accessibilityManager, FaceHelpMessageDeferral faceHelpMessageDeferral, - KeyguardLogger keyguardLogger) { + KeyguardLogger keyguardLogger, + AlternateBouncerInteractor alternateBouncerInteractor) { mContext = context; mBroadcastDispatcher = broadcastDispatcher; mDevicePolicyManager = devicePolicyManager; @@ -257,6 +260,7 @@ public class KeyguardIndicationController { mScreenLifecycle = screenLifecycle; mKeyguardLogger = keyguardLogger; mScreenLifecycle.addObserver(mScreenObserver); + mAlternateBouncerInteractor = alternateBouncerInteractor; mFaceAcquiredMessageDeferral = faceHelpMessageDeferral; mCoExFaceAcquisitionMsgIdsToShow = new HashSet<>(); @@ -929,7 +933,7 @@ public class KeyguardIndicationController { } if (mStatusBarKeyguardViewManager.isBouncerShowing()) { - if (mStatusBarKeyguardViewManager.isShowingAlternateBouncer()) { + if (mAlternateBouncerInteractor.isVisibleState()) { return; // udfps affordance is highlighted, no need to show action to unlock } else if (mKeyguardUpdateMonitor.isFaceEnrolled() && !mKeyguardUpdateMonitor.getIsFaceAuthenticated()) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java index 14d0d7e032d5..9a65e342478e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java @@ -31,6 +31,7 @@ import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dump.DumpHandler; import com.android.systemui.dump.DumpManager; +import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor; import com.android.systemui.media.controls.pipeline.MediaDataManager; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.statusbar.StatusBarStateController; @@ -61,7 +62,6 @@ import com.android.systemui.statusbar.phone.ManagedProfileControllerImpl; import com.android.systemui.statusbar.phone.StatusBarIconController; import com.android.systemui.statusbar.phone.StatusBarIconControllerImpl; import com.android.systemui.statusbar.phone.StatusBarIconList; -import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; import com.android.systemui.statusbar.phone.StatusBarRemoteInputCallback; import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController; import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallFlags; @@ -280,7 +280,7 @@ public interface CentralSurfacesDependenciesModule { @SysUISingleton static DialogLaunchAnimator provideDialogLaunchAnimator(IDreamManager dreamManager, KeyguardStateController keyguardStateController, - Lazy<StatusBarKeyguardViewManager> statusBarKeyguardViewManager, + Lazy<AlternateBouncerInteractor> alternateBouncerInteractor, InteractionJankMonitor interactionJankMonitor) { DialogLaunchAnimator.Callback callback = new DialogLaunchAnimator.Callback() { @Override @@ -300,7 +300,7 @@ public interface CentralSurfacesDependenciesModule { @Override public boolean isShowingAlternateAuthOnUnlock() { - return statusBarKeyguardViewManager.get().canShowAlternateBouncer(); + return alternateBouncerInteractor.get().canShowAlternateBouncerForFingerprint(); } }; return new DialogLaunchAnimator(callback, interactionJankMonitor); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java index 198572a51760..8f2704521d95 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java @@ -162,6 +162,7 @@ import com.android.systemui.keyguard.KeyguardUnlockAnimationController; import com.android.systemui.keyguard.KeyguardViewMediator; import com.android.systemui.keyguard.ScreenLifecycle; import com.android.systemui.keyguard.WakefulnessLifecycle; +import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor; import com.android.systemui.keyguard.ui.binder.LightRevealScrimViewBinder; import com.android.systemui.keyguard.ui.viewmodel.LightRevealScrimViewModel; import com.android.systemui.navigationbar.NavigationBarController; @@ -485,6 +486,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { private final ShadeController mShadeController; private final InitController mInitController; private final Lazy<CameraLauncher> mCameraLauncherLazy; + private final AlternateBouncerInteractor mAlternateBouncerInteractor; private final PluginDependencyProvider mPluginDependencyProvider; private final KeyguardDismissUtil mKeyguardDismissUtil; @@ -763,7 +765,9 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { WiredChargingRippleController wiredChargingRippleController, IDreamManager dreamManager, Lazy<CameraLauncher> cameraLauncherLazy, - Lazy<LightRevealScrimViewModel> lightRevealScrimViewModelLazy) { + Lazy<LightRevealScrimViewModel> lightRevealScrimViewModelLazy, + AlternateBouncerInteractor alternateBouncerInteractor + ) { mContext = context; mNotificationsController = notificationsController; mFragmentService = fragmentService; @@ -841,6 +845,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { mWallpaperManager = wallpaperManager; mJankMonitor = jankMonitor; mCameraLauncherLazy = cameraLauncherLazy; + mAlternateBouncerInteractor = alternateBouncerInteractor; mLockscreenShadeTransitionController = lockscreenShadeTransitionController; mStartingSurfaceOptional = startingSurfaceOptional; @@ -3257,8 +3262,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { private void showBouncerOrLockScreenIfKeyguard() { // If the keyguard is animating away, we aren't really the keyguard anymore and should not // show the bouncer/lockscreen. - if (!mKeyguardViewMediator.isHiding() - && !mKeyguardUnlockAnimationController.isPlayingCannedUnlockAnimation()) { + if (!mKeyguardViewMediator.isHiding() && !mKeyguardUpdateMonitor.isKeyguardGoingAway()) { if (mState == StatusBarState.SHADE_LOCKED) { // shade is showing while locked on the keyguard, so go back to showing the // lock screen where users can use the UDFPS affordance to enter the device @@ -3737,7 +3741,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { boolean launchingAffordanceWithPreview = mLaunchingAffordance; mScrimController.setLaunchingAffordanceWithPreview(launchingAffordanceWithPreview); - if (mStatusBarKeyguardViewManager.isShowingAlternateBouncer()) { + if (mAlternateBouncerInteractor.isVisibleState()) { if (mState == StatusBarState.SHADE || mState == StatusBarState.SHADE_LOCKED || mTransitionToFullShadeProgress > 0f) { mScrimController.transitionTo(ScrimState.AUTH_SCRIMMED_SHADE); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java index d480fabe75b1..7d917bd9cd1c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java @@ -58,6 +58,7 @@ import com.android.systemui.dreams.DreamOverlayStateController; import com.android.systemui.flags.FeatureFlags; import com.android.systemui.flags.Flags; import com.android.systemui.keyguard.data.BouncerView; +import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor; import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerCallbackInteractor; import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerInteractor; import com.android.systemui.navigationbar.NavigationBarView; @@ -134,6 +135,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb private KeyguardMessageAreaController<AuthKeyguardMessageArea> mKeyguardMessageAreaController; private final PrimaryBouncerCallbackInteractor mPrimaryBouncerCallbackInteractor; private final PrimaryBouncerInteractor mPrimaryBouncerInteractor; + private final AlternateBouncerInteractor mAlternateBouncerInteractor; private final BouncerView mPrimaryBouncerView; private final Lazy<ShadeController> mShadeController; @@ -253,6 +255,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb final Set<KeyguardViewManagerCallback> mCallbacks = new HashSet<>(); private boolean mIsModernBouncerEnabled; private boolean mIsUnoccludeTransitionFlagEnabled; + private boolean mIsModernAlternateBouncerEnabled; private OnDismissAction mAfterKeyguardGoneAction; private Runnable mKeyguardGoneCancelAction; @@ -269,7 +272,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb private final LatencyTracker mLatencyTracker; private final KeyguardSecurityModel mKeyguardSecurityModel; @Nullable private KeyguardBypassController mBypassController; - @Nullable private AlternateBouncer mAlternateBouncer; + @Nullable private OccludingAppBiometricUI mOccludingAppBiometricUI; private final KeyguardUpdateMonitorCallback mUpdateMonitorCallback = new KeyguardUpdateMonitorCallback() { @@ -306,7 +309,8 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb FeatureFlags featureFlags, PrimaryBouncerCallbackInteractor primaryBouncerCallbackInteractor, PrimaryBouncerInteractor primaryBouncerInteractor, - BouncerView primaryBouncerView) { + BouncerView primaryBouncerView, + AlternateBouncerInteractor alternateBouncerInteractor) { mContext = context; mViewMediatorCallback = callback; mLockPatternUtils = lockPatternUtils; @@ -331,6 +335,8 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb .map(SysUIUnfoldComponent::getFoldAodAnimationController).orElse(null); mIsModernBouncerEnabled = featureFlags.isEnabled(Flags.MODERN_BOUNCER); mIsUnoccludeTransitionFlagEnabled = featureFlags.isEnabled(Flags.UNOCCLUSION_TRANSITION); + mIsModernAlternateBouncerEnabled = featureFlags.isEnabled(Flags.MODERN_ALTERNATE_BOUNCER); + mAlternateBouncerInteractor = alternateBouncerInteractor; } @Override @@ -363,23 +369,51 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb } /** - * Sets the given alt auth interceptor to null if it's the current auth interceptor. Else, + * Sets the given legacy alternate bouncer to null if it's the current alternate bouncer. Else, + * does nothing. Only used if modern alternate bouncer is NOT enabled. + */ + public void removeLegacyAlternateBouncer( + @NonNull LegacyAlternateBouncer alternateBouncerLegacy) { + if (!mIsModernAlternateBouncerEnabled) { + if (Objects.equals(mAlternateBouncerInteractor.getLegacyAlternateBouncer(), + alternateBouncerLegacy)) { + mAlternateBouncerInteractor.setLegacyAlternateBouncer(null); + hideAlternateBouncer(true); + } + } + } + + /** + * Sets a new legacy alternate bouncer. Only used if mdoern alternate bouncer is NOT enable. + */ + public void setLegacyAlternateBouncer(@NonNull LegacyAlternateBouncer alternateBouncerLegacy) { + if (!mIsModernAlternateBouncerEnabled) { + if (!Objects.equals(mAlternateBouncerInteractor.getLegacyAlternateBouncer(), + alternateBouncerLegacy)) { + mAlternateBouncerInteractor.setLegacyAlternateBouncer(alternateBouncerLegacy); + hideAlternateBouncer(false); + } + } + + } + + + /** + * Sets the given OccludingAppBiometricUI to null if it's the current auth interceptor. Else, * does nothing. */ - public void removeAlternateAuthInterceptor(@NonNull AlternateBouncer authInterceptor) { - if (Objects.equals(mAlternateBouncer, authInterceptor)) { - mAlternateBouncer = null; - hideAlternateBouncer(true); + public void removeOccludingAppBiometricUI(@NonNull OccludingAppBiometricUI biometricUI) { + if (Objects.equals(mOccludingAppBiometricUI, biometricUI)) { + mOccludingAppBiometricUI = null; } } /** - * Sets a new alt auth interceptor. + * Sets a new OccludingAppBiometricUI. */ - public void setAlternateBouncer(@NonNull AlternateBouncer authInterceptor) { - if (!Objects.equals(mAlternateBouncer, authInterceptor)) { - mAlternateBouncer = authInterceptor; - hideAlternateBouncer(false); + public void setOccludingAppBiometricUI(@NonNull OccludingAppBiometricUI biometricUI) { + if (!Objects.equals(mOccludingAppBiometricUI, biometricUI)) { + mOccludingAppBiometricUI = biometricUI; } } @@ -566,18 +600,11 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb * {@see KeyguardBouncer#show(boolean, boolean)} */ public void showBouncer(boolean scrimmed) { - if (canShowAlternateBouncer()) { - updateAlternateBouncerShowing(mAlternateBouncer.showAlternateBouncer()); - return; + if (!mAlternateBouncerInteractor.show()) { + showPrimaryBouncer(scrimmed); + } else { + updateAlternateBouncerShowing(mAlternateBouncerInteractor.isVisibleState()); } - - showPrimaryBouncer(scrimmed); - } - - /** Whether we can show the alternate bouncer instead of the primary bouncer. */ - public boolean canShowAlternateBouncer() { - return mAlternateBouncer != null - && mKeyguardUpdateManager.isUnlockingWithBiometricAllowed(true); } /** @@ -641,9 +668,9 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb mKeyguardGoneCancelAction = cancelAction; mDismissActionWillAnimateOnKeyguard = r != null && r.willRunAnimationOnKeyguard(); - // If there is an an alternate auth interceptor (like the UDFPS), show that one + // If there is an alternate auth interceptor (like the UDFPS), show that one // instead of the bouncer. - if (canShowAlternateBouncer()) { + if (mAlternateBouncerInteractor.canShowAlternateBouncerForFingerprint()) { if (!afterKeyguardGone) { if (mPrimaryBouncer != null) { mPrimaryBouncer.setDismissAction(mAfterKeyguardGoneAction, @@ -656,7 +683,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb mKeyguardGoneCancelAction = null; } - updateAlternateBouncerShowing(mAlternateBouncer.showAlternateBouncer()); + updateAlternateBouncerShowing(mAlternateBouncerInteractor.show()); return; } @@ -725,10 +752,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb @Override public void hideAlternateBouncer(boolean forceUpdateScrim) { - final boolean updateScrim = (mAlternateBouncer != null - && mAlternateBouncer.hideAlternateBouncer()) - || forceUpdateScrim; - updateAlternateBouncerShowing(updateScrim); + updateAlternateBouncerShowing(mAlternateBouncerInteractor.hide() || forceUpdateScrim); } private void updateAlternateBouncerShowing(boolean updateScrim) { @@ -738,7 +762,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb return; } - final boolean isShowingAlternateBouncer = isShowingAlternateBouncer(); + final boolean isShowingAlternateBouncer = mAlternateBouncerInteractor.isVisibleState(); if (mKeyguardMessageAreaController != null) { mKeyguardMessageAreaController.setIsVisible(isShowingAlternateBouncer); mKeyguardMessageAreaController.setMessage(""); @@ -1095,7 +1119,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb @Override public boolean isBouncerShowing() { - return primaryBouncerIsShowing() || isShowingAlternateBouncer(); + return primaryBouncerIsShowing() || mAlternateBouncerInteractor.isVisibleState(); } @Override @@ -1339,7 +1363,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb mPrimaryBouncerInteractor.notifyKeyguardAuthenticated(strongAuth); } - if (mAlternateBouncer != null && isShowingAlternateBouncer()) { + if (mAlternateBouncerInteractor.isVisibleState()) { hideAlternateBouncer(false); executeAfterKeyguardGoneAction(); } @@ -1347,7 +1371,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb /** Display security message to relevant KeyguardMessageArea. */ public void setKeyguardMessage(String message, ColorStateList colorState) { - if (isShowingAlternateBouncer()) { + if (mAlternateBouncerInteractor.isVisibleState()) { if (mKeyguardMessageAreaController != null) { mKeyguardMessageAreaController.setMessage(message); } @@ -1421,6 +1445,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb public void dump(PrintWriter pw) { pw.println("StatusBarKeyguardViewManager:"); + pw.println(" mIsModernAlternateBouncerEnabled: " + mIsModernAlternateBouncerEnabled); pw.println(" mRemoteInputActive: " + mRemoteInputActive); pw.println(" mDozing: " + mDozing); pw.println(" mAfterKeyguardGoneAction: " + mAfterKeyguardGoneAction); @@ -1438,9 +1463,9 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb mPrimaryBouncer.dump(pw); } - if (mAlternateBouncer != null) { - pw.println("AlternateBouncer:"); - mAlternateBouncer.dump(pw); + if (mOccludingAppBiometricUI != null) { + pw.println("mOccludingAppBiometricUI:"); + mOccludingAppBiometricUI.dump(pw); } } @@ -1492,14 +1517,17 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb return mPrimaryBouncer; } - public boolean isShowingAlternateBouncer() { - return mAlternateBouncer != null && mAlternateBouncer.isShowingAlternateBouncer(); - } - /** - * Forward touches to callbacks. + * For any touches on the NPVC, show the primary bouncer if the alternate bouncer is currently + * showing. */ public void onTouch(MotionEvent event) { + if (mAlternateBouncerInteractor.isVisibleState() + && mAlternateBouncerInteractor.hasAlternateBouncerShownWithMinTime()) { + showPrimaryBouncer(true); + } + + // Forward NPVC touches to callbacks in case they want to respond to touches for (KeyguardViewManagerCallback callback: mCallbacks) { callback.onTouch(event); } @@ -1542,8 +1570,8 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb */ public void requestFp(boolean request, int udfpsColor) { mKeyguardUpdateManager.requestFingerprintAuthOnOccludingApp(request); - if (mAlternateBouncer != null) { - mAlternateBouncer.requestUdfps(request, udfpsColor); + if (mOccludingAppBiometricUI != null) { + mOccludingAppBiometricUI.requestUdfps(request, udfpsColor); } } @@ -1614,10 +1642,9 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb } /** - * Delegate used to send show and hide events to an alternate authentication method instead of - * the regular pin/pattern/password bouncer. + * @Deprecated Delegate used to send show and hide events to an alternate bouncer. */ - public interface AlternateBouncer { + public interface LegacyAlternateBouncer { /** * Show alternate authentication bouncer. * @return whether alternate auth method was newly shown @@ -1634,7 +1661,13 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb * @return true if the alternate auth bouncer is showing */ boolean isShowingAlternateBouncer(); + } + /** + * Delegate used to send show and hide events to an alternate authentication method instead of + * the regular pin/pattern/password bouncer. + */ + public interface OccludingAppBiometricUI { /** * Use when an app occluding the keyguard would like to give the user ability to * unlock the device using udfps. diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java index 83bf1834989b..ace0ccb6a25b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java @@ -739,7 +739,7 @@ public class AuthControllerTest extends SysuiTestCase { public void testForwardsDozeEvents() throws RemoteException { when(mStatusBarStateController.isDozing()).thenReturn(true); when(mWakefulnessLifecycle.getWakefulness()).thenReturn(WAKEFULNESS_AWAKE); - mAuthController.setBiometicContextListener(mContextListener); + mAuthController.setBiometricContextListener(mContextListener); mStatusBarStateListenerCaptor.getValue().onDozingChanged(true); mStatusBarStateListenerCaptor.getValue().onDozingChanged(false); @@ -754,7 +754,7 @@ public class AuthControllerTest extends SysuiTestCase { public void testForwardsWakeEvents() throws RemoteException { when(mStatusBarStateController.isDozing()).thenReturn(false); when(mWakefulnessLifecycle.getWakefulness()).thenReturn(WAKEFULNESS_AWAKE); - mAuthController.setBiometicContextListener(mContextListener); + mAuthController.setBiometricContextListener(mContextListener); mWakefullnessObserverCaptor.getValue().onStartedGoingToSleep(); mWakefullnessObserverCaptor.getValue().onFinishedGoingToSleep(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt index 53bc2c231d0c..1ef119d7fb16 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt @@ -43,6 +43,7 @@ import com.android.systemui.SysuiTestCase import com.android.systemui.animation.ActivityLaunchAnimator import com.android.systemui.dump.DumpManager import com.android.systemui.flags.FeatureFlags +import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerInteractor import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.shade.ShadeExpansionStateManager @@ -52,7 +53,6 @@ import com.android.systemui.statusbar.phone.SystemUIDialogManager import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController import com.android.systemui.statusbar.policy.ConfigurationController import com.android.systemui.statusbar.policy.KeyguardStateController -import com.android.systemui.util.time.SystemClock import com.google.common.truth.Truth.assertThat import org.junit.Before import org.junit.Rule @@ -95,7 +95,6 @@ class UdfpsControllerOverlayTest : SysuiTestCase() { @Mock private lateinit var dumpManager: DumpManager @Mock private lateinit var transitionController: LockscreenShadeTransitionController @Mock private lateinit var configurationController: ConfigurationController - @Mock private lateinit var systemClock: SystemClock @Mock private lateinit var keyguardStateController: KeyguardStateController @Mock private lateinit var unlockedScreenOffAnimationController: UnlockedScreenOffAnimationController @@ -106,7 +105,8 @@ class UdfpsControllerOverlayTest : SysuiTestCase() { @Mock private lateinit var udfpsEnrollView: UdfpsEnrollView @Mock private lateinit var activityLaunchAnimator: ActivityLaunchAnimator @Mock private lateinit var featureFlags: FeatureFlags - @Mock private lateinit var mPrimaryBouncerInteractor: PrimaryBouncerInteractor + @Mock private lateinit var primaryBouncerInteractor: PrimaryBouncerInteractor + @Mock private lateinit var alternateBouncerInteractor: AlternateBouncerInteractor @Captor private lateinit var layoutParamsCaptor: ArgumentCaptor<WindowManager.LayoutParams> private val onTouch = { _: View, _: MotionEvent, _: Boolean -> true } @@ -138,10 +138,10 @@ class UdfpsControllerOverlayTest : SysuiTestCase() { context, fingerprintManager, inflater, windowManager, accessibilityManager, statusBarStateController, shadeExpansionStateManager, statusBarKeyguardViewManager, keyguardUpdateMonitor, dialogManager, dumpManager, transitionController, - configurationController, systemClock, keyguardStateController, + configurationController, keyguardStateController, unlockedScreenOffAnimationController, udfpsDisplayMode, REQUEST_ID, reason, controllerCallback, onTouch, activityLaunchAnimator, featureFlags, - mPrimaryBouncerInteractor, isDebuggable + primaryBouncerInteractor, alternateBouncerInteractor, isDebuggable, ) block() } diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java index b061eb395119..0c34e54d2ec4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java @@ -81,6 +81,7 @@ import com.android.systemui.dump.DumpManager; import com.android.systemui.flags.FeatureFlags; import com.android.systemui.flags.Flags; import com.android.systemui.keyguard.ScreenLifecycle; +import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor; import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerInteractor; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; @@ -202,6 +203,8 @@ public class UdfpsControllerTest extends SysuiTestCase { private PrimaryBouncerInteractor mPrimaryBouncerInteractor; @Mock private SinglePointerTouchProcessor mSinglePointerTouchProcessor; + @Mock + private AlternateBouncerInteractor mAlternateBouncerInteractor; // Capture listeners so that they can be used to send events @Captor @@ -292,7 +295,8 @@ public class UdfpsControllerTest extends SysuiTestCase { mDisplayManager, mHandler, mConfigurationController, mSystemClock, mUnlockedScreenOffAnimationController, mSystemUIDialogManager, mLatencyTracker, mActivityLaunchAnimator, alternateTouchProvider, mBiometricExecutor, - mPrimaryBouncerInteractor, mSinglePointerTouchProcessor); + mPrimaryBouncerInteractor, mSinglePointerTouchProcessor, + mAlternateBouncerInteractor); verify(mFingerprintManager).setUdfpsOverlayController(mOverlayCaptor.capture()); mOverlayController = mOverlayCaptor.getValue(); verify(mScreenLifecycle).addObserver(mScreenObserverCaptor.capture()); @@ -406,7 +410,7 @@ public class UdfpsControllerTest extends SysuiTestCase { // GIVEN overlay was showing and the udfps bouncer is showing mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId, BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); - when(mStatusBarKeyguardViewManager.isShowingAlternateBouncer()).thenReturn(true); + when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true); // WHEN the overlay is hidden mOverlayController.hideUdfpsOverlay(mOpticalProps.sensorId); diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerBaseTest.java index 3c61382d9446..9c32c38e665c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerBaseTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerBaseTest.java @@ -30,6 +30,7 @@ import com.android.systemui.dump.DumpManager; import com.android.systemui.flags.FakeFeatureFlags; import com.android.systemui.flags.Flags; import com.android.systemui.keyguard.KeyguardViewMediator; +import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor; import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerInteractor; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.shade.ShadeExpansionChangeEvent; @@ -43,7 +44,6 @@ import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.util.concurrency.DelayableExecutor; -import com.android.systemui.util.time.FakeSystemClock; import org.junit.Before; import org.mockito.ArgumentCaptor; @@ -73,9 +73,9 @@ public class UdfpsKeyguardViewControllerBaseTest extends SysuiTestCase { protected @Mock ActivityLaunchAnimator mActivityLaunchAnimator; protected @Mock KeyguardBouncer mBouncer; protected @Mock PrimaryBouncerInteractor mPrimaryBouncerInteractor; + protected @Mock AlternateBouncerInteractor mAlternateBouncerInteractor; protected FakeFeatureFlags mFeatureFlags = new FakeFeatureFlags(); - protected FakeSystemClock mSystemClock = new FakeSystemClock(); protected UdfpsKeyguardViewController mController; @@ -86,10 +86,6 @@ public class UdfpsKeyguardViewControllerBaseTest extends SysuiTestCase { private @Captor ArgumentCaptor<ShadeExpansionListener> mExpansionListenerCaptor; protected List<ShadeExpansionListener> mExpansionListeners; - private @Captor ArgumentCaptor<StatusBarKeyguardViewManager.AlternateBouncer> - mAlternateBouncerCaptor; - protected StatusBarKeyguardViewManager.AlternateBouncer mAlternateBouncer; - private @Captor ArgumentCaptor<KeyguardStateController.Callback> mKeyguardStateControllerCallbackCaptor; protected KeyguardStateController.Callback mKeyguardStateControllerCallback; @@ -135,12 +131,6 @@ public class UdfpsKeyguardViewControllerBaseTest extends SysuiTestCase { } } - protected void captureAltAuthInterceptor() { - verify(mStatusBarKeyguardViewManager).setAlternateBouncer( - mAlternateBouncerCaptor.capture()); - mAlternateBouncer = mAlternateBouncerCaptor.getValue(); - } - protected void captureKeyguardStateControllerCallback() { verify(mKeyguardStateController).addCallback( mKeyguardStateControllerCallbackCaptor.capture()); @@ -160,6 +150,7 @@ public class UdfpsKeyguardViewControllerBaseTest extends SysuiTestCase { protected UdfpsKeyguardViewController createUdfpsKeyguardViewController( boolean useModernBouncer, boolean useExpandedOverlay) { mFeatureFlags.set(Flags.MODERN_BOUNCER, useModernBouncer); + mFeatureFlags.set(Flags.MODERN_ALTERNATE_BOUNCER, useModernBouncer); mFeatureFlags.set(Flags.UDFPS_NEW_TOUCH_DETECTION, useExpandedOverlay); when(mStatusBarKeyguardViewManager.getPrimaryBouncer()).thenReturn( useModernBouncer ? null : mBouncer); @@ -172,14 +163,14 @@ public class UdfpsKeyguardViewControllerBaseTest extends SysuiTestCase { mDumpManager, mLockscreenShadeTransitionController, mConfigurationController, - mSystemClock, mKeyguardStateController, mUnlockedScreenOffAnimationController, mDialogManager, mUdfpsController, mActivityLaunchAnimator, mFeatureFlags, - mPrimaryBouncerInteractor); + mPrimaryBouncerInteractor, + mAlternateBouncerInteractor); return controller; } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java index babe5334e3eb..813eeeb557b8 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java @@ -19,18 +19,15 @@ package com.android.systemui.biometrics; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyBoolean; -import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.atLeast; import static org.mockito.Mockito.atLeastOnce; -import static org.mockito.Mockito.eq; import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.testing.AndroidTestingRunner; -import android.testing.TestableLooper.RunWithLooper; +import android.testing.TestableLooper; import android.view.MotionEvent; import androidx.test.filters.SmallTest; @@ -46,7 +43,8 @@ import org.mockito.Captor; @SmallTest @RunWith(AndroidTestingRunner.class) -@RunWithLooper + +@TestableLooper.RunWithLooper(setAsMainLooper = true) public class UdfpsKeyguardViewControllerTest extends UdfpsKeyguardViewControllerBaseTest { private @Captor ArgumentCaptor<KeyguardBouncer.PrimaryBouncerExpansionCallback> mBouncerExpansionCallbackCaptor; @@ -72,8 +70,6 @@ public class UdfpsKeyguardViewControllerTest extends UdfpsKeyguardViewController assertTrue(mController.shouldPauseAuth()); } - - @Test public void testRegistersExpansionChangedListenerOnAttached() { mController.onViewAttached(); @@ -237,85 +233,9 @@ public class UdfpsKeyguardViewControllerTest extends UdfpsKeyguardViewController public void testOverrideShouldPauseAuthOnShadeLocked() { mController.onViewAttached(); captureStatusBarStateListeners(); - captureAltAuthInterceptor(); sendStatusBarStateChanged(StatusBarState.SHADE_LOCKED); assertTrue(mController.shouldPauseAuth()); - - mAlternateBouncer.showAlternateBouncer(); // force show - assertFalse(mController.shouldPauseAuth()); - assertTrue(mAlternateBouncer.isShowingAlternateBouncer()); - - mAlternateBouncer.hideAlternateBouncer(); // stop force show - assertTrue(mController.shouldPauseAuth()); - assertFalse(mAlternateBouncer.isShowingAlternateBouncer()); - } - - @Test - public void testOnDetachedStateReset() { - // GIVEN view is attached - mController.onViewAttached(); - captureAltAuthInterceptor(); - - // WHEN view is detached - mController.onViewDetached(); - - // THEN remove alternate auth interceptor - verify(mStatusBarKeyguardViewManager).removeAlternateAuthInterceptor(mAlternateBouncer); - } - - @Test - public void testHiddenUdfpsBouncerOnTouchOutside_nothingHappens() { - // GIVEN view is attached - mController.onViewAttached(); - captureAltAuthInterceptor(); - - // GIVEN udfps bouncer isn't showing - mAlternateBouncer.hideAlternateBouncer(); - - // WHEN touch is observed outside the view - mController.onTouchOutsideView(); - - // THEN bouncer / alt auth methods are never called - verify(mStatusBarKeyguardViewManager, never()).showPrimaryBouncer(anyBoolean()); - verify(mStatusBarKeyguardViewManager, never()).showBouncer(anyBoolean()); - verify(mStatusBarKeyguardViewManager, never()).hideAlternateBouncer(anyBoolean()); - } - - @Test - public void testShowingUdfpsBouncerOnTouchOutsideWithinThreshold_nothingHappens() { - // GIVEN view is attached - mController.onViewAttached(); - captureAltAuthInterceptor(); - - // GIVEN udfps bouncer is showing - mAlternateBouncer.showAlternateBouncer(); - - // WHEN touch is observed outside the view 200ms later (just within threshold) - mSystemClock.advanceTime(200); - mController.onTouchOutsideView(); - - // THEN bouncer / alt auth methods are never called because not enough time has passed - verify(mStatusBarKeyguardViewManager, never()).showPrimaryBouncer(anyBoolean()); - verify(mStatusBarKeyguardViewManager, never()).showBouncer(anyBoolean()); - verify(mStatusBarKeyguardViewManager, never()).hideAlternateBouncer(anyBoolean()); - } - - @Test - public void testShowingUdfpsBouncerOnTouchOutsideAboveThreshold_showPrimaryBouncer() { - // GIVEN view is attached - mController.onViewAttached(); - captureAltAuthInterceptor(); - - // GIVEN udfps bouncer is showing - mAlternateBouncer.showAlternateBouncer(); - - // WHEN touch is observed outside the view 205ms later - mSystemClock.advanceTime(205); - mController.onTouchOutsideView(); - - // THEN show the bouncer - verify(mStatusBarKeyguardViewManager).showPrimaryBouncer(eq(true)); } @Test @@ -334,25 +254,6 @@ public class UdfpsKeyguardViewControllerTest extends UdfpsKeyguardViewController } @Test - public void testShowUdfpsBouncer() { - // GIVEN view is attached and status bar expansion is 0 - mController.onViewAttached(); - captureStatusBarExpansionListeners(); - captureKeyguardStateControllerCallback(); - captureAltAuthInterceptor(); - updateStatusBarExpansion(0, true); - reset(mView); - when(mView.getContext()).thenReturn(mResourceContext); - when(mResourceContext.getString(anyInt())).thenReturn("test string"); - - // WHEN status bar expansion is 0 but udfps bouncer is requested - mAlternateBouncer.showAlternateBouncer(); - - // THEN alpha is 255 - verify(mView).setUnpausedAlpha(255); - } - - @Test public void testTransitionToFullShadeProgress() { // GIVEN view is attached and status bar expansion is 1f mController.onViewAttached(); @@ -370,24 +271,6 @@ public class UdfpsKeyguardViewControllerTest extends UdfpsKeyguardViewController } @Test - public void testShowUdfpsBouncer_transitionToFullShadeProgress() { - // GIVEN view is attached and status bar expansion is 1f - mController.onViewAttached(); - captureStatusBarExpansionListeners(); - captureKeyguardStateControllerCallback(); - captureAltAuthInterceptor(); - updateStatusBarExpansion(1f, true); - mAlternateBouncer.showAlternateBouncer(); - reset(mView); - - // WHEN we're transitioning to the full shade - mController.setTransitionToFullShadeProgress(1.0f); - - // THEN alpha is 255 (b/c udfps bouncer is requested) - verify(mView).setUnpausedAlpha(255); - } - - @Test public void testUpdatePanelExpansion_pauseAuth() { // GIVEN view is attached + on the keyguard mController.onViewAttached(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerWithCoroutinesTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerWithCoroutinesTest.kt index 2d412dcaa909..3b4f7e10c806 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerWithCoroutinesTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerWithCoroutinesTest.kt @@ -21,26 +21,35 @@ import android.testing.AndroidTestingRunner import android.testing.TestableLooper import androidx.test.filters.SmallTest import com.android.keyguard.KeyguardSecurityModel +import com.android.keyguard.KeyguardUpdateMonitor import com.android.systemui.classifier.FalsingCollector +import com.android.systemui.flags.FeatureFlags import com.android.systemui.keyguard.DismissCallbackRegistry import com.android.systemui.keyguard.data.BouncerView +import com.android.systemui.keyguard.data.repository.BiometricRepository import com.android.systemui.keyguard.data.repository.KeyguardBouncerRepository +import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerCallbackInteractor import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerInteractor import com.android.systemui.log.table.TableLogBuffer import com.android.systemui.statusbar.StatusBarState import com.android.systemui.statusbar.phone.KeyguardBouncer import com.android.systemui.statusbar.phone.KeyguardBypassController +import com.android.systemui.util.time.FakeSystemClock +import com.android.systemui.util.time.SystemClock import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.runBlocking import kotlinx.coroutines.test.TestCoroutineScope import kotlinx.coroutines.yield +import org.junit.Assert.assertFalse import org.junit.Assert.assertTrue import org.junit.Before import org.junit.Test import org.junit.runner.RunWith +import org.mockito.ArgumentMatchers.any import org.mockito.Mock import org.mockito.Mockito.mock +import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @RunWith(AndroidTestingRunner::class) @@ -57,6 +66,7 @@ class UdfpsKeyguardViewControllerWithCoroutinesTest : UdfpsKeyguardViewControlle keyguardBouncerRepository = KeyguardBouncerRepository( mock(com.android.keyguard.ViewMediatorCallback::class.java), + FakeSystemClock(), TestCoroutineScope(), bouncerLogger, ) @@ -77,15 +87,43 @@ class UdfpsKeyguardViewControllerWithCoroutinesTest : UdfpsKeyguardViewControlle mock(KeyguardBypassController::class.java), mKeyguardUpdateMonitor ) + mAlternateBouncerInteractor = + AlternateBouncerInteractor( + keyguardBouncerRepository, + mock(BiometricRepository::class.java), + mock(SystemClock::class.java), + mock(KeyguardUpdateMonitor::class.java), + mock(FeatureFlags::class.java) + ) return createUdfpsKeyguardViewController( /* useModernBouncer */ true, /* useExpandedOverlay */ false ) } - /** After migration, replaces LockIconViewControllerTest version */ @Test - fun testShouldPauseAuthBouncerShowing() = + fun shadeLocked_showAlternateBouncer_unpauseAuth() = + runBlocking(IMMEDIATE) { + // GIVEN view is attached + on the SHADE_LOCKED (udfps view not showing) + mController.onViewAttached() + captureStatusBarStateListeners() + sendStatusBarStateChanged(StatusBarState.SHADE_LOCKED) + + // WHEN alternate bouncer is requested + val job = mController.listenForAlternateBouncerVisibility(this) + keyguardBouncerRepository.setAlternateVisible(true) + yield() + + // THEN udfps view will animate in & pause auth is updated to NOT pause + verify(mView).animateInUdfpsBouncer(any()) + assertFalse(mController.shouldPauseAuth()) + + job.cancel() + } + + /** After migration to MODERN_BOUNCER, replaces UdfpsKeyguardViewControllerTest version */ + @Test + fun shouldPauseAuthBouncerShowing() = runBlocking(IMMEDIATE) { // GIVEN view attached and we're on the keyguard mController.onViewAttached() diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsViewTest.kt index d550b927154c..8255a1452bd1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsViewTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsViewTest.kt @@ -80,15 +80,6 @@ class UdfpsViewTest : SysuiTestCase() { } @Test - fun forwardsEvents() { - view.dozeTimeTick() - verify(animationViewController).dozeTimeTick() - - view.onTouchOutsideView() - verify(animationViewController).onTouchOutsideView() - } - - @Test fun layoutSizeFitsSensor() { val params = withArgCaptor<RectF> { verify(animationViewController).onSensorRectUpdated(capture()) diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/BiometricRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/BiometricRepositoryTest.kt new file mode 100644 index 000000000000..a92dd3b92397 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/BiometricRepositoryTest.kt @@ -0,0 +1,176 @@ +/* + * 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.keyguard.data.repository + +import android.app.admin.DevicePolicyManager +import android.content.Intent +import android.content.pm.UserInfo +import android.testing.AndroidTestingRunner +import android.testing.TestableLooper +import androidx.test.filters.SmallTest +import com.android.internal.widget.LockPatternUtils +import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED +import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT +import com.android.systemui.SysuiTestCase +import com.android.systemui.biometrics.AuthController +import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.user.data.repository.FakeUserRepository +import com.android.systemui.util.mockito.argumentCaptor +import com.android.systemui.util.mockito.whenever +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.test.StandardTestDispatcher +import kotlinx.coroutines.test.TestDispatcher +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.ArgumentMatchers.any +import org.mockito.ArgumentMatchers.anyInt +import org.mockito.Mock +import org.mockito.Mockito.verify +import org.mockito.MockitoAnnotations + +@SmallTest +@TestableLooper.RunWithLooper(setAsMainLooper = true) +@RunWith(AndroidTestingRunner::class) +class BiometricRepositoryTest : SysuiTestCase() { + private lateinit var underTest: BiometricRepository + + @Mock private lateinit var authController: AuthController + @Mock private lateinit var lockPatternUtils: LockPatternUtils + @Mock private lateinit var devicePolicyManager: DevicePolicyManager + private lateinit var userRepository: FakeUserRepository + + private lateinit var testDispatcher: TestDispatcher + private lateinit var testScope: TestScope + private var testableLooper: TestableLooper? = null + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + testableLooper = TestableLooper.get(this) + testDispatcher = StandardTestDispatcher() + testScope = TestScope(testDispatcher) + userRepository = FakeUserRepository() + } + + private suspend fun createBiometricRepository() { + userRepository.setUserInfos(listOf(PRIMARY_USER)) + userRepository.setSelectedUserInfo(PRIMARY_USER) + underTest = + BiometricRepositoryImpl( + context = context, + lockPatternUtils = lockPatternUtils, + broadcastDispatcher = fakeBroadcastDispatcher, + authController = authController, + userRepository = userRepository, + devicePolicyManager = devicePolicyManager, + scope = testScope.backgroundScope, + backgroundDispatcher = testDispatcher, + looper = testableLooper!!.looper, + ) + } + + @Test + fun fingerprintEnrollmentChange() = + testScope.runTest { + createBiometricRepository() + val fingerprintEnabledByDevicePolicy = collectLastValue(underTest.isFingerprintEnrolled) + runCurrent() + + val captor = argumentCaptor<AuthController.Callback>() + verify(authController).addCallback(captor.capture()) + whenever(authController.isFingerprintEnrolled(anyInt())).thenReturn(true) + captor.value.onEnrollmentsChanged( + BiometricType.UNDER_DISPLAY_FINGERPRINT, + PRIMARY_USER_ID, + true + ) + assertThat(fingerprintEnabledByDevicePolicy()).isTrue() + + whenever(authController.isFingerprintEnrolled(anyInt())).thenReturn(false) + captor.value.onEnrollmentsChanged( + BiometricType.UNDER_DISPLAY_FINGERPRINT, + PRIMARY_USER_ID, + false + ) + assertThat(fingerprintEnabledByDevicePolicy()).isFalse() + } + + @Test + fun strongBiometricAllowedChange() = + testScope.runTest { + createBiometricRepository() + val strongBiometricAllowed = collectLastValue(underTest.isStrongBiometricAllowed) + runCurrent() + + val captor = argumentCaptor<LockPatternUtils.StrongAuthTracker>() + verify(lockPatternUtils).registerStrongAuthTracker(captor.capture()) + + captor.value + .getStub() + .onStrongAuthRequiredChanged(STRONG_AUTH_NOT_REQUIRED, PRIMARY_USER_ID) + testableLooper?.processAllMessages() // StrongAuthTracker uses the TestableLooper + assertThat(strongBiometricAllowed()).isTrue() + + captor.value + .getStub() + .onStrongAuthRequiredChanged(STRONG_AUTH_REQUIRED_AFTER_BOOT, PRIMARY_USER_ID) + testableLooper?.processAllMessages() // StrongAuthTracker uses the TestableLooper + assertThat(strongBiometricAllowed()).isFalse() + } + + @Test + fun fingerprintDisabledByDpmChange() = + testScope.runTest { + createBiometricRepository() + val fingerprintEnabledByDevicePolicy = + collectLastValue(underTest.isFingerprintEnabledByDevicePolicy) + runCurrent() + + whenever(devicePolicyManager.getKeyguardDisabledFeatures(any(), anyInt())) + .thenReturn(DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT) + broadcastDPMStateChange() + assertThat(fingerprintEnabledByDevicePolicy()).isFalse() + + whenever(devicePolicyManager.getKeyguardDisabledFeatures(any(), anyInt())).thenReturn(0) + broadcastDPMStateChange() + assertThat(fingerprintEnabledByDevicePolicy()).isTrue() + } + + private fun broadcastDPMStateChange() { + fakeBroadcastDispatcher.registeredReceivers.forEach { receiver -> + receiver.onReceive( + context, + Intent(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED) + ) + } + } + + companion object { + private const val PRIMARY_USER_ID = 0 + private val PRIMARY_USER = + UserInfo( + /* id= */ PRIMARY_USER_ID, + /* name= */ "primary user", + /* flags= */ UserInfo.FLAG_PRIMARY + ) + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardBouncerRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardBouncerRepositoryTest.kt index 9970a6796ed6..969537d23111 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardBouncerRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardBouncerRepositoryTest.kt @@ -20,6 +20,7 @@ import androidx.test.filters.SmallTest import com.android.keyguard.ViewMediatorCallback import com.android.systemui.SysuiTestCase import com.android.systemui.log.table.TableLogBuffer +import com.android.systemui.util.time.SystemClock import kotlinx.coroutines.runBlocking import kotlinx.coroutines.test.TestCoroutineScope import org.junit.Before @@ -34,6 +35,7 @@ import org.mockito.MockitoAnnotations @RunWith(JUnit4::class) class KeyguardBouncerRepositoryTest : SysuiTestCase() { + @Mock private lateinit var systemClock: SystemClock @Mock private lateinit var viewMediatorCallback: ViewMediatorCallback @Mock private lateinit var bouncerLogger: TableLogBuffer lateinit var underTest: KeyguardBouncerRepository @@ -43,7 +45,12 @@ class KeyguardBouncerRepositoryTest : SysuiTestCase() { MockitoAnnotations.initMocks(this) val testCoroutineScope = TestCoroutineScope() underTest = - KeyguardBouncerRepository(viewMediatorCallback, testCoroutineScope, bouncerLogger) + KeyguardBouncerRepository( + viewMediatorCallback, + systemClock, + testCoroutineScope, + bouncerLogger, + ) } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/AlternateBouncerInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/AlternateBouncerInteractorTest.kt new file mode 100644 index 000000000000..1da7241e58bd --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/AlternateBouncerInteractorTest.kt @@ -0,0 +1,156 @@ +/* + * 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.keyguard.domain.interactor + +import androidx.test.filters.SmallTest +import com.android.keyguard.KeyguardUpdateMonitor +import com.android.keyguard.ViewMediatorCallback +import com.android.systemui.SysuiTestCase +import com.android.systemui.flags.FakeFeatureFlags +import com.android.systemui.flags.Flags +import com.android.systemui.keyguard.data.repository.FakeBiometricRepository +import com.android.systemui.keyguard.data.repository.KeyguardBouncerRepository +import com.android.systemui.log.table.TableLogBuffer +import com.android.systemui.util.time.FakeSystemClock +import com.android.systemui.util.time.SystemClock +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.TestCoroutineScope +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 +import org.mockito.Mock +import org.mockito.Mockito.mock +import org.mockito.MockitoAnnotations + +@OptIn(ExperimentalCoroutinesApi::class) +@SmallTest +@RunWith(JUnit4::class) +class AlternateBouncerInteractorTest : SysuiTestCase() { + private lateinit var underTest: AlternateBouncerInteractor + private lateinit var bouncerRepository: KeyguardBouncerRepository + private lateinit var biometricRepository: FakeBiometricRepository + @Mock private lateinit var systemClock: SystemClock + @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor + @Mock private lateinit var bouncerLogger: TableLogBuffer + private lateinit var featureFlags: FakeFeatureFlags + + @Before + fun setup() { + MockitoAnnotations.initMocks(this) + bouncerRepository = + KeyguardBouncerRepository( + mock(ViewMediatorCallback::class.java), + FakeSystemClock(), + TestCoroutineScope(), + bouncerLogger, + ) + biometricRepository = FakeBiometricRepository() + featureFlags = FakeFeatureFlags().apply { this.set(Flags.MODERN_ALTERNATE_BOUNCER, true) } + underTest = + AlternateBouncerInteractor( + bouncerRepository, + biometricRepository, + systemClock, + keyguardUpdateMonitor, + featureFlags, + ) + } + + @Test + fun canShowAlternateBouncerForFingerprint_givenCanShow() { + givenCanShowAlternateBouncer() + assertTrue(underTest.canShowAlternateBouncerForFingerprint()) + } + + @Test + fun canShowAlternateBouncerForFingerprint_alternateBouncerUIUnavailable() { + givenCanShowAlternateBouncer() + bouncerRepository.setAlternateBouncerUIAvailable(false) + + assertFalse(underTest.canShowAlternateBouncerForFingerprint()) + } + + @Test + fun canShowAlternateBouncerForFingerprint_noFingerprintsEnrolled() { + givenCanShowAlternateBouncer() + biometricRepository.setFingerprintEnrolled(false) + + assertFalse(underTest.canShowAlternateBouncerForFingerprint()) + } + + @Test + fun canShowAlternateBouncerForFingerprint_strongBiometricNotAllowed() { + givenCanShowAlternateBouncer() + biometricRepository.setStrongBiometricAllowed(false) + + assertFalse(underTest.canShowAlternateBouncerForFingerprint()) + } + + @Test + fun canShowAlternateBouncerForFingerprint_devicePolicyDoesNotAllowFingerprint() { + givenCanShowAlternateBouncer() + biometricRepository.setFingerprintEnabledByDevicePolicy(false) + + assertFalse(underTest.canShowAlternateBouncerForFingerprint()) + } + + @Test + fun show_whenCanShow() { + givenCanShowAlternateBouncer() + + assertTrue(underTest.show()) + assertTrue(bouncerRepository.isAlternateBouncerVisible.value) + } + + @Test + fun show_whenCannotShow() { + givenCannotShowAlternateBouncer() + + assertFalse(underTest.show()) + assertFalse(bouncerRepository.isAlternateBouncerVisible.value) + } + + @Test + fun hide_wasPreviouslyShowing() { + bouncerRepository.setAlternateVisible(true) + + assertTrue(underTest.hide()) + assertFalse(bouncerRepository.isAlternateBouncerVisible.value) + } + + @Test + fun hide_wasNotPreviouslyShowing() { + bouncerRepository.setAlternateVisible(false) + + assertFalse(underTest.hide()) + assertFalse(bouncerRepository.isAlternateBouncerVisible.value) + } + + private fun givenCanShowAlternateBouncer() { + bouncerRepository.setAlternateBouncerUIAvailable(true) + biometricRepository.setFingerprintEnrolled(true) + biometricRepository.setStrongBiometricAllowed(true) + biometricRepository.setFingerprintEnabledByDevicePolicy(true) + } + + private fun givenCannotShowAlternateBouncer() { + biometricRepository.setFingerprintEnrolled(false) + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java index 896db4b43eb2..d0b42ae8ec56 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java @@ -103,6 +103,7 @@ import com.android.systemui.flags.FeatureFlags; import com.android.systemui.fragments.FragmentHostManager; import com.android.systemui.fragments.FragmentService; import com.android.systemui.keyguard.KeyguardUnlockAnimationController; +import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor; import com.android.systemui.keyguard.domain.interactor.KeyguardBottomAreaInteractor; import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor; import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel; @@ -300,6 +301,7 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase { @Mock private KeyguardTransitionInteractor mKeyguardTransitionInteractor; @Mock private CoroutineDispatcher mMainDispatcher; + @Mock private AlternateBouncerInteractor mAlternateBouncerInteractor; @Mock private MotionEvent mDownMotionEvent; @Captor private ArgumentCaptor<NotificationStackScrollLayout.OnEmptySpaceClickListener> @@ -516,6 +518,7 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase { systemClock, mKeyguardBottomAreaViewModel, mKeyguardBottomAreaInteractor, + mAlternateBouncerInteractor, mDreamingToLockscreenTransitionViewModel, mOccludedToLockscreenTransitionViewModel, mLockscreenToDreamingTransitionViewModel, diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt index d5e6463dd380..4c768253202a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt @@ -30,6 +30,7 @@ import com.android.systemui.classifier.FalsingCollectorFake import com.android.systemui.dock.DockManager import com.android.systemui.flags.FeatureFlags import com.android.systemui.keyguard.KeyguardUnlockAnimationController +import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor import com.android.systemui.keyguard.ui.viewmodel.KeyguardBouncerViewModel import com.android.systemui.shade.NotificationShadeWindowView.InteractionEventHandler @@ -98,6 +99,8 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() { private lateinit var pulsingGestureListener: PulsingGestureListener @Mock private lateinit var notificationInsetsController: NotificationInsetsController + @Mock + private lateinit var alternateBouncerInteractor: AlternateBouncerInteractor @Mock lateinit var keyguardBouncerComponentFactory: KeyguardBouncerComponent.Factory @Mock lateinit var keyguardBouncerContainer: ViewGroup @Mock lateinit var keyguardBouncerComponent: KeyguardBouncerComponent @@ -134,8 +137,9 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() { pulsingGestureListener, featureFlags, keyguardBouncerViewModel, - keyguardTransitionInteractor, keyguardBouncerComponentFactory, + alternateBouncerInteractor, + keyguardTransitionInteractor, ) underTest.setupExpandedStatusBar() diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.java index f1d8188612a6..d43562443d6e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.java @@ -40,6 +40,7 @@ import com.android.systemui.classifier.FalsingCollectorFake; import com.android.systemui.dock.DockManager; import com.android.systemui.flags.FeatureFlags; import com.android.systemui.keyguard.KeyguardUnlockAnimationController; +import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor; import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor; import com.android.systemui.keyguard.ui.viewmodel.KeyguardBouncerViewModel; import com.android.systemui.statusbar.DragDownHelper; @@ -94,6 +95,7 @@ public class NotificationShadeWindowViewTest extends SysuiTestCase { @Mock private KeyguardBouncerViewModel mKeyguardBouncerViewModel; @Mock private KeyguardBouncerComponent.Factory mKeyguardBouncerComponentFactory; @Mock private NotificationInsetsController mNotificationInsetsController; + @Mock private AlternateBouncerInteractor mAlternateBouncerInteractor; @Mock private KeyguardTransitionInteractor mKeyguardTransitionInteractor; @Captor private ArgumentCaptor<NotificationShadeWindowView.InteractionEventHandler> @@ -134,8 +136,9 @@ public class NotificationShadeWindowViewTest extends SysuiTestCase { mPulsingGestureListener, mFeatureFlags, mKeyguardBouncerViewModel, - mKeyguardTransitionInteractor, - mKeyguardBouncerComponentFactory + mKeyguardBouncerComponentFactory, + mAlternateBouncerInteractor, + mKeyguardTransitionInteractor ); mController.setupExpandedStatusBar(); mController.setDragDownHelper(mDragDownHelper); @@ -158,7 +161,7 @@ public class NotificationShadeWindowViewTest extends SysuiTestCase { // WHEN showing alt auth, not dozing, drag down helper doesn't want to intercept when(mStatusBarStateController.isDozing()).thenReturn(false); - when(mStatusBarKeyguardViewManager.isShowingAlternateBouncer()).thenReturn(true); + when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true); when(mDragDownHelper.onInterceptTouchEvent(any())).thenReturn(false); // THEN we should intercept touch @@ -171,7 +174,7 @@ public class NotificationShadeWindowViewTest extends SysuiTestCase { // WHEN not showing alt auth, not dozing, drag down helper doesn't want to intercept when(mStatusBarStateController.isDozing()).thenReturn(false); - when(mStatusBarKeyguardViewManager.isShowingAlternateBouncer()).thenReturn(false); + when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(false); when(mDragDownHelper.onInterceptTouchEvent(any())).thenReturn(false); // THEN we shouldn't intercept touch @@ -184,7 +187,7 @@ public class NotificationShadeWindowViewTest extends SysuiTestCase { // WHEN showing alt auth, not dozing, drag down helper doesn't want to intercept when(mStatusBarStateController.isDozing()).thenReturn(false); - when(mStatusBarKeyguardViewManager.isShowingAlternateBouncer()).thenReturn(true); + when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true); when(mDragDownHelper.onInterceptTouchEvent(any())).thenReturn(false); // THEN we should handle the touch 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 d2dd43308fcc..610bb13c6016 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java @@ -99,6 +99,7 @@ import com.android.systemui.dock.DockManager; import com.android.systemui.keyguard.KeyguardIndication; import com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController; import com.android.systemui.keyguard.ScreenLifecycle; +import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.phone.KeyguardBypassController; @@ -177,6 +178,8 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase { @Mock private FaceHelpMessageDeferral mFaceHelpMessageDeferral; @Mock + private AlternateBouncerInteractor mAlternateBouncerInteractor; + @Mock private ScreenLifecycle mScreenLifecycle; @Mock private AuthController mAuthController; @@ -273,7 +276,8 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase { mUserManager, mExecutor, mExecutor, mFalsingManager, mAuthController, mLockPatternUtils, mScreenLifecycle, mKeyguardBypassController, mAccessibilityManager, - mFaceHelpMessageDeferral, mock(KeyguardLogger.class)); + mFaceHelpMessageDeferral, mock(KeyguardLogger.class), + mAlternateBouncerInteractor); mController.init(); mController.setIndicationArea(mIndicationArea); verify(mStatusBarStateController).addCallback(mStatusBarStateListenerCaptor.capture()); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java index 09254ad0faf2..5a219451198a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java @@ -110,6 +110,7 @@ import com.android.systemui.keyguard.KeyguardUnlockAnimationController; import com.android.systemui.keyguard.KeyguardViewMediator; import com.android.systemui.keyguard.ScreenLifecycle; import com.android.systemui.keyguard.WakefulnessLifecycle; +import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor; import com.android.systemui.keyguard.ui.viewmodel.LightRevealScrimViewModel; import com.android.systemui.navigationbar.NavigationBarController; import com.android.systemui.plugins.ActivityStarter.OnDismissAction; @@ -177,8 +178,6 @@ import com.android.systemui.volume.VolumeComponent; import com.android.wm.shell.bubbles.Bubbles; import com.android.wm.shell.startingsurface.StartingSurface; -import dagger.Lazy; - import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -191,6 +190,8 @@ import java.io.ByteArrayOutputStream; import java.io.PrintWriter; import java.util.Optional; +import dagger.Lazy; + @SmallTest @RunWith(AndroidTestingRunner.class) @RunWithLooper(setAsMainLooper = true) @@ -297,6 +298,7 @@ public class CentralSurfacesImplTest extends SysuiTestCase { @Mock private WiredChargingRippleController mWiredChargingRippleController; @Mock private Lazy<CameraLauncher> mCameraLauncherLazy; @Mock private CameraLauncher mCameraLauncher; + @Mock private AlternateBouncerInteractor mAlternateBouncerInteractor; /** * The process of registering/unregistering a predictive back callback requires a * ViewRootImpl, which is present IRL, but may be missing during a Mockito unit test. @@ -504,7 +506,9 @@ public class CentralSurfacesImplTest extends SysuiTestCase { mWiredChargingRippleController, mDreamManager, mCameraLauncherLazy, - () -> mLightRevealScrimViewModel) { + () -> mLightRevealScrimViewModel, + mAlternateBouncerInteractor + ) { @Override protected ViewRootImpl getViewRootImpl() { return mViewRootImpl; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java index 14a319bc87e9..04a67006d686 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java @@ -56,6 +56,7 @@ import com.android.systemui.dreams.DreamOverlayStateController; import com.android.systemui.flags.FeatureFlags; import com.android.systemui.keyguard.data.BouncerView; import com.android.systemui.keyguard.data.BouncerViewDelegate; +import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor; import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerCallbackInteractor; import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerInteractor; import com.android.systemui.navigationbar.NavigationModeController; @@ -105,7 +106,6 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { @Mock private KeyguardBouncer.Factory mKeyguardBouncerFactory; @Mock private KeyguardMessageAreaController.Factory mKeyguardMessageAreaFactory; @Mock private KeyguardMessageAreaController mKeyguardMessageAreaController; - @Mock private StatusBarKeyguardViewManager.AlternateBouncer mAlternateBouncer; @Mock private KeyguardMessageArea mKeyguardMessageArea; @Mock private ShadeController mShadeController; @Mock private SysUIUnfoldComponent mSysUiUnfoldComponent; @@ -115,6 +115,7 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { @Mock private KeyguardSecurityModel mKeyguardSecurityModel; @Mock private PrimaryBouncerCallbackInteractor mPrimaryBouncerCallbackInteractor; @Mock private PrimaryBouncerInteractor mPrimaryBouncerInteractor; + @Mock private AlternateBouncerInteractor mAlternateBouncerInteractor; @Mock private BouncerView mBouncerView; @Mock private BouncerViewDelegate mBouncerViewDelegate; @@ -163,7 +164,8 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { mFeatureFlags, mPrimaryBouncerCallbackInteractor, mPrimaryBouncerInteractor, - mBouncerView) { + mBouncerView, + mAlternateBouncerInteractor) { @Override public ViewRootImpl getViewRootImpl() { return mViewRootImpl; @@ -434,37 +436,35 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { @Test public void testShowing_whenAlternateAuthShowing() { - mStatusBarKeyguardViewManager.setAlternateBouncer(mAlternateBouncer); when(mPrimaryBouncerInteractor.isFullyShowing()).thenReturn(false); - when(mAlternateBouncer.isShowingAlternateBouncer()).thenReturn(true); + when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true); assertTrue( - "Is showing not accurate when alternative auth showing", + "Is showing not accurate when alternative bouncer is visible", mStatusBarKeyguardViewManager.isBouncerShowing()); } @Test public void testWillBeShowing_whenAlternateAuthShowing() { - mStatusBarKeyguardViewManager.setAlternateBouncer(mAlternateBouncer); when(mPrimaryBouncerInteractor.isFullyShowing()).thenReturn(false); - when(mAlternateBouncer.isShowingAlternateBouncer()).thenReturn(true); + when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true); assertTrue( - "Is or will be showing not accurate when alternative auth showing", + "Is or will be showing not accurate when alternate bouncer is visible", mStatusBarKeyguardViewManager.primaryBouncerIsOrWillBeShowing()); } @Test - public void testHideAlternateBouncer_onShowBouncer() { - // GIVEN alt auth is showing - mStatusBarKeyguardViewManager.setAlternateBouncer(mAlternateBouncer); + public void testHideAlternateBouncer_onShowPrimaryBouncer() { + reset(mAlternateBouncerInteractor); + + // GIVEN alt bouncer is showing when(mPrimaryBouncerInteractor.isFullyShowing()).thenReturn(false); - when(mAlternateBouncer.isShowingAlternateBouncer()).thenReturn(true); - reset(mAlternateBouncer); + when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true); // WHEN showBouncer is called mStatusBarKeyguardViewManager.showPrimaryBouncer(true); // THEN alt bouncer should be hidden - verify(mAlternateBouncer).hideAlternateBouncer(); + verify(mAlternateBouncerInteractor).hide(); } @Test @@ -479,11 +479,9 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { @Test public void testShowAltAuth_unlockingWithBiometricNotAllowed() { - // GIVEN alt auth exists, unlocking with biometric isn't allowed - mStatusBarKeyguardViewManager.setAlternateBouncer(mAlternateBouncer); + // GIVEN cannot use alternate bouncer when(mPrimaryBouncerInteractor.isFullyShowing()).thenReturn(false); - when(mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed(anyBoolean())) - .thenReturn(false); + when(mAlternateBouncerInteractor.canShowAlternateBouncerForFingerprint()).thenReturn(false); // WHEN showGenericBouncer is called final boolean scrimmed = true; @@ -491,21 +489,19 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { // THEN regular bouncer is shown verify(mPrimaryBouncerInteractor).show(eq(scrimmed)); - verify(mAlternateBouncer, never()).showAlternateBouncer(); } @Test public void testShowAlternateBouncer_unlockingWithBiometricAllowed() { - // GIVEN alt auth exists, unlocking with biometric is allowed - mStatusBarKeyguardViewManager.setAlternateBouncer(mAlternateBouncer); + // GIVEN will show alternate bouncer when(mPrimaryBouncerInteractor.isFullyShowing()).thenReturn(false); - when(mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(true); + when(mAlternateBouncerInteractor.show()).thenReturn(true); // WHEN showGenericBouncer is called mStatusBarKeyguardViewManager.showBouncer(true); // THEN alt auth bouncer is shown - verify(mAlternateBouncer).showAlternateBouncer(); + verify(mAlternateBouncerInteractor).show(); verify(mPrimaryBouncerInteractor, never()).show(anyBoolean()); } @@ -613,7 +609,8 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { mFeatureFlags, mPrimaryBouncerCallbackInteractor, mPrimaryBouncerInteractor, - mBouncerView) { + mBouncerView, + mAlternateBouncerInteractor) { @Override public ViewRootImpl getViewRootImpl() { return mViewRootImpl; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest_Old.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest_Old.java index 96fba39d6b59..a9c55fa06cc5 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest_Old.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest_Old.java @@ -56,6 +56,7 @@ import com.android.systemui.dreams.DreamOverlayStateController; import com.android.systemui.flags.FeatureFlags; import com.android.systemui.keyguard.data.BouncerView; import com.android.systemui.keyguard.data.BouncerViewDelegate; +import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor; import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerCallbackInteractor; import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerInteractor; import com.android.systemui.navigationbar.NavigationModeController; @@ -109,7 +110,6 @@ public class StatusBarKeyguardViewManagerTest_Old extends SysuiTestCase { @Mock private KeyguardMessageAreaController.Factory mKeyguardMessageAreaFactory; @Mock private KeyguardMessageAreaController mKeyguardMessageAreaController; @Mock private KeyguardBouncer mPrimaryBouncer; - @Mock private StatusBarKeyguardViewManager.AlternateBouncer mAlternateBouncer; @Mock private KeyguardMessageArea mKeyguardMessageArea; @Mock private ShadeController mShadeController; @Mock private SysUIUnfoldComponent mSysUiUnfoldComponent; @@ -119,6 +119,7 @@ public class StatusBarKeyguardViewManagerTest_Old extends SysuiTestCase { @Mock private KeyguardSecurityModel mKeyguardSecurityModel; @Mock private PrimaryBouncerCallbackInteractor mPrimaryBouncerCallbackInteractor; @Mock private PrimaryBouncerInteractor mPrimaryBouncerInteractor; + @Mock private AlternateBouncerInteractor mAlternateBouncerInteractor; @Mock private BouncerView mBouncerView; @Mock private BouncerViewDelegate mBouncerViewDelegate; @@ -169,7 +170,8 @@ public class StatusBarKeyguardViewManagerTest_Old extends SysuiTestCase { mFeatureFlags, mPrimaryBouncerCallbackInteractor, mPrimaryBouncerInteractor, - mBouncerView) { + mBouncerView, + mAlternateBouncerInteractor) { @Override public ViewRootImpl getViewRootImpl() { return mViewRootImpl; @@ -439,41 +441,6 @@ public class StatusBarKeyguardViewManagerTest_Old extends SysuiTestCase { } @Test - public void testShowing_whenAlternateAuthShowing() { - mStatusBarKeyguardViewManager.setAlternateBouncer(mAlternateBouncer); - when(mPrimaryBouncer.isShowing()).thenReturn(false); - when(mAlternateBouncer.isShowingAlternateBouncer()).thenReturn(true); - assertTrue( - "Is showing not accurate when alternative auth showing", - mStatusBarKeyguardViewManager.isBouncerShowing()); - } - - @Test - public void testWillBeShowing_whenAlternateAuthShowing() { - mStatusBarKeyguardViewManager.setAlternateBouncer(mAlternateBouncer); - when(mPrimaryBouncer.isShowing()).thenReturn(false); - when(mAlternateBouncer.isShowingAlternateBouncer()).thenReturn(true); - assertTrue( - "Is or will be showing not accurate when alternative auth showing", - mStatusBarKeyguardViewManager.primaryBouncerIsOrWillBeShowing()); - } - - @Test - public void testHideAlternateBouncer_onShowBouncer() { - // GIVEN alt auth is showing - mStatusBarKeyguardViewManager.setAlternateBouncer(mAlternateBouncer); - when(mPrimaryBouncer.isShowing()).thenReturn(false); - when(mAlternateBouncer.isShowingAlternateBouncer()).thenReturn(true); - reset(mAlternateBouncer); - - // WHEN showBouncer is called - mStatusBarKeyguardViewManager.showPrimaryBouncer(true); - - // THEN alt bouncer should be hidden - verify(mAlternateBouncer).hideAlternateBouncer(); - } - - @Test public void testBouncerIsOrWillBeShowing_whenBouncerIsInTransit() { when(mPrimaryBouncer.isShowing()).thenReturn(false); when(mPrimaryBouncer.inTransit()).thenReturn(true); @@ -484,38 +451,6 @@ public class StatusBarKeyguardViewManagerTest_Old extends SysuiTestCase { } @Test - public void testShowAltAuth_unlockingWithBiometricNotAllowed() { - // GIVEN alt auth exists, unlocking with biometric isn't allowed - mStatusBarKeyguardViewManager.setAlternateBouncer(mAlternateBouncer); - when(mPrimaryBouncer.isShowing()).thenReturn(false); - when(mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed(anyBoolean())) - .thenReturn(false); - - // WHEN showGenericBouncer is called - final boolean scrimmed = true; - mStatusBarKeyguardViewManager.showBouncer(scrimmed); - - // THEN regular bouncer is shown - verify(mPrimaryBouncer).show(anyBoolean(), eq(scrimmed)); - verify(mAlternateBouncer, never()).showAlternateBouncer(); - } - - @Test - public void testShowAlternateBouncer_unlockingWithBiometricAllowed() { - // GIVEN alt auth exists, unlocking with biometric is allowed - mStatusBarKeyguardViewManager.setAlternateBouncer(mAlternateBouncer); - when(mPrimaryBouncer.isShowing()).thenReturn(false); - when(mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(true); - - // WHEN showGenericBouncer is called - mStatusBarKeyguardViewManager.showBouncer(true); - - // THEN alt auth bouncer is shown - verify(mAlternateBouncer).showAlternateBouncer(); - verify(mPrimaryBouncer, never()).show(anyBoolean(), anyBoolean()); - } - - @Test public void testUpdateResources_delegatesToBouncer() { mStatusBarKeyguardViewManager.updateResources(); @@ -628,7 +563,8 @@ public class StatusBarKeyguardViewManagerTest_Old extends SysuiTestCase { mFeatureFlags, mPrimaryBouncerCallbackInteractor, mPrimaryBouncerInteractor, - mBouncerView) { + mBouncerView, + mAlternateBouncerInteractor) { @Override public ViewRootImpl getViewRootImpl() { return mViewRootImpl; diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeBiometricRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeBiometricRepository.kt new file mode 100644 index 000000000000..f3e52de0d7a0 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeBiometricRepository.kt @@ -0,0 +1,47 @@ +/* + * 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.keyguard.data.repository + +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow + +class FakeBiometricRepository : BiometricRepository { + + private val _isFingerprintEnrolled = MutableStateFlow<Boolean>(false) + override val isFingerprintEnrolled: StateFlow<Boolean> = _isFingerprintEnrolled.asStateFlow() + + private val _isStrongBiometricAllowed = MutableStateFlow(false) + override val isStrongBiometricAllowed = _isStrongBiometricAllowed.asStateFlow() + + private val _isFingerprintEnabledByDevicePolicy = MutableStateFlow(false) + override val isFingerprintEnabledByDevicePolicy = + _isFingerprintEnabledByDevicePolicy.asStateFlow() + + fun setFingerprintEnrolled(isFingerprintEnrolled: Boolean) { + _isFingerprintEnrolled.value = isFingerprintEnrolled + } + + fun setStrongBiometricAllowed(isStrongBiometricAllowed: Boolean) { + _isStrongBiometricAllowed.value = isStrongBiometricAllowed + } + + fun setFingerprintEnabledByDevicePolicy(isFingerprintEnabledByDevicePolicy: Boolean) { + _isFingerprintEnabledByDevicePolicy.value = isFingerprintEnabledByDevicePolicy + } +} |