From 7a2856ac86bebfd401367e5a0a4187e0ce5ec1e6 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Thu, 29 Jun 2017 14:17:45 +0200 Subject: [PATCH] fetch required device ids on demand --- .../java/eu/siacs/conversations/Config.java | 2 +- .../crypto/axolotl/AxolotlService.java | 117 ++++++++++++------ .../crypto/axolotl/SQLiteAxolotlStore.java | 4 + .../persistance/DatabaseBackend.java | 17 +++ 4 files changed, 104 insertions(+), 36 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/Config.java b/src/main/java/eu/siacs/conversations/Config.java index d79683d41..6393b0cdc 100644 --- a/src/main/java/eu/siacs/conversations/Config.java +++ b/src/main/java/eu/siacs/conversations/Config.java @@ -12,7 +12,7 @@ public final class Config { private static final int OTR = 4; private static final int OMEMO = 8; - private static final int ENCRYPTION_MASK = UNENCRYPTED | OPENPGP | OTR | OMEMO; + private static final int ENCRYPTION_MASK = OMEMO; public static boolean supportUnencrypted() { return (ENCRYPTION_MASK & UNENCRYPTED) != 0; diff --git a/src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlService.java b/src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlService.java index 53790892b..139b2d4bb 100644 --- a/src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlService.java +++ b/src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlService.java @@ -31,6 +31,7 @@ import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; +import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Random; @@ -74,6 +75,7 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded { private final Map> deviceIds; private final Map messageCache; private final FetchStatusMap fetchStatusMap; + private final HashMap> fetchDeviceIdsMap = new HashMap<>(); private final SerialSingleThreadExecutor executor; private int numPublishTriesOnEmptyPep = 0; private boolean pepBroken = false; @@ -210,10 +212,9 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded { private void fillMap(SQLiteAxolotlStore store) { List deviceIds = store.getSubDeviceSessions(account.getJid().toBareJid().toPreppedString()); putDevicesForJid(account.getJid().toBareJid().toPreppedString(), deviceIds, store); - for (Contact contact : account.getRoster().getContacts()) { - Jid bareJid = contact.getJid().toBareJid(); - String address = bareJid.toPreppedString(); + for (String address : store.getKnownAddresses()) { deviceIds = store.getSubDeviceSessions(address); + Log.d(Config.LOGTAG,account.getJid().toBareJid()+" adding device ids for "+address+" "+deviceIds); putDevicesForJid(address, deviceIds, store); } @@ -358,6 +359,7 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded { axolotlStore.regenerate(); sessions.clear(); fetchStatusMap.clear(); + fetchDeviceIdsMap.clear(); publishBundlesIfNeeded(true, wipeOther); } @@ -753,7 +755,8 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded { public List getCryptoTargets(Conversation conversation) { final List jids; if (conversation.getMode() == Conversation.MODE_SINGLE) { - jids = Arrays.asList(conversation.getJid().toBareJid()); + jids = new ArrayList<>(); + jids.add(conversation.getJid().toBareJid()); } else { jids = conversation.getMucOptions().getMembers(); } @@ -866,35 +869,79 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded { } public interface OnDeviceIdsFetched { - void fetched(Set deviceIds); + void fetched(Jid jid, Set deviceIds); + } + + public interface OnMultipleDeviceIdFetched { + void fetched(); } public void fetchDeviceIds(final Jid jid) { fetchDeviceIds(jid,null); } - public void fetchDeviceIds(final Jid jid, final OnDeviceIdsFetched callback) { - Log.d(Config.LOGTAG,"fetching device ids for "+jid); - IqPacket packet = mXmppConnectionService.getIqGenerator().retrieveDeviceIds(jid); - mXmppConnectionService.sendIqPacket(account, packet, new OnIqPacketReceived() { - @Override - public void onIqPacketReceived(Account account, IqPacket packet) { - if (packet.getType() == IqPacket.TYPE.RESULT) { - Element item = mXmppConnectionService.getIqParser().getItem(packet); - Set deviceIds = mXmppConnectionService.getIqParser().deviceIds(item); - registerDevices(jid,deviceIds); - if (callback != null) { - callback.fetched(deviceIds); - } - } else { - Log.d(Config.LOGTAG,packet.toString()); - if (callback != null) { - callback.fetched(null); - } + public void fetchDeviceIds(final Jid jid, OnDeviceIdsFetched callback) { + synchronized (this.fetchDeviceIdsMap) { + List callbacks = this.fetchDeviceIdsMap.get(jid); + if (callbacks != null) { + if (callback != null) { + callbacks.add(callback); } - + Log.d(Config.LOGTAG,account.getJid().toBareJid()+": fetching device ids for "+jid+" already running. adding callback"); + } else { + callbacks = new ArrayList<>(); + if (callback != null) { + callbacks.add(callback); + } + this.fetchDeviceIdsMap.put(jid,callbacks); + Log.d(Config.LOGTAG,account.getJid().toBareJid()+": fetching device ids for " + jid); + IqPacket packet = mXmppConnectionService.getIqGenerator().retrieveDeviceIds(jid); + mXmppConnectionService.sendIqPacket(account, packet, new OnIqPacketReceived() { + @Override + public void onIqPacketReceived(Account account, IqPacket packet) { + synchronized (fetchDeviceIdsMap) { + List callbacks = fetchDeviceIdsMap.remove(jid); + if (packet.getType() == IqPacket.TYPE.RESULT) { + Element item = mXmppConnectionService.getIqParser().getItem(packet); + Set deviceIds = mXmppConnectionService.getIqParser().deviceIds(item); + registerDevices(jid, deviceIds); + if (callbacks != null) { + for(OnDeviceIdsFetched callback : callbacks) { + callback.fetched(jid, deviceIds); + } + } + } else { + Log.d(Config.LOGTAG, packet.toString()); + if (callbacks != null) { + for(OnDeviceIdsFetched callback : callbacks) { + callback.fetched(jid, null); + } + } + } + } + } + }); } - }); + } + } + + private void fetchDeviceIds(List jids, final OnMultipleDeviceIdFetched callback) { + final ArrayList unfinishedJids = new ArrayList<>(jids); + synchronized (unfinishedJids) { + for (Jid jid : unfinishedJids) { + fetchDeviceIds(jid, new OnDeviceIdsFetched() { + @Override + public void fetched(Jid jid, Set deviceIds) { + synchronized (unfinishedJids) { + unfinishedJids.remove(jid); + if (unfinishedJids.size() == 0 && callback != null) { + callback.fetched(); + } + } + } + }); + } + } } private void buildSessionFromPEP(final SignalProtocolAddress address) { @@ -1028,19 +1075,19 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded { } public boolean createSessionsIfNeeded(final Conversation conversation) { - final Jid jid = conversation.getJid().toBareJid(); - if (conversation.getMode() == Conversation.MODE_SINGLE && hasEmptyDeviceList(jid)) { - final SignalProtocolAddress placeholder = new SignalProtocolAddress(jid.toPreppedString(), Integer.MIN_VALUE); - FetchStatus status = fetchStatusMap.get(placeholder); - if (status == null || status == FetchStatus.TIMEOUT) { - fetchStatusMap.put(placeholder, FetchStatus.PENDING); + final List jidsWithEmptyDeviceList = getCryptoTargets(conversation); + for(Iterator iterator = jidsWithEmptyDeviceList.iterator(); iterator.hasNext();) { + final Jid jid = iterator.next(); + if (!hasEmptyDeviceList(jid)) { + iterator.remove(); } - fetchDeviceIds(conversation.getJid().toBareJid(), new OnDeviceIdsFetched() { + } + Log.d(Config.LOGTAG,account.getJid().toBareJid()+": createSessionsIfNeeded() - jids with empty device list: "+jidsWithEmptyDeviceList); + if (jidsWithEmptyDeviceList.size() > 0) { + fetchDeviceIds(jidsWithEmptyDeviceList, new OnMultipleDeviceIdFetched() { @Override - public void fetched(Set deviceIds) { + public void fetched() { createSessionsIfNeededActual(conversation); - fetchStatusMap.put(placeholder,deviceIds != null && !deviceIds.isEmpty() ? FetchStatus.SUCCESS : FetchStatus.ERROR); - finishBuildingSessionsFromPEP(placeholder); } }); return true; diff --git a/src/main/java/eu/siacs/conversations/crypto/axolotl/SQLiteAxolotlStore.java b/src/main/java/eu/siacs/conversations/crypto/axolotl/SQLiteAxolotlStore.java index 313755537..3d1ed2e9e 100644 --- a/src/main/java/eu/siacs/conversations/crypto/axolotl/SQLiteAxolotlStore.java +++ b/src/main/java/eu/siacs/conversations/crypto/axolotl/SQLiteAxolotlStore.java @@ -283,6 +283,10 @@ public class SQLiteAxolotlStore implements SignalProtocolStore { new SignalProtocolAddress(name, 0)); } + + public List getKnownAddresses() { + return mXmppConnectionService.databaseBackend.getKnownSignalAddresses(account); + } /** * Commit to storage the {@link SessionRecord} for a given recipientId + deviceId tuple. * diff --git a/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java b/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java index b88fe0071..a42166e3a 100644 --- a/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java +++ b/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java @@ -946,6 +946,23 @@ public class DatabaseBackend extends SQLiteOpenHelper { return devices; } + public List getKnownSignalAddresses(Account account) { + List addresses = new ArrayList<>(); + String[] colums = {SQLiteAxolotlStore.NAME}; + String[] selectionArgs = {account.getUuid()}; + Cursor cursor = getReadableDatabase().query(SQLiteAxolotlStore.SESSION_TABLENAME, + colums, + SQLiteAxolotlStore.ACCOUNT + " = ?", + selectionArgs, + null,null,null + ); + while (cursor.moveToNext()) { + addresses.add(cursor.getString(cursor.getColumnIndex(SQLiteAxolotlStore.NAME))); + } + cursor.close(); + return addresses; + } + public boolean containsSession(Account account, SignalProtocolAddress contact) { Cursor cursor = getCursorForSession(account, contact); int count = cursor.getCount();