@ledgerhq/device-transport-kit-react-native-hid 0.0.0-develop-20250903001157 → 0.0.0-e2e-speculos-20250904104129
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 +23 -85
- 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 +33 -103
- 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 +46 -189
- 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/cjs/package.json +1 -1
- 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/esm/package.json +1 -1
- 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 +6 -6
|
@@ -2,9 +2,9 @@ package com.ledger.devicesdk.shared.androidMainInternal.transport.deviceconnecti
|
|
|
2
2
|
|
|
3
3
|
import com.ledger.devicesdk.shared.api.apdu.SendApduFailureReason
|
|
4
4
|
import com.ledger.devicesdk.shared.api.apdu.SendApduResult
|
|
5
|
-
import com.ledger.devicesdk.shared.api.utils.fromHexStringToBytesOrThrow
|
|
6
5
|
import com.ledger.devicesdk.shared.internal.service.logger.LoggerService
|
|
7
6
|
import com.ledger.devicesdk.shared.internal.service.logger.buildSimpleDebugLogInfo
|
|
7
|
+
import com.ledger.devicesdk.shared.internal.service.logger.buildSimpleInfoLogInfo
|
|
8
8
|
import kotlinx.coroutines.CoroutineDispatcher
|
|
9
9
|
import kotlinx.coroutines.CoroutineScope
|
|
10
10
|
import kotlinx.coroutines.Job
|
|
@@ -12,7 +12,7 @@ import kotlinx.coroutines.launch
|
|
|
12
12
|
import kotlin.time.Duration
|
|
13
13
|
|
|
14
14
|
internal class DeviceConnectionStateMachine(
|
|
15
|
-
private val sendApduFn: (apdu: ByteArray
|
|
15
|
+
private val sendApduFn: (apdu: ByteArray) -> Unit,
|
|
16
16
|
private val onTerminated: () -> Unit,
|
|
17
17
|
private val isFatalSendApduFailure: (SendApduResult.Failure) -> Boolean,
|
|
18
18
|
private val reconnectionTimeoutDuration: Duration,
|
|
@@ -26,29 +26,10 @@ internal class DeviceConnectionStateMachine(
|
|
|
26
26
|
fun getState() = state
|
|
27
27
|
|
|
28
28
|
private fun pushState(newState: State) {
|
|
29
|
-
|
|
30
|
-
val currentState = state
|
|
31
|
-
|
|
32
|
-
/* STATE EXIT EFFECTS */
|
|
33
|
-
if (newState != currentState) {
|
|
34
|
-
when (currentState) {
|
|
35
|
-
is State.WaitingForDisconnection -> {
|
|
36
|
-
currentState.requestContent.resultCallback(currentState.result)
|
|
37
|
-
}
|
|
38
|
-
else -> {}
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
/* STATE ENTRY EFFECTS */
|
|
43
29
|
when (newState) {
|
|
44
30
|
is State.Connected -> {}
|
|
45
31
|
is State.SendingApdu -> {
|
|
46
|
-
sendApduFn(newState.requestContent.apdu
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
is State.WaitingForDisconnection -> {
|
|
50
|
-
// TODO: send getAppAndVersion
|
|
51
|
-
sendApduFn ("b0010000".fromHexStringToBytesOrThrow(), Duration.INFINITE)
|
|
32
|
+
sendApduFn(newState.requestContent.apdu)
|
|
52
33
|
}
|
|
53
34
|
|
|
54
35
|
is State.WaitingForReconnection -> {
|
|
@@ -61,16 +42,11 @@ internal class DeviceConnectionStateMachine(
|
|
|
61
42
|
}
|
|
62
43
|
}
|
|
63
44
|
this.state = newState
|
|
64
|
-
loggerService.log(buildSimpleDebugLogInfo("DeviceConnectionStateMachine", "-> New state: $newState"))
|
|
65
45
|
}
|
|
66
46
|
|
|
67
47
|
private fun handleEvent(event: Event) {
|
|
68
|
-
val
|
|
69
|
-
|
|
70
|
-
In state: $state
|
|
71
|
-
""".trimIndent()
|
|
72
|
-
loggerService.log(buildSimpleDebugLogInfo("DeviceConnectionStateMachine", logMessage))
|
|
73
|
-
when (val currentState = state) {
|
|
48
|
+
val currentState = state
|
|
49
|
+
when (currentState) {
|
|
74
50
|
is State.Connected -> {
|
|
75
51
|
when (event) {
|
|
76
52
|
is Event.SendApduRequested -> {
|
|
@@ -95,27 +71,31 @@ internal class DeviceConnectionStateMachine(
|
|
|
95
71
|
when (event) {
|
|
96
72
|
is Event.ApduResultReceived -> {
|
|
97
73
|
when (event.result) {
|
|
98
|
-
is SendApduResult.
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
if (isSuccessApdu(apdu) && currentState.requestContent.triggersDisconnection) {
|
|
104
|
-
pushState(State.WaitingForDisconnection(requestContent = currentState.requestContent, result = event.result))
|
|
74
|
+
is SendApduResult.Failure -> {
|
|
75
|
+
if (isFatalSendApduFailure(event.result)) {
|
|
76
|
+
pushState(State.Terminated)
|
|
105
77
|
} else {
|
|
106
78
|
pushState(State.Connected)
|
|
107
|
-
currentState.requestContent.resultCallback(event.result)
|
|
108
79
|
}
|
|
109
80
|
}
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
81
|
+
|
|
82
|
+
is SendApduResult.Success -> {
|
|
83
|
+
// check if last 2 bytes of APDU are [0x90,OxO0]
|
|
84
|
+
val apdu = event.result.apdu
|
|
85
|
+
val apduSize = apdu.size
|
|
86
|
+
val isSuccessApdu =
|
|
87
|
+
apdu.size >= 2 &&
|
|
88
|
+
apdu[apduSize - 2] == 0x90.toByte() &&
|
|
89
|
+
apdu[apduSize - 1] == 0x00.toByte()
|
|
90
|
+
|
|
91
|
+
if (isSuccessApdu && currentState.requestContent.triggersDisconnection) {
|
|
92
|
+
pushState(State.WaitingForReconnection)
|
|
113
93
|
} else {
|
|
114
94
|
pushState(State.Connected)
|
|
115
95
|
}
|
|
116
|
-
currentState.requestContent.resultCallback(event.result)
|
|
117
96
|
}
|
|
118
97
|
}
|
|
98
|
+
currentState.requestContent.resultCallback(event.result)
|
|
119
99
|
}
|
|
120
100
|
|
|
121
101
|
is Event.CloseConnectionRequested -> {
|
|
@@ -150,42 +130,6 @@ internal class DeviceConnectionStateMachine(
|
|
|
150
130
|
}
|
|
151
131
|
}
|
|
152
132
|
|
|
153
|
-
is State.WaitingForDisconnection -> {
|
|
154
|
-
when (event) {
|
|
155
|
-
is Event.ApduResultReceived -> {
|
|
156
|
-
when (event.result) {
|
|
157
|
-
is SendApduResult.Success -> {
|
|
158
|
-
val apdu = event.result.apdu
|
|
159
|
-
if (isSendApduBusyError(apdu)) {
|
|
160
|
-
pushState(state) // Loop on same state, will trigger a new send of GetAppAndVersion (entry effect)
|
|
161
|
-
} else {
|
|
162
|
-
pushState(State.Connected)
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
is SendApduResult.Failure -> {
|
|
166
|
-
pushState(State.WaitingForReconnection)
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
is Event.SendApduRequested -> {
|
|
171
|
-
event.requestContent.resultCallback(
|
|
172
|
-
SendApduResult.Failure(
|
|
173
|
-
SendApduFailureReason.DeviceBusy
|
|
174
|
-
)
|
|
175
|
-
)
|
|
176
|
-
}
|
|
177
|
-
is Event.DeviceDisconnected -> {
|
|
178
|
-
pushState(State.WaitingForReconnection)
|
|
179
|
-
}
|
|
180
|
-
is Event.CloseConnectionRequested -> {
|
|
181
|
-
pushState(State.Terminated)
|
|
182
|
-
}
|
|
183
|
-
else -> {
|
|
184
|
-
onError(Exception("Unhandled event: $event in state: $currentState"))
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
|
|
189
133
|
is State.WaitingForReconnection -> {
|
|
190
134
|
when (event) {
|
|
191
135
|
is Event.DeviceConnected -> {
|
|
@@ -290,24 +234,13 @@ internal class DeviceConnectionStateMachine(
|
|
|
290
234
|
onError(Exception("Unhandled event: $event in state: $currentState"))
|
|
291
235
|
}
|
|
292
236
|
}
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
private fun isSendApduBusyError(apdu: ByteArray): Boolean {
|
|
303
|
-
val apduSize = apdu.size
|
|
304
|
-
return apduSize >= 2 &&
|
|
305
|
-
apdu[apduSize - 2] == 0x66.toByte() &&
|
|
306
|
-
apdu[apduSize - 1] == 0x01.toByte()
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
private fun sendGetAppAndVersionApdu() {
|
|
310
|
-
sendApduFn("b0010000".fromHexStringToBytesOrThrow(), Duration.INFINITE)
|
|
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))
|
|
311
244
|
}
|
|
312
245
|
|
|
313
246
|
private var timeoutJob: Job? = null
|
|
@@ -324,30 +257,29 @@ internal class DeviceConnectionStateMachine(
|
|
|
324
257
|
timeoutJob = null
|
|
325
258
|
}
|
|
326
259
|
|
|
327
|
-
fun requestSendApdu(requestContent: SendApduRequestContent) {
|
|
260
|
+
public fun requestSendApdu(requestContent: SendApduRequestContent) {
|
|
328
261
|
handleEvent(Event.SendApduRequested(requestContent))
|
|
329
262
|
}
|
|
330
263
|
|
|
331
|
-
fun requestCloseConnection() {
|
|
264
|
+
public fun requestCloseConnection() {
|
|
332
265
|
handleEvent(Event.CloseConnectionRequested)
|
|
333
266
|
}
|
|
334
267
|
|
|
335
|
-
fun handleApduResult(result: SendApduResult) {
|
|
268
|
+
public fun handleApduResult(result: SendApduResult) {
|
|
336
269
|
handleEvent(Event.ApduResultReceived(result))
|
|
337
270
|
}
|
|
338
271
|
|
|
339
|
-
fun handleDeviceConnected() {
|
|
272
|
+
public fun handleDeviceConnected() {
|
|
340
273
|
handleEvent(Event.DeviceConnected)
|
|
341
274
|
}
|
|
342
275
|
|
|
343
|
-
fun handleDeviceDisconnected() {
|
|
276
|
+
public fun handleDeviceDisconnected() {
|
|
344
277
|
handleEvent(Event.DeviceDisconnected)
|
|
345
278
|
}
|
|
346
279
|
|
|
347
280
|
data class SendApduRequestContent(
|
|
348
281
|
val apdu: ByteArray,
|
|
349
282
|
val triggersDisconnection: Boolean,
|
|
350
|
-
val abortTimeoutDuration: Duration,
|
|
351
283
|
val resultCallback: (SendApduResult) -> Unit
|
|
352
284
|
)
|
|
353
285
|
|
|
@@ -374,8 +306,6 @@ internal class DeviceConnectionStateMachine(
|
|
|
374
306
|
|
|
375
307
|
data object WaitingForReconnection : State()
|
|
376
308
|
|
|
377
|
-
data class WaitingForDisconnection(val requestContent: SendApduRequestContent, val result: SendApduResult): State()
|
|
378
|
-
|
|
379
309
|
data class WaitingForReconnectionWithQueuedApdu(val requestContent: SendApduRequestContent) :
|
|
380
310
|
State()
|
|
381
311
|
|
|
@@ -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
|
)
|