NFC device retrieval¶
According to ISO 18013-5 one of the device retrieval methods is via Bluetooth NFC. To accomplish that SDK provides an out of the box implementation for retrieving data using NFC.
Enabling NFC device engagement and data retrieval¶
To enable NFC device engagement for the NFCTransfer, you need to call the enableDeviceEngagement
method on the NFCTransfer
object. The enableDeviceEngagement
SHOULD be called on the onCreate
method of the activity, since it registers a lifecycle observer to the activity to handle the NFC
device engagement.
The device engagement starts on the RESUMED state of the activity and stops on the PAUSED state of the activity.
The following code snippet demonstrates how to engage with the device via NFC, determine if BLE is supported, and send a request to the device using BLE or NFC.
package com.example.app;
import static android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
import static com.scytales.mvalid.sdk.engagement.DeviceEngagement.DeviceRetrievalMethod.Type.BLE;
import android.content.pm.ActivityInfo;
import android.os.Bundle;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import com.scytales.mdl.iso.reader.sdk.app.R;
import com.scytales.mvalid.sdk.FailureType;
import com.scytales.mvalid.sdk.Received;
import com.scytales.mvalid.sdk.data.DeviceResponse;
import com.scytales.mvalid.sdk.data.Request;
import com.scytales.mvalid.sdk.data.RequestBuilder;
import com.scytales.mvalid.sdk.engagement.DeviceEngagement;
import com.scytales.mvalid.sdk.engagement.DeviceEngagementCallback;
import com.scytales.mvalid.sdk.engagement.EngagementReceived;
import com.scytales.mvalid.sdk.retrieval.TransferProgressEvent;
import com.scytales.mvalid.sdk.retrieval.TransferProgressListener;
import com.scytales.mvalid.sdk.retrieval.TransferReceiveCallback;
import com.scytales.mvalid.sdk.retrieval.TransferReceived;
import com.scytales.mvalid.sdk.retrieval.device.BLETransfer;
import com.scytales.mvalid.sdk.retrieval.device.NFCTransfer;
import com.scytales.mvalid.sdk.session.Handover;
import com.scytales.mvalid.sdk.session.SessionData;
import com.scytales.mvalid.sdk.session.SessionManager;
import com.scytales.mvalid.sdk.verify.DeviceVerifierResult;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class MainActivity extends AppCompatActivity implements DeviceEngagementCallback,
TransferProgressListener, TransferReceiveCallback {
private static final String TAG = "MainActivity";
private static final Map<String, Map<String, Map<String, Boolean>>> docRequests =
new HashMap<String, Map<String, Map<String, Boolean>>>() {{
put("org.iso.18013.5.1.mDL", new HashMap<String, Map<String, Boolean>>() {{
put("org.iso.18013.5.1", new HashMap<String, Boolean>() {{
put("family_name", true);
put("given_name", true);
put("birth_date", true);
put("issue_date", true);
put("expiry_date", true);
}});
}});
}};
private List<X509Certificate> rootCertificates;
private NFCTransfer nfcTransfer;
private BLETransfer bleTransfer;
private SessionManager sessionManager;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LOCKED);
// Keep screen on
getWindow().addFlags(FLAG_KEEP_SCREEN_ON);
this.rootCertificates =
new ArrayList<X509Certificate>() {{
try {
X509Certificate certificate = (X509Certificate) CertificateFactory.getInstance("X.509")
.generateCertificate(getResources().openRawResource(R.raw.scytales_root_ca));
add(certificate);
} catch (CertificateException e) {
Log.e(TAG, "onCreate", e);
// eat it
}
}};
try {
this.bleTransfer = new BLETransfer(this)
.setTransferProgressListener(this)
.setTransferReceiveCallback(this);
this.nfcTransfer = new NFCTransfer()
.enableDeviceEngagement(this, this)
.setTransferProgressListener(this)
.setTransferReceiveCallback(this);
} catch (Exception e) {
Log.e(TAG, "onCreate", e);
// Handle exception
}
}
@Override
public void onEngage(@NonNull Received<EngagementReceived> received) {
try {
received.runOnSuccessCatching(engagementReceived -> {
DeviceEngagement deviceEngagement = engagementReceived.getDeviceEngagement();
Handover handover = engagementReceived.getHandover();
// ... use handover to determine the device engagement QR/NFC
// ... deviceEngagement contains the engagement information to
// proceed with data transfer
// create session manager
this.sessionManager = new SessionManager(deviceEngagement, handover);
Request request = new RequestBuilder()
.setDocRequests(docRequests)
.setSessionManager(this.sessionManager)
.build();
boolean isBLESupported = deviceEngagement
.getDeviceRetrievalMethods()
.stream()
.anyMatch(method -> method.getType().equals(BLE));
if (isBLESupported) {
// suspend NFC and handover to BLE
this.nfcTransfer.suspendDeviceEngagement();
// handover to BLE
bleTransfer.forDeviceEngagement(deviceEngagement)
.send(request);
} else {
// continue with NFC
this.nfcTransfer.send(request);
}
});
received.runOnFailureCatching(failure -> {
Log.e(TAG, failure.toString());
FailureType type = failure.getType();
// handle failed device engagement
// Handle throwable
});
} catch (Exception e) {
Log.e(TAG, "onEngage", e);
// handle exception
}
}
@Override
public void onProgressEvent(@NonNull TransferProgressEvent transferProgressEvent) {
Log.i(TAG, "Transfer progress: " + transferProgressEvent);
}
@Override
public void onReceive(@NonNull Received<TransferReceived> received) {
// resume NFC device engagement and stop BLE
this.nfcTransfer.resumeDeviceEngagement();
this.bleTransfer.stop();
try {
received.runOnSuccessCatching(transferReceived -> {
SessionData sessionData = sessionManager.decryptResponse(transferReceived.getReceivedBytes());
DeviceResponse response = DeviceResponse.fromBytes(sessionData.getData());
List<DeviceVerifierResult> verifierResult = sessionManager
.getVerifier(rootCertificates)
.verify(response.getRawBytes());
Log.i(TAG, "Device response: " + response);
Log.i(TAG, "Verifier result: " + verifierResult);
});
received.runOnFailureCatching(failure -> {
Log.e(TAG, failure.toString());
FailureType type = failure.getType();
// handle failed data transfer
// Handle throwable
});
} catch (Exception e) {
Log.e(TAG, "onReceive", e);
// handle exception
}
}
}