From 90d984ee52721f758969894b8e70c53b325f0788 Mon Sep 17 00:00:00 2001 From: Torsten Grote <t@grobox.de> Date: Mon, 4 Apr 2016 12:50:43 -0300 Subject: [PATCH] Ensure responses shown after requests, clarify wording, reuse transactions When devices' clocks are out of sync, it is possible that a response is shown before the request. This commit makes sure that the timestamp of responses is always later than the last message in the conversation. Some wording could be misunderstood to thing introductions were successful even though they were not. That has been clarified. A new database transaction was created when getting contacts and local transport properties. This has been changed to re-use the existing transaction. Also addresses minor issues found in review. --- .../res/layout/list_item_contact.xml | 2 +- .../res/layout/list_item_introduction_in.xml | 4 +- .../res/layout/list_item_introduction_out.xml | 1 - .../res/layout/list_item_notice_in.xml | 1 - .../res/menu/conversation_actions.xml | 2 +- briar-android/res/values/strings.xml | 9 ++-- briar-android/res/values/styles.xml | 2 +- .../android/contact/ConversationActivity.java | 6 ++- .../IntroductionMessageFragment.java | 3 +- .../api/introduction/IntroductionManager.java | 9 ++-- .../properties/TransportPropertyManager.java | 8 ++++ .../introduction/IntroduceeEngine.java | 1 + .../introduction/IntroduceeManager.java | 15 ++++--- .../introduction/IntroducerEngine.java | 3 ++ .../introduction/IntroducerManager.java | 5 ++- .../introduction/IntroductionManagerImpl.java | 23 +++++++---- .../TransportPropertyManagerImpl.java | 41 ++++++++++--------- 17 files changed, 83 insertions(+), 52 deletions(-) diff --git a/briar-android/res/layout/list_item_contact.xml b/briar-android/res/layout/list_item_contact.xml index 78f0681bdd..8f879c90a0 100644 --- a/briar-android/res/layout/list_item_contact.xml +++ b/briar-android/res/layout/list_item_contact.xml @@ -80,6 +80,6 @@ </RelativeLayout> - <View style="@style/Divider.ContactListDevider"/> + <View style="@style/Divider.ContactList"/> </LinearLayout> \ No newline at end of file diff --git a/briar-android/res/layout/list_item_introduction_in.xml b/briar-android/res/layout/list_item_introduction_in.xml index 08da32d778..5daea06085 100644 --- a/briar-android/res/layout/list_item_introduction_in.xml +++ b/briar-android/res/layout/list_item_introduction_in.xml @@ -34,8 +34,8 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="@dimen/message_bubble_timestamp_margin" - android:layout_alignEnd="@+id/acceptButton" - android:layout_alignRight="@+id/acceptButton" + android:layout_alignEnd="@+id/introductionText" + android:layout_alignRight="@+id/introductionText" android:layout_below="@+id/acceptButton" android:textColor="@color/private_message_date" android:textSize="@dimen/text_size_tiny" diff --git a/briar-android/res/layout/list_item_introduction_out.xml b/briar-android/res/layout/list_item_introduction_out.xml index d3e1a85aa6..88070ea669 100644 --- a/briar-android/res/layout/list_item_introduction_out.xml +++ b/briar-android/res/layout/list_item_introduction_out.xml @@ -23,7 +23,6 @@ android:id="@+id/introductionText" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:minWidth="175dp" android:textIsSelectable="true" android:textSize="@dimen/text_size_medium" android:textStyle="italic" diff --git a/briar-android/res/layout/list_item_notice_in.xml b/briar-android/res/layout/list_item_notice_in.xml index e7c912f915..8f0daa0267 100644 --- a/briar-android/res/layout/list_item_notice_in.xml +++ b/briar-android/res/layout/list_item_notice_in.xml @@ -14,7 +14,6 @@ android:id="@+id/noticeText" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:minWidth="80dp" android:textIsSelectable="true" android:textSize="@dimen/text_size_medium" android:textStyle="italic" diff --git a/briar-android/res/menu/conversation_actions.xml b/briar-android/res/menu/conversation_actions.xml index 01090aaec6..0128ee56f4 100644 --- a/briar-android/res/menu/conversation_actions.xml +++ b/briar-android/res/menu/conversation_actions.xml @@ -6,7 +6,7 @@ <item android:id="@+id/action_introduction" android:icon="@drawable/introduction_white" - android:title="@string/make_introduction" + android:title="@string/introduction_button" app:showAsAction="never"/> <item diff --git a/briar-android/res/values/strings.xml b/briar-android/res/values/strings.xml index c37fa7ea6b..b47ee68b8d 100644 --- a/briar-android/res/values/strings.xml +++ b/briar-android/res/values/strings.xml @@ -141,8 +141,9 @@ <string name="transport_lan">Wi-Fi</string> <string name="no_data">No data</string> <string name="unknown_app">an unknown app</string> - <string name="make_introduction">Make Introduction</string> - <string name="introduction_activity_title">Select contact</string> + + <!-- Introduction Client --> + <string name="introduction_activity_title">Select Contact</string> <string name="introduction_message_title">Introduce Contacts</string> <string name="introduction_message_text">You can compose a message that will be sent to %1$s and %2$s along with your introduction:</string> <string name="introduction_message_hint">Type message (optional)</string> @@ -151,10 +152,10 @@ <string name="introduction_response_error">Error when responding to introduction</string> <string name="introduction_warn_different_identities_title">Warning: Different Identities</string> <string name="introduction_warn_different_identities_text">You are trying to introduce two contacts that you have added with different identities. This might reveal that both identities are yours.</string> - <string name="introduction_request_sent">You have introduced %1$s to %2$s.</string> + <string name="introduction_request_sent">You have asked to introduce %1$s to %2$s.</string> <string name="introduction_request_received">%1$s introduced you to %2$s. Do you want to add %2$s to your contact list?</string> <string name="introduction_request_exists_received">%1$s introduced you to %2$s, but %2$s is already in your contact list. Since %1$s might not know that, you can still respond:</string> - <string name="introduction_request_answered_received">%1$s introduced you to %2$s.</string> + <string name="introduction_request_answered_received">%1$s has asked to introduce you to %2$s.</string> <string name="introduction_response_accepted_sent">You accepted the introduction to %1$s.</string> <string name="introduction_response_declined_sent">You declined the introduction to %1$s.</string> <string name="introduction_response_accepted_received">%1$s accepted to be introduced to %2$s.</string> diff --git a/briar-android/res/values/styles.xml b/briar-android/res/values/styles.xml index 9b4ebbf6ca..12c1c35971 100644 --- a/briar-android/res/values/styles.xml +++ b/briar-android/res/values/styles.xml @@ -99,7 +99,7 @@ <item name="android:layout_height">1px</item> </style> - <style name="Divider.ContactListDevider" parent="Divider"> + <style name="Divider.ContactList" parent="Divider"> <item name="android:layout_width">match_parent</item> <item name="android:layout_height">2dp</item> <item name="android:layout_marginLeft">@dimen/margin_large</item> diff --git a/briar-android/src/org/briarproject/android/contact/ConversationActivity.java b/briar-android/src/org/briarproject/android/contact/ConversationActivity.java index d10a3d31cc..2d006e681b 100644 --- a/briar-android/src/org/briarproject/android/contact/ConversationActivity.java +++ b/briar-android/src/org/briarproject/android/contact/ConversationActivity.java @@ -661,11 +661,13 @@ public class ConversationActivity extends BriarActivity runOnDbThread(new Runnable() { @Override public void run() { + long timestamp = System.currentTimeMillis(); + timestamp = Math.max(timestamp, getMinTimestampForNewMessage()); try { if (accept) { - introductionManager.acceptIntroduction(contactId, sessionId); + introductionManager.acceptIntroduction(contactId, sessionId, timestamp); } else { - introductionManager.declineIntroduction(contactId, sessionId); + introductionManager.declineIntroduction(contactId, sessionId, timestamp); } loadMessages(); } catch (DbException e) { diff --git a/briar-android/src/org/briarproject/android/introduction/IntroductionMessageFragment.java b/briar-android/src/org/briarproject/android/introduction/IntroductionMessageFragment.java index 7b06c9bd7c..7646e0cf4e 100644 --- a/briar-android/src/org/briarproject/android/introduction/IntroductionMessageFragment.java +++ b/briar-android/src/org/briarproject/android/introduction/IntroductionMessageFragment.java @@ -180,7 +180,8 @@ public class IntroductionMessageFragment extends BaseFragment { // actually make the introduction try { - introductionManager.makeIntroduction(c1, c2, msg); + long timestamp = System.currentTimeMillis(); + introductionManager.makeIntroduction(c1, c2, msg, timestamp); introductionWasMade = true; postIntroduction(false); } catch (DbException e) { diff --git a/briar-api/src/org/briarproject/api/introduction/IntroductionManager.java b/briar-api/src/org/briarproject/api/introduction/IntroductionManager.java index f83581ed7b..18c5da1c69 100644 --- a/briar-api/src/org/briarproject/api/introduction/IntroductionManager.java +++ b/briar-api/src/org/briarproject/api/introduction/IntroductionManager.java @@ -21,20 +21,23 @@ public interface IntroductionManager { /** * sends two initial introduction messages */ - void makeIntroduction(Contact c1, Contact c2, String msg) + void makeIntroduction(Contact c1, Contact c2, String msg, + final long timestamp) throws DbException, FormatException; /** * Accept an introduction that had been made */ void acceptIntroduction(final ContactId contactId, - final SessionId sessionId) throws DbException, FormatException; + final SessionId sessionId, final long timestamp) + throws DbException, FormatException; /** * Decline an introduction that had been made */ void declineIntroduction(final ContactId contactId, - final SessionId sessionId) throws DbException, FormatException; + final SessionId sessionId, final long timestamp) + throws DbException, FormatException; /** * Get all introduction messages for the contact with this contactId diff --git a/briar-api/src/org/briarproject/api/properties/TransportPropertyManager.java b/briar-api/src/org/briarproject/api/properties/TransportPropertyManager.java index 8b63ae612b..38fedd9fb7 100644 --- a/briar-api/src/org/briarproject/api/properties/TransportPropertyManager.java +++ b/briar-api/src/org/briarproject/api/properties/TransportPropertyManager.java @@ -20,6 +20,14 @@ public interface TransportPropertyManager { Map<TransportId, TransportProperties> getLocalProperties() throws DbException; + /** + * Returns the local transport properties for all transports. + * <br/> + * Read-Only + * */ + Map<TransportId, TransportProperties> getLocalProperties(Transaction txn) + throws DbException; + /** Returns the local transport properties for the given transport. */ TransportProperties getLocalProperties(TransportId t) throws DbException; diff --git a/briar-core/src/org/briarproject/introduction/IntroduceeEngine.java b/briar-core/src/org/briarproject/introduction/IntroduceeEngine.java index 6f76540d08..bc1e3d5f95 100644 --- a/briar-core/src/org/briarproject/introduction/IntroduceeEngine.java +++ b/briar-core/src/org/briarproject/introduction/IntroduceeEngine.java @@ -109,6 +109,7 @@ public class IntroduceeEngine msg.put(E_PUBLIC_KEY, localState.getRaw(OUR_PUBLIC_KEY)); msg.put(TRANSPORT, localAction.getDictionary(TRANSPORT)); } + msg.put(MESSAGE_TIME, localAction.getLong(MESSAGE_TIME)); messages.add(msg); logAction(currentState, localState, msg); diff --git a/briar-core/src/org/briarproject/introduction/IntroduceeManager.java b/briar-core/src/org/briarproject/introduction/IntroduceeManager.java index 174d2af21a..64af33b44d 100644 --- a/briar-core/src/org/briarproject/introduction/IntroduceeManager.java +++ b/briar-core/src/org/briarproject/introduction/IntroduceeManager.java @@ -52,6 +52,7 @@ import static org.briarproject.api.introduction.IntroductionConstants.E_PUBLIC_K import static org.briarproject.api.introduction.IntroductionConstants.GROUP_ID; import static org.briarproject.api.introduction.IntroductionConstants.INTRODUCER; import static org.briarproject.api.introduction.IntroductionConstants.LOCAL_AUTHOR_ID; +import static org.briarproject.api.introduction.IntroductionConstants.MESSAGE_TIME; import static org.briarproject.api.introduction.IntroductionConstants.NAME; import static org.briarproject.api.introduction.IntroductionConstants.NOT_OUR_RESPONSE; import static org.briarproject.api.introduction.IntroductionConstants.OUR_PRIVATE_KEY; @@ -159,9 +160,10 @@ class IntroduceeManager { } public void acceptIntroduction(Transaction txn, final ContactId contactId, - final SessionId sessionId) throws DbException, FormatException { + final SessionId sessionId, final long timestamp) + throws DbException, FormatException { - Contact c = contactManager.getContact(contactId); + Contact c = db.getContact(txn, contactId); Group g = introductionManager.getIntroductionGroup(c); BdfDictionary state = introductionManager @@ -173,7 +175,7 @@ class IntroduceeManager { byte[] publicKey = keyPair.getPublic().getEncoded(); byte[] privateKey = keyPair.getPrivate().getEncoded(); Map<TransportId, TransportProperties> transportProperties = - transportPropertyManager.getLocalProperties(); + transportPropertyManager.getLocalProperties(txn); // update session state for later state.put(ACCEPT, true); @@ -186,6 +188,7 @@ class IntroduceeManager { localAction.put(TYPE, TYPE_RESPONSE); localAction.put(TRANSPORT, encodeTransportProperties(transportProperties)); + localAction.put(MESSAGE_TIME, timestamp); // start engine and process its state update IntroduceeEngine engine = new IntroduceeEngine(); @@ -193,9 +196,10 @@ class IntroduceeManager { } public void declineIntroduction(Transaction txn, final ContactId contactId, - final SessionId sessionId) throws DbException, FormatException { + final SessionId sessionId, final long timestamp) + throws DbException, FormatException { - Contact c = contactManager.getContact(contactId); + Contact c = db.getContact(txn, contactId); Group g = introductionManager.getIntroductionGroup(c); BdfDictionary state = introductionManager @@ -207,6 +211,7 @@ class IntroduceeManager { // define action BdfDictionary localAction = new BdfDictionary(); localAction.put(TYPE, TYPE_RESPONSE); + localAction.put(MESSAGE_TIME, timestamp); // start engine and process its state update IntroduceeEngine engine = new IntroduceeEngine(); diff --git a/briar-core/src/org/briarproject/introduction/IntroducerEngine.java b/briar-core/src/org/briarproject/introduction/IntroducerEngine.java index fb88c28df6..796d528f1d 100644 --- a/briar-core/src/org/briarproject/introduction/IntroducerEngine.java +++ b/briar-core/src/org/briarproject/introduction/IntroducerEngine.java @@ -56,6 +56,7 @@ import static org.briarproject.api.introduction.IntroductionConstants.RESPONSE_1 import static org.briarproject.api.introduction.IntroductionConstants.RESPONSE_2; import static org.briarproject.api.introduction.IntroductionConstants.SESSION_ID; import static org.briarproject.api.introduction.IntroductionConstants.STATE; +import static org.briarproject.api.introduction.IntroductionConstants.TIME; import static org.briarproject.api.introduction.IntroductionConstants.TYPE; import static org.briarproject.api.introduction.IntroductionConstants.TYPE_ABORT; import static org.briarproject.api.introduction.IntroductionConstants.TYPE_ACK; @@ -104,6 +105,7 @@ public class IntroducerEngine if (localAction.containsKey(MSG)) { msg1.put(MSG, localAction.getString(MSG)); } + msg1.put(MESSAGE_TIME, localAction.getLong(MESSAGE_TIME)); messages.add(msg1); logLocalAction(currentState, localState, msg1); BdfDictionary msg2 = new BdfDictionary(); @@ -115,6 +117,7 @@ public class IntroducerEngine if (localAction.containsKey(MSG)) { msg2.put(MSG, localAction.getString(MSG)); } + msg2.put(MESSAGE_TIME, localAction.getLong(MESSAGE_TIME)); messages.add(msg2); logLocalAction(currentState, localState, msg2); diff --git a/briar-core/src/org/briarproject/introduction/IntroducerManager.java b/briar-core/src/org/briarproject/introduction/IntroducerManager.java index 3cc906aedf..93481367df 100644 --- a/briar-core/src/org/briarproject/introduction/IntroducerManager.java +++ b/briar-core/src/org/briarproject/introduction/IntroducerManager.java @@ -30,6 +30,7 @@ import static org.briarproject.api.introduction.IntroductionConstants.CONTACT_ID import static org.briarproject.api.introduction.IntroductionConstants.CONTACT_ID_2; import static org.briarproject.api.introduction.IntroductionConstants.GROUP_ID_1; import static org.briarproject.api.introduction.IntroductionConstants.GROUP_ID_2; +import static org.briarproject.api.introduction.IntroductionConstants.MESSAGE_TIME; import static org.briarproject.api.introduction.IntroductionConstants.MSG; import static org.briarproject.api.introduction.IntroductionConstants.PUBLIC_KEY1; import static org.briarproject.api.introduction.IntroductionConstants.PUBLIC_KEY2; @@ -38,6 +39,7 @@ import static org.briarproject.api.introduction.IntroductionConstants.ROLE_INTRO import static org.briarproject.api.introduction.IntroductionConstants.SESSION_ID; import static org.briarproject.api.introduction.IntroductionConstants.STATE; import static org.briarproject.api.introduction.IntroductionConstants.STORAGE_ID; +import static org.briarproject.api.introduction.IntroductionConstants.TIME; import static org.briarproject.api.introduction.IntroductionConstants.TYPE; import static org.briarproject.api.introduction.IntroductionConstants.TYPE_ABORT; import static org.briarproject.api.introduction.IntroductionConstants.TYPE_REQUEST; @@ -99,7 +101,7 @@ class IntroducerManager { } public void makeIntroduction(Transaction txn, Contact c1, Contact c2, - String msg) throws DbException, FormatException { + String msg, long timestamp) throws DbException, FormatException { // TODO check for existing session with those contacts? // deny new introduction under which conditions? @@ -115,6 +117,7 @@ class IntroducerManager { } localAction.put(PUBLIC_KEY1, c1.getAuthor().getPublicKey()); localAction.put(PUBLIC_KEY2, c2.getAuthor().getPublicKey()); + localAction.put(MESSAGE_TIME, timestamp); // start engine and process its state update IntroducerEngine engine = new IntroducerEngine(); diff --git a/briar-core/src/org/briarproject/introduction/IntroductionManagerImpl.java b/briar-core/src/org/briarproject/introduction/IntroductionManagerImpl.java index 30cfe7083f..dbdc0091c4 100644 --- a/briar-core/src/org/briarproject/introduction/IntroductionManagerImpl.java +++ b/briar-core/src/org/briarproject/introduction/IntroductionManagerImpl.java @@ -44,7 +44,6 @@ import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; -import java.util.HashMap; import java.util.Map; import java.util.logging.Logger; @@ -268,12 +267,13 @@ class IntroductionManagerImpl extends BdfIncomingMessageHook } @Override - public void makeIntroduction(Contact c1, Contact c2, String msg) + public void makeIntroduction(Contact c1, Contact c2, String msg, + final long timestamp) throws DbException, FormatException { Transaction txn = db.startTransaction(false); try { - introducerManager.makeIntroduction(txn, c1, c2, msg); + introducerManager.makeIntroduction(txn, c1, c2, msg, timestamp); txn.setComplete(); } finally { db.endTransaction(txn); @@ -282,11 +282,13 @@ class IntroductionManagerImpl extends BdfIncomingMessageHook @Override public void acceptIntroduction(final ContactId contactId, - final SessionId sessionId) throws DbException, FormatException { + final SessionId sessionId, final long timestamp) + throws DbException, FormatException { Transaction txn = db.startTransaction(false); try { - introduceeManager.acceptIntroduction(txn, contactId, sessionId); + introduceeManager + .acceptIntroduction(txn, contactId, sessionId, timestamp); txn.setComplete(); } finally { db.endTransaction(txn); @@ -295,11 +297,13 @@ class IntroductionManagerImpl extends BdfIncomingMessageHook @Override public void declineIntroduction(final ContactId contactId, - final SessionId sessionId) throws DbException, FormatException { + final SessionId sessionId, final long timestamp) + throws DbException, FormatException { Transaction txn = db.startTransaction(false); try { - introduceeManager.declineIntroduction(txn, contactId, sessionId); + introduceeManager + .declineIntroduction(txn, contactId, sessionId, timestamp); txn.setComplete(); } finally { db.endTransaction(txn); @@ -501,9 +505,10 @@ class IntroductionManagerImpl extends BdfIncomingMessageHook byte[] body = clientHelper.toByteArray(bdfList); GroupId groupId = new GroupId(message.getRaw(GROUP_ID)); Group group = db.getGroup(txn, groupId); - long timestamp = System.currentTimeMillis(); - + long timestamp = + message.getLong(MESSAGE_TIME, System.currentTimeMillis()); message.put(MESSAGE_TIME, timestamp); + Metadata metadata = metadataEncoder.encode(message); messageQueueManager diff --git a/briar-core/src/org/briarproject/properties/TransportPropertyManagerImpl.java b/briar-core/src/org/briarproject/properties/TransportPropertyManagerImpl.java index 5663b6151a..71c93b4fd2 100644 --- a/briar-core/src/org/briarproject/properties/TransportPropertyManagerImpl.java +++ b/briar-core/src/org/briarproject/properties/TransportPropertyManagerImpl.java @@ -108,6 +108,27 @@ class TransportPropertyManagerImpl implements TransportPropertyManager, return Collections.unmodifiableMap(local); } + @Override + public Map<TransportId, TransportProperties> getLocalProperties( + Transaction txn) throws DbException { + try { + Map<TransportId, TransportProperties> local = + new HashMap<TransportId, TransportProperties>(); + // Find the latest local update for each transport + Map<TransportId, LatestUpdate> latest = findLatest(txn, + localGroup.getId(), true); + // Retrieve and parse the latest local properties + for (Entry<TransportId, LatestUpdate> e : latest.entrySet()) { + BdfList message = clientHelper.getMessageAsList(txn, + e.getValue().messageId); + local.put(e.getKey(), parseProperties(message)); + } + return local; + } catch (FormatException e) { + throw new DbException(e); + } + } + @Override public TransportProperties getLocalProperties(TransportId t) throws DbException { @@ -212,26 +233,6 @@ class TransportPropertyManagerImpl implements TransportPropertyManager, return privateGroupFactory.createPrivateGroup(CLIENT_ID, c); } - private Map<TransportId, TransportProperties> getLocalProperties( - Transaction txn) throws DbException { - try { - Map<TransportId, TransportProperties> local = - new HashMap<TransportId, TransportProperties>(); - // Find the latest local update for each transport - Map<TransportId, LatestUpdate> latest = findLatest(txn, - localGroup.getId(), true); - // Retrieve and parse the latest local properties - for (Entry<TransportId, LatestUpdate> e : latest.entrySet()) { - BdfList message = clientHelper.getMessageAsList(txn, - e.getValue().messageId); - local.put(e.getKey(), parseProperties(message)); - } - return local; - } catch (FormatException e) { - throw new DbException(e); - } - } - private void storeMessage(Transaction txn, GroupId g, TransportId t, TransportProperties p, long version, boolean local, boolean shared) throws DbException { -- GitLab