Fix biometric lint issues and update lint-baseline

Test: Biometric integration test app on API 23, 27-30
Test: ./gradlew biometric:biometric:test
Test: ./gradlew biometric:biometric:connectedAndroidTest
Test: ./gradlew biometric:integration-tests:testapp:connectedAndroidTest
Change-Id: Icc2f9b0c4358d2c207f0992258d8217800160c36
diff --git a/biometric/biometric/lint-baseline.xml b/biometric/biometric/lint-baseline.xml
index 3b429d7..8f1aa4b 100644
--- a/biometric/biometric/lint-baseline.xml
+++ b/biometric/biometric/lint-baseline.xml
@@ -1,99 +1,4 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="5" by="lint 4.2.0-alpha15" client="gradle" variant="debug" version="4.2.0-alpha15">
-
-    <issue
-        id="ObsoleteLintCustomCheck"
-        message="Lint found an issue registry (`androidx.fragment.lint.FragmentIssueRegistry`) which is older than the current API level; these checks may not work correctly.&#xA;&#xA;Recompile the checks against the latest version. Custom check API version is 6 (3.6), current lint API level is 8 (4.1)">
-        <location
-            file="../../../../../../home/jeffrygaston/.gradle/caches/transforms-2/files-2.1/e3e8070ff55bdd44622c92131fecd482/fragment-1.2.5/jars/lint.jar"/>
-    </issue>
-
-    <issue
-        id="UnusedAttribute"
-        message="Attribute `accessibilityLiveRegion` is only used in API level 19 and higher (current min is 14)"
-        errorLine1="            android:accessibilityLiveRegion=&quot;polite&quot;"
-        errorLine2="            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/res/layout/fingerprint_dialog_layout.xml"
-            line="69"
-            column="13"/>
-    </issue>
-
-    <issue
-        id="BanUncheckedReflection"
-        message="Calling Method.invoke without an SDK check"
-        errorLine1="                            canAuthenticateWithCrypto.invoke(mBiometricManager, crypto);"
-        errorLine2="                            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/biometric/BiometricManager.java"
-            line="426"
-            column="29"/>
-    </issue>
-
-    <issue
-        id="UnsafeNewApiCall"
-        message="This call is to a method from API 28, the call containing class null is not annotated with @RequiresApi(x) where x is at least 28. Either annotate the containing class with at least @RequiresApi(28) or move the call to a static method in a wrapper class annotated with at least @RequiresApi(28)."
-        errorLine1="                            ? CryptoObjectUtils.unwrapFromBiometricPrompt(result.getCryptoObject())"
-        errorLine2="                                                                                 ~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/biometric/AuthenticationCallbackProvider.java"
-            line="229"
-            column="82"/>
-    </issue>
-
-    <issue
-        id="VectorRaster"
-        message="This attribute is not supported in images generated from this vector icon for API &lt; 24; check generated icon to make sure it looks acceptable"
-        errorLine1="                  android:trimPathEnd=&quot;1&quot; android:trimPathOffset=&quot;0&quot;"
-        errorLine2="                  ~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/res/drawable-v23/fingerprint_dialog_error.xml"
-            line="26"
-            column="19"/>
-    </issue>
-
-    <issue
-        id="VectorRaster"
-        message="This attribute is not supported in images generated from this vector icon for API &lt; 24; check generated icon to make sure it looks acceptable"
-        errorLine1="                  android:trimPathEnd=&quot;1&quot; android:trimPathOffset=&quot;0&quot;"
-        errorLine2="                                          ~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/res/drawable-v23/fingerprint_dialog_error.xml"
-            line="26"
-            column="43"/>
-    </issue>
-
-    <issue
-        id="VectorRaster"
-        message="This attribute is not supported in images generated from this vector icon for API &lt; 24; check generated icon to make sure it looks acceptable"
-        errorLine1="                  android:trimPathStart=&quot;0&quot;/>"
-        errorLine2="                  ~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/res/drawable-v23/fingerprint_dialog_error.xml"
-            line="27"
-            column="19"/>
-    </issue>
-
-    <issue
-        id="TrulyRandom"
-        message="Potentially insecure random numbers on Android 4.3 and older. Read https://android-developers.blogspot.com/2013/08/some-securerandom-thoughts.html for more info."
-        errorLine1="                    KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, KEYSTORE_INSTANCE);"
-        errorLine2="                                 ~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/biometric/CryptoObjectUtils.java"
-            line="253"
-            column="34"/>
-    </issue>
-
-    <issue
-        id="ContentDescription"
-        message="Missing `contentDescription` attribute on image"
-        errorLine1="        &lt;ImageView"
-        errorLine2="         ~~~~~~~~~">
-        <location
-            file="src/main/res/layout/fingerprint_dialog_layout.xml"
-            line="51"
-            column="10"/>
-    </issue>
+<issues format="5" by="lint 4.2.0-beta02" client="gradle" variant="debug" version="4.2.0-beta02">
 
 </issues>
diff --git a/biometric/biometric/src/main/java/androidx/biometric/AuthenticationCallbackProvider.java b/biometric/biometric/src/main/java/androidx/biometric/AuthenticationCallbackProvider.java
index db6e9f0..973c722 100644
--- a/biometric/biometric/src/main/java/androidx/biometric/AuthenticationCallbackProvider.java
+++ b/biometric/biometric/src/main/java/androidx/biometric/AuthenticationCallbackProvider.java
@@ -226,7 +226,7 @@
                         android.hardware.biometrics.BiometricPrompt.AuthenticationResult result) {
 
                     final BiometricPrompt.CryptoObject crypto = result != null
-                            ? CryptoObjectUtils.unwrapFromBiometricPrompt(result.getCryptoObject())
+                            ? CryptoObjectUtils.unwrapFromBiometricPrompt(getCryptoObject(result))
                             : null;
 
                     @BiometricPrompt.AuthenticationResultType final int authenticationType;
@@ -252,5 +252,20 @@
                 }
             };
         }
+
+        /**
+         * Gets the crypto object from the given framework authentication result.
+         *
+         * @param result An instance of
+         *               {@link android.hardware.biometrics.BiometricPrompt.AuthenticationResult}.
+         * @return The value returned by calling {@link
+         * android.hardware.biometrics.BiometricPrompt.AuthenticationResult#getCryptoObject()} for
+         * the given result object.
+         */
+        @Nullable
+        static android.hardware.biometrics.BiometricPrompt.CryptoObject getCryptoObject(
+                @NonNull android.hardware.biometrics.BiometricPrompt.AuthenticationResult result) {
+            return result.getCryptoObject();
+        }
     }
 }
diff --git a/biometric/biometric/src/main/java/androidx/biometric/BiometricFragment.java b/biometric/biometric/src/main/java/androidx/biometric/BiometricFragment.java
index 3b28c82..3121f90 100644
--- a/biometric/biometric/src/main/java/androidx/biometric/BiometricFragment.java
+++ b/biometric/biometric/src/main/java/androidx/biometric/BiometricFragment.java
@@ -440,6 +440,11 @@
             return;
         }
 
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
+            Log.e(TAG, "Unable to show fingerprint dialog on API <19.");
+            return;
+        }
+
         if (isAdded()) {
             mViewModel.setFingerprintDialogDismissedInstantly(true);
             if (!DeviceUtils.shouldHideFingerprintDialog(context, Build.MODEL)) {
diff --git a/biometric/biometric/src/main/java/androidx/biometric/BiometricManager.java b/biometric/biometric/src/main/java/androidx/biometric/BiometricManager.java
index 2334cd6..ae96709 100644
--- a/biometric/biometric/src/main/java/androidx/biometric/BiometricManager.java
+++ b/biometric/biometric/src/main/java/androidx/biometric/BiometricManager.java
@@ -423,7 +423,9 @@
             if (crypto != null) {
                 try {
                     final Object result =
-                            canAuthenticateWithCrypto.invoke(mBiometricManager, crypto);
+                            Build.VERSION.SDK_INT == Build.VERSION_CODES.Q
+                                    ? canAuthenticateWithCrypto.invoke(mBiometricManager, crypto)
+                                    : null;
                     if (result instanceof Integer) {
                         return (int) result;
                     }
diff --git a/biometric/biometric/src/main/java/androidx/biometric/BiometricViewModel.java b/biometric/biometric/src/main/java/androidx/biometric/BiometricViewModel.java
index 12ecf70..47efe15 100644
--- a/biometric/biometric/src/main/java/androidx/biometric/BiometricViewModel.java
+++ b/biometric/biometric/src/main/java/androidx/biometric/BiometricViewModel.java
@@ -154,7 +154,7 @@
     /**
      * Reference to latest {@link androidx.fragment.app.FragmentActivity} hosting BiometricPrompt
      */
-    @NonNull private WeakReference<FragmentActivity> mClientActivity;
+    @Nullable private WeakReference<FragmentActivity> mClientActivity;
 
     /**
      * Info about the appearance and behavior of the prompt provided by the client application.
@@ -299,7 +299,7 @@
      */
     @Nullable
     public FragmentActivity getClientActivity() {
-        return mClientActivity.get();
+        return mClientActivity != null ? mClientActivity.get() : null;
     }
 
     /**
diff --git a/biometric/biometric/src/main/java/androidx/biometric/CryptoObjectUtils.java b/biometric/biometric/src/main/java/androidx/biometric/CryptoObjectUtils.java
index 051a392b..436d6a3 100644
--- a/biometric/biometric/src/main/java/androidx/biometric/CryptoObjectUtils.java
+++ b/biometric/biometric/src/main/java/androidx/biometric/CryptoObjectUtils.java
@@ -16,6 +16,7 @@
 
 package androidx.biometric;
 
+import android.annotation.SuppressLint;
 import android.os.Build;
 import android.security.keystore.KeyGenParameterSpec;
 import android.security.keystore.KeyProperties;
@@ -235,6 +236,7 @@
      *
      * @return An internal-only instance of {@link androidx.biometric.BiometricPrompt.CryptoObject}.
      */
+    @SuppressLint("TrulyRandom")
     @RequiresApi(Build.VERSION_CODES.M)
     @Nullable
     static BiometricPrompt.CryptoObject createFakeCryptoObject() {
diff --git a/biometric/biometric/src/main/java/androidx/biometric/FingerprintDialogFragment.java b/biometric/biometric/src/main/java/androidx/biometric/FingerprintDialogFragment.java
index 13da791..13ae176 100644
--- a/biometric/biometric/src/main/java/androidx/biometric/FingerprintDialogFragment.java
+++ b/biometric/biometric/src/main/java/androidx/biometric/FingerprintDialogFragment.java
@@ -142,11 +142,15 @@
     @Nullable
     TextView mHelpMessageView;
 
+    // Prevent direct instantiation.
+    private FingerprintDialogFragment() {}
+
     /**
      * Creates a new instance of {@link FingerprintDialogFragment}.
      *
      * @return A {@link FingerprintDialogFragment}.
      */
+    @RequiresApi(Build.VERSION_CODES.KITKAT)
     @NonNull
     static FingerprintDialogFragment newInstance() {
         return new FingerprintDialogFragment();
diff --git a/biometric/biometric/src/main/res/drawable-v23/fingerprint_dialog_error.xml b/biometric/biometric/src/main/res/drawable-v23/fingerprint_dialog_error.xml
index 5faccf8..568431be 100644
--- a/biometric/biometric/src/main/res/drawable-v23/fingerprint_dialog_error.xml
+++ b/biometric/biometric/src/main/res/drawable-v23/fingerprint_dialog_error.xml
@@ -15,8 +15,9 @@
   ~ limitations under the License.
   -->
 
-<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp"
-        android:height="24dp" android:viewportHeight="24" android:viewportWidth="24">
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:tools="http://schemas.android.com/tools" android:width="24dp" android:height="24dp"
+        android:viewportHeight="24" android:viewportWidth="24">
     <group android:name="_R_G">
         <group android:name="_R_G_L_1_G" android:rotation="10" android:translateX="12"
                android:translateY="12">
@@ -24,7 +25,7 @@
                   android:strokeAlpha="1" android:strokeColor="#ff5722"
                   android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="2"
                   android:trimPathEnd="1" android:trimPathOffset="0"
-                  android:trimPathStart="0"/>
+                  android:trimPathStart="0" tools:ignore="VectorRaster" />
         </group>
         <group android:name="_R_G_L_0_G" android:translateX="12" android:translateY="12">
             <group android:name="_R_G_L_0_G_D_0_P_0_G_0_T_0" android:pivotY="-0.012"
@@ -41,4 +42,5 @@
             </group>
         </group>
     </group>
+
 </vector>
\ No newline at end of file
diff --git a/biometric/biometric/src/main/res/layout/fingerprint_dialog_layout.xml b/biometric/biometric/src/main/res/layout-v19/fingerprint_dialog_layout.xml
similarity index 85%
rename from biometric/biometric/src/main/res/layout/fingerprint_dialog_layout.xml
rename to biometric/biometric/src/main/res/layout-v19/fingerprint_dialog_layout.xml
index 8b3fa21..b79910a 100644
--- a/biometric/biometric/src/main/res/layout/fingerprint_dialog_layout.xml
+++ b/biometric/biometric/src/main/res/layout-v19/fingerprint_dialog_layout.xml
@@ -15,14 +15,16 @@
   ~ limitations under the License.
   -->
 
-<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
-              android:layout_width="match_parent"
-              android:layout_height="match_parent"
-              android:orientation="vertical">
+<ScrollView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical">
 
-    <LinearLayout android:layout_width="match_parent"
-                  android:layout_height="wrap_content"
-                  android:orientation="vertical">
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="vertical">
 
         <TextView
             android:id="@+id/fingerprint_subtitle"
@@ -54,6 +56,7 @@
             android:layout_height="@dimen/fingerprint_icon_size"
             android:layout_gravity="center_horizontal"
             android:layout_marginTop="32dp"
+            android:contentDescription="@string/fingerprint_dialog_icon_description"
             android:scaleType="fitXY" />
 
         <TextView
diff --git a/biometric/biometric/src/main/res/values/strings.xml b/biometric/biometric/src/main/res/values/strings.xml
index a5bff76..605afc7 100644
--- a/biometric/biometric/src/main/res/values/strings.xml
+++ b/biometric/biometric/src/main/res/values/strings.xml
@@ -45,4 +45,7 @@
     <!-- Error message for when the app requests PIN, pattern, or password authentication on a
     device without Keyguard. Generally not shown to the user. [CHAR LIMIT=NONE] -->
     <string name="generic_error_no_keyguard">This device does not support PIN, pattern, or password.</string>
+    <!-- Content description for the icon shown on the fingerprint dialog during authentication.
+    This is not shown but may be read aloud to the user for accessibility. [CHAR LIMIT=NONE] -->
+    <string name="fingerprint_dialog_icon_description">Fingerprint icon</string>
 </resources>