Merge "[DO NOT MERGE]Fix AuthCredentialPatternView#onErrorTimeoutFinish() bug" into tm-qpr-dev

This commit is contained in:
Bill Lin
2022-11-02 04:51:25 +00:00
committed by Android (Google) Code Review
4 changed files with 169 additions and 6 deletions

View File

@@ -120,7 +120,7 @@ public class AuthContainerView extends LinearLayout
@VisibleForTesting final BiometricCallback mBiometricCallback; @VisibleForTesting final BiometricCallback mBiometricCallback;
@Nullable private AuthBiometricView mBiometricView; @Nullable private AuthBiometricView mBiometricView;
@Nullable private AuthCredentialView mCredentialView; @VisibleForTesting @Nullable AuthCredentialView mCredentialView;
private final AuthPanelController mPanelController; private final AuthPanelController mPanelController;
private final FrameLayout mFrameLayout; private final FrameLayout mFrameLayout;
private final ImageView mBackgroundView; private final ImageView mBackgroundView;

View File

@@ -20,6 +20,7 @@ import android.annotation.NonNull;
import android.content.Context; import android.content.Context;
import android.util.AttributeSet; import android.util.AttributeSet;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.widget.LockPatternChecker; import com.android.internal.widget.LockPatternChecker;
import com.android.internal.widget.LockPatternUtils; import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.LockPatternView; import com.android.internal.widget.LockPatternView;
@@ -34,7 +35,7 @@ import java.util.List;
*/ */
public class AuthCredentialPatternView extends AuthCredentialView { public class AuthCredentialPatternView extends AuthCredentialView {
private LockPatternView mLockPatternView; @VisibleForTesting LockPatternView mLockPatternView;
private class UnlockPatternListener implements LockPatternView.OnPatternListener { private class UnlockPatternListener implements LockPatternView.OnPatternListener {
@@ -93,9 +94,7 @@ public class AuthCredentialPatternView extends AuthCredentialView {
@Override @Override
protected void onErrorTimeoutFinish() { protected void onErrorTimeoutFinish() {
super.onErrorTimeoutFinish(); super.onErrorTimeoutFinish();
// select to enable marquee unless a screen reader is enabled mLockPatternView.setEnabled(true);
mLockPatternView.setEnabled(!mAccessibilityManager.isEnabled()
|| !mAccessibilityManager.isTouchExplorationEnabled());
} }
public AuthCredentialPatternView(Context context, AttributeSet attrs) { public AuthCredentialPatternView(Context context, AttributeSet attrs) {

View File

@@ -47,6 +47,7 @@ import android.widget.LinearLayout;
import android.widget.TextView; import android.widget.TextView;
import androidx.annotation.StringRes; import androidx.annotation.StringRes;
import androidx.annotation.VisibleForTesting;
import com.android.internal.widget.LockPatternUtils; import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.VerifyCredentialResponse; import com.android.internal.widget.VerifyCredentialResponse;
@@ -98,7 +99,7 @@ public abstract class AuthCredentialView extends LinearLayout {
protected int mUserId; protected int mUserId;
protected long mOperationId; protected long mOperationId;
protected int mEffectiveUserId; protected int mEffectiveUserId;
protected ErrorTimer mErrorTimer; @VisibleForTesting ErrorTimer mErrorTimer;
protected @Background DelayableExecutor mBackgroundExecutor; protected @Background DelayableExecutor mBackgroundExecutor;

View File

@@ -0,0 +1,163 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.systemui.biometrics
import android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_SOMETHING
import android.hardware.biometrics.BiometricManager
import android.hardware.biometrics.PromptInfo
import android.hardware.face.FaceSensorPropertiesInternal
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal
import android.os.Handler
import android.os.IBinder
import android.os.UserManager
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.testing.ViewUtils
import androidx.test.filters.SmallTest
import com.android.internal.jank.InteractionJankMonitor
import com.android.internal.widget.LockPatternUtils
import com.android.internal.widget.LockPatternView
import com.android.internal.widget.VerifyCredentialResponse
import com.android.systemui.SysuiTestCase
import com.android.systemui.biometrics.AuthContainerView.BiometricCallback
import com.android.systemui.biometrics.AuthCredentialView.ErrorTimer
import com.android.systemui.keyguard.WakefulnessLifecycle
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
import org.junit.After
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito.anyInt
import org.mockito.Mockito.spy
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when`
import org.mockito.junit.MockitoJUnit
@RunWith(AndroidTestingRunner::class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
@SmallTest
class AuthCredentialViewTest : SysuiTestCase() {
@JvmField @Rule var mockitoRule = MockitoJUnit.rule()
@Mock lateinit var callback: AuthDialogCallback
@Mock lateinit var lockPatternUtils: LockPatternUtils
@Mock lateinit var userManager: UserManager
@Mock lateinit var wakefulnessLifecycle: WakefulnessLifecycle
@Mock lateinit var windowToken: IBinder
@Mock lateinit var interactionJankMonitor: InteractionJankMonitor
private var authContainer: TestAuthContainerView? = null
private var authCredentialView: AuthCredentialPatternView? = null
private var lockPatternView: LockPatternView? = null
private var biometricCallback: BiometricCallback? = null
private var errorTimer: ErrorTimer? = null
@After
fun tearDown() {
if (authContainer?.isAttachedToWindow == true) {
ViewUtils.detachView(authContainer)
}
}
@Test
fun testAuthCredentialPatternView_onErrorTimeoutFinish_setPatternEnabled() {
`when`(lockPatternUtils.getCredentialTypeForUser(anyInt()))
.thenReturn(LockPatternUtils.CREDENTIAL_TYPE_PATTERN)
`when`(lockPatternUtils.getKeyguardStoredPasswordQuality(anyInt()))
.thenReturn(PASSWORD_QUALITY_SOMETHING)
val errorResponse: VerifyCredentialResponse = VerifyCredentialResponse.fromError()
assertThat(initializeFingerprintContainer()).isNotNull()
authContainer?.animateToCredentialUI()
waitForIdleSync()
authCredentialView = spy(authContainer?.mCredentialView as AuthCredentialPatternView)
authCredentialView?.onCredentialVerified(errorResponse, 5000)
errorTimer = authCredentialView?.mErrorTimer
errorTimer?.onFinish()
waitForIdleSync()
verify(authCredentialView)?.onErrorTimeoutFinish()
lockPatternView = authCredentialView?.mLockPatternView
assertThat(lockPatternView?.isEnabled).isTrue()
}
private fun initializeFingerprintContainer(
authenticators: Int = BiometricManager.Authenticators.BIOMETRIC_WEAK,
addToView: Boolean = true
) =
initializeContainer(
TestAuthContainerView(
authenticators = authenticators,
fingerprintProps = fingerprintSensorPropertiesInternal()
),
addToView
)
private fun initializeContainer(
view: TestAuthContainerView,
addToView: Boolean
): TestAuthContainerView {
authContainer = view
if (addToView) {
authContainer!!.addToView()
biometricCallback = authContainer?.mBiometricCallback
}
return authContainer!!
}
private inner class TestAuthContainerView(
authenticators: Int = BiometricManager.Authenticators.BIOMETRIC_WEAK,
fingerprintProps: List<FingerprintSensorPropertiesInternal> = listOf(),
faceProps: List<FaceSensorPropertiesInternal> = listOf()
) :
AuthContainerView(
Config().apply {
mContext = context
mCallback = callback
mSensorIds =
(fingerprintProps.map { it.sensorId } + faceProps.map { it.sensorId })
.toIntArray()
mSkipAnimation = true
mPromptInfo = PromptInfo().apply { this.authenticators = authenticators }
},
fingerprintProps,
faceProps,
wakefulnessLifecycle,
userManager,
lockPatternUtils,
interactionJankMonitor,
Handler(TestableLooper.get(this).looper),
FakeExecutor(FakeSystemClock())
) {
override fun postOnAnimation(runnable: Runnable) {
runnable.run()
}
}
override fun waitForIdleSync() = TestableLooper.get(this).processAllMessages()
private fun AuthContainerView.addToView() {
ViewUtils.attachView(this)
waitForIdleSync()
assertThat(isAttachedToWindow).isTrue()
}
}