Ensure that available sessions are always used

Any time a new session is established, call syncRosterToDisk() to ensure
that on subsequent restoreFromDatabase() calls, the roster is actually
available. This is important so that initAccountServices() can properly
initialize the SessionMap. This prevents a race condition where after
adding a new account and initiating sessions with it, if the app is
killed (e.g. by reinstall) before triggering a syncRosterToDisk(),
subsequent restores will not have the roster available, leading to
missing XmppAxolotlSessions in the SessionMap cache. As a result of
this, a new session was initiated when sending a new message, and
received messages could not be tagged with the originating session's
fingerprint.

As an added sanity check, go to the database to confirm no records are
present before creating fresh XmppAxolotlSession objects (both in the
sending and receiving case).
This commit is contained in:
Andreas Straub 2015-07-10 02:36:29 +02:00
parent 31d375c2c3
commit 35714d3d08
1 changed files with 30 additions and 9 deletions

View File

@ -595,13 +595,17 @@ public class AxolotlService {
} }
private static class SessionMap extends AxolotlAddressMap<XmppAxolotlSession> { private static class SessionMap extends AxolotlAddressMap<XmppAxolotlSession> {
private final XmppConnectionService xmppConnectionService;
private final Account account;
public SessionMap(SQLiteAxolotlStore store, Account account) { public SessionMap(XmppConnectionService service, SQLiteAxolotlStore store, Account account) {
super(); super();
this.fillMap(store, account); this.xmppConnectionService = service;
this.account = account;
this.fillMap(store);
} }
private void fillMap(SQLiteAxolotlStore store, Account account) { private void fillMap(SQLiteAxolotlStore store) {
for (Contact contact : account.getRoster().getContacts()) { for (Contact contact : account.getRoster().getContacts()) {
Jid bareJid = contact.getJid().toBareJid(); Jid bareJid = contact.getJid().toBareJid();
if (bareJid == null) { if (bareJid == null) {
@ -618,6 +622,11 @@ public class AxolotlService {
} }
} }
@Override
public void put(AxolotlAddress address, XmppAxolotlSession value) {
super.put(address, value);
xmppConnectionService.syncRosterToDisk(account);
}
} }
private static enum FetchStatus { private static enum FetchStatus {
@ -640,7 +649,7 @@ public class AxolotlService {
this.axolotlStore = new SQLiteAxolotlStore(this.account, this.mXmppConnectionService); this.axolotlStore = new SQLiteAxolotlStore(this.account, this.mXmppConnectionService);
this.deviceIds = new HashMap<>(); this.deviceIds = new HashMap<>();
this.messageCache = new HashMap<>(); this.messageCache = new HashMap<>();
this.sessions = new SessionMap(axolotlStore, account); this.sessions = new SessionMap(mXmppConnectionService, axolotlStore, account);
this.fetchStatusMap = new FetchStatusMap(); this.fetchStatusMap = new FetchStatusMap();
this.executor = new SerialSingleThreadExecutor(); this.executor = new SerialSingleThreadExecutor();
} }
@ -933,9 +942,16 @@ public class AxolotlService {
FetchStatus status = fetchStatusMap.get(address); FetchStatus status = fetchStatusMap.get(address);
XmppAxolotlSession session = sessions.get(address); XmppAxolotlSession session = sessions.get(address);
if ( session == null && ( status == null || status == FetchStatus.ERROR) ) { if ( session == null && ( status == null || status == FetchStatus.ERROR) ) {
fetchStatusMap.put(address, FetchStatus.PENDING); IdentityKey identityKey = axolotlStore.loadSession(address).getSessionState().getRemoteIdentityKey();
this.buildSessionFromPEP(conversation, address); if ( identityKey != null ) {
newSessions = true; Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account)+"Already have session for " + address.toString() + ", adding to cache...");
session = new XmppAxolotlSession(account, axolotlStore, address, identityKey.getFingerprint().replaceAll("\\s", ""));
sessions.put(address, session);
} else {
fetchStatusMap.put(address, FetchStatus.PENDING);
this.buildSessionFromPEP(conversation, address);
newSessions = true;
}
} else { } else {
Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account)+"Already have session for " + address.toString()); Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account)+"Already have session for " + address.toString());
} }
@ -1022,7 +1038,12 @@ public class AxolotlService {
if (session == null) { if (session == null) {
Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account)+"Account: "+account.getJid()+" No axolotl session found while parsing received message " + message); Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account)+"Account: "+account.getJid()+" No axolotl session found while parsing received message " + message);
// TODO: handle this properly // TODO: handle this properly
session = new XmppAxolotlSession(account, axolotlStore, senderAddress); IdentityKey identityKey = axolotlStore.loadSession(senderAddress).getSessionState().getRemoteIdentityKey();
if ( identityKey != null ) {
session = new XmppAxolotlSession(account, axolotlStore, senderAddress, identityKey.getFingerprint().replaceAll("\\s", ""));
} else {
session = new XmppAxolotlSession(account, axolotlStore, senderAddress);
}
newSession = true; newSession = true;
} }
@ -1044,7 +1065,7 @@ public class AxolotlService {
} }
if (newSession && plaintextMessage != null) { if (newSession && plaintextMessage != null) {
sessions.put(senderAddress,session); sessions.put(senderAddress, session);
} }
return plaintextMessage; return plaintextMessage;