Fix BP disappear when devices change to unfold mode from fold mode

1. Remove the logic that close BP when losing window focus.
2. Monitot notification shade status and close BP when users interact
   with notification shade

Bug: 259622034
Test: 1. Show BP in fold mode, check if BP still exists in Unfold mode
      2. Show BP and rotate device, check is BP still exists
      3. atest AuthContainerViewTest
         atest AuthControllerTest
         atest biometrics-simple-test
Change-Id: Id9e643dcf166eef35250ea625cd382a0687a84ad
This commit is contained in:
Vincent Wang
2022-12-21 15:47:37 +00:00
parent 3d6dffb3a0
commit d3301fdc27
6 changed files with 85 additions and 85 deletions

View File

@@ -120,6 +120,7 @@ public class AuthContainerView extends LinearLayout
private final Interpolator mLinearOutSlowIn;
private final LockPatternUtils mLockPatternUtils;
private final WakefulnessLifecycle mWakefulnessLifecycle;
private final AuthDialogPanelInteractionDetector mPanelInteractionDetector;
private final InteractionJankMonitor mInteractionJankMonitor;
// TODO: these should be migrated out once ready
@@ -141,7 +142,6 @@ public class AuthContainerView extends LinearLayout
private final OnBackInvokedCallback mBackCallback = this::onBackInvoked;
private final @Background DelayableExecutor mBackgroundExecutor;
private boolean mIsOrientationChanged = false;
// Non-null only if the dialog is in the act of dismissing and has not sent the reason yet.
@Nullable @AuthDialogCallback.DismissedReason private Integer mPendingCallbackReason;
@@ -235,6 +235,7 @@ public class AuthContainerView extends LinearLayout
@Nullable List<FingerprintSensorPropertiesInternal> fpProps,
@Nullable List<FaceSensorPropertiesInternal> faceProps,
@NonNull WakefulnessLifecycle wakefulnessLifecycle,
@NonNull AuthDialogPanelInteractionDetector panelInteractionDetector,
@NonNull UserManager userManager,
@NonNull LockPatternUtils lockPatternUtils,
@NonNull InteractionJankMonitor jankMonitor,
@@ -242,8 +243,9 @@ public class AuthContainerView extends LinearLayout
@NonNull Provider<CredentialViewModel> credentialViewModelProvider) {
mConfig.mSensorIds = sensorIds;
return new AuthContainerView(mConfig, fpProps, faceProps, wakefulnessLifecycle,
userManager, lockPatternUtils, jankMonitor, biometricPromptInteractor,
credentialViewModelProvider, new Handler(Looper.getMainLooper()), bgExecutor);
panelInteractionDetector, userManager, lockPatternUtils, jankMonitor,
biometricPromptInteractor, credentialViewModelProvider,
new Handler(Looper.getMainLooper()), bgExecutor);
}
}
@@ -331,6 +333,7 @@ public class AuthContainerView extends LinearLayout
@Nullable List<FingerprintSensorPropertiesInternal> fpProps,
@Nullable List<FaceSensorPropertiesInternal> faceProps,
@NonNull WakefulnessLifecycle wakefulnessLifecycle,
@NonNull AuthDialogPanelInteractionDetector panelInteractionDetector,
@NonNull UserManager userManager,
@NonNull LockPatternUtils lockPatternUtils,
@NonNull InteractionJankMonitor jankMonitor,
@@ -346,6 +349,7 @@ public class AuthContainerView extends LinearLayout
mHandler = mainHandler;
mWindowManager = mContext.getSystemService(WindowManager.class);
mWakefulnessLifecycle = wakefulnessLifecycle;
mPanelInteractionDetector = panelInteractionDetector;
mTranslationY = getResources()
.getDimension(R.dimen.biometric_dialog_animation_translation_offset);
@@ -490,22 +494,6 @@ public class AuthContainerView extends LinearLayout
@Override
public void onOrientationChanged() {
maybeUpdatePositionForUdfps(true /* invalidate */);
mIsOrientationChanged = true;
}
@Override
public void onWindowFocusChanged(boolean hasWindowFocus) {
super.onWindowFocusChanged(hasWindowFocus);
if (!hasWindowFocus) {
//it's a workaround to avoid closing BP incorrectly
//BP gets a onWindowFocusChanged(false) and then gets a onWindowFocusChanged(true)
if (mIsOrientationChanged) {
mIsOrientationChanged = false;
return;
}
Log.v(TAG, "Lost window focus, dismissing the dialog");
animateAway(AuthDialogCallback.DISMISSED_USER_CANCELED);
}
}
@Override
@@ -513,6 +501,8 @@ public class AuthContainerView extends LinearLayout
super.onAttachedToWindow();
mWakefulnessLifecycle.addObserver(this);
mPanelInteractionDetector.enable(
() -> animateAway(AuthDialogCallback.DISMISSED_USER_CANCELED));
if (Utils.isBiometricAllowed(mConfig.mPromptInfo)) {
mBiometricScrollView.addView(mBiometricView);
@@ -666,11 +656,6 @@ public class AuthContainerView extends LinearLayout
mBiometricView.restoreState(savedState);
}
if (savedState != null) {
mIsOrientationChanged = savedState.getBoolean(
AuthDialog.KEY_BIOMETRIC_ORIENTATION_CHANGED);
}
wm.addView(this, getLayoutParams(mWindowToken, mConfig.mPromptInfo.getTitle()));
}
@@ -689,6 +674,7 @@ public class AuthContainerView extends LinearLayout
@Override
public void dismissWithoutCallback(boolean animate) {
mPanelInteractionDetector.disable();
if (animate) {
animateAway(false /* sendReason */, 0 /* reason */);
} else {
@@ -699,6 +685,7 @@ public class AuthContainerView extends LinearLayout
@Override
public void dismissFromSystemServer() {
mPanelInteractionDetector.disable();
animateAway(false /* sendReason */, 0 /* reason */);
}
@@ -761,8 +748,6 @@ public class AuthContainerView extends LinearLayout
mBiometricView != null && mCredentialView == null);
outState.putBoolean(AuthDialog.KEY_CREDENTIAL_SHOWING, mCredentialView != null);
outState.putBoolean(AuthDialog.KEY_BIOMETRIC_ORIENTATION_CHANGED, mIsOrientationChanged);
if (mBiometricView != null) {
mBiometricView.onSaveState(outState);
}

View File

@@ -164,6 +164,7 @@ public class AuthController implements CoreStartable, CommandQueue.Callbacks,
@NonNull private final SparseBooleanArray mSfpsEnrolledForUser;
@NonNull private final SensorPrivacyManager mSensorPrivacyManager;
private final WakefulnessLifecycle mWakefulnessLifecycle;
private final AuthDialogPanelInteractionDetector mPanelInteractionDetector;
private boolean mAllFingerprintAuthenticatorsRegistered;
@NonNull private final UserManager mUserManager;
@NonNull private final LockPatternUtils mLockPatternUtils;
@@ -721,6 +722,7 @@ public class AuthController implements CoreStartable, CommandQueue.Callbacks,
Provider<SideFpsController> sidefpsControllerFactory,
@NonNull DisplayManager displayManager,
@NonNull WakefulnessLifecycle wakefulnessLifecycle,
@NonNull AuthDialogPanelInteractionDetector panelInteractionDetector,
@NonNull UserManager userManager,
@NonNull LockPatternUtils lockPatternUtils,
@NonNull UdfpsLogger udfpsLogger,
@@ -767,6 +769,8 @@ public class AuthController implements CoreStartable, CommandQueue.Callbacks,
});
mWakefulnessLifecycle = wakefulnessLifecycle;
mPanelInteractionDetector = panelInteractionDetector;
mFaceProps = mFaceManager != null ? mFaceManager.getSensorPropertiesInternal() : null;
int[] faceAuthLocation = context.getResources().getIntArray(
@@ -1149,6 +1153,7 @@ public class AuthController implements CoreStartable, CommandQueue.Callbacks,
requestId,
multiSensorConfig,
mWakefulnessLifecycle,
mPanelInteractionDetector,
mUserManager,
mLockPatternUtils);
@@ -1239,6 +1244,7 @@ public class AuthController implements CoreStartable, CommandQueue.Callbacks,
String opPackageName, boolean skipIntro, long operationId, long requestId,
@BiometricMultiSensorMode int multiSensorConfig,
@NonNull WakefulnessLifecycle wakefulnessLifecycle,
@NonNull AuthDialogPanelInteractionDetector panelInteractionDetector,
@NonNull UserManager userManager,
@NonNull LockPatternUtils lockPatternUtils) {
return new AuthContainerView.Builder(mContext)
@@ -1253,8 +1259,9 @@ public class AuthController implements CoreStartable, CommandQueue.Callbacks,
.setMultiSensorConfig(multiSensorConfig)
.setScaleFactorProvider(() -> getScaleFactor())
.build(bgExecutor, sensorIds, mFpProps, mFaceProps, wakefulnessLifecycle,
userManager, lockPatternUtils, mInteractionJankMonitor,
mBiometricPromptInteractor, mCredentialViewModelProvider);
panelInteractionDetector, userManager, lockPatternUtils,
mInteractionJankMonitor, mBiometricPromptInteractor,
mCredentialViewModelProvider);
}
@Override

View File

@@ -48,8 +48,6 @@ public interface AuthDialog extends Dumpable {
String KEY_BIOMETRIC_SENSOR_TYPE = "sensor_type";
String KEY_BIOMETRIC_SENSOR_PROPS = "sensor_props";
String KEY_BIOMETRIC_ORIENTATION_CHANGED = "orientation_changed";
int SIZE_UNKNOWN = 0;
/**
* Minimal UI, showing only biometric icon.

View File

@@ -0,0 +1,53 @@
package com.android.systemui.biometrics
import android.annotation.AnyThread
import android.annotation.MainThread
import android.util.Log
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.shade.ShadeExpansionChangeEvent
import com.android.systemui.shade.ShadeExpansionStateManager
import java.util.concurrent.Executor
import javax.inject.Inject
class AuthDialogPanelInteractionDetector
@Inject
constructor(
private val shadeExpansionStateManager: ShadeExpansionStateManager,
@Main private val mainExecutor: Executor,
) {
private var action: Action? = null
@MainThread
fun enable(onPanelInteraction: Runnable) {
if (action == null) {
action = Action(onPanelInteraction)
shadeExpansionStateManager.addExpansionListener(this::onPanelExpansionChanged)
} else {
Log.e(TAG, "Already enabled")
}
}
@MainThread
fun disable() {
if (action != null) {
action = null
shadeExpansionStateManager.removeExpansionListener(this::onPanelExpansionChanged)
}
}
@AnyThread
private fun onPanelExpansionChanged(event: ShadeExpansionChangeEvent) =
mainExecutor.execute {
action?.let {
if (event.tracking) {
Log.v(TAG, "Detected panel interaction, event: $event")
it.onPanelInteraction.run()
disable()
}
}
}
}
private data class Action(val onPanelInteraction: Runnable)
private const val TAG = "AuthDialogPanelInteractionDetector"

View File

@@ -35,8 +35,6 @@ import android.view.WindowInsets
import android.view.WindowManager
import android.widget.ScrollView
import androidx.test.filters.SmallTest
import com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn
import com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn
import com.android.internal.jank.InteractionJankMonitor
import com.android.internal.widget.LockPatternUtils
import com.android.systemui.R
@@ -81,6 +79,8 @@ class AuthContainerViewTest : SysuiTestCase() {
@Mock
lateinit var wakefulnessLifecycle: WakefulnessLifecycle
@Mock
lateinit var panelInteractionDetector: AuthDialogPanelInteractionDetector
@Mock
lateinit var windowToken: IBinder
@Mock
lateinit var interactionJankMonitor: InteractionJankMonitor
@@ -169,26 +169,6 @@ class AuthContainerViewTest : SysuiTestCase() {
verify(callback, times(2)).onDialogAnimatedIn(authContainer?.requestId ?: 0L)
}
@Test
fun testDismissesOnFocusLoss() {
val container = initializeFingerprintContainer()
waitForIdleSync()
val requestID = authContainer?.requestId ?: 0L
verify(callback).onDialogAnimatedIn(requestID)
container.onWindowFocusChanged(false)
waitForIdleSync()
verify(callback).onDismissed(
eq(AuthDialogCallback.DISMISSED_USER_CANCELED),
eq<ByteArray?>(null), /* credentialAttestation */
eq(requestID)
)
assertThat(container.parent).isNull()
}
@Test
fun testFocusLossAfterRotating() {
val container = initializeFingerprintContainer()
@@ -208,35 +188,6 @@ class AuthContainerViewTest : SysuiTestCase() {
)
}
@Test
fun testDismissesOnFocusLoss_hidesKeyboardWhenVisible() {
val container = initializeFingerprintContainer(
authenticators = BiometricManager.Authenticators.DEVICE_CREDENTIAL
)
waitForIdleSync()
val requestID = authContainer?.requestId ?: 0L
// Simulate keyboard was shown on the credential view
val windowInsetsController = container.windowInsetsController
spyOn(windowInsetsController)
spyOn(container.rootWindowInsets)
doReturn(true).`when`(container.rootWindowInsets).isVisible(WindowInsets.Type.ime())
container.onWindowFocusChanged(false)
waitForIdleSync()
// Expect hiding IME request will be invoked when dismissing the view
verify(windowInsetsController)?.hide(WindowInsets.Type.ime())
verify(callback).onDismissed(
eq(AuthDialogCallback.DISMISSED_USER_CANCELED),
eq<ByteArray?>(null), /* credentialAttestation */
eq(requestID)
)
assertThat(container.parent).isNull()
}
@Test
fun testActionAuthenticated_sendsDismissedAuthenticated() {
val container = initializeFingerprintContainer()
@@ -519,6 +470,7 @@ class AuthContainerViewTest : SysuiTestCase() {
fingerprintProps,
faceProps,
wakefulnessLifecycle,
panelInteractionDetector,
userManager,
lockPatternUtils,
interactionJankMonitor,

View File

@@ -152,6 +152,8 @@ public class AuthControllerTest extends SysuiTestCase {
@Mock
private WakefulnessLifecycle mWakefulnessLifecycle;
@Mock
private AuthDialogPanelInteractionDetector mPanelInteractionDetector;
@Mock
private UserManager mUserManager;
@Mock
private LockPatternUtils mLockPatternUtils;
@@ -953,9 +955,10 @@ public class AuthControllerTest extends SysuiTestCase {
super(context, mExecution, mCommandQueue, mActivityTaskManager, mWindowManager,
mFingerprintManager, mFaceManager, () -> mUdfpsController,
() -> mSideFpsController, mDisplayManager, mWakefulnessLifecycle,
mUserManager, mLockPatternUtils, mUdfpsLogger, mLogContextInteractor,
() -> mBiometricPromptCredentialInteractor, () -> mCredentialViewModel,
mInteractionJankMonitor, mHandler, mBackgroundExecutor, mVibratorHelper);
mPanelInteractionDetector, mUserManager, mLockPatternUtils, mUdfpsLogger,
mLogContextInteractor, () -> mBiometricPromptCredentialInteractor,
() -> mCredentialViewModel, mInteractionJankMonitor, mHandler,
mBackgroundExecutor, mVibratorHelper);
}
@Override
@@ -963,7 +966,9 @@ public class AuthControllerTest extends SysuiTestCase {
boolean requireConfirmation, int userId, int[] sensorIds,
String opPackageName, boolean skipIntro, long operationId, long requestId,
@BiometricManager.BiometricMultiSensorMode int multiSensorConfig,
WakefulnessLifecycle wakefulnessLifecycle, UserManager userManager,
WakefulnessLifecycle wakefulnessLifecycle,
AuthDialogPanelInteractionDetector panelInteractionDetector,
UserManager userManager,
LockPatternUtils lockPatternUtils) {
mLastBiometricPromptInfo = promptInfo;