Merge "Identity Credential: Use assumptions to avoid failures when IC HAL not present."
diff --git a/tests/tests/identity/src/android/security/identity/cts/DynamicAuthTest.java b/tests/tests/identity/src/android/security/identity/cts/DynamicAuthTest.java
index 6d11c7b..ac34c36 100644
--- a/tests/tests/identity/src/android/security/identity/cts/DynamicAuthTest.java
+++ b/tests/tests/identity/src/android/security/identity/cts/DynamicAuthTest.java
@@ -21,6 +21,7 @@
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
 
 import android.content.Context;
 
@@ -61,6 +62,9 @@
     public void dynamicAuthTest() throws Exception {
         Context appContext = InstrumentationRegistry.getTargetContext();
         IdentityCredentialStore store = IdentityCredentialStore.getInstance(appContext);
+        if (Util.isHalOptional()) {
+            assumeTrue("IC HAL not found on device", store != null);
+        }
 
         String credentialName = "test";
 
diff --git a/tests/tests/identity/src/android/security/identity/cts/EphemeralKeyTest.java b/tests/tests/identity/src/android/security/identity/cts/EphemeralKeyTest.java
index 29e7311..791be12 100644
--- a/tests/tests/identity/src/android/security/identity/cts/EphemeralKeyTest.java
+++ b/tests/tests/identity/src/android/security/identity/cts/EphemeralKeyTest.java
@@ -21,6 +21,7 @@
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertNotNull;
+import static org.junit.Assume.assumeTrue;
 
 import android.content.Context;
 import android.security.keystore.KeyProperties;
@@ -61,6 +62,9 @@
     public void createEphemeralKey() throws IdentityCredentialException {
         Context appContext = InstrumentationRegistry.getTargetContext();
         IdentityCredentialStore store = IdentityCredentialStore.getInstance(appContext);
+        if (Util.isHalOptional()) {
+            assumeTrue("IC HAL not found on device", store != null);
+        }
 
         String credentialName = "ephemeralKeyTest";
 
diff --git a/tests/tests/identity/src/android/security/identity/cts/ProvisioningTest.java b/tests/tests/identity/src/android/security/identity/cts/ProvisioningTest.java
index d903fac..8269234 100644
--- a/tests/tests/identity/src/android/security/identity/cts/ProvisioningTest.java
+++ b/tests/tests/identity/src/android/security/identity/cts/ProvisioningTest.java
@@ -27,6 +27,7 @@
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
 
 import android.content.Context;
 
@@ -348,6 +349,9 @@
     public void alreadyPersonalized() throws IdentityCredentialException {
         Context appContext = InstrumentationRegistry.getTargetContext();
         IdentityCredentialStore store = IdentityCredentialStore.getInstance(appContext);
+        if (Util.isHalOptional()) {
+            assumeTrue("IC HAL not found on device", store != null);
+        }
 
         store.deleteCredentialByName("test");
         createCredential(store, "test");
@@ -365,6 +369,9 @@
     public void nonExistent() throws IdentityCredentialException {
         Context appContext = InstrumentationRegistry.getTargetContext();
         IdentityCredentialStore store = IdentityCredentialStore.getInstance(appContext);
+        if (Util.isHalOptional()) {
+            assumeTrue("IC HAL not found on device", store != null);
+        }
 
         store.deleteCredentialByName("test");
         IdentityCredential credential = store.getCredentialByName("test",
@@ -376,6 +383,10 @@
     public void defaultStoreSupportsAnyDocumentType() throws IdentityCredentialException {
         Context appContext = InstrumentationRegistry.getTargetContext();
         IdentityCredentialStore store = IdentityCredentialStore.getInstance(appContext);
+        if (Util.isHalOptional()) {
+            assumeTrue("IC HAL not found on device", store != null);
+        }
+
         String[] supportedDocTypes = store.getSupportedDocTypes();
         assertEquals(0, supportedDocTypes.length);
     }
@@ -385,6 +396,9 @@
             throws IdentityCredentialException, CborException, CertificateEncodingException {
         Context appContext = InstrumentationRegistry.getTargetContext();
         IdentityCredentialStore store = IdentityCredentialStore.getInstance(appContext);
+        if (Util.isHalOptional()) {
+            assumeTrue("IC HAL not found on device", store != null);
+        }
 
         store.deleteCredentialByName("test");
         assertNull(store.deleteCredentialByName("test"));
@@ -420,6 +434,9 @@
     public void testProvisionAndRetrieve() throws IdentityCredentialException, CborException {
         Context appContext = InstrumentationRegistry.getTargetContext();
         IdentityCredentialStore store = IdentityCredentialStore.getInstance(appContext);
+        if (Util.isHalOptional()) {
+            assumeTrue("IC HAL not found on device", store != null);
+        }
 
         store.deleteCredentialByName("test");
         Collection<X509Certificate> certChain = createCredential(store, "test");
@@ -508,6 +525,9 @@
             InvalidKeyException {
         Context appContext = InstrumentationRegistry.getTargetContext();
         IdentityCredentialStore store = IdentityCredentialStore.getInstance(appContext);
+        if (Util.isHalOptional()) {
+            assumeTrue("IC HAL not found on device", store != null);
+        }
 
         // This checks we can do multiple getEntries() calls
 
@@ -569,6 +589,9 @@
     public void testProvisionAndRetrieveWithFiltering() throws IdentityCredentialException {
         Context appContext = InstrumentationRegistry.getTargetContext();
         IdentityCredentialStore store = IdentityCredentialStore.getInstance(appContext);
+        if (Util.isHalOptional()) {
+            assumeTrue("IC HAL not found on device", store != null);
+        }
 
         store.deleteCredentialByName("test");
         Collection<X509Certificate> certChain = createCredential(store, "test");
@@ -620,6 +643,9 @@
     public void testProvisionAndRetrieveElementWithNoACP() throws IdentityCredentialException {
         Context appContext = InstrumentationRegistry.getTargetContext();
         IdentityCredentialStore store = IdentityCredentialStore.getInstance(appContext);
+        if (Util.isHalOptional()) {
+            assumeTrue("IC HAL not found on device", store != null);
+        }
 
         store.deleteCredentialByName("test");
         Collection<X509Certificate> certChain = createCredential(store, "test");
@@ -659,6 +685,9 @@
     public void testProvisionAndRetrieveWithEntryNotInRequest() throws IdentityCredentialException {
         Context appContext = InstrumentationRegistry.getTargetContext();
         IdentityCredentialStore store = IdentityCredentialStore.getInstance(appContext);
+        if (Util.isHalOptional()) {
+            assumeTrue("IC HAL not found on device", store != null);
+        }
 
         store.deleteCredentialByName("test");
         Collection<X509Certificate> certChain = createCredential(store, "test");
@@ -713,6 +742,9 @@
     public void nonExistentEntries() throws IdentityCredentialException {
         Context appContext = InstrumentationRegistry.getTargetContext();
         IdentityCredentialStore store = IdentityCredentialStore.getInstance(appContext);
+        if (Util.isHalOptional()) {
+            assumeTrue("IC HAL not found on device", store != null);
+        }
 
         store.deleteCredentialByName("test");
         Collection<X509Certificate> certChain = createCredential(store, "test");
@@ -756,6 +788,9 @@
     public void multipleNamespaces() throws IdentityCredentialException, CborException {
         Context appContext = InstrumentationRegistry.getTargetContext();
         IdentityCredentialStore store = IdentityCredentialStore.getInstance(appContext);
+        if (Util.isHalOptional()) {
+            assumeTrue("IC HAL not found on device", store != null);
+        }
 
         store.deleteCredentialByName("test");
         Collection<X509Certificate> certChain = createCredentialMultipleNamespaces(
diff --git a/tests/tests/identity/src/android/security/identity/cts/ReaderAuthTest.java b/tests/tests/identity/src/android/security/identity/cts/ReaderAuthTest.java
index 88abce4..1abc0ae 100644
--- a/tests/tests/identity/src/android/security/identity/cts/ReaderAuthTest.java
+++ b/tests/tests/identity/src/android/security/identity/cts/ReaderAuthTest.java
@@ -24,6 +24,7 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
 
 import android.content.Context;
 import android.security.keystore.KeyGenParameterSpec;
@@ -155,6 +156,10 @@
         // Provision the credential.
         Context appContext = InstrumentationRegistry.getTargetContext();
         IdentityCredentialStore store = IdentityCredentialStore.getInstance(appContext);
+        if (Util.isHalOptional()) {
+            assumeTrue("IC HAL not found on device", store != null);
+        }
+
         String credentialName = "readerAuthTestCredential";
         store.deleteCredentialByName(credentialName);
         WritableIdentityCredential wc = store.createCredential(credentialName,
diff --git a/tests/tests/identity/src/android/security/identity/cts/UserAuthTest.java b/tests/tests/identity/src/android/security/identity/cts/UserAuthTest.java
index 244c636..295fdaf 100644
--- a/tests/tests/identity/src/android/security/identity/cts/UserAuthTest.java
+++ b/tests/tests/identity/src/android/security/identity/cts/UserAuthTest.java
@@ -31,11 +31,11 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
 
 import android.content.Context;
 import android.os.SystemClock;
 import android.util.Log;
-import android.test.AndroidTestCase;
 
 import androidx.test.InstrumentationRegistry;
 
@@ -92,7 +92,7 @@
 import android.security.keystore.UserNotAuthenticatedException;
 
 
-public class UserAuthTest extends AndroidTestCase {
+public class UserAuthTest {
     private static final String TAG = "UserAuthTest";
 
     private class DeviceLockSession extends ActivityManagerTestBase implements AutoCloseable {
@@ -100,14 +100,14 @@
         private LockScreenSession mLockCredential;
 
         public DeviceLockSession() throws Exception {
-            setUp();
             mLockCredential = new LockScreenSession();
             mLockCredential.setLockCredential();
         }
 
         public void performDeviceLock() {
             mLockCredential.sleepDevice();
-            KeyguardManager keyguardManager = (KeyguardManager)getContext().
+            Context appContext = InstrumentationRegistry.getTargetContext();
+            KeyguardManager keyguardManager = (KeyguardManager)appContext.
                                               getSystemService(Context.KEYGUARD_SERVICE);
             for (int i = 0; i < 25 && !keyguardManager.isDeviceLocked(); i++) {
                 SystemClock.sleep(200);
@@ -123,7 +123,6 @@
         @Override
         public void close() throws Exception {
             mLockCredential.close();
-            tearDown();
         }
     }
 
@@ -170,14 +169,18 @@
         }
     }
 
+    @Test
     public void testUserAuth() throws Exception {
         String alias = "authbound";
 
         try (DeviceLockSession dl = new DeviceLockSession()) {
-            KeyguardManager keyguardManager = (KeyguardManager)getContext()
+            Context appContext = InstrumentationRegistry.getTargetContext();
+            KeyguardManager keyguardManager = (KeyguardManager)appContext
                                               .getSystemService(Context.KEYGUARD_SERVICE);
 
             doTestUserAuth(dl, keyguardManager);
+        } catch (org.junit.AssumptionViolatedException e) {
+            /* do nothing */
         }
     }
 
@@ -196,6 +199,10 @@
         // Provision the credential.
         Context appContext = InstrumentationRegistry.getTargetContext();
         IdentityCredentialStore store = IdentityCredentialStore.getInstance(appContext);
+        if (Util.isHalOptional()) {
+            assumeTrue("IC HAL not found on device", store != null);
+        }
+
         store.deleteCredentialByName("test");
         WritableIdentityCredential wc = store.createCredential("test",
                 "org.iso.18013-5.2019.mdl");
@@ -318,7 +325,7 @@
         dl.performDeviceLock();
         assertTrue(keyguardManager.isDeviceLocked());
         dl.performDeviceUnlock();
-        assertFalse(keyguardManager.isDeviceLocked());
+        assertTrue(!keyguardManager.isDeviceLocked());
 
         credential = store.getCredentialByName("test",
                 CIPHERSUITE_ECDHE_HKDF_ECDSA_WITH_AES_256_GCM_SHA256);
diff --git a/tests/tests/identity/src/android/security/identity/cts/Util.java b/tests/tests/identity/src/android/security/identity/cts/Util.java
index 52649f1..b950b89 100644
--- a/tests/tests/identity/src/android/security/identity/cts/Util.java
+++ b/tests/tests/identity/src/android/security/identity/cts/Util.java
@@ -18,8 +18,9 @@
 
 import android.security.identity.ResultData;
 
-import android.util.Log;
+import android.os.SystemProperties;
 import android.security.keystore.KeyProperties;
+import android.util.Log;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -1224,4 +1225,24 @@
             throw new RuntimeException("Error generating ephemeral key-pair", e);
         }
     }
+
+    // Returns true if, and only if, the Identity Credential HAL (and credstore) is optional for
+    // the device under test.
+    static boolean isHalOptional() {
+        int firstApiLevel = SystemProperties.getInt("ro.product.first_api_level", 0);
+        if (firstApiLevel == 0) {
+            // ro.product.first_api_level is not set and per
+            //
+            //  https://source.android.com/compatibility/cts/setup
+            //
+            // this is optional to set. In this case we simply just require the HAL.
+            return false;
+        }
+        if (firstApiLevel >= 30) {
+            // Device launched with R or later, Identity Credential HAL is not optional.
+            return false;
+        }
+        // Device launched before R, Identity Credential HAL is optional.
+        return true;
+    }
 }