Move lookup of latest conversation timestamp to core.

This commit is contained in:
akwizgran
2020-12-01 15:55:37 +00:00
committed by Torsten Grote
parent 8488499da6
commit 751c5a3245
12 changed files with 204 additions and 197 deletions

View File

@@ -736,18 +736,10 @@ public class ConversationActivity extends BriarActivity
List<AttachmentHeader> attachmentHeaders) {
if (isNullOrEmpty(text) && attachmentHeaders.isEmpty())
throw new AssertionError();
long timestamp = System.currentTimeMillis();
timestamp = Math.max(timestamp, getMinTimestampForNewMessage());
viewModel.sendMessage(text, attachmentHeaders, timestamp);
viewModel.sendMessage(text, attachmentHeaders);
textInputView.clearText();
}
private long getMinTimestampForNewMessage() {
// Don't use an earlier timestamp than the newest message
ConversationItem item = adapter.getLastItem();
return item == null ? 0 : item.getTime() + 1;
}
private void onAddedPrivateMessage(@Nullable PrivateMessageHeader h) {
if (h == null) return;
addConversationItem(h.accept(visitor));
@@ -955,13 +947,11 @@ public class ConversationActivity extends BriarActivity
adapter.notifyItemChanged(position, item);
}
runOnDbThread(() -> {
long timestamp = System.currentTimeMillis();
timestamp = Math.max(timestamp, getMinTimestampForNewMessage());
try {
switch (item.getRequestType()) {
case INTRODUCTION:
respondToIntroductionRequest(item.getSessionId(),
accept, timestamp);
accept);
break;
case FORUM:
respondToForumRequest(item.getSessionId(), accept);
@@ -1037,9 +1027,8 @@ public class ConversationActivity extends BriarActivity
@DatabaseExecutor
private void respondToIntroductionRequest(SessionId sessionId,
boolean accept, long time) throws DbException {
introductionManager.respondToIntroduction(contactId, sessionId, time,
accept);
boolean accept) throws DbException {
introductionManager.respondToIntroduction(contactId, sessionId, accept);
}
@DatabaseExecutor

View File

@@ -35,6 +35,7 @@ import org.briarproject.briar.android.viewmodel.MutableLiveEvent;
import org.briarproject.briar.api.attachment.AttachmentHeader;
import org.briarproject.briar.api.autodelete.AutoDeleteManager;
import org.briarproject.briar.api.avatar.event.AvatarUpdatedEvent;
import org.briarproject.briar.api.conversation.ConversationManager;
import org.briarproject.briar.api.identity.AuthorInfo;
import org.briarproject.briar.api.identity.AuthorManager;
import org.briarproject.briar.api.messaging.MessagingManager;
@@ -90,6 +91,7 @@ public class ConversationViewModel extends DbViewModel
private final AttachmentRetriever attachmentRetriever;
private final AttachmentCreator attachmentCreator;
private final AutoDeleteManager autoDeleteManager;
private final ConversationManager conversationManager;
@Nullable
private ContactId contactId = null;
@@ -125,7 +127,8 @@ public class ConversationViewModel extends DbViewModel
PrivateMessageFactory privateMessageFactory,
AttachmentRetriever attachmentRetriever,
AttachmentCreator attachmentCreator,
AutoDeleteManager autoDeleteManager) {
AutoDeleteManager autoDeleteManager,
ConversationManager conversationManager) {
super(application, dbExecutor, lifecycleManager, db, androidExecutor);
this.db = db;
this.eventBus = eventBus;
@@ -137,6 +140,7 @@ public class ConversationViewModel extends DbViewModel
this.attachmentRetriever = attachmentRetriever;
this.attachmentCreator = attachmentCreator;
this.autoDeleteManager = autoDeleteManager;
this.conversationManager = conversationManager;
messagingGroupId = map(contactItem, c ->
messagingManager.getContactGroup(c.getContact()).getId());
contactDeleted.setValue(false);
@@ -244,14 +248,13 @@ public class ConversationViewModel extends DbViewModel
}
@UiThread
void sendMessage(@Nullable String text,
List<AttachmentHeader> headers, long timestamp) {
void sendMessage(@Nullable String text, List<AttachmentHeader> headers) {
// messagingGroupId is loaded with the contact
observeForeverOnce(messagingGroupId, groupId -> {
requireNonNull(groupId);
observeForeverOnce(privateMessageFormat, format ->
storeMessage(requireNonNull(contactId), groupId, text,
headers, timestamp, format));
headers, format));
});
}
@@ -313,8 +316,10 @@ public class ConversationViewModel extends DbViewModel
private PrivateMessage createMessage(Transaction txn, ContactId c,
GroupId groupId, @Nullable String text,
List<AttachmentHeader> headers, long timestamp,
PrivateMessageFormat format) throws DbException {
List<AttachmentHeader> headers, PrivateMessageFormat format)
throws DbException {
long timestamp =
conversationManager.getTimestampForOutgoingMessage(txn, c);
try {
if (format == TEXT_ONLY) {
return privateMessageFactory.createLegacyPrivateMessage(
@@ -335,13 +340,13 @@ public class ConversationViewModel extends DbViewModel
@UiThread
private void storeMessage(ContactId c, GroupId groupId,
@Nullable String text, List<AttachmentHeader> headers,
long timestamp, PrivateMessageFormat format) {
PrivateMessageFormat format) {
runOnDbThread(() -> {
try {
db.transaction(false, txn -> {
long start = now();
PrivateMessage m = createMessage(txn, c, groupId, text,
headers, timestamp, format);
headers, format);
messagingManager.addLocalMessage(txn, m);
logDuration(LOG, "Storing message", start);
Message message = m.getMessage();

View File

@@ -165,10 +165,9 @@ class IntroductionViewModel extends ContactsViewModel {
runOnDbThread(() -> {
// actually make the introduction
try {
long timestamp = System.currentTimeMillis();
introductionManager.makeIntroduction(
info.getContact1().getContact(),
info.getContact2().getContact(), text, timestamp);
info.getContact2().getContact(), text);
} catch (DbException e) {
logException(LOG, WARNING, e);
androidExecutor.runOnUiThread(() -> Toast.makeText(

View File

@@ -48,6 +48,13 @@ public interface ConversationManager {
*/
GroupCount getGroupCount(Transaction txn, ContactId c) throws DbException;
/**
* Returns a timestamp for an outgoing message, which is later than the
* timestamp of any visible message sent or received so far.
*/
long getTimestampForOutgoingMessage(Transaction txn, ContactId c)
throws DbException;
/**
* Deletes all messages exchanged with the given contact.
*/

View File

@@ -36,13 +36,13 @@ public interface IntroductionManager extends ConversationClient {
/**
* Sends two initial introduction messages.
*/
void makeIntroduction(Contact c1, Contact c2, @Nullable String text,
long timestamp) throws DbException;
void makeIntroduction(Contact c1, Contact c2, @Nullable String text)
throws DbException;
/**
* Responds to an introduction.
*/
void respondToIntroduction(ContactId contactId, SessionId sessionId,
long timestamp, boolean accept) throws DbException;
boolean accept) throws DbException;
}

View File

@@ -20,11 +20,11 @@ import org.briarproject.bramble.api.properties.TransportProperties;
import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.Message;
import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.api.versioning.ClientVersioningManager;
import org.briarproject.briar.api.autodelete.AutoDeleteManager;
import org.briarproject.briar.api.client.MessageTracker;
import org.briarproject.briar.api.client.SessionId;
import org.briarproject.briar.api.conversation.ConversationManager;
import org.briarproject.briar.api.identity.AuthorInfo;
import org.briarproject.briar.api.identity.AuthorManager;
import org.briarproject.briar.api.introduction.IntroductionResponse;
@@ -61,7 +61,7 @@ abstract class AbstractProtocolEngine<S extends Session<?>>
protected final MessageEncoder messageEncoder;
protected final ClientVersioningManager clientVersioningManager;
protected final AutoDeleteManager autoDeleteManager;
protected final Clock clock;
protected final ConversationManager conversationManager;
AbstractProtocolEngine(
DatabaseComponent db,
@@ -75,7 +75,7 @@ abstract class AbstractProtocolEngine<S extends Session<?>>
MessageEncoder messageEncoder,
ClientVersioningManager clientVersioningManager,
AutoDeleteManager autoDeleteManager,
Clock clock) {
ConversationManager conversationManager) {
this.db = db;
this.clientHelper = clientHelper;
this.contactManager = contactManager;
@@ -87,7 +87,7 @@ abstract class AbstractProtocolEngine<S extends Session<?>>
this.messageEncoder = messageEncoder;
this.clientVersioningManager = clientVersioningManager;
this.autoDeleteManager = autoDeleteManager;
this.clock = clock;
this.conversationManager = conversationManager;
}
Message sendRequestMessage(Transaction txn, PeerSession s,
@@ -231,14 +231,10 @@ abstract class AbstractProtocolEngine<S extends Session<?>>
return !dependency.equals(lastRemoteMessageId);
}
long getLocalTimestamp(long localTimestamp, long requestTimestamp) {
return Math.max(
clock.currentTimeMillis(),
Math.max(
localTimestamp,
requestTimestamp
) + 1
);
long getTimestampForOutgoingMessage(Transaction txn, GroupId contactGroupId)
throws DbException {
ContactId c = getContactId(txn, contactGroupId);
return conversationManager.getTimestampForOutgoingMessage(txn, c);
}
private ContactId getContactId(Transaction txn, GroupId contactGroupId)

View File

@@ -22,7 +22,6 @@ import org.briarproject.bramble.api.properties.TransportProperties;
import org.briarproject.bramble.api.properties.TransportPropertyManager;
import org.briarproject.bramble.api.sync.Message;
import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.api.transport.KeyManager;
import org.briarproject.bramble.api.transport.KeySetId;
import org.briarproject.bramble.api.versioning.ClientVersioningManager;
@@ -30,6 +29,7 @@ import org.briarproject.briar.api.autodelete.AutoDeleteManager;
import org.briarproject.briar.api.client.MessageTracker;
import org.briarproject.briar.api.client.ProtocolStateException;
import org.briarproject.briar.api.client.SessionId;
import org.briarproject.briar.api.conversation.ConversationManager;
import org.briarproject.briar.api.identity.AuthorInfo;
import org.briarproject.briar.api.identity.AuthorManager;
import org.briarproject.briar.api.introduction.IntroductionRequest;
@@ -44,6 +44,7 @@ import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
import javax.inject.Inject;
import static java.lang.Math.max;
import static java.util.logging.Level.WARNING;
import static org.briarproject.bramble.util.LogUtils.logException;
import static org.briarproject.briar.introduction.IntroduceeState.AWAIT_AUTH;
@@ -77,16 +78,16 @@ class IntroduceeProtocolEngine
AuthorManager authorManager,
MessageParser messageParser,
MessageEncoder messageEncoder,
Clock clock,
IntroductionCrypto crypto,
KeyManager keyManager,
TransportPropertyManager transportPropertyManager,
ClientVersioningManager clientVersioningManager,
AutoDeleteManager autoDeleteManager) {
AutoDeleteManager autoDeleteManager,
ConversationManager conversationManager) {
super(db, clientHelper, contactManager, contactGroupFactory,
messageTracker, identityManager, authorManager, messageParser,
messageEncoder, clientVersioningManager, autoDeleteManager,
clock);
conversationManager);
this.crypto = crypto;
this.keyManager = keyManager;
this.transportPropertyManager = transportPropertyManager;
@@ -94,18 +95,18 @@ class IntroduceeProtocolEngine
@Override
public IntroduceeSession onRequestAction(Transaction txn,
IntroduceeSession session, @Nullable String text, long timestamp) {
IntroduceeSession session, @Nullable String text) {
throw new UnsupportedOperationException(); // Invalid in this role
}
@Override
public IntroduceeSession onAcceptAction(Transaction txn,
IntroduceeSession session, long timestamp) throws DbException {
IntroduceeSession session) throws DbException {
switch (session.getState()) {
case AWAIT_RESPONSES:
case REMOTE_DECLINED:
case REMOTE_ACCEPTED:
return onLocalAccept(txn, session, timestamp);
return onLocalAccept(txn, session);
case START:
case LOCAL_DECLINED:
case LOCAL_ACCEPTED:
@@ -119,12 +120,12 @@ class IntroduceeProtocolEngine
@Override
public IntroduceeSession onDeclineAction(Transaction txn,
IntroduceeSession session, long timestamp) throws DbException {
IntroduceeSession session) throws DbException {
switch (session.getState()) {
case AWAIT_RESPONSES:
case REMOTE_DECLINED:
case REMOTE_ACCEPTED:
return onLocalDecline(txn, session, timestamp);
return onLocalDecline(txn, session);
case START:
case LOCAL_DECLINED:
case LOCAL_ACCEPTED:
@@ -275,7 +276,7 @@ class IntroduceeProtocolEngine
}
private IntroduceeSession onLocalAccept(Transaction txn,
IntroduceeSession s, long timestamp) throws DbException {
IntroduceeSession s) throws DbException {
// Mark the request message unavailable to answer
markRequestsUnavailableToAnswer(txn, s);
@@ -287,7 +288,7 @@ class IntroduceeProtocolEngine
transportPropertyManager.getLocalProperties(txn);
// Send a ACCEPT message
long localTimestamp = Math.max(timestamp + 1, getLocalTimestamp(s));
long localTimestamp = getLocalTimestamp(txn, s);
Message sent = sendAcceptMessage(txn, s, localTimestamp, publicKey,
localTimestamp, transportProperties, true);
// Track the message
@@ -312,12 +313,12 @@ class IntroduceeProtocolEngine
}
private IntroduceeSession onLocalDecline(Transaction txn,
IntroduceeSession s, long timestamp) throws DbException {
IntroduceeSession s) throws DbException {
// Mark the request message unavailable to answer
markRequestsUnavailableToAnswer(txn, s);
// Send a DECLINE message
long localTimestamp = Math.max(timestamp + 1, getLocalTimestamp(s));
long localTimestamp = getLocalTimestamp(txn, s);
Message sent = sendDeclineMessage(txn, s, localTimestamp, true);
// Track the message
@@ -415,8 +416,8 @@ class IntroduceeProtocolEngine
return abort(txn, s);
}
if (s.getState() != AWAIT_AUTH) throw new AssertionError();
Message sent = sendAuthMessage(txn, s, getLocalTimestamp(s), mac,
signature);
long localTimestamp = getLocalTimestamp(txn, s);
Message sent = sendAuthMessage(txn, s, localTimestamp, mac, signature);
return IntroduceeSession.addLocalAuth(s, AWAIT_AUTH, sent, masterKey,
aliceMacKey, bobMacKey);
}
@@ -465,7 +466,8 @@ class IntroduceeProtocolEngine
// send ACTIVATE message with a MAC
byte[] mac = crypto.activateMac(s);
Message sent = sendActivateMessage(txn, s, getLocalTimestamp(s), mac);
long localTimestamp = getLocalTimestamp(txn, s);
Message sent = sendActivateMessage(txn, s, localTimestamp, mac);
// Move to AWAIT_ACTIVATE state and clear key material from session
return IntroduceeSession.awaitActivate(s, m, sent, keys);
@@ -516,7 +518,8 @@ class IntroduceeProtocolEngine
markRequestsUnavailableToAnswer(txn, s);
// Send an ABORT message
Message sent = sendAbortMessage(txn, s, getLocalTimestamp(s));
long localTimestamp = getLocalTimestamp(txn, s);
Message sent = sendAbortMessage(txn, s, localTimestamp);
// Broadcast abort event for testing
txn.attach(new IntroductionAbortedEvent(s.getSessionId()));
@@ -531,9 +534,18 @@ class IntroduceeProtocolEngine
return isInvalidDependency(s.getLastRemoteMessageId(), dependency);
}
private long getLocalTimestamp(IntroduceeSession s) {
return getLocalTimestamp(s.getLocalTimestamp(),
s.getRequestTimestamp());
/**
* Returns a timestamp for an outgoing message, which is later than the
* timestamp of any message sent or received so far in the conversation
* or the session.
*/
private long getLocalTimestamp(Transaction txn, IntroduceeSession s)
throws DbException {
long conversationTimestamp =
getTimestampForOutgoingMessage(txn, s.getContactGroupId());
long sessionTimestamp =
max(s.getLocalTimestamp(), s.getRequestTimestamp()) + 1;
return max(conversationTimestamp, sessionTimestamp);
}
private void addSessionId(Transaction txn, MessageId m, SessionId sessionId)

View File

@@ -12,11 +12,11 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.Message;
import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.api.versioning.ClientVersioningManager;
import org.briarproject.briar.api.autodelete.AutoDeleteManager;
import org.briarproject.briar.api.client.MessageTracker;
import org.briarproject.briar.api.client.ProtocolStateException;
import org.briarproject.briar.api.conversation.ConversationManager;
import org.briarproject.briar.api.identity.AuthorManager;
import org.briarproject.briar.api.introduction.event.IntroductionAbortedEvent;
import org.briarproject.briar.introduction.IntroducerSession.Introducee;
@@ -25,6 +25,7 @@ import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
import javax.inject.Inject;
import static java.lang.Math.max;
import static org.briarproject.briar.introduction.IntroducerState.AWAIT_ACTIVATES;
import static org.briarproject.briar.introduction.IntroducerState.AWAIT_ACTIVATE_A;
import static org.briarproject.briar.introduction.IntroducerState.AWAIT_ACTIVATE_B;
@@ -56,20 +57,20 @@ class IntroducerProtocolEngine
MessageEncoder messageEncoder,
ClientVersioningManager clientVersioningManager,
AutoDeleteManager autoDeleteManager,
Clock clock) {
ConversationManager conversationManager) {
super(db, clientHelper, contactManager, contactGroupFactory,
messageTracker, identityManager, authorManager, messageParser,
messageEncoder, clientVersioningManager, autoDeleteManager,
clock);
conversationManager);
}
@Override
public IntroducerSession onRequestAction(Transaction txn,
IntroducerSession s, @Nullable String text, long timestamp)
IntroducerSession s, @Nullable String text)
throws DbException {
switch (s.getState()) {
case START:
return onLocalRequest(txn, s, text, timestamp);
return onLocalRequest(txn, s, text);
case AWAIT_RESPONSES:
case AWAIT_RESPONSE_A:
case AWAIT_RESPONSE_B:
@@ -89,37 +90,24 @@ class IntroducerProtocolEngine
@Override
public IntroducerSession onAcceptAction(Transaction txn,
IntroducerSession s, long timestamp) {
IntroducerSession s) {
throw new UnsupportedOperationException(); // Invalid in this role
}
@Override
public IntroducerSession onDeclineAction(Transaction txn,
IntroducerSession s, long timestamp) {
IntroducerSession s) {
throw new UnsupportedOperationException(); // Invalid in this role
}
IntroducerSession onIntroduceeRemoved(Transaction txn,
Introducee remainingIntroducee, IntroducerSession session)
throws DbException {
// abort session
IntroducerSession s = abort(txn, session);
// reset information for introducee that was removed
Introducee introduceeA, introduceeB;
if (remainingIntroducee.author.equals(s.getIntroduceeA().author)) {
introduceeA = s.getIntroduceeA();
introduceeB =
new Introducee(s.getSessionId(), s.getIntroduceeB().groupId,
s.getIntroduceeB().author);
} else if (remainingIntroducee.author
.equals(s.getIntroduceeB().author)) {
introduceeA =
new Introducee(s.getSessionId(), s.getIntroduceeA().groupId,
s.getIntroduceeA().author);
introduceeB = s.getIntroduceeB();
} else throw new DbException();
// abort session with remaining introducee
IntroducerSession s = abort(txn, session, remainingIntroducee);
return new IntroducerSession(s.getSessionId(), s.getState(),
s.getRequestTimestamp(), introduceeA, introduceeB);
s.getRequestTimestamp(), s.getIntroduceeA(),
s.getIntroduceeB());
}
@Override
@@ -229,13 +217,11 @@ class IntroducerProtocolEngine
}
private IntroducerSession onLocalRequest(Transaction txn,
IntroducerSession s, @Nullable String text, long timestamp)
throws DbException {
IntroducerSession s, @Nullable String text) throws DbException {
// Send REQUEST messages
long maxIntroduceeTimestamp =
Math.max(getLocalTimestamp(s, s.getIntroduceeA()),
getLocalTimestamp(s, s.getIntroduceeB()));
long localTimestamp = Math.max(timestamp, maxIntroduceeTimestamp);
long timestampA = getLocalTimestamp(txn, s, s.getIntroduceeA());
long timestampB = getLocalTimestamp(txn, s, s.getIntroduceeB());
long localTimestamp = max(timestampA, timestampB);
Message sentA = sendRequestMessage(txn, s.getIntroduceeA(),
localTimestamp, s.getIntroduceeB().author, text);
Message sentB = sendRequestMessage(txn, s.getIntroduceeB(),
@@ -275,11 +261,10 @@ class IntroducerProtocolEngine
// Forward ACCEPT message
Introducee i = getOtherIntroducee(s, m.getGroupId());
long timestamp = getLocalTimestamp(s, i);
Message sent =
sendAcceptMessage(txn, i, timestamp, m.getEphemeralPublicKey(),
m.getAcceptTimestamp(), m.getTransportProperties(),
false);
long localTimestamp = getLocalTimestamp(txn, s, i);
Message sent = sendAcceptMessage(txn, i, localTimestamp,
m.getEphemeralPublicKey(), m.getAcceptTimestamp(),
m.getTransportProperties(), false);
// Create the next state
IntroducerState state = AWAIT_AUTHS;
@@ -336,7 +321,8 @@ class IntroducerProtocolEngine
// Forward ACCEPT message
Introducee i = getOtherIntroducee(s, m.getGroupId());
Message sent = sendAcceptMessage(txn, i, getLocalTimestamp(s, i),
long localTimestamp = getLocalTimestamp(txn, s, i);
Message sent = sendAcceptMessage(txn, i, localTimestamp,
m.getEphemeralPublicKey(), m.getAcceptTimestamp(),
m.getTransportProperties(), false);
@@ -387,8 +373,8 @@ class IntroducerProtocolEngine
// Forward DECLINE message
Introducee i = getOtherIntroducee(s, m.getGroupId());
long timestamp = getLocalTimestamp(s, i);
Message sent = sendDeclineMessage(txn, i, timestamp, false);
long localTimestamp = getLocalTimestamp(txn, s, i);
Message sent = sendDeclineMessage(txn, i, localTimestamp, false);
// Create the next state
IntroducerState state = START;
@@ -439,8 +425,8 @@ class IntroducerProtocolEngine
// Forward DECLINE message
Introducee i = getOtherIntroducee(s, m.getGroupId());
long timestamp = getLocalTimestamp(s, i);
Message sent = sendDeclineMessage(txn, i, timestamp, false);
long localTimestamp = getLocalTimestamp(txn, s, i);
Message sent = sendDeclineMessage(txn, i, localTimestamp, false);
Introducee introduceeA, introduceeB;
Author sender, other;
@@ -480,8 +466,8 @@ class IntroducerProtocolEngine
// Forward AUTH message
Introducee i = getOtherIntroducee(s, m.getGroupId());
long timestamp = getLocalTimestamp(s, i);
Message sent = sendAuthMessage(txn, i, timestamp, m.getMac(),
long localTimestamp = getLocalTimestamp(txn, s, i);
Message sent = sendAuthMessage(txn, i, localTimestamp, m.getMac(),
m.getSignature());
// Move to the next state
@@ -516,8 +502,8 @@ class IntroducerProtocolEngine
// Forward ACTIVATE message
Introducee i = getOtherIntroducee(s, m.getGroupId());
long timestamp = getLocalTimestamp(s, i);
Message sent = sendActivateMessage(txn, i, timestamp, m.getMac());
long localTimestamp = getLocalTimestamp(txn, s, i);
Message sent = sendActivateMessage(txn, i, localTimestamp, m.getMac());
// Move to the next state
IntroducerState state = START;
@@ -539,8 +525,9 @@ class IntroducerProtocolEngine
IntroducerSession s, AbortMessage m) throws DbException {
// Forward ABORT message
Introducee i = getOtherIntroducee(s, m.getGroupId());
long timestamp = getLocalTimestamp(s, i);
Message sent = sendAbortMessage(txn, i, timestamp);
long localTimestamp = max(i.getLocalTimestamp(),
s.getRequestTimestamp()) + 1;
Message sent = sendAbortMessage(txn, i, localTimestamp);
// Broadcast abort event for testing
txn.attach(new IntroductionAbortedEvent(s.getSessionId()));
@@ -558,15 +545,42 @@ class IntroducerProtocolEngine
s.getRequestTimestamp(), introduceeA, introduceeB);
}
private IntroducerSession abort(Transaction txn,
IntroducerSession s) throws DbException {
private IntroducerSession abort(Transaction txn, IntroducerSession s,
Introducee remainingIntroducee) throws DbException {
// Broadcast abort event for testing
txn.attach(new IntroductionAbortedEvent(s.getSessionId()));
// Send an ABORT message to the remaining introducee
long localTimestamp = getLocalTimestamp(txn, s, remainingIntroducee);
Message sent =
sendAbortMessage(txn, remainingIntroducee, localTimestamp);
// Reset the session back to initial state
Introducee introduceeA = s.getIntroduceeA();
Introducee introduceeB = s.getIntroduceeB();
if (remainingIntroducee.author.equals(introduceeA.author)) {
introduceeA = new Introducee(introduceeA, sent);
introduceeB = new Introducee(s.getSessionId(), introduceeB.groupId,
introduceeB.author);
} else if (remainingIntroducee.author.equals(introduceeB.author)) {
introduceeA = new Introducee(s.getSessionId(), introduceeA.groupId,
introduceeA.author);
introduceeB = new Introducee(introduceeB, sent);
} else {
throw new DbException();
}
return new IntroducerSession(s.getSessionId(), START,
s.getRequestTimestamp(), introduceeA, introduceeB);
}
private IntroducerSession abort(Transaction txn, IntroducerSession s)
throws DbException {
// Broadcast abort event for testing
txn.attach(new IntroductionAbortedEvent(s.getSessionId()));
// Send an ABORT message to both introducees
long timestampA = getLocalTimestamp(s, s.getIntroduceeA());
long timestampA = getLocalTimestamp(txn, s, s.getIntroduceeA());
Message sentA = sendAbortMessage(txn, s.getIntroduceeA(), timestampA);
long timestampB = getLocalTimestamp(s, s.getIntroduceeB());
long timestampB = getLocalTimestamp(txn, s, s.getIntroduceeB());
Message sentB = sendAbortMessage(txn, s.getIntroduceeB(), timestampB);
// Reset the session back to initial state
Introducee introduceeA = new Introducee(s.getIntroduceeA(), sentA);
@@ -596,9 +610,17 @@ class IntroducerProtocolEngine
return isInvalidDependency(expected, dependency);
}
private long getLocalTimestamp(IntroducerSession s, PeerSession p) {
return getLocalTimestamp(p.getLocalTimestamp(),
s.getRequestTimestamp());
/**
* Returns a timestamp for an outgoing message, which is later than the
* timestamp of any message sent or received so far in the conversation
* or the session.
*/
private long getLocalTimestamp(Transaction txn, IntroducerSession s,
PeerSession p) throws DbException {
long conversationTimestamp =
getTimestampForOutgoingMessage(txn, p.getContactGroupId());
long sessionTimestamp =
max(p.getLocalTimestamp(), s.getRequestTimestamp()) + 1;
return max(conversationTimestamp, sessionTimestamp);
}
}

View File

@@ -315,8 +315,8 @@ class IntroductionManagerImpl extends ConversationClientImpl
}
@Override
public void makeIntroduction(Contact c1, Contact c2, @Nullable String text,
long timestamp) throws DbException {
public void makeIntroduction(Contact c1, Contact c2, @Nullable String text)
throws DbException {
Transaction txn = db.startTransaction(false);
try {
// Look up the session, if there is one
@@ -348,8 +348,7 @@ class IntroductionManagerImpl extends ConversationClientImpl
storageId = ss.storageId;
}
// Handle the request action
session = introducerEngine
.onRequestAction(txn, session, text, timestamp);
session = introducerEngine.onRequestAction(txn, session, text);
// Store the updated session
storeSession(txn, storageId, session);
db.commitTransaction(txn);
@@ -362,7 +361,7 @@ class IntroductionManagerImpl extends ConversationClientImpl
@Override
public void respondToIntroduction(ContactId contactId, SessionId sessionId,
long timestamp, boolean accept) throws DbException {
boolean accept) throws DbException {
Transaction txn = db.startTransaction(false);
try {
// Look up the session
@@ -380,11 +379,9 @@ class IntroductionManagerImpl extends ConversationClientImpl
.parseIntroduceeSession(contactGroupId, ss.bdfSession);
// Handle the join or leave action
if (accept) {
session = introduceeEngine
.onAcceptAction(txn, session, timestamp);
session = introduceeEngine.onAcceptAction(txn, session);
} else {
session = introduceeEngine
.onDeclineAction(txn, session, timestamp);
session = introduceeEngine.onDeclineAction(txn, session);
}
// Store the updated session
storeSession(txn, ss.storageId, session);

View File

@@ -8,16 +8,14 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.Nullable;
@NotNullByDefault
interface ProtocolEngine<S extends Session> {
interface ProtocolEngine<S extends Session<?>> {
S onRequestAction(Transaction txn, S session, @Nullable String text,
long timestamp) throws DbException;
S onAcceptAction(Transaction txn, S session, long timestamp)
S onRequestAction(Transaction txn, S session, @Nullable String text)
throws DbException;
S onDeclineAction(Transaction txn, S session, long timestamp)
throws DbException;
S onAcceptAction(Transaction txn, S session) throws DbException;
S onDeclineAction(Transaction txn, S session) throws DbException;
S onRequestMessage(Transaction txn, S session, RequestMessage m)
throws DbException, FormatException;

View File

@@ -6,6 +6,7 @@ import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.bramble.api.system.Clock;
import org.briarproject.briar.api.client.MessageTracker.GroupCount;
import org.briarproject.briar.api.conversation.ConversationManager;
import org.briarproject.briar.api.conversation.ConversationMessageHeader;
@@ -20,16 +21,20 @@ import java.util.concurrent.CopyOnWriteArraySet;
import javax.annotation.concurrent.ThreadSafe;
import javax.inject.Inject;
import static java.lang.Math.max;
@ThreadSafe
@NotNullByDefault
class ConversationManagerImpl implements ConversationManager {
private final DatabaseComponent db;
private final Clock clock;
private final Set<ConversationClient> clients;
@Inject
ConversationManagerImpl(DatabaseComponent db) {
ConversationManagerImpl(DatabaseComponent db, Clock clock) {
this.db = db;
this.clock = clock;
clients = new CopyOnWriteArraySet<>();
}
@@ -76,6 +81,14 @@ class ConversationManagerImpl implements ConversationManager {
return new GroupCount(msgCount, unreadCount, latestTime);
}
@Override
public long getTimestampForOutgoingMessage(Transaction txn, ContactId c)
throws DbException {
long now = clock.currentTimeMillis();
GroupCount gc = getGroupCount(txn, c);
return max(now, gc.getLatestMsgTime() + 1);
}
@Override
public DeletionResult deleteAllMessages(ContactId c) throws DbException {
return db.transactionWithResult(false, txn -> {

View File

@@ -140,11 +140,9 @@ public class IntroductionIntegrationTest
addListeners(true, true);
// make introduction
long time = clock.currentTimeMillis();
Contact introducee1 = contact1From0;
Contact introducee2 = contact2From0;
introductionManager0
.makeIntroduction(introducee1, introducee2, "Hi!", time);
introductionManager0.makeIntroduction(introducee1, introducee2, "Hi!");
// check that messages are tracked properly
Group g1 = introductionManager0.getContactGroup(introducee1);
@@ -264,11 +262,9 @@ public class IntroductionIntegrationTest
addListeners(false, true);
// make introduction
long time = clock.currentTimeMillis();
Contact introducee1 = contact1From0;
Contact introducee2 = contact2From0;
introductionManager0
.makeIntroduction(introducee1, introducee2, null, time);
introductionManager0.makeIntroduction(introducee1, introducee2, null);
// sync request messages
sync0To1(1, true);
@@ -356,9 +352,8 @@ public class IntroductionIntegrationTest
addListeners(true, false);
// make introduction
long time = clock.currentTimeMillis();
introductionManager0
.makeIntroduction(contact1From0, contact2From0, null, time);
.makeIntroduction(contact1From0, contact2From0, null);
// sync request messages
sync0To1(1, true);
@@ -412,9 +407,8 @@ public class IntroductionIntegrationTest
addListeners(false, true);
// make introduction
long time = clock.currentTimeMillis();
introductionManager0
.makeIntroduction(contact1From0, contact2From0, null, time);
.makeIntroduction(contact1From0, contact2From0, null);
// sync request messages
sync0To1(1, true);
@@ -438,9 +432,8 @@ public class IntroductionIntegrationTest
assertFalse(listener1.aborted);
assertFalse(listener2.aborted);
time = clock.currentTimeMillis();
introductionManager0
.makeIntroduction(contact1From0, contact2From0, null, time);
.makeIntroduction(contact1From0, contact2From0, null);
// sync request messages
sync0To1(1, true);
@@ -457,9 +450,8 @@ public class IntroductionIntegrationTest
addListeners(true, true);
// make introduction
long time = clock.currentTimeMillis();
introductionManager0
.makeIntroduction(contact1From0, contact2From0, "Hi!", time);
.makeIntroduction(contact1From0, contact2From0, "Hi!");
// sync first request message
sync0To1(1, true);
@@ -482,7 +474,7 @@ public class IntroductionIntegrationTest
// answer request manually
introductionManager2.respondToIntroduction(contactId0From2,
listener2.sessionId, time, true);
listener2.sessionId, true);
// sync second response and AUTH
sync2To0(2, true);
@@ -518,11 +510,10 @@ public class IntroductionIntegrationTest
listener2.answerRequests = false;
// make introduction
long time = clock.currentTimeMillis();
Contact introducee1 = contact1From0;
Contact introducee2 = contact2From0;
introductionManager0
.makeIntroduction(introducee1, introducee2, null, time);
.makeIntroduction(introducee1, introducee2, null);
// sync request messages
sync0To1(1, true);
@@ -564,7 +555,7 @@ public class IntroductionIntegrationTest
// answer request manually
introductionManager2.respondToIntroduction(contactId0From2,
listener2.sessionId, time, false);
listener2.sessionId, false);
// now introducee2 should have returned to the START state
introduceeSession = getIntroduceeSession(c2);
@@ -611,9 +602,8 @@ public class IntroductionIntegrationTest
addListeners(true, false);
// make introduction
long time = clock.currentTimeMillis();
introductionManager0
.makeIntroduction(contact1From0, contact1From0, null, time);
.makeIntroduction(contact1From0, contact1From0, null);
// sync request messages
sync0To1(1, false);
@@ -637,9 +627,8 @@ public class IntroductionIntegrationTest
.canIntroduce(contact1From0, contact2From0));
// make the introduction
long time = clock.currentTimeMillis();
introductionManager0
.makeIntroduction(contact1From0, contact2From0, null, time);
.makeIntroduction(contact1From0, contact2From0, null);
// no more introduction allowed while the existing one is in progress
assertFalse(introductionManager0
@@ -647,7 +636,7 @@ public class IntroductionIntegrationTest
// try it anyway and fail
introductionManager0
.makeIntroduction(contact1From0, contact2From0, null, time);
.makeIntroduction(contact1From0, contact2From0, null);
}
@Test
@@ -661,9 +650,8 @@ public class IntroductionIntegrationTest
addListeners(true, true);
// make the introduction
long time = clock.currentTimeMillis();
introductionManager0
.makeIntroduction(contact1From0, contact2From0, null, time);
.makeIntroduction(contact1From0, contact2From0, null);
// sync REQUEST messages
sync0To1(1, true);
@@ -719,9 +707,8 @@ public class IntroductionIntegrationTest
addListeners(true, true);
// make the introduction
long time = clock.currentTimeMillis();
introductionManager0
.makeIntroduction(contact1From0, contact2From0, null, time);
.makeIntroduction(contact1From0, contact2From0, null);
// sync REQUEST messages
sync0To1(1, true);
@@ -766,9 +753,8 @@ public class IntroductionIntegrationTest
addListeners(true, true);
// make the introduction
long time = clock.currentTimeMillis();
introductionManager0
.makeIntroduction(contact1From0, contact2From0, null, time);
.makeIntroduction(contact1From0, contact2From0, null);
// sync REQUEST to introducee1
sync0To1(1, true);
@@ -803,9 +789,8 @@ public class IntroductionIntegrationTest
addListeners(true, true);
// make the introduction
long time = clock.currentTimeMillis();
introductionManager0
.makeIntroduction(contact1From0, contact2From0, null, time);
.makeIntroduction(contact1From0, contact2From0, null);
// sync REQUEST to introducee1
sync0To1(1, true);
@@ -838,9 +823,8 @@ public class IntroductionIntegrationTest
addListeners(false, true);
// make the introduction
long time = clock.currentTimeMillis();
introductionManager0
.makeIntroduction(contact1From0, contact2From0, null, time);
.makeIntroduction(contact1From0, contact2From0, null);
// sync REQUEST to introducee1
sync0To1(1, true);
@@ -873,9 +857,8 @@ public class IntroductionIntegrationTest
addListeners(true, true);
// make the introduction
long time = clock.currentTimeMillis();
introductionManager0
.makeIntroduction(contact1From0, contact2From0, null, time);
.makeIntroduction(contact1From0, contact2From0, null);
// sync REQUEST messages
sync0To1(1, true);
@@ -914,9 +897,8 @@ public class IntroductionIntegrationTest
addListeners(true, true);
// make introduction
long time = clock.currentTimeMillis();
introductionManager0
.makeIntroduction(contact1From0, contact2From0, "Hi!", time);
.makeIntroduction(contact1From0, contact2From0, "Hi!");
// sync first request message
sync0To1(1, true);
@@ -943,9 +925,8 @@ public class IntroductionIntegrationTest
addListeners(true, true);
// make introduction
long time = clock.currentTimeMillis();
introductionManager0
.makeIntroduction(contact1From0, contact2From0, "Hi!", time);
.makeIntroduction(contact1From0, contact2From0, "Hi!");
// sync first request message
sync0To1(1, true);
@@ -987,9 +968,8 @@ public class IntroductionIntegrationTest
@Test
public void testIntroductionAfterReAddingContacts() throws Exception {
// make introduction
long time = clock.currentTimeMillis();
introductionManager0
.makeIntroduction(contact1From0, contact2From0, null, time);
.makeIntroduction(contact1From0, contact2From0, null);
// 0 and 1 remove and re-add each other
contactManager0.removeContact(contactId1From0);
@@ -1016,9 +996,8 @@ public class IntroductionIntegrationTest
addListeners(true, true);
// make new introduction
time = clock.currentTimeMillis();
introductionManager0
.makeIntroduction(contact1From0, contact2From0, null, time);
.makeIntroduction(contact1From0, contact2From0, null);
// introduction should sync and not be INVALID or PENDING
sync0To1(1, true);
@@ -1032,9 +1011,8 @@ public class IntroductionIntegrationTest
addListeners(true, true);
// make introduction
long time = clock.currentTimeMillis();
introductionManager0
.makeIntroduction(contact1From0, contact2From0, "Hi!", time);
.makeIntroduction(contact1From0, contact2From0, "Hi!");
// sync request messages
sync0To1(1, true);
@@ -1147,9 +1125,8 @@ public class IntroductionIntegrationTest
addListeners(true, true);
// make introduction
long time = clock.currentTimeMillis();
introductionManager0
.makeIntroduction(contact1From0, contact2From0, "Hi!", time);
.makeIntroduction(contact1From0, contact2From0, "Hi!");
// sync first REQUEST message
sync0To1(1, true);
@@ -1292,7 +1269,7 @@ public class IntroductionIntegrationTest
assertTrue(introductionManager0
.canIntroduce(contact1From0, contact2From0));
introductionManager0
.makeIntroduction(contact1From0, contact2From0, "Ho!", time);
.makeIntroduction(contact1From0, contact2From0, "Ho!");
sync0To1(1, true);
sync0To2(1, true);
@@ -1332,9 +1309,8 @@ public class IntroductionIntegrationTest
addListeners(false, false);
// make introduction
long time = clock.currentTimeMillis();
introductionManager0
.makeIntroduction(contact1From0, contact2From0, "Hi!", time);
.makeIntroduction(contact1From0, contact2From0, "Hi!");
// sync REQUEST messages
sync0To1(1, true);
@@ -1399,9 +1375,8 @@ public class IntroductionIntegrationTest
// a new introduction is still possible
assertTrue(introductionManager0
.canIntroduce(contact1From0, contact2From0));
time = clock.currentTimeMillis();
introductionManager0
.makeIntroduction(contact1From0, contact2From0, "Ho!", time);
.makeIntroduction(contact1From0, contact2From0, "Ho!");
sync0To1(1, true);
sync0To2(1, true);
@@ -1428,9 +1403,8 @@ public class IntroductionIntegrationTest
addListeners(false, false);
// make introduction
long time = clock.currentTimeMillis();
introductionManager0
.makeIntroduction(contact1From0, contact2From0, "Hi!", time);
.makeIntroduction(contact1From0, contact2From0, "Hi!");
// sync REQUEST messages
sync0To1(1, true);
@@ -1458,9 +1432,8 @@ public class IntroductionIntegrationTest
// a new introduction is still possible
assertTrue(introductionManager0
.canIntroduce(contact1From0, contact2From0));
time = clock.currentTimeMillis();
introductionManager0
.makeIntroduction(contact1From0, contact2From0, "Ho!", time);
.makeIntroduction(contact1From0, contact2From0, "Ho!");
sync0To1(1, true);
sync0To2(1, true);
@@ -1496,9 +1469,8 @@ public class IntroductionIntegrationTest
addListeners(false, false);
// make introduction
long time = clock.currentTimeMillis();
introductionManager0.makeIntroduction(contact1From0, contact2From0,
"Hi!", time);
introductionManager0
.makeIntroduction(contact1From0, contact2From0, "Hi!");
// deleting the introduction for introducee1 will fail
Collection<ConversationMessageHeader> m1From0 = getMessages1From0();
@@ -1795,16 +1767,13 @@ public class IntroductionIntegrationTest
IntroductionRequest ir = introEvent.getMessageHeader();
ContactId contactId = introEvent.getContactId();
sessionId = ir.getSessionId();
long time = clock.currentTimeMillis();
try {
if (introducee == 1 && answerRequests) {
introductionManager1
.respondToIntroduction(contactId, sessionId,
time, accept);
introductionManager1.respondToIntroduction(contactId,
sessionId, accept);
} else if (introducee == 2 && answerRequests) {
introductionManager2
.respondToIntroduction(contactId, sessionId,
time, accept);
introductionManager2.respondToIntroduction(contactId,
sessionId, accept);
}
} catch (DbException exception) {
eventWaiter.rethrow(exception);