Merge "Add CredDescRegistry unit tests" into udc-dev am: e334986d9b

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/21689143

Change-Id: I7ff934db9e16781fc57d2acb6a61c62dc01259c1
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/services/credentials/java/com/android/server/credentials/CredentialDescriptionRegistry.java b/services/credentials/java/com/android/server/credentials/CredentialDescriptionRegistry.java
index 9c50a5a..d768d23 100644
--- a/services/credentials/java/com/android/server/credentials/CredentialDescriptionRegistry.java
+++ b/services/credentials/java/com/android/server/credentials/CredentialDescriptionRegistry.java
@@ -23,6 +23,8 @@
 import android.util.SparseArray;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+
 
 import java.util.HashMap;
 import java.util.HashSet;
@@ -90,6 +92,18 @@
         }
     }
 
+    /** Clears an existing session for a given user identifier. */
+    @GuardedBy("sLock")
+    @VisibleForTesting
+    public static void clearAllSessions() {
+        sLock.lock();
+        try {
+            sCredentialDescriptionSessionPerUser.clear();
+        } finally {
+            sLock.unlock();
+        }
+    }
+
     private Map<String, Set<CredentialDescription>> mCredentialDescriptions;
     private int mTotalDescriptionCount;
 
@@ -138,6 +152,9 @@
     public Set<FilterResult> getFilteredResultForProvider(String packageName,
             String flatRequestStrings) {
         Set<FilterResult> result = new HashSet<>();
+        if (!mCredentialDescriptions.containsKey(packageName)) {
+            return result;
+        }
         Set<CredentialDescription> currentSet = mCredentialDescriptions.get(packageName);
         for (CredentialDescription containedDescription: currentSet) {
             if (flatRequestStrings.equals(containedDescription.getFlattenedRequestString())) {
diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp
index 90dbd53..6f26a5f 100644
--- a/services/tests/servicestests/Android.bp
+++ b/services/tests/servicestests/Android.bp
@@ -31,6 +31,7 @@
         "services.backup",
         "services.companion",
         "services.core",
+        "services.credentials",
         "services.devicepolicy",
         "services.net",
         "services.people",
diff --git a/services/tests/servicestests/src/com/android/server/credentials/CredentialDescriptionRegistryTest.java b/services/tests/servicestests/src/com/android/server/credentials/CredentialDescriptionRegistryTest.java
new file mode 100644
index 0000000..b7085f15
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/credentials/CredentialDescriptionRegistryTest.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2023 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.server.credentials;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.credentials.CredentialDescription;
+import android.credentials.RegisterCredentialDescriptionRequest;
+import android.service.credentials.CredentialEntry;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * Tests for CredentialDescriptionRegistry.
+ *
+ * atest FrameworksServicesTests:com.android.server.credentials.CredentialDescriptionRegistryTest
+ */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class CredentialDescriptionRegistryTest {
+
+    private static final int USER_ID_1 = 1;
+    private static final int USER_ID_2 = 2;
+    private static final String CALLING_PACKAGE_NAME = "com.credman.app";
+    private static final String CALLING_PACKAGE_NAME_2 = "com.credman.app2";
+    private static final String MDOC_CREDENTIAL_TYPE = "MDOC";
+    private static final String PASSKEY_CREDENTIAL_TYPE = "PASSKEY";
+    private static final String FLATTENED_REQUEST = "FLATTENED_REQ";
+    private static final String FLATTENED_REQUEST_2 = "FLATTENED_REQ_2";
+
+    private CredentialDescriptionRegistry mCredentialDescriptionRegistry;
+    private CredentialEntry mEntry;
+    private CredentialEntry mEntry2;
+    private CredentialEntry mEntry3;
+
+    @SuppressWarnings("GuardedBy")
+    @Before
+    public void setUp() {
+        CredentialDescriptionRegistry.clearAllSessions();
+        mEntry = mock(CredentialEntry.class);
+        mEntry2 = mock(CredentialEntry.class);
+        mEntry3 = mock(CredentialEntry.class);
+        when(mEntry.getType()).thenReturn(MDOC_CREDENTIAL_TYPE);
+        when(mEntry2.getType()).thenReturn(MDOC_CREDENTIAL_TYPE);
+        when(mEntry3.getType()).thenReturn(PASSKEY_CREDENTIAL_TYPE);
+        mCredentialDescriptionRegistry = CredentialDescriptionRegistry.forUser(USER_ID_1);
+    }
+
+    @SuppressWarnings("GuardedBy")
+    @Test
+    public void testForUser_createsUniqueInstanceForEachUserID() {
+        final CredentialDescriptionRegistry secondRegistry = CredentialDescriptionRegistry
+                .forUser(USER_ID_2);
+
+        assertThat(mCredentialDescriptionRegistry).isNotSameInstanceAs(secondRegistry);
+    }
+
+    @SuppressWarnings("GuardedBy")
+    @Test
+    public void testForUser_returnsSameInstanceForSameUserID() {
+        final CredentialDescriptionRegistry secondRegistry = CredentialDescriptionRegistry
+                .forUser(USER_ID_1);
+
+        assertThat(mCredentialDescriptionRegistry).isSameInstanceAs(secondRegistry);
+    }
+
+    @SuppressWarnings("GuardedBy")
+    @Test
+    public void testClearUserSession_removesExistingSessionForUserID() {
+        CredentialDescriptionRegistry.clearUserSession(USER_ID_1);
+        final CredentialDescriptionRegistry secondRegistry = CredentialDescriptionRegistry
+                .forUser(USER_ID_1);
+
+        assertThat(mCredentialDescriptionRegistry).isNotSameInstanceAs(secondRegistry);
+    }
+
+    @Test
+    public void testEvictProvider_existingProviders_succeeds() {
+        final CredentialDescription credentialDescription =
+                new CredentialDescription(MDOC_CREDENTIAL_TYPE, FLATTENED_REQUEST,
+                        Collections.emptyList());
+        final RegisterCredentialDescriptionRequest registerCredentialDescriptionRequest =
+                new RegisterCredentialDescriptionRequest(credentialDescription);
+        final CredentialDescription credentialDescription2 =
+                new CredentialDescription(MDOC_CREDENTIAL_TYPE, FLATTENED_REQUEST_2,
+                        Collections.emptyList());
+        final RegisterCredentialDescriptionRequest registerCredentialDescriptionRequest2 =
+                new RegisterCredentialDescriptionRequest(credentialDescription2);
+
+
+        mCredentialDescriptionRegistry
+                .executeRegisterRequest(registerCredentialDescriptionRequest, CALLING_PACKAGE_NAME);
+        mCredentialDescriptionRegistry
+                .executeRegisterRequest(registerCredentialDescriptionRequest2,
+                        CALLING_PACKAGE_NAME);
+        mCredentialDescriptionRegistry.evictProviderWithPackageName(CALLING_PACKAGE_NAME);
+        Set<CredentialDescriptionRegistry.FilterResult> providers = mCredentialDescriptionRegistry
+                .getMatchingProviders(Set.of(FLATTENED_REQUEST));
+
+        assertThat(providers).isEmpty();
+    }
+
+    @Test
+    public void testGetMatchingProviders_existingProviders_succeeds() {
+        final CredentialDescription credentialDescription =
+                new CredentialDescription(MDOC_CREDENTIAL_TYPE, FLATTENED_REQUEST,
+                        Collections.emptyList());
+        final RegisterCredentialDescriptionRequest registerCredentialDescriptionRequest =
+                new RegisterCredentialDescriptionRequest(credentialDescription);
+        final CredentialDescription credentialDescription2 =
+                new CredentialDescription(MDOC_CREDENTIAL_TYPE, FLATTENED_REQUEST,
+                        Collections.emptyList());
+        final RegisterCredentialDescriptionRequest registerCredentialDescriptionRequest2 =
+                new RegisterCredentialDescriptionRequest(credentialDescription2);
+
+
+        mCredentialDescriptionRegistry
+                .executeRegisterRequest(registerCredentialDescriptionRequest,
+                        CALLING_PACKAGE_NAME);
+        mCredentialDescriptionRegistry
+                .executeRegisterRequest(registerCredentialDescriptionRequest2,
+                        CALLING_PACKAGE_NAME_2);
+
+        Set<CredentialDescriptionRegistry.FilterResult> providers = mCredentialDescriptionRegistry
+                .getMatchingProviders(Set.of(FLATTENED_REQUEST));
+        Set<String> packageNames = providers.stream().map(
+                filterResult -> filterResult.mPackageName).collect(Collectors.toSet());
+
+        assertThat(providers).hasSize(2);
+        assertThat(packageNames).contains(CALLING_PACKAGE_NAME);
+        assertThat(packageNames).contains(CALLING_PACKAGE_NAME_2);
+    }
+
+    @Test
+    public void testExecuteRegisterRequest_noProviders_filterSucceedsWithNoResults() {
+        List<CredentialDescriptionRegistry.FilterResult> results = mCredentialDescriptionRegistry
+                .getFilteredResultForProvider(CALLING_PACKAGE_NAME,
+                        FLATTENED_REQUEST).stream().toList();
+
+        assertThat(results).isEmpty();
+    }
+
+    @Test
+    public void testExecuteRegisterRequest_existingProviders_filterSucceeds() {
+        final CredentialDescription credentialDescription =
+                new CredentialDescription(MDOC_CREDENTIAL_TYPE,
+                        FLATTENED_REQUEST,
+                        List.of(mEntry, mEntry2));
+        final CredentialDescription credentialDescription2 =
+                new CredentialDescription(PASSKEY_CREDENTIAL_TYPE,
+                        FLATTENED_REQUEST_2,
+                        List.of(mEntry3));
+        final RegisterCredentialDescriptionRequest registerCredentialDescriptionRequest =
+                new RegisterCredentialDescriptionRequest(Set.of(credentialDescription,
+                credentialDescription2));
+
+        mCredentialDescriptionRegistry
+                .executeRegisterRequest(registerCredentialDescriptionRequest, CALLING_PACKAGE_NAME);
+
+        List<CredentialDescriptionRegistry.FilterResult> results = mCredentialDescriptionRegistry
+                .getFilteredResultForProvider(CALLING_PACKAGE_NAME, FLATTENED_REQUEST)
+                .stream().toList();
+
+        assertThat(results).hasSize(1);
+        assertThat(results.get(0).mCredentialEntries).hasSize(2);
+        assertThat(results.get(0).mCredentialEntries.get(0)).isSameInstanceAs(mEntry);
+        assertThat(results.get(0).mCredentialEntries.get(1)).isSameInstanceAs(mEntry2);
+    }
+
+}
diff --git a/services/tests/servicestests/src/com/android/server/credentials/OWNERS b/services/tests/servicestests/src/com/android/server/credentials/OWNERS
new file mode 100644
index 0000000..cc73854
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/credentials/OWNERS
@@ -0,0 +1 @@
+include platform/frameworks/base:/core/java/android/credentials/OWNERS
\ No newline at end of file