Show appropriate error message if mailbox QR code is scanned.

This commit is contained in:
akwizgran
2022-12-02 13:29:52 +00:00
parent badccac90c
commit b4f3604584
9 changed files with 124 additions and 44 deletions

View File

@@ -0,0 +1,25 @@
package org.briarproject.bramble.api.qrcode;
import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.qrcode.QrCodeClassifier.QrCodeType;
import org.briarproject.nullsafety.NotNullByDefault;
import javax.annotation.concurrent.Immutable;
/**
* Thrown when a QR code that has been scanned does not have the expected type.
*/
@Immutable
@NotNullByDefault
public class WrongQrCodeTypeException extends FormatException {
private final QrCodeType qrCodeType;
public WrongQrCodeTypeException(QrCodeType qrCodeType) {
this.qrCodeType = qrCodeType;
}
public QrCodeType getQrCodeType() {
return qrCodeType;
}
}

View File

@@ -14,6 +14,7 @@ import org.briarproject.bramble.api.plugin.LanTcpConstants;
import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.qrcode.QrCodeClassifier;
import org.briarproject.bramble.api.qrcode.QrCodeClassifier.QrCodeType;
import org.briarproject.bramble.api.qrcode.WrongQrCodeTypeException;
import org.briarproject.nullsafety.NotNullByDefault;
import java.io.ByteArrayInputStream;
@@ -49,7 +50,8 @@ class PayloadParserImpl implements PayloadParser {
public Payload parse(String payloadString) throws IOException {
Pair<QrCodeType, Integer> typeAndVersion =
qrCodeClassifier.classifyQrCode(payloadString);
if (typeAndVersion.getFirst() != BQP) throw new FormatException();
QrCodeType qrCodeType = typeAndVersion.getFirst();
if (qrCodeType != BQP) throw new WrongQrCodeTypeException(qrCodeType);
int formatVersion = typeAndVersion.getSecond();
if (formatVersion != QR_FORMAT_VERSION) {
boolean tooOld = formatVersion < QR_FORMAT_VERSION;

View File

@@ -10,6 +10,7 @@ import org.briarproject.bramble.api.data.BdfReaderFactory;
import org.briarproject.bramble.api.keyagreement.Payload;
import org.briarproject.bramble.api.qrcode.QrCodeClassifier;
import org.briarproject.bramble.api.qrcode.QrCodeClassifier.QrCodeType;
import org.briarproject.bramble.api.qrcode.WrongQrCodeTypeException;
import org.briarproject.bramble.test.BrambleMockTestCase;
import org.jmock.Expectations;
import org.junit.Test;
@@ -40,8 +41,8 @@ public class PayloadParserImplTest extends BrambleMockTestCase {
private final PayloadParserImpl payloadParser =
new PayloadParserImpl(bdfReaderFactory, qrCodeClassifier);
@Test(expected = FormatException.class)
public void testThrowsFormatExceptionForWrongQrCodeType() throws Exception {
@Test(expected = WrongQrCodeTypeException.class)
public void testThrowsExceptionForWrongQrCodeType() throws Exception {
expectClassifyQrCode(payload, MAILBOX, QR_FORMAT_VERSION);
payloadParser.parse(payload);

View File

@@ -3,12 +3,14 @@ package org.briarproject.briar.android.contact.add.nearby;
import android.graphics.Bitmap;
import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.qrcode.QrCodeClassifier.QrCodeType;
import androidx.annotation.Nullable;
abstract class AddContactState {
static class KeyAgreementListening extends AddContactState {
final Bitmap qrCode;
KeyAgreementListening(Bitmap qrCode) {
@@ -29,6 +31,7 @@ abstract class AddContactState {
}
static class ContactExchangeFinished extends AddContactState {
final ContactExchangeResult result;
ContactExchangeFinished(ContactExchangeResult result) {
@@ -37,25 +40,30 @@ abstract class AddContactState {
}
static class Failed extends AddContactState {
/**
* Non-null if failed due to the scanned QR code version.
* True if the app producing the code is too old.
* False if the scanning app is too old.
*/
@Nullable
final Boolean qrCodeTooOld;
Failed(@Nullable Boolean qrCodeTooOld) {
this.qrCodeTooOld = qrCodeTooOld;
static class WrongQrCodeType extends Failed {
final QrCodeType qrCodeType;
WrongQrCodeType(QrCodeType qrCodeType) {
this.qrCodeType = qrCodeType;
}
}
Failed() {
this(null);
static class WrongQrCodeVersion extends Failed {
final boolean qrCodeTooOld;
WrongQrCodeVersion(boolean qrCodeTooOld) {
this.qrCodeTooOld = qrCodeTooOld;
}
}
}
abstract static class ContactExchangeResult {
static class Success extends ContactExchangeResult {
final Author remoteAuthor;
Success(Author remoteAuthor) {
@@ -64,6 +72,7 @@ abstract class AddContactState {
}
static class Error extends ContactExchangeResult {
@Nullable
final Author duplicateAuthor;

View File

@@ -6,12 +6,15 @@ import android.view.MenuItem;
import android.widget.Toast;
import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.qrcode.QrCodeClassifier.QrCodeType;
import org.briarproject.briar.R;
import org.briarproject.briar.android.activity.ActivityComponent;
import org.briarproject.briar.android.activity.BriarActivity;
import org.briarproject.briar.android.contact.add.nearby.AddContactState.ContactExchangeFinished;
import org.briarproject.briar.android.contact.add.nearby.AddContactState.ContactExchangeResult;
import org.briarproject.briar.android.contact.add.nearby.AddContactState.Failed;
import org.briarproject.briar.android.contact.add.nearby.AddContactState.Failed.WrongQrCodeType;
import org.briarproject.briar.android.contact.add.nearby.AddContactState.Failed.WrongQrCodeVersion;
import org.briarproject.briar.android.contact.add.nearby.AddNearbyContactViewModel.BluetoothDecision;
import org.briarproject.briar.android.fragment.BaseFragment;
import org.briarproject.briar.android.fragment.BaseFragment.BaseFragmentListener;
@@ -34,6 +37,7 @@ import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
import static android.widget.Toast.LENGTH_LONG;
import static java.util.Objects.requireNonNull;
import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.api.qrcode.QrCodeClassifier.QrCodeType.MAILBOX;
import static org.briarproject.briar.android.contact.add.nearby.AddNearbyContactViewModel.BluetoothDecision.ACCEPTED;
import static org.briarproject.briar.android.contact.add.nearby.AddNearbyContactViewModel.BluetoothDecision.REFUSED;
@@ -141,9 +145,15 @@ public class AddNearbyContactActivity extends BriarActivity
ContactExchangeResult result =
((ContactExchangeFinished) state).result;
onContactExchangeResult(result);
} else if (state instanceof WrongQrCodeType) {
QrCodeType qrCodeType = ((WrongQrCodeType) state).qrCodeType;
if (qrCodeType == MAILBOX) onMailboxQrCodeScanned();
else showErrorFragment();
} else if (state instanceof WrongQrCodeVersion) {
boolean qrCodeTooOld = ((WrongQrCodeVersion) state).qrCodeTooOld;
onWrongQrCodeVersion(qrCodeTooOld);
} else if (state instanceof Failed) {
Boolean qrCodeTooOld = ((Failed) state).qrCodeTooOld;
onAddingContactFailed(qrCodeTooOld);
showErrorFragment();
}
}
@@ -170,15 +180,20 @@ public class AddNearbyContactActivity extends BriarActivity
} else throw new AssertionError();
}
private void onAddingContactFailed(@Nullable Boolean qrCodeTooOld) {
if (qrCodeTooOld == null) {
showErrorFragment();
} else {
private void onMailboxQrCodeScanned() {
String title = getString(R.string.qr_code_invalid);
String msg = getString(R.string.mailbox_qr_code_for_contact);
showNextFragment(
AddNearbyContactErrorFragment.newInstance(title, msg, false));
}
private void onWrongQrCodeVersion(boolean qrCodeTooOld) {
String title = getString(R.string.qr_code_invalid);
String msg;
if (qrCodeTooOld) msg = getString(R.string.qr_code_too_old_1);
else msg = getString(R.string.qr_code_too_new_1);
showNextFragment(AddNearbyContactErrorFragment.newInstance(msg));
}
showNextFragment(
AddNearbyContactErrorFragment.newInstance(title, msg, false));
}
private void showErrorFragment() {

View File

@@ -22,6 +22,7 @@ import androidx.fragment.app.FragmentActivity;
import androidx.lifecycle.ViewModelProvider;
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
import static android.view.View.GONE;
import static org.briarproject.briar.android.util.UiUtils.hideViewOnSmallScreen;
import static org.briarproject.briar.android.util.UiUtils.onSingleLinkClick;
@@ -31,17 +32,22 @@ public class AddNearbyContactErrorFragment extends BaseFragment {
public static final String TAG =
AddNearbyContactErrorFragment.class.getName();
private static final String ERROR_MSG = "errorMessage";
private static final String ARG_TITLE = "title";
private static final String ARG_ERROR_MSG = "message";
private static final String ARG_FEEDBACK = "feedback";
@Inject
ViewModelProvider.Factory viewModelFactory;
private AddNearbyContactViewModel viewModel;
public static AddNearbyContactErrorFragment newInstance(String errorMsg) {
public static AddNearbyContactErrorFragment newInstance(String title,
String errorMessage, boolean feedback) {
AddNearbyContactErrorFragment f = new AddNearbyContactErrorFragment();
Bundle args = new Bundle();
args.putString(ERROR_MSG, errorMsg);
args.putString(ARG_TITLE, title);
args.putString(ARG_ERROR_MSG, errorMessage);
args.putBoolean(ARG_FEEDBACK, feedback);
f.setArguments(args);
return f;
}
@@ -66,19 +72,32 @@ public class AddNearbyContactErrorFragment extends BaseFragment {
View v = inflater.inflate(R.layout.fragment_error_contact_exchange,
container, false);
// set optional error message
TextView explanation = v.findViewById(R.id.errorMessage);
String title = null, errorMessage = null;
boolean feedback = true;
Bundle args = getArguments();
String errorMessage = args == null ? null : args.getString(ERROR_MSG);
if (errorMessage == null) {
explanation.setText(getString(R.string.add_contact_error_two_way));
} else {
explanation.setText(args.getString(ERROR_MSG));
if (args != null) {
title = args.getString(ARG_TITLE);
errorMessage = args.getString(ARG_ERROR_MSG);
feedback = args.getBoolean(ARG_FEEDBACK, true);
}
if (title != null) {
TextView titleView = v.findViewById(R.id.errorTitle);
titleView.setText(title);
}
if (errorMessage != null) {
TextView messageView = v.findViewById(R.id.errorMessage);
messageView.setText(errorMessage);
}
// make feedback link clickable
TextView sendFeedback = v.findViewById(R.id.sendFeedback);
if (feedback) {
// make feedback link clickable
onSingleLinkClick(sendFeedback, this::triggerFeedback);
} else {
sendFeedback.setVisibility(GONE);
}
// buttons
Button tryAgain = v.findViewById(R.id.tryAgainButton);

View File

@@ -43,6 +43,7 @@ import org.briarproject.bramble.api.plugin.PluginManager;
import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
import org.briarproject.bramble.api.plugin.event.TransportStateEvent;
import org.briarproject.bramble.api.qrcode.WrongQrCodeTypeException;
import org.briarproject.bramble.api.system.AndroidExecutor;
import org.briarproject.bramble.plugin.bluetooth.BluetoothPlugin;
import org.briarproject.briar.R;
@@ -50,9 +51,13 @@ import org.briarproject.briar.android.contact.add.nearby.AddContactState.Contact
import org.briarproject.briar.android.contact.add.nearby.AddContactState.ContactExchangeResult.Error;
import org.briarproject.briar.android.contact.add.nearby.AddContactState.ContactExchangeResult.Success;
import org.briarproject.briar.android.contact.add.nearby.AddContactState.ContactExchangeStarted;
import org.briarproject.briar.android.contact.add.nearby.AddContactState.Failed;
import org.briarproject.briar.android.contact.add.nearby.AddContactState.Failed.WrongQrCodeType;
import org.briarproject.briar.android.contact.add.nearby.AddContactState.Failed.WrongQrCodeVersion;
import org.briarproject.briar.android.contact.add.nearby.AddContactState.KeyAgreementListening;
import org.briarproject.briar.android.contact.add.nearby.AddContactState.KeyAgreementStarted;
import org.briarproject.briar.android.contact.add.nearby.AddContactState.KeyAgreementWaiting;
import org.briarproject.briar.android.contact.add.nearby.AddContactState.QrCodeScanned;
import org.briarproject.briar.android.qrcode.QrCodeDecoder;
import org.briarproject.briar.android.qrcode.QrCodeUtils;
import org.briarproject.briar.android.viewmodel.LiveEvent;
@@ -373,11 +378,11 @@ class AddNearbyContactViewModel extends AndroidViewModel
} else if (e instanceof KeyAgreementAbortedEvent) {
LOG.info("KeyAgreementAbortedEvent received");
resetPayloadFlags();
state.setValue(new AddContactState.Failed());
state.setValue(new Failed());
} else if (e instanceof KeyAgreementFailedEvent) {
LOG.info("KeyAgreementFailedEvent received");
resetPayloadFlags();
state.setValue(new AddContactState.Failed());
state.setValue(new Failed());
}
}
@@ -446,16 +451,19 @@ class AddNearbyContactViewModel extends AndroidViewModel
Payload remotePayload = payloadParser.parse(result.getText());
gotRemotePayload = true;
currentTask.connectAndRunProtocol(remotePayload);
state.postValue(new AddContactState.QrCodeScanned());
state.postValue(new QrCodeScanned());
} catch (WrongQrCodeTypeException e) {
resetPayloadFlags();
state.postValue(new WrongQrCodeType(e.getQrCodeType()));
} catch (UnsupportedVersionException e) {
resetPayloadFlags();
state.postValue(new AddContactState.Failed(e.isTooOld()));
state.postValue(new WrongQrCodeVersion(e.isTooOld()));
} catch (IOException | IllegalArgumentException e) {
LOG.log(WARNING, "QR Code Invalid", e);
androidExecutor.runOnUiThread(() -> Toast.makeText(getApplication(),
R.string.qr_code_invalid, LENGTH_LONG).show());
resetPayloadFlags();
state.postValue(new AddContactState.Failed());
state.postValue(new Failed());
}
}

View File

@@ -51,12 +51,12 @@
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/margin_xlarge"
android:text="@string/add_contact_error_two_way"
android:textAppearance="@style/TextAppearance.MaterialComponents.Body1"
app:layout_constraintBottom_toTopOf="@+id/sendFeedback"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/errorTitle"
tools:text="error explanation" />
app:layout_constraintTop_toBottomOf="@+id/errorTitle" />
<TextView
android:id="@+id/sendFeedback"

View File

@@ -252,6 +252,7 @@
<string name="qr_code_invalid">The QR code is invalid</string>
<string name="qr_code_too_old_1">The QR code you have scanned comes from an older version of Briar.\n\nPlease ask your contact to upgrade to the latest version and then try again.</string>
<string name="qr_code_too_new_1">The QR code you have scanned comes from a newer version of Briar.\n\nPlease upgrade to the latest version and then try again.</string>
<string name="mailbox_qr_code_for_contact">The QR code you have scanned comes from Briar Mailbox.\n\nIf you want to link a Mailbox, please choose Settings > Mailbox from the Briar menu.</string>
<string name="camera_error">Camera error</string>
<string name="connecting_to_device">Connecting to device\u2026</string>
<string name="authenticating_with_device">Authenticating with device\u2026</string>