Using TransferManager¶
Overview¶
TransferManager provides the functionality to automatically enable device engagement and then select the appropriate transfer method. The transfer method is selected according the configuration passed during initialization, the features supported by your license and the received device engagement.
It also instantiates a SessionManager each time a device engagement is received, ready to use for during current session for encrypting and decrypting data.
Permissions¶
For android SDK version 31 and later the following permissions required to be granted in order to use QR Scan for device engagement and BLETransfer for transfering data.
- Manifest.permission.CAMERA
- Manifest.permission.BLUETOOTH_ADVERTISE
- Manifest.permission.BLUETOOTH_SCAN
- Manifest.permission.BLUETOOTH_CONNECT
For android SDK prior to version 31 the required permissions are:
- Manifest.permission.CAMERA
- Manifest.permission.ACCESS_FINE_LOCATION
- Manifest.permission.ACCESS_COARSE_LOCATION
Creating the configuration¶
The following example demonstrates a configuration when device engagement received via QR scanning. First TransferManager will try to use BLETransfer for sending and if not possible then it will try the WebAPITransfer.
Example
TransferManager.Config config = new TransferManager.Config()
.setTransferPriority(TransferManager.Config.HANDOVER_QR,
TransferManager.Config.TRANSFER_BLE,
TransferManager.Config.TRANSFER_NFC,
TransferManager.Config.TRANSFER_WEB_API);
The following example demonstrates how to use only the WebAPITransfer when device engagement received via QR scanning. WebAPITransfer.
Example
TransferManager.Config config = new TransferManager.Config()
.setTransferPriority(TransferManager.Config.HANDOVER_QR,
TransferManager.Config.TRANSFER_WEB_API);
Initializing TransferManager¶
Example
package com.example.app;
import android.os.Bundle;
import android.widget.Button;
import androidx.appcompat.app.AppCompatActivity;
import com.scytales.mvalid.sdk.engagement.DeviceEngagementCallback;
import com.scytales.mvalid.sdk.retrieval.TransferManager;
import com.scytales.mvalid.sdk.retrieval.TransferReceiveCallback;
public class MainActivity extends AppCompatActivity
implements DeviceEngagementCallback, TransferReceiveCallback {
private TransferManager transferManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// check permissions are granted and other activity initializations
TransferManager.Config config = new TransferManager.Config()
.setTransferPriority(TransferManager.Config.HANDOVER_QR,
TransferManager.Config.TRANSFER_BLE,
TransferManager.Config.TRANSFER_WEB_API);
try {
transferManager = new TransferManager(getApplicationContext(), config)
.enableDeviceEngagement(this, this)
.setTransferReceiveCallback(this);
((Button) findViewById(R.id.scan_qr_btn))
.setOnClickListener(v -> transferManager.scanQR());
} catch (Exception e) {
// handle initialization exception
}
}
// rest of class implementation
}
Send request¶
Example
package com.example.app;
import ...
public class MainActivity extends AppCompatActivity
implements DeviceEngagementCallback, TransferProgressListener, TransferReceiveCallback {
...
@Override
public void onEngage(@NonNull Received<EngagementReceived> received) {
try {
received.runCatching(
failure -> {
Log.e(TAG, failure.toString());
// handle failure
},
success -> {
Request request = new RequestBuilder()
.setSessionManager(transferManager.getSessionManager())
.forIsoDocTypeAgeOver(21, true)
.build();
transferManager.send(request);
}
);
} catch (Exception e) {
Log.e(TAG, "onEngage", e);
// handle exception
}
}
...
}
Receiving response¶
Example
package com.example.app;
import ...
public class MainActivity extends AppCompatActivity
implements DeviceEngagementCallback, TransferProgressListener, TransferReceiveCallback {
...
@Override
public void onReceive(@NonNull Received<TransferReceived> received) {
try {
received.runCatching(
failure -> {
Log.e(TAG, failure.toString());
// handle failure
},
success -> {
Collection<X509Certificate> rootCertificates =
Verifier.getRootCertificates(getApplicationContext());
success.runCatchingForServer(receivedBytes -> {
ServerResponse response = ServerResponse.fromBytes(receivedBytes);
Log.i(TAG, response.toString());
List<ServerVerifierResult> verifierResults =
new ServerResponseVerifier(rootCertificates)
.verify(receivedBytes);
Log.i(TAG, verifierResults.toString());
});
success.runCatchingForDevice(receivedBytes -> {
transferManager.getSessionManager()
.decryptResponse(receivedBytes)
.runOnDataCatching(data -> {
DeviceResponse response =
DeviceResponse.fromBytes(data);
Log.i(TAG, response.toString());
List<DeviceVerifierResult> verifierResults =
transferManager.getSessionManager()
.getVerifier(rootCertificates)
.verify(data);
Log.i(TAG, verifierResults.toString());
})
.runOnErrorCatching(status -> {
Log.e(TAG, status.toString());
})
.runOnTerminationCatching(() -> {
transferManager.sendTermination();
});
});
}
);
} catch (Exception e) {
Log.e(TAG, "onReceive", e);
// handle exception
}
}
...
}
Sample code¶
The following example demonstrates a typical implementation for retrieving data using TransferManager.
Example
package com.example.app;
import android.os.Bundle;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import com.scytales.mvalid.sdk.Functions;
import com.scytales.mvalid.sdk.Received;
import com.scytales.mvalid.sdk.SDKException;
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.data.ServerResponse;
import com.scytales.mvalid.sdk.engagement.DeviceEngagementCallback;
import com.scytales.mvalid.sdk.engagement.EngagementReceived;
import com.scytales.mvalid.sdk.retrieval.TransferManager;
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.verify.DeviceVerifierResult;
import com.scytales.mvalid.sdk.verify.ServerResponseVerifier;
import com.scytales.mvalid.sdk.verify.ServerVerifierResult;
import com.scytales.mvalid.sdk.verify.Verifier;
import java.security.cert.X509Certificate;
import java.util.Collection;
import java.util.List;
public class MainActivity extends AppCompatActivity
implements DeviceEngagementCallback, TransferProgressListener, TransferReceiveCallback {
private static final String TAG = "TransferManagerActivity";
private TransferManager transferManager;
private Collection<X509Certificate> rootCertificates;
// Consumes received bytes from server
private final Functions.FailableConsumer<byte[], Exception> serverResponseConsumer =
receivedBytes -> {
// create a ServerResponse to manipulate received data
ServerResponse response = ServerResponse.fromBytes(receivedBytes);
// verify received data; a ServerVerifierResult for each
// requested document
List<ServerVerifierResult> verifierResults =
new ServerResponseVerifier(rootCertificates)
.verify(receivedBytes);
// check verification for all verified documents
boolean isVerified = verifierResults.stream()
.allMatch(VerifierResult::isValid);
};
// Consumes received bytes from device
private final Functions.FailableConsumer<byte[], Exception> deviceResponseConsumer =
receivedBytes -> transferManager.getSessionManager()
.decryptResponse(receivedBytes)
.runOnDataCatching(data -> {
// create a DeviceResponse to manipulate received data
DeviceResponse response = DeviceResponse.fromBytes(data);
// verify received data; a DeviceVerifierResult for each
// requested document
List<DeviceVerifierResult> verifierResults = transferManager
.getSessionManager()
.getVerifier(rootCertificates)
.verify(data);
// check verification for all verified documents
boolean isVerified = verifierResults.stream()
.allMatch(VerifierResult::isValid);
})
.runOnErrorCatching(status -> {
Log.e(TAG, status.toString());
})
.runOnTerminationCatching(() -> {
transferManager.sendTermination();
});
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
rootCertificates = Verifier.getRootCertificates(this);
TransferManager.Config config = new TransferManager.Config()
.setTransferPriority(TransferManager.Config.HANDOVER_QR,
TransferManager.Config.TRANSFER_BLE,
TransferManager.Config.TRANSFER_WEB_API);
try {
transferManager = new TransferManager(getApplicationContext(), config)
.enableDeviceEngagement(this, this)
.setTransferProgressListener(this)
.setTransferReceiveCallback(this);
findViewById(R.id.scan_qr_btn)
.setOnClickListener(v -> transferManager.scanQR());
} catch (SDKException e) {
Log.e(TAG, "onCreate", e);
// handle exception
}
}
@Override
public void onEngage(@NonNull Received<EngagementReceived> received) {
try {
received.runCatching(
failure -> {
Log.e(TAG, failure.toString());
// handle failure
},
success -> {
Request request = new RequestBuilder()
.setSessionManager(transferManager.getSessionManager())
.forIsoDocTypeAgeOver(21, true)
.build();
transferManager.send(request);
}
);
} catch (Exception e) {
Log.e(TAG, "onEngage", e);
// handle exception
}
}
@Override
public void onProgressEvent(@NonNull TransferProgressEvent transferProgressEvent) {
Log.i(TAG, transferProgressEvent.toString());
// update UI with event
}
@Override
public void onReceive(@NonNull Received<TransferReceived> received) {
try {
received.runCatching(
failure -> {
Log.e(TAG, failure.toString());
// handle failure
},
success -> {
success.runCatchingForServer(serverResponseConsumer);
success.runCatchingForDevice(deviceResponseConsumer);
}
);
} catch (Exception e) {
Log.e(TAG, "onReceive", e);
// handle exception
}
}
}