diff --git a/briar-core/src/main/java/org/briarproject/briar/introduction/AbstractProtocolEngine.java b/briar-core/src/main/java/org/briarproject/briar/introduction/AbstractProtocolEngine.java index 95e500e94..69bc47752 100644 --- a/briar-core/src/main/java/org/briarproject/briar/introduction/AbstractProtocolEngine.java +++ b/briar-core/src/main/java/org/briarproject/briar/introduction/AbstractProtocolEngine.java @@ -20,6 +20,7 @@ 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; @@ -62,6 +63,7 @@ abstract class AbstractProtocolEngine> protected final ClientVersioningManager clientVersioningManager; protected final AutoDeleteManager autoDeleteManager; protected final ConversationManager conversationManager; + protected final Clock clock; AbstractProtocolEngine( DatabaseComponent db, @@ -75,7 +77,8 @@ abstract class AbstractProtocolEngine> MessageEncoder messageEncoder, ClientVersioningManager clientVersioningManager, AutoDeleteManager autoDeleteManager, - ConversationManager conversationManager) { + ConversationManager conversationManager, + Clock clock) { this.db = db; this.clientHelper = clientHelper; this.contactManager = contactManager; @@ -88,6 +91,7 @@ abstract class AbstractProtocolEngine> this.clientVersioningManager = clientVersioningManager; this.autoDeleteManager = autoDeleteManager; this.conversationManager = conversationManager; + this.clock = clock; } Message sendRequestMessage(Transaction txn, PeerSession s, diff --git a/briar-core/src/main/java/org/briarproject/briar/introduction/IntroduceeProtocolEngine.java b/briar-core/src/main/java/org/briarproject/briar/introduction/IntroduceeProtocolEngine.java index 8031115fe..5f1a2833f 100644 --- a/briar-core/src/main/java/org/briarproject/briar/introduction/IntroduceeProtocolEngine.java +++ b/briar-core/src/main/java/org/briarproject/briar/introduction/IntroduceeProtocolEngine.java @@ -22,6 +22,7 @@ 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; @@ -83,11 +84,12 @@ class IntroduceeProtocolEngine TransportPropertyManager transportPropertyManager, ClientVersioningManager clientVersioningManager, AutoDeleteManager autoDeleteManager, - ConversationManager conversationManager) { + ConversationManager conversationManager, + Clock clock) { super(db, clientHelper, contactManager, contactGroupFactory, messageTracker, identityManager, authorManager, messageParser, messageEncoder, clientVersioningManager, autoDeleteManager, - conversationManager); + conversationManager, clock); this.crypto = crypto; this.keyManager = keyManager; this.transportPropertyManager = transportPropertyManager; @@ -287,8 +289,8 @@ class IntroduceeProtocolEngine Map transportProperties = transportPropertyManager.getLocalProperties(txn); - // Send a ACCEPT message - long localTimestamp = getLocalTimestamp(txn, s); + // Send an ACCEPT message + long localTimestamp = getTimestampForVisibleMessage(txn, s); Message sent = sendAcceptMessage(txn, s, localTimestamp, publicKey, localTimestamp, transportProperties, true); // Track the message @@ -318,7 +320,7 @@ class IntroduceeProtocolEngine markRequestsUnavailableToAnswer(txn, s); // Send a DECLINE message - long localTimestamp = getLocalTimestamp(txn, s); + long localTimestamp = getTimestampForVisibleMessage(txn, s); Message sent = sendDeclineMessage(txn, s, localTimestamp, true); // Track the message @@ -416,7 +418,7 @@ class IntroduceeProtocolEngine return abort(txn, s); } if (s.getState() != AWAIT_AUTH) throw new AssertionError(); - long localTimestamp = getLocalTimestamp(txn, s); + long localTimestamp = getTimestampForInvisibleMessage(s); Message sent = sendAuthMessage(txn, s, localTimestamp, mac, signature); return IntroduceeSession.addLocalAuth(s, AWAIT_AUTH, sent, masterKey, aliceMacKey, bobMacKey); @@ -466,7 +468,7 @@ class IntroduceeProtocolEngine // send ACTIVATE message with a MAC byte[] mac = crypto.activateMac(s); - long localTimestamp = getLocalTimestamp(txn, s); + long localTimestamp = getTimestampForInvisibleMessage(s); Message sent = sendActivateMessage(txn, s, localTimestamp, mac); // Move to AWAIT_ACTIVATE state and clear key material from session @@ -518,7 +520,7 @@ class IntroduceeProtocolEngine markRequestsUnavailableToAnswer(txn, s); // Send an ABORT message - long localTimestamp = getLocalTimestamp(txn, s); + long localTimestamp = getTimestampForInvisibleMessage(s); Message sent = sendAbortMessage(txn, s, localTimestamp); // Broadcast abort event for testing @@ -535,17 +537,33 @@ class IntroduceeProtocolEngine } /** - * 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. + * Returns a timestamp for a visible outgoing message. The timestamp is + * later than the timestamp of any message sent or received so far in the + * conversation, and later than the {@link + * #getSessionTimestamp(IntroduceeSession) session timestamp}. */ - private long getLocalTimestamp(Transaction txn, IntroduceeSession s) - throws DbException { + private long getTimestampForVisibleMessage(Transaction txn, + IntroduceeSession s) throws DbException { long conversationTimestamp = getTimestampForOutgoingMessage(txn, s.getContactGroupId()); - long sessionTimestamp = - max(s.getLocalTimestamp(), s.getRequestTimestamp()) + 1; - return max(conversationTimestamp, sessionTimestamp); + return max(conversationTimestamp, getSessionTimestamp(s) + 1); + } + + /** + * Returns a timestamp for an invisible outgoing message. The timestamp is + * later than the {@link #getSessionTimestamp(IntroduceeSession) session + * timestamp}. + */ + private long getTimestampForInvisibleMessage(IntroduceeSession s) { + return max(clock.currentTimeMillis(), getSessionTimestamp(s) + 1); + } + + /** + * Returns the latest timestamp of any message sent so far in the session, + * and any request message received so far in the session. + */ + private long getSessionTimestamp(IntroduceeSession s) { + return max(s.getLocalTimestamp(), s.getRequestTimestamp()); } private void addSessionId(Transaction txn, MessageId m, SessionId sessionId) diff --git a/briar-core/src/main/java/org/briarproject/briar/introduction/IntroducerProtocolEngine.java b/briar-core/src/main/java/org/briarproject/briar/introduction/IntroducerProtocolEngine.java index e79d0ad97..e547b2700 100644 --- a/briar-core/src/main/java/org/briarproject/briar/introduction/IntroducerProtocolEngine.java +++ b/briar-core/src/main/java/org/briarproject/briar/introduction/IntroducerProtocolEngine.java @@ -12,6 +12,7 @@ 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; @@ -57,11 +58,12 @@ class IntroducerProtocolEngine MessageEncoder messageEncoder, ClientVersioningManager clientVersioningManager, AutoDeleteManager autoDeleteManager, - ConversationManager conversationManager) { + ConversationManager conversationManager, + Clock clock) { super(db, clientHelper, contactManager, contactGroupFactory, messageTracker, identityManager, authorManager, messageParser, messageEncoder, clientVersioningManager, autoDeleteManager, - conversationManager); + conversationManager, clock); } @Override @@ -219,8 +221,10 @@ class IntroducerProtocolEngine private IntroducerSession onLocalRequest(Transaction txn, IntroducerSession s, @Nullable String text) throws DbException { // Send REQUEST messages - long timestampA = getLocalTimestamp(txn, s, s.getIntroduceeA()); - long timestampB = getLocalTimestamp(txn, s, s.getIntroduceeB()); + long timestampA = + getTimestampForVisibleMessage(txn, s, s.getIntroduceeA()); + long timestampB = + getTimestampForVisibleMessage(txn, s, s.getIntroduceeB()); long localTimestamp = max(timestampA, timestampB); Message sentA = sendRequestMessage(txn, s.getIntroduceeA(), localTimestamp, s.getIntroduceeB().author, text); @@ -261,7 +265,8 @@ class IntroducerProtocolEngine // Forward ACCEPT message Introducee i = getOtherIntroducee(s, m.getGroupId()); - long localTimestamp = getLocalTimestamp(txn, s, i); + // The message will be visible to the introducee + long localTimestamp = getTimestampForVisibleMessage(txn, s, i); Message sent = sendAcceptMessage(txn, i, localTimestamp, m.getEphemeralPublicKey(), m.getAcceptTimestamp(), m.getTransportProperties(), false); @@ -321,7 +326,8 @@ class IntroducerProtocolEngine // Forward ACCEPT message Introducee i = getOtherIntroducee(s, m.getGroupId()); - long localTimestamp = getLocalTimestamp(txn, s, i); + // The message will be visible to the introducee + long localTimestamp = getTimestampForVisibleMessage(txn, s, i); Message sent = sendAcceptMessage(txn, i, localTimestamp, m.getEphemeralPublicKey(), m.getAcceptTimestamp(), m.getTransportProperties(), false); @@ -373,7 +379,8 @@ class IntroducerProtocolEngine // Forward DECLINE message Introducee i = getOtherIntroducee(s, m.getGroupId()); - long localTimestamp = getLocalTimestamp(txn, s, i); + // The message will be visible to the introducee + long localTimestamp = getTimestampForVisibleMessage(txn, s, i); Message sent = sendDeclineMessage(txn, i, localTimestamp, false); // Create the next state @@ -425,7 +432,8 @@ class IntroducerProtocolEngine // Forward DECLINE message Introducee i = getOtherIntroducee(s, m.getGroupId()); - long localTimestamp = getLocalTimestamp(txn, s, i); + // The message will be visible to the introducee + long localTimestamp = getTimestampForVisibleMessage(txn, s, i); Message sent = sendDeclineMessage(txn, i, localTimestamp, false); Introducee introduceeA, introduceeB; @@ -466,7 +474,7 @@ class IntroducerProtocolEngine // Forward AUTH message Introducee i = getOtherIntroducee(s, m.getGroupId()); - long localTimestamp = getLocalTimestamp(txn, s, i); + long localTimestamp = getTimestampForInvisibleMessage(s, i); Message sent = sendAuthMessage(txn, i, localTimestamp, m.getMac(), m.getSignature()); @@ -502,7 +510,7 @@ class IntroducerProtocolEngine // Forward ACTIVATE message Introducee i = getOtherIntroducee(s, m.getGroupId()); - long localTimestamp = getLocalTimestamp(txn, s, i); + long localTimestamp = getTimestampForInvisibleMessage(s, i); Message sent = sendActivateMessage(txn, i, localTimestamp, m.getMac()); // Move to the next state @@ -525,7 +533,7 @@ class IntroducerProtocolEngine IntroducerSession s, AbortMessage m) throws DbException { // Forward ABORT message Introducee i = getOtherIntroducee(s, m.getGroupId()); - long localTimestamp = getLocalTimestamp(txn, s, i); + long localTimestamp = getTimestampForInvisibleMessage(s, i); Message sent = sendAbortMessage(txn, i, localTimestamp); // Broadcast abort event for testing @@ -550,7 +558,8 @@ class IntroducerProtocolEngine txn.attach(new IntroductionAbortedEvent(s.getSessionId())); // Send an ABORT message to the remaining introducee - long localTimestamp = getLocalTimestamp(txn, s, remainingIntroducee); + long localTimestamp = + getTimestampForInvisibleMessage(s, remainingIntroducee); Message sent = sendAbortMessage(txn, remainingIntroducee, localTimestamp); // Reset the session back to initial state @@ -577,9 +586,11 @@ class IntroducerProtocolEngine txn.attach(new IntroductionAbortedEvent(s.getSessionId())); // Send an ABORT message to both introducees - long timestampA = getLocalTimestamp(txn, s, s.getIntroduceeA()); + long timestampA = + getTimestampForInvisibleMessage(s, s.getIntroduceeA()); Message sentA = sendAbortMessage(txn, s.getIntroduceeA(), timestampA); - long timestampB = getLocalTimestamp(txn, s, s.getIntroduceeB()); + long timestampB = + getTimestampForInvisibleMessage(s, s.getIntroduceeB()); Message sentB = sendAbortMessage(txn, s.getIntroduceeB(), timestampB); // Reset the session back to initial state Introducee introduceeA = new Introducee(s.getIntroduceeA(), sentA); @@ -610,16 +621,32 @@ class IntroducerProtocolEngine } /** - * 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. + * Returns a timestamp for a visible outgoing message. The timestamp is + * later than the timestamp of any message sent or received so far in the + * conversation, and later than the {@link + * #getSessionTimestamp(IntroducerSession, PeerSession) session timestamp}. */ - private long getLocalTimestamp(Transaction txn, IntroducerSession s, - PeerSession p) throws DbException { + private long getTimestampForVisibleMessage(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); + return max(conversationTimestamp, getSessionTimestamp(s, p) + 1); + } + + /** + * Returns a timestamp for an invisible outgoing message. The timestamp is + * later than the {@link #getSessionTimestamp(IntroducerSession, PeerSession) + * session timestamp}. + */ + private long getTimestampForInvisibleMessage(IntroducerSession s, + PeerSession p) { + return max(clock.currentTimeMillis(), getSessionTimestamp(s, p) + 1); + } + + /** + * Returns the latest timestamp of any message sent so far in the session. + */ + private long getSessionTimestamp(IntroducerSession s, PeerSession p) { + return max(p.getLocalTimestamp(), s.getRequestTimestamp()); } } diff --git a/briar-core/src/main/java/org/briarproject/briar/privategroup/invitation/AbstractProtocolEngine.java b/briar-core/src/main/java/org/briarproject/briar/privategroup/invitation/AbstractProtocolEngine.java index abccc404d..a8d355005 100644 --- a/briar-core/src/main/java/org/briarproject/briar/privategroup/invitation/AbstractProtocolEngine.java +++ b/briar-core/src/main/java/org/briarproject/briar/privategroup/invitation/AbstractProtocolEngine.java @@ -146,7 +146,9 @@ abstract class AbstractProtocolEngine> Message sendJoinMessage(Transaction txn, S s, boolean visibleInUi) throws DbException { Message m; - long localTimestamp = getLocalTimestamp(txn, s); + long localTimestamp = visibleInUi + ? getTimestampForVisibleMessage(txn, s) + : getTimestampForInvisibleMessage(s); ContactId c = getContactId(txn, s.getContactGroupId()); if (contactSupportsAutoDeletion(txn, c)) { // Set auto-delete timer if manually accepting an invitation @@ -171,7 +173,9 @@ abstract class AbstractProtocolEngine> Message sendLeaveMessage(Transaction txn, S s, boolean visibleInUi) throws DbException { Message m; - long localTimestamp = getLocalTimestamp(txn, s); + long localTimestamp = visibleInUi + ? getTimestampForVisibleMessage(txn, s) + : getTimestampForInvisibleMessage(s); ContactId c = getContactId(txn, s.getContactGroupId()); if (contactSupportsAutoDeletion(txn, c)) { // Set auto-delete timer if manually accepting an invitation @@ -196,7 +200,7 @@ abstract class AbstractProtocolEngine> Message sendAbortMessage(Transaction txn, S session) throws DbException { Message m = messageEncoder.encodeAbortMessage( session.getContactGroupId(), session.getPrivateGroupId(), - getLocalTimestamp(txn, session)); + getTimestampForInvisibleMessage(session)); sendMessage(txn, m, ABORT, session.getPrivateGroupId(), false, NO_AUTO_DELETE_TIMER); return m; @@ -263,17 +267,33 @@ abstract class AbstractProtocolEngine> } /** - * 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. + * Returns a timestamp for a visible outgoing message. The timestamp is + * later than the timestamp of any message sent or received so far in the + * conversation, and later than the {@link #getSessionTimestamp(Session) + * session timestamp}. */ - long getLocalTimestamp(Transaction txn, S s) throws DbException { + long getTimestampForVisibleMessage(Transaction txn, S s) + throws DbException { ContactId c = getContactId(txn, s.getContactGroupId()); long conversationTimestamp = conversationManager.getTimestampForOutgoingMessage(txn, c); - long sessionTimestamp = - max(s.getLocalTimestamp(), s.getInviteTimestamp()) + 1; - return max(conversationTimestamp, sessionTimestamp); + return max(conversationTimestamp, getSessionTimestamp(s) + 1); + } + + /** + * Returns a timestamp for an invisible outgoing message. The timestamp is + * later than the {@link #getSessionTimestamp(Session) session timestamp}. + */ + long getTimestampForInvisibleMessage(S s) { + return max(clock.currentTimeMillis(), getSessionTimestamp(s) + 1); + } + + /** + * Returns the latest timestamp of any message sent so far in the session, + * and any invite message sent or received so far in the session. + */ + private long getSessionTimestamp(S s) { + return max(s.getLocalTimestamp(), s.getInviteTimestamp()); } private void sendMessage(Transaction txn, Message m, MessageType type, diff --git a/briar-core/src/main/java/org/briarproject/briar/privategroup/invitation/CreatorProtocolEngine.java b/briar-core/src/main/java/org/briarproject/briar/privategroup/invitation/CreatorProtocolEngine.java index 2cf40d270..ac483b1d6 100644 --- a/briar-core/src/main/java/org/briarproject/briar/privategroup/invitation/CreatorProtocolEngine.java +++ b/briar-core/src/main/java/org/briarproject/briar/privategroup/invitation/CreatorProtocolEngine.java @@ -164,7 +164,8 @@ class CreatorProtocolEngine extends AbstractProtocolEngine { // Track the message messageTracker.trackOutgoingMessage(txn, sent); // Move to the INVITED state - long localTimestamp = max(timestamp, getLocalTimestamp(txn, s)); + long localTimestamp = + max(timestamp, getTimestampForVisibleMessage(txn, s)); return new CreatorSession(s.getContactGroupId(), s.getPrivateGroupId(), sent.getId(), s.getLastRemoteMessageId(), localTimestamp, timestamp, INVITED); diff --git a/briar-core/src/main/java/org/briarproject/briar/sharing/BlogProtocolEngineImpl.java b/briar-core/src/main/java/org/briarproject/briar/sharing/BlogProtocolEngineImpl.java index 7ffa0679e..6ebaa137f 100644 --- a/briar-core/src/main/java/org/briarproject/briar/sharing/BlogProtocolEngineImpl.java +++ b/briar-core/src/main/java/org/briarproject/briar/sharing/BlogProtocolEngineImpl.java @@ -9,6 +9,7 @@ import org.briarproject.bramble.api.db.Transaction; import org.briarproject.bramble.api.event.Event; import org.briarproject.bramble.api.nullsafety.NotNullByDefault; 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.blog.Blog; @@ -42,11 +43,12 @@ class BlogProtocolEngineImpl extends ProtocolEngineImpl { MessageTracker messageTracker, AutoDeleteManager autoDeleteManager, ConversationManager conversationManager, + Clock clock, BlogManager blogManager, InvitationFactory invitationFactory) { super(db, clientHelper, clientVersioningManager, messageEncoder, messageParser, messageTracker, autoDeleteManager, - conversationManager, BlogSharingManager.CLIENT_ID, + conversationManager, clock, BlogSharingManager.CLIENT_ID, BlogSharingManager.MAJOR_VERSION, BlogManager.CLIENT_ID, BlogManager.MAJOR_VERSION); this.blogManager = blogManager; diff --git a/briar-core/src/main/java/org/briarproject/briar/sharing/ForumProtocolEngineImpl.java b/briar-core/src/main/java/org/briarproject/briar/sharing/ForumProtocolEngineImpl.java index 55a1b76f3..8a1dabcc0 100644 --- a/briar-core/src/main/java/org/briarproject/briar/sharing/ForumProtocolEngineImpl.java +++ b/briar-core/src/main/java/org/briarproject/briar/sharing/ForumProtocolEngineImpl.java @@ -9,6 +9,7 @@ import org.briarproject.bramble.api.db.Transaction; import org.briarproject.bramble.api.event.Event; import org.briarproject.bramble.api.nullsafety.NotNullByDefault; 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; @@ -42,11 +43,12 @@ class ForumProtocolEngineImpl extends ProtocolEngineImpl { MessageTracker messageTracker, AutoDeleteManager autoDeleteManager, ConversationManager conversationManager, + Clock clock, ForumManager forumManager, InvitationFactory invitationFactory) { super(db, clientHelper, clientVersioningManager, messageEncoder, messageParser, messageTracker, autoDeleteManager, - conversationManager, ForumSharingManager.CLIENT_ID, + conversationManager, clock, ForumSharingManager.CLIENT_ID, ForumSharingManager.MAJOR_VERSION, ForumManager.CLIENT_ID, ForumManager.MAJOR_VERSION); this.forumManager = forumManager; diff --git a/briar-core/src/main/java/org/briarproject/briar/sharing/ProtocolEngineImpl.java b/briar-core/src/main/java/org/briarproject/briar/sharing/ProtocolEngineImpl.java index 21503179e..37726ac70 100644 --- a/briar-core/src/main/java/org/briarproject/briar/sharing/ProtocolEngineImpl.java +++ b/briar-core/src/main/java/org/briarproject/briar/sharing/ProtocolEngineImpl.java @@ -16,6 +16,7 @@ import org.briarproject.bramble.api.sync.Group.Visibility; 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; @@ -60,6 +61,7 @@ abstract class ProtocolEngineImpl private final MessageTracker messageTracker; private final AutoDeleteManager autoDeleteManager; private final ConversationManager conversationManager; + private final Clock clock; private final ClientId sharingClientId, shareableClientId; private final int sharingClientMajorVersion, shareableClientMajorVersion; @@ -72,6 +74,7 @@ abstract class ProtocolEngineImpl MessageTracker messageTracker, AutoDeleteManager autoDeleteManager, ConversationManager conversationManager, + Clock clock, ClientId sharingClientId, int sharingClientMajorVersion, ClientId shareableClientId, @@ -84,6 +87,7 @@ abstract class ProtocolEngineImpl this.messageTracker = messageTracker; this.autoDeleteManager = autoDeleteManager; this.conversationManager = conversationManager; + this.clock = clock; this.sharingClientId = sharingClientId; this.sharingClientMajorVersion = sharingClientMajorVersion; this.shareableClientId = shareableClientId; @@ -134,8 +138,8 @@ abstract class ProtocolEngineImpl } catch (FormatException e) { throw new DbException(e); // Invalid group descriptor } - long localTimestamp = getLocalTimestamp(txn, s); Message m; + long localTimestamp = getTimestampForVisibleMessage(txn, s); ContactId c = getContactId(txn, s.getContactGroupId()); if (contactSupportsAutoDeletion(txn, c)) { long timer = autoDeleteManager.getAutoDeleteTimer(txn, c); @@ -202,7 +206,7 @@ abstract class ProtocolEngineImpl private Message sendAcceptMessage(Transaction txn, Session s) throws DbException { Message m; - long localTimestamp = getLocalTimestamp(txn, s); + long localTimestamp = getTimestampForVisibleMessage(txn, s); ContactId c = getContactId(txn, s.getContactGroupId()); if (contactSupportsAutoDeletion(txn, c)) { long timer = autoDeleteManager.getAutoDeleteTimer(txn, c); @@ -256,7 +260,7 @@ abstract class ProtocolEngineImpl private Message sendDeclineMessage(Transaction txn, Session s) throws DbException { Message m; - long localTimestamp = getLocalTimestamp(txn, s); + long localTimestamp = getTimestampForVisibleMessage(txn, s); ContactId c = getContactId(txn, s.getContactGroupId()); if (contactSupportsAutoDeletion(txn, c)) { long timer = autoDeleteManager.getAutoDeleteTimer(txn, c); @@ -310,7 +314,7 @@ abstract class ProtocolEngineImpl private Message sendLeaveMessage(Transaction txn, Session session) throws DbException { - long localTimestamp = getLocalTimestamp(txn, session); + long localTimestamp = getTimestampForInvisibleMessage(session); Message m = messageEncoder.encodeLeaveMessage( session.getContactGroupId(), session.getShareableId(), localTimestamp, session.getLastLocalMessageId()); @@ -609,7 +613,7 @@ abstract class ProtocolEngineImpl private Message sendAbortMessage(Transaction txn, Session session) throws DbException { - long localTimestamp = getLocalTimestamp(txn, session); + long localTimestamp = getTimestampForInvisibleMessage(session); Message m = messageEncoder.encodeAbortMessage( session.getContactGroupId(), session.getShareableId(), localTimestamp, session.getLastLocalMessageId()); @@ -683,18 +687,33 @@ abstract class ProtocolEngineImpl } /** - * 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. + * Returns a timestamp for a visible outgoing message. The timestamp is + * later than the timestamp of any message sent or received so far in the + * conversation, and later than the {@link #getSessionTimestamp(Session) + * session timestamp}. */ - private long getLocalTimestamp(Transaction txn, Session session) + private long getTimestampForVisibleMessage(Transaction txn, Session s) throws DbException { - ContactId c = getContactId(txn, session.getContactGroupId()); + ContactId c = getContactId(txn, s.getContactGroupId()); long conversationTimestamp = conversationManager.getTimestampForOutgoingMessage(txn, c); - long sessionTimestamp = max(session.getLocalTimestamp(), - session.getInviteTimestamp()) + 1; - return max(conversationTimestamp, sessionTimestamp); + return max(conversationTimestamp, getSessionTimestamp(s) + 1); + } + + /** + * Returns a timestamp for an invisible outgoing message. The timestamp is + * later than the {@link #getSessionTimestamp(Session) session timestamp}. + */ + private long getTimestampForInvisibleMessage(Session s) { + return max(clock.currentTimeMillis(), getSessionTimestamp(s) + 1); + } + + /** + * Returns the latest timestamp of any message sent so far in the session, + * and any invite message sent or received so far in the session. + */ + private long getSessionTimestamp(Session s) { + return max(s.getLocalTimestamp(), s.getInviteTimestamp()); } private ContactId getContactId(Transaction txn, GroupId contactGroupId) diff --git a/briar-core/src/test/java/org/briarproject/briar/privategroup/invitation/AbstractProtocolEngineTest.java b/briar-core/src/test/java/org/briarproject/briar/privategroup/invitation/AbstractProtocolEngineTest.java index d7c4fd5e2..8eb16aa3c 100644 --- a/briar-core/src/test/java/org/briarproject/briar/privategroup/invitation/AbstractProtocolEngineTest.java +++ b/briar-core/src/test/java/org/briarproject/briar/privategroup/invitation/AbstractProtocolEngineTest.java @@ -118,7 +118,14 @@ abstract class AbstractProtocolEngineTest extends BrambleMockTestCase { assertEquals(inviteTimestamp, s.getInviteTimestamp()); } - void expectGetLocalTimestamp(long time) throws Exception { + void expectGetTimestampForInvisibleMessage(long time) { + context.checking(new Expectations() {{ + oneOf(clock).currentTimeMillis(); + will(returnValue(time)); + }}); + } + + void expectGetTimestampForVisibleMessage(long time) throws Exception { context.checking(new Expectations() {{ oneOf(clientHelper).getContactId(txn, contactGroupId); will(returnValue(contactId)); @@ -129,7 +136,7 @@ abstract class AbstractProtocolEngineTest extends BrambleMockTestCase { } void expectSendInviteMessage(String text) throws Exception { - expectGetLocalTimestamp(messageTimestamp); + expectGetTimestampForVisibleMessage(messageTimestamp); expectCheckWhetherContactSupportsAutoDeletion(); context.checking(new Expectations() {{ oneOf(messageEncoder).encodeInviteMessage(contactGroupId, @@ -143,7 +150,8 @@ abstract class AbstractProtocolEngineTest extends BrambleMockTestCase { void expectSendJoinMessage(JoinMessage m, boolean visible) throws Exception { - expectGetLocalTimestamp(messageTimestamp); + if (visible) expectGetTimestampForVisibleMessage(messageTimestamp); + else expectGetTimestampForInvisibleMessage(messageTimestamp); expectCheckWhetherContactSupportsAutoDeletion(); if (visible) expectGetAutoDeleteTimer(); context.checking(new Expectations() {{ @@ -156,7 +164,8 @@ abstract class AbstractProtocolEngineTest extends BrambleMockTestCase { } void expectSendLeaveMessage(boolean visible) throws Exception { - expectGetLocalTimestamp(messageTimestamp); + if (visible) expectGetTimestampForVisibleMessage(messageTimestamp); + else expectGetTimestampForInvisibleMessage(messageTimestamp); expectCheckWhetherContactSupportsAutoDeletion(); if (visible) expectGetAutoDeleteTimer(); context.checking(new Expectations() {{ @@ -169,7 +178,7 @@ abstract class AbstractProtocolEngineTest extends BrambleMockTestCase { } void expectSendAbortMessage() throws Exception { - expectGetLocalTimestamp(messageTimestamp); + expectGetTimestampForInvisibleMessage(messageTimestamp); context.checking(new Expectations() {{ oneOf(messageEncoder) .encodeAbortMessage(contactGroupId, privateGroupId,