Merge changes I800833ab,I75f82e2d,Id5fb4325,I40a765be,I2057a0ef, ... into rvc-dev

* changes:
  Remove resultCode test from CredentialEnrolledTests
  Add tests for BiometricPrompt negative button
  Add MAC tests
  Add signature tests
  Add Biometric, Biometric|Credential Cipher tests
  Add CtsVerifier tests for setUserAuthenticationParameters
diff --git a/apps/CtsVerifier/AndroidManifest.xml b/apps/CtsVerifier/AndroidManifest.xml
index 36c2284..92b881b 100644
--- a/apps/CtsVerifier/AndroidManifest.xml
+++ b/apps/CtsVerifier/AndroidManifest.xml
@@ -1381,6 +1381,168 @@
                        android:value="android.hardware.type.television:android.software.leanback:android.hardware.type.watch" />
         </activity>
 
+        <activity
+            android:name=".biometrics.UserAuthenticationCredentialCipherTest"
+            android:configChanges="keyboardHidden|orientation|screenSize"
+            android:label="@string/biometric_test_set_user_authentication_credential_cipher_label" >
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+
+                <category android:name="android.cts.intent.category.MANUAL_TEST" />
+            </intent-filter>
+
+            <meta-data android:name="test_category" android:value="@string/biometric_test_category_combination" />
+            <meta-data android:name="test_parent"
+                       android:value="com.android.cts.verifier.biometrics.BiometricTestList" />
+            <meta-data android:name="test_required_features" android:value="android.software.secure_lock_screen" />
+            <meta-data android:name="test_excluded_features"
+                       android:value="android.hardware.type.television:android.software.leanback:android.hardware.type.watch" />
+        </activity>
+
+        <activity
+            android:name=".biometrics.UserAuthenticationBiometricCipherTest"
+            android:configChanges="keyboardHidden|orientation|screenSize"
+            android:label="@string/biometric_test_set_user_authentication_biometric_cipher_label" >
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+
+                <category android:name="android.cts.intent.category.MANUAL_TEST" />
+            </intent-filter>
+
+            <meta-data android:name="test_category" android:value="@string/biometric_test_category_combination" />
+            <meta-data android:name="test_parent"
+                       android:value="com.android.cts.verifier.biometrics.BiometricTestList" />
+            <meta-data android:name="test_required_features" android:value="android.software.secure_lock_screen" />
+            <meta-data android:name="test_excluded_features"
+                       android:value="android.hardware.type.television:android.software.leanback:android.hardware.type.watch" />
+        </activity>
+
+        <activity
+            android:name=".biometrics.UserAuthenticationBiometricOrCredentialCipherTest"
+            android:configChanges="keyboardHidden|orientation|screenSize"
+            android:label="@string/biometric_test_set_user_authentication_biometric_credential_cipher_label" >
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+
+                <category android:name="android.cts.intent.category.MANUAL_TEST" />
+            </intent-filter>
+
+            <meta-data android:name="test_category" android:value="@string/biometric_test_category_combination" />
+            <meta-data android:name="test_parent"
+                       android:value="com.android.cts.verifier.biometrics.BiometricTestList" />
+            <meta-data android:name="test_required_features" android:value="android.software.secure_lock_screen" />
+            <meta-data android:name="test_excluded_features"
+                       android:value="android.hardware.type.television:android.software.leanback:android.hardware.type.watch" />
+        </activity>
+
+        <activity
+            android:name=".biometrics.UserAuthenticationCredentialSignatureTest"
+            android:configChanges="keyboardHidden|orientation|screenSize"
+            android:label="@string/biometric_test_set_user_authentication_credential_signature_label" >
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+
+                <category android:name="android.cts.intent.category.MANUAL_TEST" />
+            </intent-filter>
+
+            <meta-data android:name="test_category" android:value="@string/biometric_test_category_combination" />
+            <meta-data android:name="test_parent"
+                       android:value="com.android.cts.verifier.biometrics.BiometricTestList" />
+            <meta-data android:name="test_required_features" android:value="android.software.secure_lock_screen" />
+            <meta-data android:name="test_excluded_features"
+                       android:value="android.hardware.type.television:android.software.leanback:android.hardware.type.watch" />
+        </activity>
+
+        <activity
+            android:name=".biometrics.UserAuthenticationBiometricSignatureTest"
+            android:configChanges="keyboardHidden|orientation|screenSize"
+            android:label="@string/biometric_test_set_user_authentication_biometric_signature_label" >
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+
+                <category android:name="android.cts.intent.category.MANUAL_TEST" />
+            </intent-filter>
+
+            <meta-data android:name="test_category" android:value="@string/biometric_test_category_combination" />
+            <meta-data android:name="test_parent"
+                       android:value="com.android.cts.verifier.biometrics.BiometricTestList" />
+            <meta-data android:name="test_required_features" android:value="android.software.secure_lock_screen" />
+            <meta-data android:name="test_excluded_features"
+                       android:value="android.hardware.type.television:android.software.leanback:android.hardware.type.watch" />
+        </activity>
+
+        <activity
+            android:name=".biometrics.UserAuthenticationBiometricOrCredentialSignatureTest"
+            android:configChanges="keyboardHidden|orientation|screenSize"
+            android:label="@string/biometric_test_set_user_authentication_biometric_or_credential_signature_label" >
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+
+                <category android:name="android.cts.intent.category.MANUAL_TEST" />
+            </intent-filter>
+
+            <meta-data android:name="test_category" android:value="@string/biometric_test_category_combination" />
+            <meta-data android:name="test_parent"
+                       android:value="com.android.cts.verifier.biometrics.BiometricTestList" />
+            <meta-data android:name="test_required_features" android:value="android.software.secure_lock_screen" />
+            <meta-data android:name="test_excluded_features"
+                       android:value="android.hardware.type.television:android.software.leanback:android.hardware.type.watch" />
+        </activity>
+
+        <activity
+            android:name=".biometrics.UserAuthenticationCredentialMacTest"
+            android:configChanges="keyboardHidden|orientation|screenSize"
+            android:label="@string/biometric_test_set_user_authentication_credential_mac_label" >
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+
+                <category android:name="android.cts.intent.category.MANUAL_TEST" />
+            </intent-filter>
+
+            <meta-data android:name="test_category" android:value="@string/biometric_test_category_combination" />
+            <meta-data android:name="test_parent"
+                       android:value="com.android.cts.verifier.biometrics.BiometricTestList" />
+            <meta-data android:name="test_required_features" android:value="android.software.secure_lock_screen" />
+            <meta-data android:name="test_excluded_features"
+                       android:value="android.hardware.type.television:android.software.leanback:android.hardware.type.watch" />
+        </activity>
+
+        <activity
+            android:name=".biometrics.UserAuthenticationBiometricMacTest"
+            android:configChanges="keyboardHidden|orientation|screenSize"
+            android:label="@string/biometric_test_set_user_authentication_biometric_mac_label" >
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+
+                <category android:name="android.cts.intent.category.MANUAL_TEST" />
+            </intent-filter>
+
+            <meta-data android:name="test_category" android:value="@string/biometric_test_category_combination" />
+            <meta-data android:name="test_parent"
+                       android:value="com.android.cts.verifier.biometrics.BiometricTestList" />
+            <meta-data android:name="test_required_features" android:value="android.software.secure_lock_screen" />
+            <meta-data android:name="test_excluded_features"
+                       android:value="android.hardware.type.television:android.software.leanback:android.hardware.type.watch" />
+        </activity>
+
+        <activity
+            android:name=".biometrics.UserAuthenticationBiometricOrCredentialMacTest"
+            android:configChanges="keyboardHidden|orientation|screenSize"
+            android:label="@string/biometric_test_set_user_authentication_biometric_or_credential_mac_label" >
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+
+                <category android:name="android.cts.intent.category.MANUAL_TEST" />
+            </intent-filter>
+
+            <meta-data android:name="test_category" android:value="@string/biometric_test_category_combination" />
+            <meta-data android:name="test_parent"
+                       android:value="com.android.cts.verifier.biometrics.BiometricTestList" />
+            <meta-data android:name="test_required_features" android:value="android.software.secure_lock_screen" />
+            <meta-data android:name="test_excluded_features"
+                       android:value="android.hardware.type.television:android.software.leanback:android.hardware.type.watch" />
+        </activity>
+
         <activity android:name=".security.IdentityCredentialAuthentication"
                 android:label="@string/sec_identity_credential_authentication_test"
                 android:configChanges="keyboardHidden|orientation|screenSize" >
diff --git a/apps/CtsVerifier/res/layout/biometric_test_strong_tests.xml b/apps/CtsVerifier/res/layout/biometric_test_strong_tests.xml
index e0236dc..20cf86b 100644
--- a/apps/CtsVerifier/res/layout/biometric_test_strong_tests.xml
+++ b/apps/CtsVerifier/res/layout/biometric_test_strong_tests.xml
@@ -64,6 +64,13 @@
                 android:text="@string/biometric_test_strong_authenticate_ui_button"
                 android:enabled="false"/>
             <Button
+                android:id="@+id/authenticate_negative_button_button"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_centerInParent="true"
+                android:text="@string/biometric_test_negative_button_button"
+                android:enabled="false"/>
+            <Button
                 android:id="@+id/authenticate_credential_setDeviceCredentialAllowed_biometric_button"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
diff --git a/apps/CtsVerifier/res/layout/biometric_test_user_authentication_credential_tests.xml b/apps/CtsVerifier/res/layout/biometric_test_user_authentication_credential_tests.xml
new file mode 100644
index 0000000..eded805
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/biometric_test_user_authentication_credential_tests.xml
@@ -0,0 +1,114 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 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.
+  -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent"
+              android:orientation="vertical">
+
+    <ScrollView  android:layout_width="match_parent"
+                 android:layout_height="match_parent"
+                 android:orientation="vertical">
+
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:orientation="vertical">
+
+            <TextView
+                style="@style/InstructionsFont"
+                android:id="@+id/instructions"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_alignParentTop="true"
+                android:gravity="center" />
+
+            <!-- This section contains tests that will be repeated again below with/without strongbox -->
+
+            <Button
+                android:id="@+id/per_use_auth_with_credential"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_centerInParent="true"
+                android:text="@string/biometric_test_set_user_authentication_credential_per_use_auth_with_credential"/>
+
+            <Button
+                android:id="@+id/duration_auth_with_credential"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_centerInParent="true"
+                android:text="@string/biometric_test_set_user_authentication_credential_duration_auth_with_credential"/>
+
+            <Button
+                android:id="@+id/per_use_auth_with_biometric"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_centerInParent="true"
+                android:text="@string/biometric_test_set_user_authentication_credential_per_use_auth_with_biometric"/>
+
+            <Button
+                android:id="@+id/duration_auth_with_biometric"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_centerInParent="true"
+                android:text="@string/biometric_test_set_user_authentication_credential_duration_auth_with_biometric"/>
+
+            <!-- The below are the same as above, except with strongbox -->
+
+            <Button
+                android:id="@+id/per_use_auth_with_credential_strongbox"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_centerInParent="true"
+                android:text="@string/biometric_test_set_user_authentication_credential_per_use_auth_with_credential_strongbox"/>
+
+            <Button
+                android:id="@+id/duration_auth_with_credential_strongbox"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_centerInParent="true"
+                android:text="@string/biometric_test_set_user_authentication_credential_duration_auth_with_credential_strongbox"/>
+
+            <Button
+                android:id="@+id/per_use_auth_with_biometric_strongbox"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_centerInParent="true"
+                android:text="@string/biometric_test_set_user_authentication_credential_per_use_auth_with_biometric_strongbox"/>
+
+            <Button
+                android:id="@+id/duration_auth_with_biometric_strongbox"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_centerInParent="true"
+                android:text="@string/biometric_test_set_user_authentication_credential_duration_auth_with_biometric_strongbox"/>
+
+            <Space
+                android:layout_width="match_parent"
+                android:layout_height="0dp"
+                android:layout_weight="1"/>
+
+            <include
+                layout="@layout/pass_fail_buttons"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"/>
+
+        </LinearLayout>
+
+    </ScrollView>
+
+</LinearLayout>
diff --git a/apps/CtsVerifier/res/layout/biometric_test_weak_tests.xml b/apps/CtsVerifier/res/layout/biometric_test_weak_tests.xml
index fbec4ac..456552d 100644
--- a/apps/CtsVerifier/res/layout/biometric_test_weak_tests.xml
+++ b/apps/CtsVerifier/res/layout/biometric_test_weak_tests.xml
@@ -85,6 +85,13 @@
                 android:layout_centerInParent="true"
                 android:text="@string/biometric_test_reject_first"
                 android:enabled="false"/>
+            <Button
+                android:id="@+id/authenticate_negative_button_button"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_centerInParent="true"
+                android:text="@string/biometric_test_negative_button_button"
+                android:enabled="false"/>
 
             <Space
                 android:layout_width="match_parent"
diff --git a/apps/CtsVerifier/res/values/strings.xml b/apps/CtsVerifier/res/values/strings.xml
index 3bc5bb6..9332c9b 100755
--- a/apps/CtsVerifier/res/values/strings.xml
+++ b/apps/CtsVerifier/res/values/strings.xml
@@ -229,11 +229,12 @@
     <!-- Strings for BiometricTest -->
     <string name="biometric_test">Biometric Tests</string>
 
-    <string name="biometric_test_category_credential">Credential Tests</string>
-    <string name="biometric_test_category_strong">Strong Biometric Tests</string>
-    <string name="biometric_test_category_weak">Weak Biometric Tests</string>
+    <string name="biometric_test_category_credential">1) Credential Tests</string>
+    <string name="biometric_test_category_strong">2) Strong Biometric Tests</string>
+    <string name="biometric_test_category_weak">3) Weak Biometric Tests</string>
+    <string name="biometric_test_category_combination">4) setUserAuthParams Tests</string>
 
-    <string name="biometric_test_credential_not_enrolled_label">1) Credential Not Enrolled Tests</string>
+    <string name="biometric_test_credential_not_enrolled_label">1a: Credential Not Enrolled Tests</string>
     <string name="biometric_test_credential_not_enrolled_instructions">This test checks that the BiometricManager/BiometricPrompt
         APIs return results consistent with credential (PIN/Pattern/Password) state. Before starting this test, please ensure that you do
         NOT have a credential set up.</string>
@@ -241,7 +242,7 @@
     <string name="biometric_test_credential_not_enrolled_bp_setAllowedAuthenticators_button">Check BiometricPrompt setAllowedAuthenticators(DEVICE_CREDENTIAL)</string>
     <string name="biometric_test_credential_not_enrolled_bp_setDeviceCredentialAllowed_button">Check BiometricPrompt setDeviceCredentialAllowed(true)</string>
 
-    <string name="biometric_test_credential_enrolled_label">2) Credential Enrolled Tests</string>
+    <string name="biometric_test_credential_enrolled_label">1b: Credential Enrolled Tests</string>
     <string name="biometric_test_credential_enrolled_instructions">This test checks that apps are able to request credential enrollment, and that the BiometricManager/BiometricPrompt
         APIs return results consistent with credential (PIN/Pattern/Password) state.</string>
     <string name="biometric_test_credential_enroll_button">Enroll credential</string>
@@ -249,7 +250,7 @@
     <string name="biometric_test_credential_enrolled_bp_setAllowedAuthenticators_button">Check BiometricPrompt setAllowedAuthenticators(DEVICE_CREDENTIAL)</string>
     <string name="biometric_test_credential_enrolled_bp_setDeviceCredentialAllowed_button">Check BiometricPrompt setDeviceCredentialAllowed(true)</string>
 
-    <string name="biometric_test_credential_crypto_label">3) Credential Crypto</string>
+    <string name="biometric_test_credential_crypto_label">1c: Credential Crypto</string>
     <string name="biometric_test_credential_crypto_instructions">This test checks that PIN/Pattern/Password successfully unlocks the relevant KeyStore operations. Please
         ensure that you have a PIN/Pattern/Password set up.</string>
     <string name="biometric_test_credential_crypto_timed_key_strongbox">Create and unlock timed key (StrongBox)</string>
@@ -271,12 +272,14 @@
     <string name="biometric_test_invalid_inputs">Test invalid inputs</string>
     <!-- Rejecting does not end the authentication lifecycle -->
     <string name="biometric_test_reject_first">Reject, then authenticate</string>
+    <!-- Negative button callback is received -->
+    <string name="biometric_test_negative_button_button">Test Negative Button</string>
 
     <string name="biometric_test_reject_continues_instruction_title">Instructions</string>
     <string name="biometric_test_reject_continues_instruction_contents">Please authenticate with a non-enrolled biometric before authenticating with the actual enrolled biometric.</string>
     <string name="biometric_test_reject_continues_instruction_continue">Continue</string>
 
-    <string name="biometric_test_strong_label">4) Strong Biometrics + Crypto</string>
+    <string name="biometric_test_strong_label">2a: Strong Biometrics + Crypto</string>
     <string name="biometric_test_strong_instructions">This test checks that the Settings.ACTION_BIOMETRIC_ENROLL and its corresponding
         EXTRA_BIOMETRIC_AUTHENTICATOR_REQUIREMENTS APIs enroll only a STRONG biometric. Please ensure that the device
         does NOT have ANY biometrics enrolled before starting this test. After passing the first part of the test, it will check various use cases
@@ -290,7 +293,7 @@
     <string name="biometric_test_strong_authenticate_invalidated_instruction_contents">Before starting the next test, please add another enrollment to your strong biometric sensor. If only one enrollment is supported, please remove the current enrollment, then enroll.</string>
     <string name="biometric_test_strong_authenticate_invalidated_instruction_continue">Continue</string>
 
-    <string name="biometric_test_weak_label">5) Weak Biometrics</string>
+    <string name="biometric_test_weak_label">3a: Weak Biometrics</string>
     <string name="biometric_test_weak_instructions">This test checks that the Settings.ACTION_BIOMETRIC_ENROLL and its corresponding
         EXTRA_BIOMETRIC_AUTHENTICATOR_REQUIREMENTS APIs enroll a biometric that meets or exceeds WEAK. Please ensure that the device
         does NOT have ANY biometrics enrolled before starting this test. After passing the first part of the test, it will check various use cases
@@ -298,6 +301,32 @@
     <string name="biometric_test_weak_enroll">Start enrollment</string>
     <string name="biometric_test_weak_authenticate">Authenticate</string>
 
+    <string name="biometric_test_set_user_authentication_credential_cipher_label">4a: Cipher, Credential</string>
+    <string name="biometric_test_set_user_authentication_biometric_cipher_label">4b: Cipher, Biometric</string>
+    <string name="biometric_test_set_user_authentication_biometric_credential_cipher_label">4c: Cipher, Biometric|Credential</string>
+    <string name="biometric_test_set_user_authentication_credential_signature_label">4d: Signature, Credential</string>
+    <string name="biometric_test_set_user_authentication_biometric_signature_label">4e: Signature, Biometric</string>
+    <string name="biometric_test_set_user_authentication_biometric_or_credential_signature_label">4f: Signature, Biometric|Credential</string>
+    <string name="biometric_test_set_user_authentication_credential_mac_label">4g: MAC, Credential</string>
+    <string name="biometric_test_set_user_authentication_biometric_mac_label">4h: MAC, Biometric</string>
+    <string name="biometric_test_set_user_authentication_biometric_or_credential_mac_label">4h: MAC, Biometric|Credential</string>
+    <string name="biometric_test_set_user_authentication_credential_instructions">This test checks the correctness of the KeyGenParameterSpec.Builder#setUserAuthenticationParameters(int, int) API for AUTH_DEVICE_CREDENTIAL.
+        Buttons for completed tasks will become invisible. It\'s normal for buttons to be disabled for a few seconds during this test.</string>
+    <string name="biometric_test_set_user_authentication_biometric_instructions">This test checks the correctness of the KeyGenParameterSpec.Builder#setUserAuthenticationParameters(int, int) API for AUTH_BIOMETRIC_STRONG.
+        Buttons for completed tasks will become invisible. It\'s normal for buttons to be disabled for a few seconds during this test.</string>
+    <string name="biometric_test_set_user_authentication_biometric_or_credential_instructions">This test checks the correctness of the KeyGenParameterSpec.Builder#setUserAuthenticationParameters(int, int) API for AUTH_BIOMETRIC_STRONG|AUTH_DEVICE_CREDENTIAL.
+        Buttons for completed tasks will become invisible. It\'s normal for buttons to be disabled for a few seconds during this test.</string>
+    <!-- no strongbox-->
+    <string name="biometric_test_set_user_authentication_credential_per_use_auth_with_credential">auth-per-use key with credential</string>
+    <string name="biometric_test_set_user_authentication_credential_duration_auth_with_credential">time-based key with credential</string>
+    <string name="biometric_test_set_user_authentication_credential_per_use_auth_with_biometric">auth-per-use key with biometric</string>
+    <string name="biometric_test_set_user_authentication_credential_duration_auth_with_biometric">time-based key with biometric</string>
+    <!-- strongbox -->
+    <string name="biometric_test_set_user_authentication_credential_per_use_auth_with_credential_strongbox">auth-per-use key with credential (strongbox)</string>
+    <string name="biometric_test_set_user_authentication_credential_duration_auth_with_credential_strongbox">time-based key with credential (strongbox)</string>
+    <string name="biometric_test_set_user_authentication_credential_per_use_auth_with_biometric_strongbox">auth-per-use key with biometric (strongbox)</string>
+    <string name="biometric_test_set_user_authentication_credential_duration_auth_with_biometric_strongbox">time-based key with biometric (strongbox)</string>
+
     <!-- Strings for lock bound keys test -->
     <string name="sec_lock_bound_key_test">Lock Bound Keys Test</string>
     <string name="sec_lock_bound_key_test_info">
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/biometrics/AbstractBaseTest.java b/apps/CtsVerifier/src/com/android/cts/verifier/biometrics/AbstractBaseTest.java
index a79d82a..aa58ba8 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/biometrics/AbstractBaseTest.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/biometrics/AbstractBaseTest.java
@@ -43,7 +43,6 @@
 public abstract class AbstractBaseTest extends PassFailButtons.Activity {
 
     private static final int REQUEST_ENROLL_WHEN_NONE_ENROLLED = 1;
-    private static final int REQUEST_ENROLL_WHEN_ENROLLED = 2;
 
     abstract protected String getTag();
     abstract protected boolean isOnPauseAllowed();
@@ -72,7 +71,7 @@
         // Assume we only enable the pass button when all tests pass. There actually  isn't a way
         // to easily do something like `this.isTestPassed()`
         if (!getPassButton().isEnabled() && !isOnPauseAllowed()) {
-            showToastAndLog("This test must be completed without going onPause");
+            showToastAndLog("This test must be completed without pausing the app");
             // Do not allow the test to continue if it loses foreground. Testers must start over.
             // 1) This is to avoid any potential change to the current enrollment / biometric state.
             // 2) The authentication UI must not affect the caller's activity lifecycle.
@@ -85,19 +84,7 @@
         mCurrentlyEnrolling = false;
 
         if (requestCode == REQUEST_ENROLL_WHEN_NONE_ENROLLED) {
-            if (resultCode == RESULT_OK) {
-                startBiometricEnroll(REQUEST_ENROLL_WHEN_ENROLLED, mRequestedStrength);
-            } else {
-                showToastAndLog("Unexpected result when requesting enrollment when not enrolled"
-                        + " yet: " + resultCode);
-            }
-        } else if (requestCode == REQUEST_ENROLL_WHEN_ENROLLED) {
-            if (resultCode == RESULT_CANCELED) {
-                onBiometricEnrollFinished();
-            } else {
-                showToastAndLog("Unexpected result when requesting enrollment when already"
-                        + " enrolled: " + resultCode);
-            }
+            onBiometricEnrollFinished();
         }
     }
 
@@ -396,4 +383,19 @@
                 });
     }
 
+    void testNegativeButtonCallback(int allowedAuthenticators, Runnable successRunnable) {
+        final BiometricPrompt.Builder builder = new BiometricPrompt.Builder(this);
+        builder.setTitle("Press the negative button");
+        builder.setAllowedAuthenticators(allowedAuthenticators);
+        builder.setNegativeButton("Press me", mExecutor, (dialog1, which1) -> {
+            mExecutor.execute(successRunnable);
+        });
+
+        final BiometricPrompt prompt = builder.build();
+        prompt.authenticate(new CancellationSignal(), mExecutor,
+                new AuthenticationCallback() {
+            // Do nothing
+        });
+    }
+
 }
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/biometrics/AbstractUserAuthenticationCipherTest.java b/apps/CtsVerifier/src/com/android/cts/verifier/biometrics/AbstractUserAuthenticationCipherTest.java
new file mode 100644
index 0000000..dff9023
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/biometrics/AbstractUserAuthenticationCipherTest.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2020 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.cts.verifier.biometrics;
+
+import android.hardware.biometrics.BiometricPrompt;
+import android.security.keystore.KeyGenParameterSpec;
+import android.security.keystore.KeyProperties;
+
+import java.security.KeyPairGenerator;
+import java.security.KeyStore;
+
+import javax.crypto.Cipher;
+import javax.crypto.KeyGenerator;
+
+public abstract class AbstractUserAuthenticationCipherTest extends AbstractUserAuthenticationTest {
+    private Cipher mCipher;
+
+    @Override
+    void createUserAuthenticationKey(String keyName, int timeout, int authType,
+            boolean useStrongBox) throws Exception {
+        KeyGenParameterSpec.Builder builder = new KeyGenParameterSpec.Builder(
+                keyName, KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT);
+        builder.setBlockModes(KeyProperties.BLOCK_MODE_CBC)
+                .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7)
+                .setUserAuthenticationRequired(true)
+                .setUserAuthenticationParameters(timeout, authType)
+                .setIsStrongBoxBacked(useStrongBox);
+
+        KeyGenerator keyGenerator = KeyGenerator.getInstance(
+                KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore");
+        keyGenerator.init(builder.build());
+        keyGenerator.generateKey();
+    }
+
+    @Override
+    void initializeKeystoreOperation(String keyName) throws Exception {
+        mCipher = Utils.initCipher(keyName);
+    }
+
+    @Override
+    BiometricPrompt.CryptoObject getCryptoObject() {
+        return new BiometricPrompt.CryptoObject(mCipher);
+    }
+
+    @Override
+    void doKeystoreOperation(byte[] payload) throws Exception {
+        try {
+            Utils.doEncrypt(mCipher, payload);
+        } finally {
+            mCipher = null;
+        }
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/biometrics/AbstractUserAuthenticationMacTest.java b/apps/CtsVerifier/src/com/android/cts/verifier/biometrics/AbstractUserAuthenticationMacTest.java
new file mode 100644
index 0000000..638ba80
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/biometrics/AbstractUserAuthenticationMacTest.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2020 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.cts.verifier.biometrics;
+
+import android.hardware.biometrics.BiometricPrompt;
+import android.security.keystore.KeyGenParameterSpec;
+import android.security.keystore.KeyProperties;
+
+import javax.crypto.KeyGenerator;
+import javax.crypto.Mac;
+
+public abstract class AbstractUserAuthenticationMacTest extends AbstractUserAuthenticationTest {
+    private Mac mMac;
+
+    @Override
+    void createUserAuthenticationKey(String keyName, int timeout, int authType,
+            boolean useStrongBox) throws Exception {
+        KeyGenParameterSpec.Builder builder = new KeyGenParameterSpec.Builder(
+                keyName, KeyProperties.PURPOSE_SIGN);
+        builder.setUserAuthenticationRequired(true)
+                .setIsStrongBoxBacked(useStrongBox)
+                .setUserAuthenticationParameters(timeout, authType);
+
+        KeyGenerator keyGenerator = KeyGenerator.getInstance(
+                KeyProperties.KEY_ALGORITHM_HMAC_SHA256, "AndroidKeyStore");
+        keyGenerator.init(builder.build());
+        keyGenerator.generateKey();
+    }
+
+    @Override
+    void initializeKeystoreOperation(String keyName) throws Exception {
+        mMac = Utils.initMac(keyName);
+    }
+
+    @Override
+    BiometricPrompt.CryptoObject getCryptoObject() {
+        return new BiometricPrompt.CryptoObject(mMac);
+    }
+
+    @Override
+    void doKeystoreOperation(byte[] payload) throws Exception {
+        try {
+            mMac.doFinal(payload);
+        } finally {
+            mMac = null;
+        }
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/biometrics/AbstractUserAuthenticationSignatureTest.java b/apps/CtsVerifier/src/com/android/cts/verifier/biometrics/AbstractUserAuthenticationSignatureTest.java
new file mode 100644
index 0000000..4708620
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/biometrics/AbstractUserAuthenticationSignatureTest.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2020 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.cts.verifier.biometrics;
+
+import android.hardware.biometrics.BiometricPrompt;
+import android.security.keystore.KeyGenParameterSpec;
+import android.security.keystore.KeyProperties;
+
+import java.security.KeyPairGenerator;
+import java.security.Signature;
+import java.security.spec.ECGenParameterSpec;
+
+public abstract class AbstractUserAuthenticationSignatureTest
+        extends AbstractUserAuthenticationTest {
+    private Signature mSignature;
+
+    @Override
+    void createUserAuthenticationKey(String keyName, int timeout, int authType,
+            boolean useStrongBox) throws Exception {
+        KeyGenParameterSpec.Builder builder = new KeyGenParameterSpec.Builder(
+                keyName, KeyProperties.PURPOSE_SIGN);
+        builder.setDigests(KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA512)
+                .setAlgorithmParameterSpec(new ECGenParameterSpec("secp256r1"))
+                .setUserAuthenticationRequired(true)
+                .setIsStrongBoxBacked(useStrongBox)
+                .setUserAuthenticationParameters(timeout, authType);
+
+        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(
+                KeyProperties.KEY_ALGORITHM_EC, "AndroidKeyStore");
+        keyPairGenerator.initialize(builder.build());
+        keyPairGenerator.generateKeyPair();
+    }
+
+    @Override
+    void initializeKeystoreOperation(String keyName) throws Exception {
+        mSignature = Utils.initSignature(keyName);
+    }
+
+    @Override
+    BiometricPrompt.CryptoObject getCryptoObject() {
+        return new BiometricPrompt.CryptoObject(mSignature);
+    }
+
+    @Override
+    void doKeystoreOperation(byte[] payload) throws Exception {
+        try {
+            Utils.doSign(mSignature, payload);
+        } finally {
+            mSignature = null;
+        }
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/biometrics/AbstractUserAuthenticationTest.java b/apps/CtsVerifier/src/com/android/cts/verifier/biometrics/AbstractUserAuthenticationTest.java
new file mode 100644
index 0000000..c6e3cb7
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/biometrics/AbstractUserAuthenticationTest.java
@@ -0,0 +1,399 @@
+/*
+ * Copyright (C) 2020 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.cts.verifier.biometrics;
+
+import android.content.pm.PackageManager;
+import android.hardware.biometrics.BiometricManager;
+import android.hardware.biometrics.BiometricManager.Authenticators;
+import android.hardware.biometrics.BiometricPrompt;
+import android.hardware.biometrics.BiometricPrompt.AuthenticationCallback;
+import android.hardware.biometrics.BiometricPrompt.AuthenticationResult;
+import android.hardware.biometrics.BiometricPrompt.CryptoObject;
+import android.os.Bundle;
+import android.os.CancellationSignal;
+import android.os.Handler;
+import android.os.Looper;
+import android.security.keystore.KeyProperties;
+import android.util.Log;
+import android.view.View;
+import android.widget.Button;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.android.cts.verifier.PassFailButtons;
+import com.android.cts.verifier.R;
+
+import java.util.concurrent.Executor;
+
+/**
+ * This is the abstract base class for testing/checking that keys generated via
+ * setUserAuthenticationParameters(timeout, CREDENTIAL) can be unlocked (or not) depending on the
+ * type of authenticator used. This tests various combinations of
+ * {timeout, authenticator, strongbox}. Extending classes currently consist of:
+ * {@link UserAuthenticationCredentialCipherTest} for testing {@link javax.crypto.Cipher}.
+ */
+public abstract class AbstractUserAuthenticationTest extends PassFailButtons.Activity {
+
+    private static final String TAG = "AbstractUserAuthenticationCredentialTest";
+
+    private static final int TIMED_KEY_DURATION = 1;
+    private static final byte[] PAYLOAD = new byte[] {1, 2, 3, 4, 5, 6};
+
+    abstract class ExpectedResults {
+        abstract boolean shouldCredentialUnlockPerUseKey();
+        abstract boolean shouldCredentialUnlockTimedKey();
+        abstract boolean shouldBiometricUnlockPerUseKey();
+        abstract boolean shouldBiometricUnlockTimedKey();
+    }
+
+    /**
+     * @return Log tag.
+     */
+    abstract String getTag();
+
+    abstract int getInstructionsResourceId();
+
+    abstract void createUserAuthenticationKey(String keyName, int timeout, int authType,
+            boolean useStrongBox) throws Exception;
+
+    abstract ExpectedResults getExpectedResults();
+
+    /**
+     * @return The authenticators allowed to unlock the cryptographic operation. See
+     * {@link KeyProperties#AUTH_DEVICE_CREDENTIAL} and {@link KeyProperties#AUTH_BIOMETRIC_STRONG}
+     */
+    abstract int getKeyAuthenticators();
+
+    /**
+     * Due to the differences between auth-per-use operations and time-based operations, the
+     * initialization of the keystore operation may be before or after authentication. Initializing
+     * the operation will require the extending class to store it somewhere for later use. This
+     * cached operation should be cleared after {@link #doKeystoreOperation(byte[])} is invoked.
+     */
+    abstract void initializeKeystoreOperation(String keyName) throws Exception;
+
+    /**
+     * This method is used only for auth-per-use keys. This requires the keystore operation to
+     * already be initialized and cached within the extending class.
+     */
+    abstract CryptoObject getCryptoObject();
+
+    /**
+     * Attempts to perform the initialized/cached keystore operation. This method must guarantee
+     * that the cached operation is null after it's run (both passing and failing cases).
+     */
+    abstract void doKeystoreOperation(byte[] payload) throws Exception;
+
+    protected final Handler mHandler = new Handler(Looper.getMainLooper());
+    protected final Executor mExecutor = mHandler::post;
+
+    private BiometricManager mBiometricManager;
+
+    private Button mCredentialPerUseButton;
+    private Button mCredentialTimedButton;
+    private Button mBiometricPerUseButton;
+    private Button mBiometricTimedButton;
+    private Button mCredentialPerUseButton_strongbox;
+    private Button mCredentialTimedButton_strongbox;
+    private Button mBiometricPerUseButton_strongbox;
+    private Button mBiometricTimedButton_strongbox;
+
+    private Button[] mButtons;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.biometric_test_user_authentication_credential_tests);
+        setPassFailButtonClickListeners();
+        getPassButton().setEnabled(false);
+
+        mBiometricManager = getSystemService(BiometricManager.class);
+
+        TextView instructionsText = findViewById(R.id.instructions);
+        instructionsText.setText(getInstructionsResourceId());
+
+        mCredentialPerUseButton = findViewById(R.id.per_use_auth_with_credential);
+        mCredentialTimedButton = findViewById(R.id.duration_auth_with_credential);
+        mBiometricPerUseButton = findViewById(R.id.per_use_auth_with_biometric);
+        mBiometricTimedButton = findViewById(R.id.duration_auth_with_biometric);
+        mCredentialPerUseButton_strongbox
+                = findViewById(R.id.per_use_auth_with_credential_strongbox);
+        mCredentialTimedButton_strongbox
+                = findViewById(R.id.duration_auth_with_credential_strongbox);
+        mBiometricPerUseButton_strongbox
+                = findViewById(R.id.per_use_auth_with_biometric_strongbox);
+        mBiometricTimedButton_strongbox
+                = findViewById(R.id.duration_auth_with_biometric_strongbox);
+
+        mButtons = new Button[] {
+                mCredentialPerUseButton,
+                mCredentialTimedButton,
+                mBiometricPerUseButton,
+                mBiometricTimedButton,
+                mCredentialPerUseButton_strongbox,
+                mCredentialTimedButton_strongbox,
+                mBiometricPerUseButton_strongbox,
+                mBiometricTimedButton_strongbox
+        };
+
+        final boolean hasStrongBox = getPackageManager().hasSystemFeature(
+                PackageManager.FEATURE_STRONGBOX_KEYSTORE);
+        final boolean noStrongBiometricHardware =
+                mBiometricManager.canAuthenticate(Authenticators.BIOMETRIC_STRONG)
+                        == BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE;
+
+        if (!hasStrongBox) {
+            mCredentialPerUseButton_strongbox.setVisibility(View.GONE);
+            mCredentialTimedButton_strongbox.setVisibility(View.GONE);
+            mBiometricPerUseButton_strongbox.setVisibility(View.GONE);
+            mBiometricTimedButton_strongbox.setVisibility(View.GONE);
+        }
+
+        if (noStrongBiometricHardware) {
+            mBiometricPerUseButton.setVisibility(View.GONE);
+            mBiometricTimedButton.setVisibility(View.GONE);
+            mBiometricPerUseButton_strongbox.setVisibility(View.GONE);
+            mBiometricTimedButton_strongbox.setVisibility(View.GONE);
+        }
+
+        // No strongbox
+
+        mCredentialPerUseButton.setOnClickListener((view) -> {
+            testCredentialBoundEncryption("key1",
+                    0 /* timeout */,
+                    false /* useStrongBox */,
+                    Authenticators.DEVICE_CREDENTIAL,
+                    getExpectedResults().shouldCredentialUnlockPerUseKey(),
+                    PAYLOAD,
+                    mCredentialPerUseButton);
+        });
+
+        mCredentialTimedButton.setOnClickListener((view) -> {
+            testCredentialBoundEncryption("key2",
+                    TIMED_KEY_DURATION /* timeout */,
+                    false /* useStrongBox */,
+                    Authenticators.DEVICE_CREDENTIAL,
+                    getExpectedResults().shouldCredentialUnlockTimedKey(),
+                    PAYLOAD,
+                    mCredentialTimedButton);
+        });
+
+        mBiometricPerUseButton.setOnClickListener((view) -> {
+            testCredentialBoundEncryption("key3",
+                    0 /* timeout */,
+                    false /* useStrongBox */,
+                    Authenticators.BIOMETRIC_STRONG,
+                    getExpectedResults().shouldBiometricUnlockPerUseKey(),
+                    PAYLOAD,
+                    mBiometricPerUseButton);
+        });
+
+        mBiometricTimedButton.setOnClickListener((view) -> {
+            testCredentialBoundEncryption("key4",
+                    TIMED_KEY_DURATION /* timeout */,
+                    false /* useStrongBox */,
+                    Authenticators.BIOMETRIC_STRONG,
+                    getExpectedResults().shouldBiometricUnlockTimedKey(),
+                    PAYLOAD,
+                    mBiometricTimedButton);
+        });
+
+        // Strongbox
+
+        mCredentialPerUseButton_strongbox.setOnClickListener((view) -> {
+            testCredentialBoundEncryption("key5",
+                    0 /* timeout */,
+                    true /* useStrongBox */,
+                    Authenticators.DEVICE_CREDENTIAL,
+                    getExpectedResults().shouldCredentialUnlockPerUseKey(),
+                    PAYLOAD,
+                    mCredentialPerUseButton_strongbox);
+        });
+
+        mCredentialTimedButton_strongbox.setOnClickListener((view) -> {
+            testCredentialBoundEncryption("key6",
+                    TIMED_KEY_DURATION /* timeout */,
+                    true /* useStrongBox */,
+                    Authenticators.DEVICE_CREDENTIAL,
+                    getExpectedResults().shouldCredentialUnlockTimedKey(),
+                    PAYLOAD,
+                    mCredentialTimedButton_strongbox);
+        });
+
+        mBiometricPerUseButton_strongbox.setOnClickListener((view) -> {
+            testCredentialBoundEncryption("key7",
+                    0 /* timeout */,
+                    true /* useStrongBox */,
+                    Authenticators.BIOMETRIC_STRONG,
+                    getExpectedResults().shouldBiometricUnlockPerUseKey(),
+                    PAYLOAD,
+                    mBiometricPerUseButton_strongbox);
+        });
+
+        mBiometricTimedButton_strongbox.setOnClickListener((view) -> {
+            testCredentialBoundEncryption("key8",
+                    TIMED_KEY_DURATION /* timeout */,
+                    true /* useStrongBox */,
+                    Authenticators.BIOMETRIC_STRONG,
+                    getExpectedResults().shouldBiometricUnlockTimedKey(),
+                    PAYLOAD,
+                    mBiometricTimedButton_strongbox);
+        });
+    }
+
+    @Override
+    protected void onPause() {
+        super.onPause();
+
+        if (!getPassButton().isEnabled()) {
+            // This test is affected by PIN/Pattern/Password authentication. So, do not allow
+            // the test to complete if the user leaves the app (lockscreen, etc will affect this
+            // test).
+            showToastAndLog("This test must be completed without pausing the app");
+            finish();
+        }
+    }
+
+    private void testCredentialBoundEncryption(String keyName, int timeout, boolean useStrongBox,
+            int allowedAuthenticators, boolean shouldKeyBeUsable, byte[] payload,
+            Button testButton) {
+
+        final boolean requiresCryptoObject = timeout == 0;
+
+        final int canAuthenticate = mBiometricManager.canAuthenticate(allowedAuthenticators);
+        if (canAuthenticate != BiometricManager.BIOMETRIC_SUCCESS) {
+            showToastAndLog("Please ensure you can authenticate with the following authenticators: "
+                    + allowedAuthenticators + " Result: " + canAuthenticate);
+            return;
+        }
+
+        try {
+            if (mBiometricManager.canAuthenticate(allowedAuthenticators)
+                    != BiometricManager.BIOMETRIC_SUCCESS) {
+                showToastAndLog("Please ensure you have the authenticator combination set up: "
+                        + allowedAuthenticators);
+                return;
+            }
+
+            createUserAuthenticationKey(keyName, timeout, getKeyAuthenticators(), useStrongBox);
+
+            CryptoObject crypto;
+
+            // For auth-per-use keys, the keystore operation needs to be initialized before
+            // authenticating, so we can wrap it into a CryptoObject. For time-based keys, the
+            // keystore operation can only be initialized after authentication has occurred.
+            if (requiresCryptoObject) {
+                initializeKeystoreOperation(keyName);
+                crypto = getCryptoObject();
+            } else {
+                crypto = null;
+            }
+
+            final BiometricPrompt.Builder builder = new BiometricPrompt.Builder(this);
+            builder.setTitle("Please authenticate");
+            builder.setAllowedAuthenticators(allowedAuthenticators);
+
+            // The BiometricPrompt API requires a negative button if credential is not allowed.
+            if ((allowedAuthenticators & Authenticators.DEVICE_CREDENTIAL) == 0) {
+                builder.setNegativeButton("Cancel", mExecutor, (dialog, which) -> {
+                    // Do nothing
+                });
+            }
+
+            final AuthenticationCallback callback = new AuthenticationCallback() {
+                @Override
+                public void onAuthenticationSucceeded(AuthenticationResult result) {
+                    // Key generation / initialization can depend on past authentication. Ensure
+                    // that the user has not authenticated within n+1 seconds before allowing the
+                    // next test to start.
+                    disableTestsForFewSeconds();
+
+                    Exception exception = null;
+                    boolean keyUsed;
+                    try {
+                        if (!requiresCryptoObject) {
+                            initializeKeystoreOperation(keyName);
+                        }
+
+                        doKeystoreOperation(payload);
+
+                        keyUsed = true;
+                    } catch (Exception e) {
+                        keyUsed = false;
+                        exception = e;
+                    }
+
+                    if (keyUsed != shouldKeyBeUsable) {
+                        showToastAndLog("Test failed. shouldKeyBeUsable: " + shouldKeyBeUsable
+                                + " keyUsed: " + keyUsed + " Exception: " + exception);
+                        if (exception != null) {
+                            exception.printStackTrace();
+                        }
+                    } else {
+                        // Set them to invisible, because for this test, disabled actually means
+                        // something else. For the initialization of some keys, its success/failure
+                        // can depend on if the user has entered their credential within the last
+                        // "n" seconds. Those tests need to be disabled until "n" has passed.
+                        testButton.setVisibility(View.INVISIBLE);
+                    }
+                    updatePassButton();
+                }
+            };
+
+
+            final BiometricPrompt prompt = builder.build();
+
+            if (requiresCryptoObject) {
+                prompt.authenticate(crypto, new CancellationSignal(), mExecutor, callback);
+            } else {
+                prompt.authenticate(new CancellationSignal(), mExecutor, callback);
+            }
+
+        } catch (Exception e) {
+            showToastAndLog("Failed during Crypto test: " + e);
+            e.printStackTrace();
+        }
+    }
+
+    private void disableTestsForFewSeconds() {
+        for (Button b : mButtons) {
+            b.setEnabled(false);
+        }
+
+        mHandler.postDelayed(() -> {
+            for (Button b : mButtons) {
+                b.setEnabled(true);
+            }
+        }, TIMED_KEY_DURATION * 1000 + 1000);
+    }
+
+    private void updatePassButton() {
+        for (Button b : mButtons) {
+            if (b.getVisibility() == View.VISIBLE) {
+                return;
+            }
+        }
+
+        showToastAndLog("All tests passed");
+        getPassButton().setEnabled(true);
+    }
+
+    private void showToastAndLog(String s) {
+        Log.d(getTag(), s);
+        Toast.makeText(this, s, Toast.LENGTH_SHORT).show();
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/biometrics/BiometricStrongTests.java b/apps/CtsVerifier/src/com/android/cts/verifier/biometrics/BiometricStrongTests.java
index 079c56f..0d54cbd 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/biometrics/BiometricStrongTests.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/biometrics/BiometricStrongTests.java
@@ -75,6 +75,7 @@
     private Button mAuthenticateCredential3Button; // setAllowedAuthenticators(CREDENTIAL|BIOMETRIC)
     private Button mCheckInvalidInputsButton;
     private Button mRejectThenAuthenticateButton;
+    private Button mNegativeButtonButton;
     private Button mKeyInvalidatedButton;
 
     private boolean mAuthenticateWithoutStrongBoxPassed;
@@ -85,6 +86,7 @@
     private boolean mAuthenticateCredential3Passed;
     private boolean mCheckInvalidInputsPassed;
     private boolean mRejectThenAuthenticatePassed;
+    private boolean mNegativeButtonPassed;
     private boolean mKeyInvalidatedStrongboxPassed;
     private boolean mKeyInvalidatedNoStrongboxPassed;
 
@@ -108,6 +110,7 @@
             mAuthenticateCredential3Button.setEnabled(true);
             mCheckInvalidInputsButton.setEnabled(true);
             mRejectThenAuthenticateButton.setEnabled(true);
+            mNegativeButtonButton.setEnabled(true);
         } else {
             showToastAndLog("Unexpected result after enrollment: " + biometricStatus);
         }
@@ -132,6 +135,7 @@
                 R.id.authenticate_credential_setAllowedAuthenticators_credential_button);
         mCheckInvalidInputsButton = findViewById(R.id.authenticate_invalid_inputs);
         mRejectThenAuthenticateButton = findViewById(R.id.authenticate_reject_first);
+        mNegativeButtonButton = findViewById(R.id.authenticate_negative_button_button);
         mKeyInvalidatedButton = findViewById(R.id.authenticate_key_invalidated_button);
 
         mHasStrongBox = getPackageManager()
@@ -210,6 +214,14 @@
             });
         });
 
+        mNegativeButtonButton.setOnClickListener((view) -> {
+            testNegativeButtonCallback(Authenticators.BIOMETRIC_STRONG, () -> {
+                mNegativeButtonPassed = true;
+                mNegativeButtonButton.setEnabled(false);
+                updatePassButton();
+            });
+        });
+
         mKeyInvalidatedButton.setOnClickListener((view) -> {
             Utils.showInstructionDialog(this,
                     R.string.biometric_test_strong_authenticate_invalidated_instruction_title,
@@ -254,7 +266,8 @@
         if (mAuthenticateWithoutStrongBoxPassed && mAuthenticateWithStrongBoxPassed
                 && mAuthenticateUIPassed && mAuthenticateCredential1Passed
                 && mAuthenticateCredential2Passed && mAuthenticateCredential3Passed
-                && mCheckInvalidInputsPassed && mRejectThenAuthenticatePassed) {
+                && mCheckInvalidInputsPassed && mRejectThenAuthenticatePassed
+                && mNegativeButtonPassed) {
             return true;
         }
 
@@ -345,7 +358,8 @@
         if (mAuthenticateWithoutStrongBoxPassed && mAuthenticateWithStrongBoxPassed
                 && mAuthenticateUIPassed && mAuthenticateCredential1Passed
                 && mAuthenticateCredential2Passed && mAuthenticateCredential3Passed
-                && mCheckInvalidInputsPassed && mRejectThenAuthenticatePassed) {
+                && mCheckInvalidInputsPassed && mRejectThenAuthenticatePassed
+                && mNegativeButtonPassed) {
 
             if (!mKeyInvalidatedStrongboxPassed || !mKeyInvalidatedNoStrongboxPassed) {
                 mKeyInvalidatedButton.setEnabled(true);
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/biometrics/BiometricWeakTests.java b/apps/CtsVerifier/src/com/android/cts/verifier/biometrics/BiometricWeakTests.java
index 3ce9ccb..3cac14b 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/biometrics/BiometricWeakTests.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/biometrics/BiometricWeakTests.java
@@ -56,6 +56,7 @@
     private Button mAuthenticateCredential3Button; // setAllowedAuthenticators(CREDENTIAL|BIOMETRIC)
     private Button mCheckInvalidInputsButton;
     private Button mRejectThenAuthenticateButton;
+    private Button mNegativeButtonButton;
 
     private boolean mAuthenticatePassed;
     private boolean mAuthenticateCredential1Passed;
@@ -63,6 +64,7 @@
     private boolean mAuthenticateCredential3Passed;
     private boolean mCheckInvalidInputsPassed;
     private boolean mRejectThenAuthenticatePassed;
+    private boolean mNegativeButtonPassed;
 
     @Override
     protected String getTag() {
@@ -86,6 +88,7 @@
                 R.id.authenticate_credential_setAllowedAuthenticators_credential_button);
         mCheckInvalidInputsButton = findViewById(R.id.authenticate_invalid_inputs);
         mRejectThenAuthenticateButton = findViewById(R.id.authenticate_reject_first);
+        mNegativeButtonButton = findViewById(R.id.authenticate_negative_button_button);
 
         mEnrollButton.setOnClickListener((view) -> {
             checkAndEnroll(mEnrollButton, Authenticators.BIOMETRIC_WEAK,
@@ -181,6 +184,14 @@
                 updatePassButton();
             });
         });
+
+        mNegativeButtonButton.setOnClickListener((view) -> {
+            testNegativeButtonCallback(Authenticators.BIOMETRIC_WEAK, () -> {
+                mNegativeButtonPassed = true;
+                mNegativeButtonButton.setEnabled(false);
+                updatePassButton();
+            });
+        });
     }
 
     @Override
@@ -200,7 +211,8 @@
     private void updatePassButton() {
         if (mAuthenticatePassed && mAuthenticateCredential1Passed
                 && mAuthenticateCredential2Passed && mAuthenticateCredential3Passed
-                && mCheckInvalidInputsPassed && mRejectThenAuthenticatePassed) {
+                && mCheckInvalidInputsPassed && mRejectThenAuthenticatePassed
+                && mNegativeButtonPassed) {
             showToastAndLog("All tests passed");
             getPassButton().setEnabled(true);
         }
@@ -219,6 +231,7 @@
             mAuthenticateCredential3Button.setEnabled(true);
             mCheckInvalidInputsButton.setEnabled(true);
             mRejectThenAuthenticateButton.setEnabled(true);
+            mNegativeButtonButton.setEnabled(true);
         } else {
             showToastAndLog("Unexpected result after enrollment: " + biometricStatus);
         }
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/biometrics/CredentialEnrolledTests.java b/apps/CtsVerifier/src/com/android/cts/verifier/biometrics/CredentialEnrolledTests.java
index 0186ecb..9b07146 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/biometrics/CredentialEnrolledTests.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/biometrics/CredentialEnrolledTests.java
@@ -25,11 +25,8 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.provider.Settings;
-import android.util.Log;
 import android.widget.Button;
-import android.widget.Toast;
 
-import com.android.cts.verifier.PassFailButtons;
 import com.android.cts.verifier.R;
 
 import java.util.concurrent.Executor;
@@ -41,8 +38,7 @@
 public class CredentialEnrolledTests extends AbstractBaseTest {
     private static final String TAG = "CredentialEnrolledTests";
 
-    private static final int REQUEST_ENROLL_WHEN_NONE_ENROLLED = 1;
-    private static final int REQUEST_ENROLL_WHEN_ENROLLED = 2;
+    private static final int REQUEST_ENROLL = 1;
 
     private Button mEnrollButton;
     private Button mBiometricManagerButton;
@@ -79,7 +75,7 @@
                 return;
             }
 
-            requestCredentialEnrollment(REQUEST_ENROLL_WHEN_NONE_ENROLLED);
+            requestCredentialEnrollment(REQUEST_ENROLL);
         });
 
         // Test BiometricManager#canAuthenticate(DEVICE_CREDENTIAL)
@@ -189,31 +185,19 @@
 
     @Override
     public void onActivityResult(int requestCode, int resultCode, Intent data) {
-        if (requestCode == REQUEST_ENROLL_WHEN_NONE_ENROLLED) {
-            if (resultCode == RESULT_OK) {
-                final BiometricManager bm = getSystemService(BiometricManager.class);
-                final int result = bm.canAuthenticate(Authenticators.DEVICE_CREDENTIAL);
-                if (result == BiometricManager.BIOMETRIC_SUCCESS) {
-                    // Request enrollment one more time. Ensure that we receive RESULT_CANCELED
-                    requestCredentialEnrollment(REQUEST_ENROLL_WHEN_ENROLLED);
-                } else {
-                    showToastAndLog("Unexpected result: " + result + ". Please ensure that tapping"
-                            + " the button sends you to credential enrollment, and that you have"
-                            + " enrolled a credential.");
-                }
-            } else {
-                showToastAndLog("Unexpected result after enroll: " + resultCode);
-            }
-        } else if (requestCode == REQUEST_ENROLL_WHEN_ENROLLED) {
-            if (resultCode == RESULT_CANCELED) {
+        if (requestCode == REQUEST_ENROLL) {
+            final BiometricManager bm = getSystemService(BiometricManager.class);
+            final int result = bm.canAuthenticate(Authenticators.DEVICE_CREDENTIAL);
+            if (result == BiometricManager.BIOMETRIC_SUCCESS) {
                 mEnrollPass = true;
                 mEnrollButton.setEnabled(false);
                 mBiometricManagerButton.setEnabled(true);
                 mBPSetAllowedAuthenticatorsButton.setEnabled(true);
                 mBPSetDeviceCredentialAllowedButton.setEnabled(true);
             } else {
-                showToastAndLog("Unexpected result when requesting enrolling with"
-                        + " pre-existing credential: " + resultCode);
+                showToastAndLog("Unexpected result: " + result + ". Please ensure that tapping"
+                        + " the button sends you to credential enrollment, and that you have"
+                        + " enrolled a credential.");
             }
         }
     }
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/biometrics/UserAuthenticationBiometricCipherTest.java b/apps/CtsVerifier/src/com/android/cts/verifier/biometrics/UserAuthenticationBiometricCipherTest.java
new file mode 100644
index 0000000..e73c902
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/biometrics/UserAuthenticationBiometricCipherTest.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2020 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.cts.verifier.biometrics;
+
+import android.security.keystore.KeyProperties;
+
+import com.android.cts.verifier.R;
+
+public class UserAuthenticationBiometricCipherTest extends AbstractUserAuthenticationCipherTest {
+
+    private static final String TAG = "UserAuthenticationBiometricCipherTest";
+
+    @Override
+    String getTag() {
+        return TAG;
+    }
+
+    @Override
+    int getInstructionsResourceId() {
+        return R.string.biometric_test_set_user_authentication_biometric_instructions;
+    }
+
+    @Override
+    ExpectedResults getExpectedResults() {
+        return new ExpectedResults() {
+            @Override
+            boolean shouldCredentialUnlockPerUseKey() {
+                return false;
+            }
+
+            @Override
+            boolean shouldCredentialUnlockTimedKey() {
+                return false;
+            }
+
+            @Override
+            boolean shouldBiometricUnlockPerUseKey() {
+                return true;
+            }
+
+            @Override
+            boolean shouldBiometricUnlockTimedKey() {
+                return true;
+            }
+        };
+    }
+
+    @Override
+    int getKeyAuthenticators() {
+        return KeyProperties.AUTH_BIOMETRIC_STRONG;
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/biometrics/UserAuthenticationBiometricMacTest.java b/apps/CtsVerifier/src/com/android/cts/verifier/biometrics/UserAuthenticationBiometricMacTest.java
new file mode 100644
index 0000000..6e7c8ef
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/biometrics/UserAuthenticationBiometricMacTest.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2020 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.cts.verifier.biometrics;
+
+import android.security.keystore.KeyProperties;
+
+import com.android.cts.verifier.R;
+
+public class UserAuthenticationBiometricMacTest extends AbstractUserAuthenticationMacTest {
+    private static final String TAG = "UserAuthenticationBiometricMacTest";
+
+    @Override
+    String getTag() {
+        return TAG;
+    }
+
+    @Override
+    int getInstructionsResourceId() {
+        return R.string.biometric_test_set_user_authentication_biometric_instructions;
+    }
+
+    @Override
+    ExpectedResults getExpectedResults() {
+        return new ExpectedResults() {
+            @Override
+            boolean shouldCredentialUnlockPerUseKey() {
+                return false;
+            }
+
+            @Override
+            boolean shouldCredentialUnlockTimedKey() {
+                return false;
+            }
+
+            @Override
+            boolean shouldBiometricUnlockPerUseKey() {
+                return true;
+            }
+
+            @Override
+            boolean shouldBiometricUnlockTimedKey() {
+                return true;
+            }
+        };
+    }
+
+    @Override
+    int getKeyAuthenticators() {
+        return KeyProperties.AUTH_BIOMETRIC_STRONG;
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/biometrics/UserAuthenticationBiometricOrCredentialCipherTest.java b/apps/CtsVerifier/src/com/android/cts/verifier/biometrics/UserAuthenticationBiometricOrCredentialCipherTest.java
new file mode 100644
index 0000000..03f4ff6
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/biometrics/UserAuthenticationBiometricOrCredentialCipherTest.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2020 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.cts.verifier.biometrics;
+
+import android.security.keystore.KeyProperties;
+
+import com.android.cts.verifier.R;
+
+public class UserAuthenticationBiometricOrCredentialCipherTest
+        extends AbstractUserAuthenticationCipherTest {
+
+    private static final String TAG = "UserAuthenticationBiometricOrCredentialCipherTest";
+
+    @Override
+    String getTag() {
+        return TAG;
+    }
+
+    @Override
+    int getInstructionsResourceId() {
+        return R.string.biometric_test_set_user_authentication_biometric_or_credential_instructions;
+    }
+
+    @Override
+    ExpectedResults getExpectedResults() {
+        return new ExpectedResults() {
+            @Override
+            boolean shouldCredentialUnlockPerUseKey() {
+                return true;
+            }
+
+            @Override
+            boolean shouldCredentialUnlockTimedKey() {
+                return true;
+            }
+
+            @Override
+            boolean shouldBiometricUnlockPerUseKey() {
+                return true;
+            }
+
+            @Override
+            boolean shouldBiometricUnlockTimedKey() {
+                return true;
+            }
+        };
+    }
+
+    @Override
+    int getKeyAuthenticators() {
+        return KeyProperties.AUTH_DEVICE_CREDENTIAL | KeyProperties.AUTH_BIOMETRIC_STRONG;
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/biometrics/UserAuthenticationBiometricOrCredentialMacTest.java b/apps/CtsVerifier/src/com/android/cts/verifier/biometrics/UserAuthenticationBiometricOrCredentialMacTest.java
new file mode 100644
index 0000000..8657b8b
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/biometrics/UserAuthenticationBiometricOrCredentialMacTest.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2020 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.cts.verifier.biometrics;
+
+import android.security.keystore.KeyProperties;
+
+import com.android.cts.verifier.R;
+
+public class UserAuthenticationBiometricOrCredentialMacTest
+        extends AbstractUserAuthenticationMacTest {
+    private static final String TAG = "UserAuthenticationBiometricOrCredentialMacTest";
+    @Override
+    String getTag() {
+        return TAG;
+    }
+
+    @Override
+    int getInstructionsResourceId() {
+        return R.string.biometric_test_set_user_authentication_biometric_or_credential_instructions;
+    }
+
+    @Override
+    ExpectedResults getExpectedResults() {
+        return new ExpectedResults() {
+            @Override
+            boolean shouldCredentialUnlockPerUseKey() {
+                return true;
+            }
+
+            @Override
+            boolean shouldCredentialUnlockTimedKey() {
+                return true;
+            }
+
+            @Override
+            boolean shouldBiometricUnlockPerUseKey() {
+                return true;
+            }
+
+            @Override
+            boolean shouldBiometricUnlockTimedKey() {
+                return true;
+            }
+        };
+    }
+
+    @Override
+    int getKeyAuthenticators() {
+        return KeyProperties.AUTH_DEVICE_CREDENTIAL | KeyProperties.AUTH_BIOMETRIC_STRONG;
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/biometrics/UserAuthenticationBiometricOrCredentialSignatureTest.java b/apps/CtsVerifier/src/com/android/cts/verifier/biometrics/UserAuthenticationBiometricOrCredentialSignatureTest.java
new file mode 100644
index 0000000..a88b081
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/biometrics/UserAuthenticationBiometricOrCredentialSignatureTest.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2020 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.cts.verifier.biometrics;
+
+import android.security.keystore.KeyProperties;
+
+import com.android.cts.verifier.R;
+
+public class UserAuthenticationBiometricOrCredentialSignatureTest
+        extends AbstractUserAuthenticationSignatureTest {
+    private static final String TAG = "UserAuthenticationBiometricOrCredentialSignatureTest";
+
+    @Override
+    String getTag() {
+        return TAG;
+    }
+
+    @Override
+    int getInstructionsResourceId() {
+        return R.string.biometric_test_set_user_authentication_biometric_or_credential_instructions;
+    }
+
+    @Override
+    ExpectedResults getExpectedResults() {
+        return new ExpectedResults() {
+            @Override
+            boolean shouldCredentialUnlockPerUseKey() {
+                return true;
+            }
+
+            @Override
+            boolean shouldCredentialUnlockTimedKey() {
+                return true;
+            }
+
+            @Override
+            boolean shouldBiometricUnlockPerUseKey() {
+                return true;
+            }
+
+            @Override
+            boolean shouldBiometricUnlockTimedKey() {
+                return true;
+            }
+        };
+    }
+
+    @Override
+    int getKeyAuthenticators() {
+        return KeyProperties.AUTH_DEVICE_CREDENTIAL | KeyProperties.AUTH_BIOMETRIC_STRONG;
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/biometrics/UserAuthenticationBiometricSignatureTest.java b/apps/CtsVerifier/src/com/android/cts/verifier/biometrics/UserAuthenticationBiometricSignatureTest.java
new file mode 100644
index 0000000..8e8a8b2
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/biometrics/UserAuthenticationBiometricSignatureTest.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2020 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.cts.verifier.biometrics;
+
+import android.security.keystore.KeyProperties;
+
+import com.android.cts.verifier.R;
+
+public class UserAuthenticationBiometricSignatureTest
+        extends AbstractUserAuthenticationSignatureTest {
+    private static final String TAG = "UserAuthenticationBiometricSignatureTest";
+
+    @Override
+    String getTag() {
+        return TAG;
+    }
+
+    @Override
+    int getInstructionsResourceId() {
+        return R.string.biometric_test_set_user_authentication_biometric_instructions;
+    }
+
+    @Override
+    ExpectedResults getExpectedResults() {
+        return new ExpectedResults() {
+            @Override
+            boolean shouldCredentialUnlockPerUseKey() {
+                return false;
+            }
+
+            @Override
+            boolean shouldCredentialUnlockTimedKey() {
+                return false;
+            }
+
+            @Override
+            boolean shouldBiometricUnlockPerUseKey() {
+                return true;
+            }
+
+            @Override
+            boolean shouldBiometricUnlockTimedKey() {
+                return true;
+            }
+        };
+    }
+
+    @Override
+    int getKeyAuthenticators() {
+        return KeyProperties.AUTH_BIOMETRIC_STRONG;
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/biometrics/UserAuthenticationCredentialCipherTest.java b/apps/CtsVerifier/src/com/android/cts/verifier/biometrics/UserAuthenticationCredentialCipherTest.java
new file mode 100644
index 0000000..ccdee1a
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/biometrics/UserAuthenticationCredentialCipherTest.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2020 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.cts.verifier.biometrics;
+
+import android.security.keystore.KeyProperties;
+
+import com.android.cts.verifier.R;
+
+public class UserAuthenticationCredentialCipherTest extends AbstractUserAuthenticationCipherTest {
+
+    private static final String TAG = "UserAuthenticationCredentialCipherTest";
+
+    @Override
+    String getTag() {
+        return TAG;
+    }
+
+    @Override
+    int getInstructionsResourceId() {
+        return R.string.biometric_test_set_user_authentication_credential_instructions;
+    }
+
+    @Override
+    ExpectedResults getExpectedResults() {
+        return new ExpectedResults() {
+            @Override
+            boolean shouldCredentialUnlockPerUseKey() {
+                return true;
+            }
+
+            @Override
+            boolean shouldCredentialUnlockTimedKey() {
+                return true;
+            }
+
+            @Override
+            boolean shouldBiometricUnlockPerUseKey() {
+                return false;
+            }
+
+            @Override
+            boolean shouldBiometricUnlockTimedKey() {
+                return false;
+            }
+        };
+    }
+
+    @Override
+    int getKeyAuthenticators() {
+        return KeyProperties.AUTH_DEVICE_CREDENTIAL;
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/biometrics/UserAuthenticationCredentialMacTest.java b/apps/CtsVerifier/src/com/android/cts/verifier/biometrics/UserAuthenticationCredentialMacTest.java
new file mode 100644
index 0000000..9c4a63d
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/biometrics/UserAuthenticationCredentialMacTest.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2020 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.cts.verifier.biometrics;
+
+import android.security.keystore.KeyProperties;
+
+import com.android.cts.verifier.R;
+
+public class UserAuthenticationCredentialMacTest extends AbstractUserAuthenticationMacTest {
+    private static final String TAG = "UserAuthenticationCredentialMacTest";
+
+    @Override
+    String getTag() {
+        return TAG;
+    }
+
+    @Override
+    int getInstructionsResourceId() {
+        return R.string.biometric_test_set_user_authentication_credential_instructions;
+    }
+
+    @Override
+    ExpectedResults getExpectedResults() {
+        return new ExpectedResults() {
+            @Override
+            boolean shouldCredentialUnlockPerUseKey() {
+                return true;
+            }
+
+            @Override
+            boolean shouldCredentialUnlockTimedKey() {
+                return true;
+            }
+
+            @Override
+            boolean shouldBiometricUnlockPerUseKey() {
+                return false;
+            }
+
+            @Override
+            boolean shouldBiometricUnlockTimedKey() {
+                return false;
+            }
+        };
+    }
+
+    @Override
+    int getKeyAuthenticators() {
+        return KeyProperties.AUTH_DEVICE_CREDENTIAL;
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/biometrics/UserAuthenticationCredentialSignatureTest.java b/apps/CtsVerifier/src/com/android/cts/verifier/biometrics/UserAuthenticationCredentialSignatureTest.java
new file mode 100644
index 0000000..16487ea
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/biometrics/UserAuthenticationCredentialSignatureTest.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2020 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.cts.verifier.biometrics;
+
+import android.security.keystore.KeyProperties;
+
+import com.android.cts.verifier.R;
+
+public class UserAuthenticationCredentialSignatureTest
+        extends AbstractUserAuthenticationSignatureTest {
+    private static final String TAG = "UserAuthenticationCredentialSignatureTest";
+
+    @Override
+    String getTag() {
+        return TAG;
+    }
+
+    @Override
+    int getInstructionsResourceId() {
+        return R.string.biometric_test_set_user_authentication_credential_instructions;
+    }
+
+    @Override
+    ExpectedResults getExpectedResults() {
+        return new ExpectedResults() {
+            @Override
+            boolean shouldCredentialUnlockPerUseKey() {
+                return true;
+            }
+
+            @Override
+            boolean shouldCredentialUnlockTimedKey() {
+                return true;
+            }
+
+            @Override
+            boolean shouldBiometricUnlockPerUseKey() {
+                return false;
+            }
+
+            @Override
+            boolean shouldBiometricUnlockTimedKey() {
+                return false;
+            }
+        };
+    }
+
+    @Override
+    int getKeyAuthenticators() {
+        return KeyProperties.AUTH_DEVICE_CREDENTIAL;
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/biometrics/Utils.java b/apps/CtsVerifier/src/com/android/cts/verifier/biometrics/Utils.java
index 3cce889..5d8db94 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/biometrics/Utils.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/biometrics/Utils.java
@@ -28,11 +28,16 @@
 import android.widget.LinearLayout;
 import android.widget.Toast;
 
+import java.security.KeyPair;
 import java.security.KeyStore;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.Signature;
 import java.util.Random;
 
 import javax.crypto.Cipher;
 import javax.crypto.KeyGenerator;
+import javax.crypto.Mac;
 import javax.crypto.SecretKey;
 
 public class Utils {
@@ -88,10 +93,42 @@
         return cipher;
     }
 
+    static Signature initSignature(String keyName) throws Exception {
+        KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
+        keyStore.load(null);
+
+        KeyStore.Entry entry = keyStore.getEntry(keyName, null);
+
+        PrivateKey privateKey = ((KeyStore.PrivateKeyEntry) entry).getPrivateKey();
+
+        // TODO: This can be used to verify signature
+        // PublicKey publicKey = keyStore.getCertificate(keyName).getPublicKey();
+
+        Signature signature = Signature.getInstance("SHA256withECDSA");
+        signature.initSign(privateKey);
+        return signature;
+    }
+
+    static Mac initMac(String keyName) throws Exception {
+        KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
+        keyStore.load(null);
+
+        SecretKey secretKey = (SecretKey) keyStore.getKey(keyName, null);
+
+        Mac mac = Mac.getInstance("HmacSHA256");
+        mac.init(secretKey);
+        return mac;
+    }
+
     static byte[] doEncrypt(Cipher cipher, byte[] data) throws Exception {
         return cipher.doFinal(data);
     }
 
+    static byte[] doSign(Signature signature, byte[] data) throws Exception {
+        signature.update(data);
+        return signature.sign();
+    }
+
     static void showInstructionDialog(Context context, int titleRes, int messageRes,
             int positiveButtonRes, DialogInterface.OnClickListener listener) {
         AlertDialog.Builder builder = new AlertDialog.Builder(context);