Fix asynchronous axolotl message sending

Andreas Straub created

XmppConnectionService.sendMessage() now dispatches messages to the
AxolotlService, where they only are prepared for sending and cached.
AxolotlService now triggers a XmppConnectionService.resendMessage(),
which then handles sending the cached message packet.

This transparently fixes, e.g., handling of messages sent while we are
offline.

Change summary

src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlService.java  | 29 
src/main/java/eu/siacs/conversations/services/XmppConnectionService.java |  6 
2 files changed, 27 insertions(+), 8 deletions(-)

Detailed changes

src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlService.java 🔗

@@ -67,6 +67,7 @@ public class AxolotlService {
 	private final SQLiteAxolotlStore axolotlStore;
 	private final SessionMap sessions;
 	private final Map<Jid, Set<Integer>> deviceIds;
+	private final Map<String, MessagePacket> messageCache;
 	private final FetchStatusMap fetchStatusMap;
 	private final SerialSingleThreadExecutor executor;
 	private int ownDeviceId;
@@ -580,6 +581,7 @@ public class AxolotlService {
 		this.account = account;
 		this.axolotlStore = new SQLiteAxolotlStore(this.account, this.mXmppConnectionService);
 		this.deviceIds = new HashMap<>();
+		this.messageCache = new HashMap<>();
 		this.sessions = new SessionMap(axolotlStore, account);
 		this.fetchStatusMap = new FetchStatusMap();
 		this.executor = new SerialSingleThreadExecutor();
@@ -892,20 +894,35 @@ public class AxolotlService {
 						.generateAxolotlChat(message);
 				if (packet == null) {
 					mXmppConnectionService.markMessage(message, Message.STATUS_SEND_FAILED);
+					//mXmppConnectionService.updateConversationUi();
 				} else {
-					mXmppConnectionService.markMessage(message, Message.STATUS_UNSEND);
-					mXmppConnectionService.sendMessagePacket(account, packet);
+					Log.d(Config.LOGTAG, "Generated message, caching: " + message.getUuid());
+					messageCache.put(message.getUuid(), packet);
+					mXmppConnectionService.resendMessage(message);
 				}
 			}
 		});
 	}
 
-	public void sendMessage(Message message) {
-		boolean newSessions = createSessionsIfNeeded(message.getConversation());
+	public void prepareMessage(Message message) {
+		if (!messageCache.containsKey(message.getUuid())) {
+			boolean newSessions = createSessionsIfNeeded(message.getConversation());
 
-		if (!newSessions) {
-			this.processSending(message);
+			if (!newSessions) {
+				this.processSending(message);
+			}
+		}
+	}
+
+	public MessagePacket fetchPacketFromCache(Message message) {
+		MessagePacket packet = messageCache.get(message.getUuid());
+		if (packet != null) {
+			Log.d(Config.LOGTAG, "Cache hit: " + message.getUuid());
+			messageCache.remove(message.getUuid());
+		} else {
+			Log.d(Config.LOGTAG, "Cache miss: " + message.getUuid());
 		}
+		return packet;
 	}
 
 	public XmppAxolotlMessage.XmppAxolotlPlaintextMessage processReceiving(XmppAxolotlMessage message) {

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

@@ -52,7 +52,6 @@ import de.duenndns.ssl.MemorizingTrustManager;
 import eu.siacs.conversations.Config;
 import eu.siacs.conversations.R;
 import eu.siacs.conversations.crypto.PgpEngine;
-import eu.siacs.conversations.crypto.axolotl.NoSessionsCreatedException;
 import eu.siacs.conversations.entities.Account;
 import eu.siacs.conversations.entities.Blockable;
 import eu.siacs.conversations.entities.Bookmark;
@@ -760,7 +759,10 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
 					break;
 				case Message.ENCRYPTION_AXOLOTL:
 					message.setStatus(Message.STATUS_WAITING);
-					account.getAxolotlService().sendMessage(message);
+					packet = account.getAxolotlService().fetchPacketFromCache(message);
+					if (packet == null && account.isOnlineAndConnected()) {
+						account.getAxolotlService().prepareMessage(message);
+					}
 					break;
 
 			}