Merge "Return a copy of the "real" set in AssociationStore" into tm-dev
diff --git a/services/companion/java/com/android/server/companion/AssociationStoreImpl.java b/services/companion/java/com/android/server/companion/AssociationStoreImpl.java
index 21a677b8..cb28254 100644
--- a/services/companion/java/com/android/server/companion/AssociationStoreImpl.java
+++ b/services/companion/java/com/android/server/companion/AssociationStoreImpl.java
@@ -171,12 +171,20 @@
         broadcastChange(CHANGE_TYPE_REMOVED, association);
     }
 
+    /**
+     * @return a "snapshot" of the current state of the existing associations.
+     */
     public @NonNull Collection<AssociationInfo> getAssociations() {
-        final Collection<AssociationInfo> allAssociations;
         synchronized (mLock) {
-            allAssociations = mIdMap.values();
+            // IMPORTANT: make and return a COPY of the mIdMap.values(), NOT a "direct" reference.
+            // The HashMap.values() returns a collection which is backed by the HashMap, so changes
+            // to the HashMap are reflected in this collection.
+            // For us this means that if mIdMap is modified while the iteration over mIdMap.values()
+            // is in progress it may lead to "undefined results" (according to the HashMap's
+            // documentation) or cause ConcurrentModificationExceptions in the iterator (according
+            // to the bugreports...).
+            return List.copyOf(mIdMap.values());
         }
-        return Collections.unmodifiableCollection(allAssociations);
     }
 
     public @NonNull List<AssociationInfo> getAssociationsForUser(@UserIdInt int userId) {