diff --git a/services/companion/java/com/android/server/companion/AssociationStoreImpl.java b/services/companion/java/com/android/server/companion/AssociationStoreImpl.java index 21a677b8383c8..cb28254f70db6 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 @@ class AssociationStoreImpl implements AssociationStore { broadcastChange(CHANGE_TYPE_REMOVED, association); } + /** + * @return a "snapshot" of the current state of the existing associations. + */ public @NonNull Collection getAssociations() { - final Collection 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 getAssociationsForUser(@UserIdInt int userId) {