Skip to main content
Version: v0.13

Device Retrieval Methods

The device retrieval methods determine how credential data is transferred after engagement is established. The IDK supports multiple retrieval transports that can be configured on engagement instances.

Available Retrieval Methods

MethodDescriptionUse Case
BLE Central ClientDevice connects as BLE centralMost common for mobile wallets
BLE Peripheral ServerDevice advertises as BLE peripheralReader-initiated flows
NFCNear Field CommunicationTap-and-go scenarios
WiFi AwareDirect WiFi connectionHigh bandwidth transfers
REST APIHTTP-based retrievalRemote verification

Accessing Retrieval Methods

The available retrieval methods are exposed through the EngagementInstance:

val engagementManager = session.component.mdocEngagementManager
val engagement = engagementManager.qrEngagement.value

// Get available retrieval methods
val retrievalMethods: Set<DeviceRetrievalMethod> = engagement?.getRetrievalMethods() ?: emptySet()

for (method in retrievalMethods) {
when (method) {
is DeviceRetrievalMethod.Ble -> {
println("BLE available")
println(" Central client: ${method.centralClientMode}")
println(" Peripheral server: ${method.peripheralServerMode}")
}
is DeviceRetrievalMethod.Nfc -> {
println("NFC available")
}
is DeviceRetrievalMethod.Wifi -> {
println("WiFi Aware available")
}
}
}

QR Code Engagement Flow

For QR code-based engagement, the holder displays a QR code and the verifier scans it:

val engagementManager = session.component.mdocEngagementManager

// Observe QR engagement state
lifecycleScope.launch {
engagementManager.qrEngagement.collect { engagement ->
if (engagement != null) {
// Get the URI to encode in QR code
val qrUri = engagement.getEngagementUri()
displayQrCode(qrUri)

// Monitor engagement state
engagement.events.collect { event ->
when (event.state) {
is MdocEngagementState.Connected -> {
// Verifier connected, start transfer
val transferManager = engagement.start()
handleTransfer(transferManager)
}
is MdocEngagementState.Error -> {
showError((event.state as MdocEngagementState.Error).error)
}
}
}
}
}
}

TO_APP Engagement Flow

For app-to-app engagement initiated by the verifier via deep link:

val engagementManager = session.component.mdocEngagementManager

// Handle incoming deep link
fun handleIncomingUri(uri: String) {
lifecycleScope.launch {
// Create engagement from URI
val result = engagementManager.toApp(
mdocUri = uri,
autoStart = true // Automatically start the engagement
)

when (result) {
is IdkResult.Success -> {
val engagement = result.value

// Get transfer manager (auto-started)
engagement.events.collect { event ->
when (event.state) {
is MdocEngagementState.Connected -> {
// Transfer ready
}
}
}
}
is IdkResult.Failure -> {
showError(result.error)
}
}
}
}

NFC Engagement Flow

For NFC tap-based engagement:

val engagementManager = session.component.mdocEngagementManager

// Observe NFC engagement
lifecycleScope.launch {
engagementManager.nfcEngagement.collect { engagement ->
if (engagement != null) {
// NFC tap detected, engagement created
engagement.events.collect { event ->
when (event.state) {
is MdocEngagementState.Connected -> {
// Start transfer after NFC connection
val transferManager = engagement.start()
handleTransfer(transferManager)
}
}
}
}
}
}

Shared Parameters

The SharedParameters manage BLE UUIDs and ephemeral keys shared across engagements:

val sharedParams = engagementManager.sharedParameters

// Access BLE UUIDs
val centralClientUuid = sharedParams.bleCentralClientUuid.value
val peripheralServerUuid = sharedParams.blePeripheralServerUuid.value

// Access shared ephemeral key
val ephemeralKey = sharedParams.ephemeralKey.value

// Regenerate parameters for new session
sharedParams.regenerate()

// Use same UUID for both BLE modes (some verifiers require this)
sharedParams.useSameUuidForBothModes()

Active Engagement Tracking

Only one engagement can be active at a time:

// Track the currently active engagement
lifecycleScope.launch {
engagementManager.activeEngagement.collect { active ->
if (active != null) {
updateUI("Active engagement: ${active.data.type}")
} else {
updateUI("No active engagement")
}
}
}

// Track all engagements by type
lifecycleScope.launch {
engagementManager.engagementsByType.collect { engagements ->
for ((type, instance) in engagements) {
println("$type: ${instance.id}")
}
}
}

Closing Engagements

Clean up engagements when done:

// Close specific engagement types
engagementManager.closeQrEngagement()
engagementManager.closeNfcEngagement()
engagementManager.closeToAppEngagement()

// Close by instance
engagementManager.closeEngagementByInstance(engagement)

// Close by ID
engagementManager.closeEngagementById(engagementId)

// Close all engagements
engagementManager.closeAll()

Best Practices

When implementing retrieval methods:

  • Handle all engagement types: Support QR, NFC, and TO_APP for maximum compatibility
  • Monitor state changes: Use the event streams to provide responsive UI feedback
  • Close engagements: Always clean up after transfer completes or on error
  • Regenerate parameters: Use fresh ephemeral keys for each session
  • Test on real devices: Transport behavior varies across device manufacturers