fixed concurrent modification when iterating over presences

Daniel Gultsch created

Change summary

src/main/java/eu/siacs/conversations/entities/Account.java                         |  2 
src/main/java/eu/siacs/conversations/entities/Presences.java                       | 12 
src/main/java/eu/siacs/conversations/services/XmppConnectionService.java           |  4 
src/main/java/eu/siacs/conversations/xmpp/jingle/JingleFileTransferConnection.java |  2 
src/main/java/eu/siacs/conversations/xmpp/jingle/RtpCapability.java                |  2 
5 files changed, 15 insertions(+), 7 deletions(-)

Detailed changes

src/main/java/eu/siacs/conversations/entities/Account.java 🔗

@@ -432,7 +432,7 @@ public class Account extends AbstractEntity implements AvatarService.Avatarable
 
     public int activeDevicesWithRtpCapability() {
         int i = 0;
-        for(Presence presence : getSelfContact().getPresences().getPresences().values()) {
+        for(Presence presence : getSelfContact().getPresences().getPresences()) {
             if (RtpCapability.check(presence) != RtpCapability.Capability.NONE) {
                 i++;
             }

src/main/java/eu/siacs/conversations/entities/Presences.java 🔗

@@ -11,8 +11,16 @@ import java.util.Map;
 public class Presences {
 	private final Hashtable<String, Presence> presences = new Hashtable<>();
 
-	public Hashtable<String, Presence> getPresences() {
-		return this.presences;
+	public List<Presence> getPresences() {
+		synchronized (this.presences) {
+			return new ArrayList<>(this.presences.values());
+		}
+	}
+
+	public Presence get(String resource) {
+		synchronized (this.presences) {
+			return this.presences.get(resource);
+		}
 	}
 
 	public void updatePresence(String resource, Presence presence) {

src/main/java/eu/siacs/conversations/services/XmppConnectionService.java 🔗

@@ -4468,8 +4468,8 @@ public class XmppConnectionService extends Service {
     }
 
     private void injectServiceDiscoveryResult(Roster roster, String hash, String ver, ServiceDiscoveryResult disco) {
-        for (Contact contact : roster.getContacts()) {
-            for (Presence presence : contact.getPresences().getPresences().values()) {
+        for (final Contact contact : roster.getContacts()) {
+            for (final Presence presence : contact.getPresences().getPresences()) {
                 if (hash.equals(presence.getHash()) && ver.equals(presence.getVer())) {
                     presence.setServiceDiscoveryResult(disco);
                 }

src/main/java/eu/siacs/conversations/xmpp/jingle/JingleFileTransferConnection.java 🔗

@@ -417,7 +417,7 @@ public class JingleFileTransferConnection extends AbstractJingleConnection imple
         final Jid jid = this.id.with;
         String resource = jid != null ? jid.getResource() : null;
         if (resource != null) {
-            Presence presence = this.id.account.getRoster().getContact(jid).getPresences().getPresences().get(resource);
+            Presence presence = this.id.account.getRoster().getContact(jid).getPresences().get(resource);
             ServiceDiscoveryResult result = presence != null ? presence.getServiceDiscoveryResult() : null;
             return result == null ? Collections.emptyList() : result.getFeatures();
         } else {

src/main/java/eu/siacs/conversations/xmpp/jingle/RtpCapability.java 🔗

@@ -40,7 +40,7 @@ public class RtpCapability {
     public static Capability check(final Contact contact) {
         final Presences presences = contact.getPresences();
         Capability result = Capability.NONE;
-        for(Presence presence : presences.getPresences().values()) {
+        for(Presence presence : presences.getPresences()) {
             Capability capability = check(presence);
             if (capability == Capability.VIDEO) {
                 result = capability;