@ledgerhq/device-transport-kit-react-native-hid 0.0.0-hid-candidate-3-20250528083456 → 0.0.0-io-revamp-20250618083854
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/android/src/main/kotlin/com/ledger/androidtransporthid/TransportHidModule.kt +3 -12
- package/android/src/main/kotlin/com/ledger/androidtransporthid/bridge/serialization.kt +0 -2
- package/android/src/main/kotlin/com/ledger/devicesdk/shared/androidMain/transport/usb/DefaultAndroidUsbTransport.kt +12 -61
- package/android/src/main/kotlin/com/ledger/devicesdk/shared/androidMain/transport/usb/connection/AndroidUsbApduSender.kt +29 -70
- package/android/src/main/kotlin/com/ledger/devicesdk/shared/androidMainInternal/transport/deviceconnection/DeviceApduSender.kt +1 -2
- package/android/src/main/kotlin/com/ledger/devicesdk/shared/androidMainInternal/transport/deviceconnection/DeviceConnection.kt +4 -5
- package/android/src/main/kotlin/com/ledger/devicesdk/shared/androidMainInternal/transport/deviceconnection/DeviceConnectionStateMachine.kt +12 -10
- package/android/src/main/kotlin/com/ledger/devicesdk/shared/api/apdu/SendApduResult.kt +0 -4
- package/android/src/main/kotlin/com/ledger/devicesdk/shared/internal/connection/InternalConnectedDevice.kt +1 -1
- package/android/src/test/kotlin/com/ledger/devicesdk/shared/androidMainInternal/transport/deviceconnection/DeviceConnectionStateMachineTest.kt +31 -47
- package/android/src/test/kotlin/com/ledger/devicesdk/shared/androidMainInternal/transport/deviceconnection/DeviceConnectionTest.kt +5 -9
- package/lib/cjs/api/bridge/DefaultNativeModuleWrapper.js +1 -1
- package/lib/cjs/api/bridge/DefaultNativeModuleWrapper.js.map +3 -3
- package/lib/cjs/api/bridge/mapper.js +1 -1
- package/lib/cjs/api/bridge/mapper.js.map +2 -2
- package/lib/cjs/api/bridge/mapper.test.js +1 -1
- package/lib/cjs/api/bridge/mapper.test.js.map +2 -2
- package/lib/cjs/api/bridge/types.js +1 -1
- package/lib/cjs/api/bridge/types.js.map +1 -1
- package/lib/cjs/api/transport/Errors.js +1 -1
- package/lib/cjs/api/transport/Errors.js.map +3 -3
- package/lib/cjs/api/transport/NativeModuleWrapper.js +1 -1
- package/lib/cjs/api/transport/NativeModuleWrapper.js.map +1 -1
- package/lib/cjs/api/transport/RNHidTransport.js +1 -1
- package/lib/cjs/api/transport/RNHidTransport.js.map +3 -3
- package/lib/cjs/api/transport/RNHidTransport.test.js +1 -1
- package/lib/cjs/api/transport/RNHidTransport.test.js.map +2 -2
- package/lib/esm/api/bridge/DefaultNativeModuleWrapper.js +1 -1
- package/lib/esm/api/bridge/DefaultNativeModuleWrapper.js.map +3 -3
- package/lib/esm/api/bridge/mapper.js +1 -1
- package/lib/esm/api/bridge/mapper.js.map +3 -3
- package/lib/esm/api/bridge/mapper.test.js +1 -1
- package/lib/esm/api/bridge/mapper.test.js.map +3 -3
- package/lib/esm/api/bridge/types.js.map +1 -1
- package/lib/esm/api/transport/Errors.js +1 -1
- package/lib/esm/api/transport/Errors.js.map +3 -3
- package/lib/esm/api/transport/RNHidTransport.js +1 -1
- package/lib/esm/api/transport/RNHidTransport.js.map +3 -3
- package/lib/esm/api/transport/RNHidTransport.test.js +1 -1
- package/lib/esm/api/transport/RNHidTransport.test.js.map +3 -3
- package/lib/types/api/bridge/DefaultNativeModuleWrapper.d.ts +1 -1
- package/lib/types/api/bridge/DefaultNativeModuleWrapper.d.ts.map +1 -1
- package/lib/types/api/bridge/mapper.d.ts.map +1 -1
- package/lib/types/api/bridge/types.d.ts +2 -2
- package/lib/types/api/bridge/types.d.ts.map +1 -1
- package/lib/types/api/transport/Errors.d.ts +1 -1
- package/lib/types/api/transport/Errors.d.ts.map +1 -1
- package/lib/types/api/transport/NativeModuleWrapper.d.ts +1 -1
- package/lib/types/api/transport/NativeModuleWrapper.d.ts.map +1 -1
- package/lib/types/api/transport/RNHidTransport.d.ts.map +1 -1
- package/lib/types/tsconfig.prod.tsbuildinfo +1 -1
- package/package.json +4 -4
|
@@ -31,7 +31,6 @@ import kotlinx.coroutines.flow.onEach
|
|
|
31
31
|
import kotlinx.coroutines.launch
|
|
32
32
|
import timber.log.Timber
|
|
33
33
|
import kotlin.random.Random
|
|
34
|
-
import kotlin.time.Duration
|
|
35
34
|
import kotlin.time.Duration.Companion.milliseconds
|
|
36
35
|
|
|
37
36
|
class TransportHidModule(
|
|
@@ -49,7 +48,7 @@ class TransportHidModule(
|
|
|
49
48
|
private val loggerService: LoggerService =
|
|
50
49
|
LoggerService { info ->
|
|
51
50
|
Timber.tag("LDMKTransportHIDModule " + info.tag).d(info.message)
|
|
52
|
-
|
|
51
|
+
sendEvent(reactContext, BridgeEvents.TransportLog(info))
|
|
53
52
|
}
|
|
54
53
|
|
|
55
54
|
private val transport: AndroidUsbTransport? by lazy {
|
|
@@ -207,13 +206,7 @@ class TransportHidModule(
|
|
|
207
206
|
}
|
|
208
207
|
|
|
209
208
|
@ReactMethod
|
|
210
|
-
fun sendApdu(
|
|
211
|
-
sessionId: String,
|
|
212
|
-
apduBase64: String,
|
|
213
|
-
triggersDisconnection: Boolean,
|
|
214
|
-
abortTimeout: Int,
|
|
215
|
-
promise: Promise
|
|
216
|
-
) {
|
|
209
|
+
fun sendApdu(sessionId: String, apduBase64: String, promise: Promise) {
|
|
217
210
|
try {
|
|
218
211
|
val device = connectedDevices.firstOrNull() { it.id == sessionId }
|
|
219
212
|
if (device == null) {
|
|
@@ -223,9 +216,7 @@ class TransportHidModule(
|
|
|
223
216
|
coroutineScope.launch {
|
|
224
217
|
try {
|
|
225
218
|
val apdu: ByteArray = Base64.decode(apduBase64, Base64.DEFAULT)
|
|
226
|
-
val
|
|
227
|
-
val res =
|
|
228
|
-
device.sendApduFn(apdu, triggersDisconnection, abortTimeoutDuration)
|
|
219
|
+
val res = device.sendApduFn(apdu)
|
|
229
220
|
promise.resolve(res.toWritableMap())
|
|
230
221
|
} catch (e: Exception) {
|
|
231
222
|
Timber.i("$e, ${e.cause}")
|
|
@@ -103,8 +103,6 @@ internal fun SendApduResult.toWritableMap(): WritableMap {
|
|
|
103
103
|
SendApduFailureReason.NoUsbEndpointFound -> "NoUsbEndpointFound"
|
|
104
104
|
SendApduFailureReason.DeviceDisconnected -> "DeviceDisconnected"
|
|
105
105
|
SendApduFailureReason.Unknown -> "Unknown"
|
|
106
|
-
SendApduFailureReason.AbortTimeout -> "SendApduTimeout"
|
|
107
|
-
SendApduFailureReason.EmptyResponse -> "EmptyResponse"
|
|
108
106
|
})
|
|
109
107
|
}
|
|
110
108
|
}
|
|
@@ -97,57 +97,31 @@ internal class DefaultAndroidUsbTransport(
|
|
|
97
97
|
override fun updateUsbState(state: UsbState) {
|
|
98
98
|
when (state) {
|
|
99
99
|
is UsbState.Detached -> {
|
|
100
|
-
loggerService.log(
|
|
101
|
-
buildSimpleDebugLogInfo(
|
|
102
|
-
"AndroidUsbTransport",
|
|
103
|
-
"Detached deviceId=${state.ledgerUsbDevice.uid}"
|
|
104
|
-
)
|
|
105
|
-
)
|
|
100
|
+
loggerService.log(buildSimpleDebugLogInfo("AndroidUsbTransport", "Detached deviceId=${state.ledgerUsbDevice.uid}"))
|
|
106
101
|
usbConnections.entries.find {
|
|
107
102
|
it.value.getApduSender().dependencies.ledgerUsbDevice.uid == state.ledgerUsbDevice.uid
|
|
108
103
|
}.let { item ->
|
|
109
104
|
scope.launch {
|
|
110
105
|
if (item == null) {
|
|
111
|
-
loggerService.log(
|
|
112
|
-
buildSimpleWarningLogInfo(
|
|
113
|
-
"AndroidUsbTransport",
|
|
114
|
-
"No connection found"
|
|
115
|
-
)
|
|
116
|
-
)
|
|
106
|
+
loggerService.log(buildSimpleWarningLogInfo("AndroidUsbTransport", "No connection found"))
|
|
117
107
|
return@launch
|
|
118
108
|
}
|
|
119
109
|
val (key, deviceConnection) = item
|
|
120
|
-
loggerService.log(
|
|
121
|
-
|
|
122
|
-
"AndroidUsbTransport",
|
|
123
|
-
"Device disconnected (sessionId=${deviceConnection.sessionId})"
|
|
124
|
-
)
|
|
125
|
-
)
|
|
110
|
+
loggerService.log(buildSimpleInfoLogInfo("AndroidUsbTransport", "Device disconnected (sessionId=${deviceConnection.sessionId})"))
|
|
111
|
+
deviceConnection.handleDeviceDisconnected()
|
|
126
112
|
usbConnections.remove(key)
|
|
127
113
|
usbConnectionsPendingReconnection.add(deviceConnection)
|
|
128
|
-
deviceConnection.handleDeviceDisconnected()
|
|
129
|
-
(deviceConnection.getApduSender() as AndroidUsbApduSender).release()
|
|
130
114
|
}
|
|
131
115
|
}
|
|
132
116
|
}
|
|
133
117
|
|
|
134
118
|
is UsbState.Attached -> {
|
|
135
|
-
loggerService.log(
|
|
136
|
-
buildSimpleDebugLogInfo(
|
|
137
|
-
"AndroidUsbTransport",
|
|
138
|
-
"Attached deviceId=${state.ledgerUsbDevice.uid}, pendingReconnections=${usbConnectionsPendingReconnection}"
|
|
139
|
-
)
|
|
140
|
-
)
|
|
119
|
+
loggerService.log(buildSimpleDebugLogInfo("AndroidUsbTransport", "Attached deviceId=${state.ledgerUsbDevice.uid}, pendingReconnections=${usbConnectionsPendingReconnection}"))
|
|
141
120
|
val usbDevice = usbManager.deviceList.values.firstOrNull {
|
|
142
121
|
it.toLedgerUsbDevice()?.uid == state.ledgerUsbDevice.uid
|
|
143
122
|
}
|
|
144
123
|
if (usbDevice == null) {
|
|
145
|
-
loggerService.log(
|
|
146
|
-
buildSimpleWarningLogInfo(
|
|
147
|
-
"AndroidUsbTransport",
|
|
148
|
-
"No UsbDevice found"
|
|
149
|
-
)
|
|
150
|
-
)
|
|
124
|
+
loggerService.log(buildSimpleWarningLogInfo("AndroidUsbTransport", "No UsbDevice found"))
|
|
151
125
|
return
|
|
152
126
|
}
|
|
153
127
|
usbConnectionsPendingReconnection.firstOrNull {
|
|
@@ -164,29 +138,14 @@ internal class DefaultAndroidUsbTransport(
|
|
|
164
138
|
)
|
|
165
139
|
return@launch
|
|
166
140
|
}
|
|
167
|
-
loggerService.log(
|
|
168
|
-
buildSimpleDebugLogInfo(
|
|
169
|
-
"AndroidUsbTransport",
|
|
170
|
-
"Found matching device connection $deviceConnection"
|
|
171
|
-
)
|
|
172
|
-
)
|
|
141
|
+
loggerService.log(buildSimpleDebugLogInfo("AndroidUsbTransport", "Found matching device connection $deviceConnection"))
|
|
173
142
|
|
|
174
143
|
val permissionResult = checkOrRequestPermission(usbDevice)
|
|
175
144
|
if (permissionResult is PermissionResult.Denied) {
|
|
176
|
-
loggerService.log(
|
|
177
|
-
buildSimpleDebugLogInfo(
|
|
178
|
-
"AndroidUsbTransport",
|
|
179
|
-
"Permission denied"
|
|
180
|
-
)
|
|
181
|
-
)
|
|
145
|
+
loggerService.log(buildSimpleDebugLogInfo("AndroidUsbTransport", "Permission denied"))
|
|
182
146
|
return@launch
|
|
183
147
|
}
|
|
184
|
-
loggerService.log(
|
|
185
|
-
buildSimpleInfoLogInfo(
|
|
186
|
-
"AndroidUsbTransport",
|
|
187
|
-
"Reconnecting device (sessionId=${deviceConnection.sessionId})"
|
|
188
|
-
)
|
|
189
|
-
)
|
|
148
|
+
loggerService.log(buildSimpleInfoLogInfo("AndroidUsbTransport", "Reconnecting device (sessionId=${deviceConnection.sessionId})"))
|
|
190
149
|
deviceConnection.handleDeviceConnected(
|
|
191
150
|
AndroidUsbApduSender(
|
|
192
151
|
dependencies = AndroidUsbApduSender.Dependencies(
|
|
@@ -236,12 +195,7 @@ internal class DefaultAndroidUsbTransport(
|
|
|
236
195
|
device = usbDevice,
|
|
237
196
|
)
|
|
238
197
|
|
|
239
|
-
loggerService.log(
|
|
240
|
-
buildSimpleDebugLogInfo(
|
|
241
|
-
"AndroidUsbTransport",
|
|
242
|
-
"Waiting for permission result"
|
|
243
|
-
)
|
|
244
|
-
)
|
|
198
|
+
loggerService.log(buildSimpleDebugLogInfo("AndroidUsbTransport", "Waiting for permission result"))
|
|
245
199
|
|
|
246
200
|
val result = eventsFlow.first {
|
|
247
201
|
it is UsbPermissionEvent.PermissionGranted ||
|
|
@@ -305,10 +259,9 @@ internal class DefaultAndroidUsbTransport(
|
|
|
305
259
|
val deviceConnection = DeviceConnection(
|
|
306
260
|
sessionId = sessionId,
|
|
307
261
|
deviceApduSender = apduSender,
|
|
308
|
-
isFatalSendApduFailure = { false },
|
|
262
|
+
isFatalSendApduFailure = { false }, // TODO: refine this
|
|
309
263
|
reconnectionTimeoutDuration = 5.seconds,
|
|
310
264
|
onTerminated = {
|
|
311
|
-
(it.getApduSender() as AndroidUsbApduSender).release()
|
|
312
265
|
usbConnections.remove(sessionId)
|
|
313
266
|
usbConnectionsPendingReconnection.remove(it)
|
|
314
267
|
eventDispatcher.dispatch(TransportEvent.DeviceConnectionLost(sessionId))
|
|
@@ -323,9 +276,7 @@ internal class DefaultAndroidUsbTransport(
|
|
|
323
276
|
discoveryDevice.name,
|
|
324
277
|
discoveryDevice.ledgerDevice,
|
|
325
278
|
discoveryDevice.connectivityType,
|
|
326
|
-
sendApduFn = { apdu
|
|
327
|
-
deviceConnection.requestSendApdu(apdu, triggersDisconnection, abortTimeoutDuration)
|
|
328
|
-
}
|
|
279
|
+
sendApduFn = { apdu -> deviceConnection.requestSendApdu(apdu) },
|
|
329
280
|
)
|
|
330
281
|
|
|
331
282
|
usbConnections[sessionId] = deviceConnection
|
|
@@ -13,21 +13,21 @@ import android.hardware.usb.UsbInterface
|
|
|
13
13
|
import android.hardware.usb.UsbManager
|
|
14
14
|
import android.hardware.usb.UsbRequest
|
|
15
15
|
import com.ledger.devicesdk.shared.androidMain.transport.usb.model.LedgerUsbDevice
|
|
16
|
-
import com.ledger.devicesdk.shared.androidMainInternal.transport.USB_MTU
|
|
17
|
-
import com.ledger.devicesdk.shared.androidMainInternal.transport.deviceconnection.DeviceApduSender
|
|
18
16
|
import com.ledger.devicesdk.shared.api.apdu.SendApduFailureReason
|
|
19
17
|
import com.ledger.devicesdk.shared.api.apdu.SendApduResult
|
|
18
|
+
import com.ledger.devicesdk.shared.androidMainInternal.transport.deviceconnection.DeviceApduSender
|
|
19
|
+
import com.ledger.devicesdk.shared.api.utils.toHexadecimalString
|
|
20
|
+
import com.ledger.devicesdk.shared.androidMainInternal.transport.USB_MTU
|
|
20
21
|
import com.ledger.devicesdk.shared.internal.service.logger.LoggerService
|
|
21
22
|
import com.ledger.devicesdk.shared.internal.service.logger.buildSimpleErrorLogInfo
|
|
23
|
+
import com.ledger.devicesdk.shared.internal.service.logger.buildSimpleInfoLogInfo
|
|
22
24
|
import com.ledger.devicesdk.shared.internal.transport.framer.FramerService
|
|
23
25
|
import com.ledger.devicesdk.shared.internal.transport.framer.to2BytesArray
|
|
24
|
-
import kotlinx.coroutines.CoroutineDispatcher
|
|
25
|
-
import kotlinx.coroutines.delay
|
|
26
|
-
import kotlinx.coroutines.launch
|
|
27
|
-
import kotlinx.coroutines.withContext
|
|
28
26
|
import java.nio.ByteBuffer
|
|
29
27
|
import kotlin.random.Random
|
|
30
|
-
import
|
|
28
|
+
import kotlinx.coroutines.CoroutineDispatcher
|
|
29
|
+
import kotlinx.coroutines.withContext
|
|
30
|
+
import timber.log.Timber
|
|
31
31
|
|
|
32
32
|
private const val USB_TIMEOUT = 500
|
|
33
33
|
|
|
@@ -35,75 +35,44 @@ private const val DEFAULT_USB_INTERFACE = 0
|
|
|
35
35
|
|
|
36
36
|
internal class AndroidUsbApduSender(
|
|
37
37
|
override val dependencies: Dependencies,
|
|
38
|
-
usbManager: UsbManager,
|
|
38
|
+
private val usbManager: UsbManager,
|
|
39
39
|
private val framerService: FramerService,
|
|
40
40
|
private val request: UsbRequest,
|
|
41
41
|
private val ioDispatcher: CoroutineDispatcher,
|
|
42
42
|
private val loggerService: LoggerService,
|
|
43
43
|
) : DeviceApduSender<AndroidUsbApduSender.Dependencies> {
|
|
44
|
+
|
|
44
45
|
data class Dependencies(
|
|
45
46
|
val usbDevice: UsbDevice,
|
|
46
47
|
val ledgerUsbDevice: LedgerUsbDevice,
|
|
47
48
|
)
|
|
48
49
|
|
|
49
|
-
|
|
50
|
-
private val usbInterface = usbDevice.getInterface(DEFAULT_USB_INTERFACE)
|
|
51
|
-
private val androidToUsbEndpoint =
|
|
52
|
-
usbInterface.firstEndpointOrThrow { it == UsbConstants.USB_DIR_OUT }
|
|
53
|
-
private val usbToAndroidEndpoint =
|
|
54
|
-
usbInterface.firstEndpointOrThrow { it == UsbConstants.USB_DIR_IN }
|
|
55
|
-
private val usbConnection = usbManager.openDevice(usbDevice)
|
|
56
|
-
.apply { claimInterface(usbInterface, true) }
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
fun release() {
|
|
60
|
-
usbConnection.releaseInterface(usbInterface)
|
|
61
|
-
usbConnection.close()
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
override suspend fun send(apdu: ByteArray, abortTimeoutDuration: Duration): SendApduResult =
|
|
50
|
+
override suspend fun send(apdu: ByteArray): SendApduResult =
|
|
65
51
|
try {
|
|
52
|
+
val usbDevice = dependencies.usbDevice
|
|
66
53
|
withContext(context = ioDispatcher) {
|
|
67
|
-
|
|
68
|
-
val
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
}
|
|
54
|
+
val usbInterface = usbDevice.getInterface(DEFAULT_USB_INTERFACE)
|
|
55
|
+
val androidToUsbEndpoint = usbInterface.firstEndpointOrThrow { it == UsbConstants.USB_DIR_OUT }
|
|
56
|
+
val usbToAndroidEndpoint = usbInterface.firstEndpointOrThrow { it == UsbConstants.USB_DIR_IN }
|
|
57
|
+
val usbConnection = usbManager.openDevice(usbDevice).apply { claimInterface(usbInterface, true) }
|
|
72
58
|
|
|
73
59
|
transmitApdu(
|
|
74
60
|
usbConnection = usbConnection,
|
|
75
61
|
androidToUsbEndpoint = androidToUsbEndpoint,
|
|
76
62
|
rawApdu = apdu,
|
|
77
63
|
)
|
|
78
|
-
|
|
79
64
|
val apduResponse =
|
|
80
65
|
receiveApdu(
|
|
81
66
|
usbConnection = usbConnection,
|
|
82
67
|
usbToAndroidEndpoint = usbToAndroidEndpoint,
|
|
83
68
|
)
|
|
84
|
-
timeoutJob.cancel()
|
|
85
69
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
}
|
|
70
|
+
usbConnection.releaseInterface(usbInterface)
|
|
71
|
+
usbConnection.close()
|
|
89
72
|
|
|
90
|
-
|
|
73
|
+
SendApduResult.Success(apdu = apduResponse)
|
|
91
74
|
}
|
|
92
|
-
} catch (e: SendApduTimeoutException) {
|
|
93
|
-
loggerService.log(
|
|
94
|
-
buildSimpleErrorLogInfo(
|
|
95
|
-
"AndroidUsbApduSender",
|
|
96
|
-
"timeout in send: $e"
|
|
97
|
-
)
|
|
98
|
-
)
|
|
99
|
-
SendApduResult.Failure(reason = SendApduFailureReason.AbortTimeout)
|
|
100
75
|
} catch (e: NoSuchElementException) {
|
|
101
|
-
loggerService.log(
|
|
102
|
-
buildSimpleErrorLogInfo(
|
|
103
|
-
"AndroidUsbApduSender",
|
|
104
|
-
"no endpoint found: $e"
|
|
105
|
-
)
|
|
106
|
-
)
|
|
107
76
|
SendApduResult.Failure(reason = SendApduFailureReason.NoUsbEndpointFound)
|
|
108
77
|
} catch (e: Exception) {
|
|
109
78
|
loggerService.log(buildSimpleErrorLogInfo("AndroidUsbApduSender", "error in send: $e"))
|
|
@@ -115,27 +84,22 @@ internal class AndroidUsbApduSender(
|
|
|
115
84
|
androidToUsbEndpoint: UsbEndpoint,
|
|
116
85
|
rawApdu: ByteArray,
|
|
117
86
|
) {
|
|
118
|
-
framerService.serialize(mtu = USB_MTU, channelId = generateChannelId(), rawApdu = rawApdu)
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
buffer,
|
|
124
|
-
apduFrame.size(),
|
|
125
|
-
USB_TIMEOUT
|
|
126
|
-
)
|
|
127
|
-
}
|
|
87
|
+
framerService.serialize(mtu = USB_MTU, channelId = generateChannelId(), rawApdu = rawApdu).forEach { apduFrame ->
|
|
88
|
+
val buffer = apduFrame.toByteArray()
|
|
89
|
+
Timber.i("APDU sent = ${buffer.toHexadecimalString()}")
|
|
90
|
+
usbConnection.bulkTransfer(androidToUsbEndpoint, buffer, apduFrame.size(), USB_TIMEOUT)
|
|
91
|
+
}
|
|
128
92
|
}
|
|
129
93
|
|
|
130
94
|
private fun receiveApdu(
|
|
131
95
|
usbConnection: UsbDeviceConnection,
|
|
132
96
|
usbToAndroidEndpoint: UsbEndpoint,
|
|
133
|
-
): ByteArray
|
|
134
|
-
|
|
97
|
+
): ByteArray =
|
|
98
|
+
if (!request.initialize(usbConnection, usbToAndroidEndpoint)) {
|
|
135
99
|
request.close()
|
|
136
100
|
byteArrayOf()
|
|
137
101
|
} else {
|
|
138
|
-
val frames = framerService.createApduFrames(mtu = USB_MTU, isUsbTransport = true)
|
|
102
|
+
val frames = framerService.createApduFrames(mtu = USB_MTU, isUsbTransport = true){
|
|
139
103
|
val buffer = ByteArray(USB_MTU)
|
|
140
104
|
val responseBuffer = ByteBuffer.allocate(USB_MTU)
|
|
141
105
|
|
|
@@ -143,7 +107,8 @@ internal class AndroidUsbApduSender(
|
|
|
143
107
|
if (!queuingResult) {
|
|
144
108
|
request.close()
|
|
145
109
|
byteArrayOf()
|
|
146
|
-
}
|
|
110
|
+
}
|
|
111
|
+
else{
|
|
147
112
|
usbConnection.requestWait()
|
|
148
113
|
responseBuffer.rewind()
|
|
149
114
|
responseBuffer.get(buffer, 0, responseBuffer.remaining())
|
|
@@ -152,7 +117,6 @@ internal class AndroidUsbApduSender(
|
|
|
152
117
|
}
|
|
153
118
|
framerService.deserialize(mtu = USB_MTU, frames)
|
|
154
119
|
}
|
|
155
|
-
}
|
|
156
120
|
|
|
157
121
|
private fun UsbInterface.firstEndpointOrThrow(predicate: (Int) -> Boolean): UsbEndpoint {
|
|
158
122
|
for (endp in 0..this.endpointCount) {
|
|
@@ -165,10 +129,5 @@ internal class AndroidUsbApduSender(
|
|
|
165
129
|
throw NoSuchElementException("No endpoint matching the predicate")
|
|
166
130
|
}
|
|
167
131
|
|
|
168
|
-
private fun generateChannelId(): ByteArray =
|
|
169
|
-
Random.nextInt(0, until = Int.MAX_VALUE).to2BytesArray()
|
|
170
|
-
|
|
171
|
-
private data object SendApduTimeoutException : Exception() {
|
|
172
|
-
private fun readResolve(): Any = SendApduTimeoutException
|
|
173
|
-
}
|
|
132
|
+
private fun generateChannelId(): ByteArray = Random.nextInt(0, until = Int.MAX_VALUE).to2BytesArray()
|
|
174
133
|
}
|
|
@@ -6,9 +6,8 @@
|
|
|
6
6
|
package com.ledger.devicesdk.shared.androidMainInternal.transport.deviceconnection
|
|
7
7
|
|
|
8
8
|
import com.ledger.devicesdk.shared.api.apdu.SendApduResult
|
|
9
|
-
import kotlin.time.Duration
|
|
10
9
|
|
|
11
10
|
internal interface DeviceApduSender<Dependencies> {
|
|
12
|
-
suspend fun send(apdu: ByteArray
|
|
11
|
+
suspend fun send(apdu: ByteArray): SendApduResult
|
|
13
12
|
val dependencies: Dependencies
|
|
14
13
|
}
|
|
@@ -24,9 +24,9 @@ internal class DeviceConnection<Dependencies>(
|
|
|
24
24
|
|
|
25
25
|
init {
|
|
26
26
|
stateMachine = DeviceConnectionStateMachine(
|
|
27
|
-
sendApduFn = {
|
|
27
|
+
sendApduFn = {
|
|
28
28
|
coroutineScope.launch {
|
|
29
|
-
val res = deviceApduSender.send(
|
|
29
|
+
val res = deviceApduSender.send(it)
|
|
30
30
|
handleApduResult(res)
|
|
31
31
|
}
|
|
32
32
|
},
|
|
@@ -66,13 +66,12 @@ internal class DeviceConnection<Dependencies>(
|
|
|
66
66
|
stateMachine.handleDeviceDisconnected()
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
-
public suspend fun requestSendApdu(apdu: ByteArray
|
|
69
|
+
public suspend fun requestSendApdu(apdu: ByteArray): SendApduResult =
|
|
70
70
|
suspendCoroutine { cont ->
|
|
71
71
|
stateMachine.requestSendApdu(
|
|
72
72
|
DeviceConnectionStateMachine.SendApduRequestContent(
|
|
73
73
|
apdu = apdu,
|
|
74
|
-
triggersDisconnection = apduTriggersDisconnection(apdu)
|
|
75
|
-
abortTimeoutDuration = abortTimeoutDuration,
|
|
74
|
+
triggersDisconnection = apduTriggersDisconnection(apdu),
|
|
76
75
|
resultCallback = cont::resume
|
|
77
76
|
)
|
|
78
77
|
)
|
|
@@ -4,6 +4,7 @@ import com.ledger.devicesdk.shared.api.apdu.SendApduFailureReason
|
|
|
4
4
|
import com.ledger.devicesdk.shared.api.apdu.SendApduResult
|
|
5
5
|
import com.ledger.devicesdk.shared.internal.service.logger.LoggerService
|
|
6
6
|
import com.ledger.devicesdk.shared.internal.service.logger.buildSimpleDebugLogInfo
|
|
7
|
+
import com.ledger.devicesdk.shared.internal.service.logger.buildSimpleInfoLogInfo
|
|
7
8
|
import kotlinx.coroutines.CoroutineDispatcher
|
|
8
9
|
import kotlinx.coroutines.CoroutineScope
|
|
9
10
|
import kotlinx.coroutines.Job
|
|
@@ -11,7 +12,7 @@ import kotlinx.coroutines.launch
|
|
|
11
12
|
import kotlin.time.Duration
|
|
12
13
|
|
|
13
14
|
internal class DeviceConnectionStateMachine(
|
|
14
|
-
private val sendApduFn: (apdu: ByteArray
|
|
15
|
+
private val sendApduFn: (apdu: ByteArray) -> Unit,
|
|
15
16
|
private val onTerminated: () -> Unit,
|
|
16
17
|
private val isFatalSendApduFailure: (SendApduResult.Failure) -> Boolean,
|
|
17
18
|
private val reconnectionTimeoutDuration: Duration,
|
|
@@ -28,7 +29,7 @@ internal class DeviceConnectionStateMachine(
|
|
|
28
29
|
when (newState) {
|
|
29
30
|
is State.Connected -> {}
|
|
30
31
|
is State.SendingApdu -> {
|
|
31
|
-
sendApduFn(newState.requestContent.apdu
|
|
32
|
+
sendApduFn(newState.requestContent.apdu)
|
|
32
33
|
}
|
|
33
34
|
|
|
34
35
|
is State.WaitingForReconnection -> {
|
|
@@ -41,16 +42,11 @@ internal class DeviceConnectionStateMachine(
|
|
|
41
42
|
}
|
|
42
43
|
}
|
|
43
44
|
this.state = newState
|
|
44
|
-
loggerService.log(buildSimpleDebugLogInfo("DeviceConnectionStateMachine", "-> New state: $newState"))
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
private fun handleEvent(event: Event) {
|
|
48
|
-
val
|
|
49
|
-
|
|
50
|
-
In state: $state
|
|
51
|
-
""".trimIndent()
|
|
52
|
-
loggerService.log(buildSimpleDebugLogInfo("DeviceConnectionStateMachine", logMessage))
|
|
53
|
-
when (val currentState = state) {
|
|
48
|
+
val currentState = state
|
|
49
|
+
when (currentState) {
|
|
54
50
|
is State.Connected -> {
|
|
55
51
|
when (event) {
|
|
56
52
|
is Event.SendApduRequested -> {
|
|
@@ -238,6 +234,13 @@ internal class DeviceConnectionStateMachine(
|
|
|
238
234
|
onError(Exception("Unhandled event: $event in state: $currentState"))
|
|
239
235
|
}
|
|
240
236
|
}
|
|
237
|
+
val logMessage = """
|
|
238
|
+
Received event:
|
|
239
|
+
In state: $currentState
|
|
240
|
+
-> Event: $event
|
|
241
|
+
-> New state: $state
|
|
242
|
+
""".trimIndent()
|
|
243
|
+
loggerService.log(buildSimpleDebugLogInfo("DeviceConnectionStateMachine", logMessage))
|
|
241
244
|
}
|
|
242
245
|
|
|
243
246
|
private var timeoutJob: Job? = null
|
|
@@ -277,7 +280,6 @@ internal class DeviceConnectionStateMachine(
|
|
|
277
280
|
data class SendApduRequestContent(
|
|
278
281
|
val apdu: ByteArray,
|
|
279
282
|
val triggersDisconnection: Boolean,
|
|
280
|
-
val abortTimeoutDuration: Duration,
|
|
281
283
|
val resultCallback: (SendApduResult) -> Unit
|
|
282
284
|
)
|
|
283
285
|
|
|
@@ -44,8 +44,4 @@ public sealed class SendApduFailureReason {
|
|
|
44
44
|
public data object DeviceDisconnected : SendApduFailureReason()
|
|
45
45
|
|
|
46
46
|
public data object Unknown : SendApduFailureReason()
|
|
47
|
-
|
|
48
|
-
public data object AbortTimeout : SendApduFailureReason()
|
|
49
|
-
|
|
50
|
-
public data object EmptyResponse : SendApduFailureReason()
|
|
51
47
|
}
|
|
@@ -9,5 +9,5 @@ internal data class InternalConnectedDevice(
|
|
|
9
9
|
val name: String,
|
|
10
10
|
val ledgerDevice: LedgerDevice,
|
|
11
11
|
val connectivity: ConnectivityType,
|
|
12
|
-
val sendApduFn: suspend (
|
|
12
|
+
val sendApduFn: suspend (ByteArray) -> SendApduResult,
|
|
13
13
|
)
|