@ledgerhq/device-transport-kit-react-native-hid 0.0.0-try-to-fix-20250429171448 → 0.0.0-v0-transaction-unfunded-20250918091119
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/build.gradle +0 -9
- package/android/src/main/kotlin/com/ledger/androidtransporthid/TransportHidModule.kt +12 -3
- package/android/src/main/kotlin/com/ledger/androidtransporthid/bridge/serialization.kt +2 -0
- package/android/src/main/kotlin/com/ledger/devicesdk/shared/androidMain/transport/usb/DefaultAndroidUsbTransport.kt +85 -23
- package/android/src/main/kotlin/com/ledger/devicesdk/shared/androidMain/transport/usb/connection/AndroidUsbApduSender.kt +70 -29
- package/android/src/main/kotlin/com/ledger/devicesdk/shared/androidMain/transport/usb/utils/UsbDeviceMapper.kt +3 -0
- package/android/src/main/kotlin/com/ledger/devicesdk/shared/androidMainInternal/transport/deviceconnection/DeviceApduSender.kt +2 -1
- package/android/src/main/kotlin/com/ledger/devicesdk/shared/androidMainInternal/transport/deviceconnection/DeviceConnection.kt +5 -4
- package/android/src/main/kotlin/com/ledger/devicesdk/shared/androidMainInternal/transport/deviceconnection/DeviceConnectionStateMachine.kt +103 -33
- package/android/src/main/kotlin/com/ledger/devicesdk/shared/api/apdu/SendApduResult.kt +4 -0
- package/android/src/main/kotlin/com/ledger/devicesdk/shared/api/device/LedgerDevice.kt +16 -0
- 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 +189 -46
- package/android/src/test/kotlin/com/ledger/devicesdk/shared/androidMainInternal/transport/deviceconnection/DeviceConnectionTest.kt +9 -5
- 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 +11 -11
- 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 +11 -11
- 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 +15 -15
|
@@ -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
|
|
5
6
|
import com.ledger.devicesdk.shared.internal.service.logger.LoggerService
|
|
6
7
|
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) -> Unit,
|
|
15
|
+
private val sendApduFn: (apdu: ByteArray, abortTimeoutDuration: Duration) -> Unit,
|
|
16
16
|
private val onTerminated: () -> Unit,
|
|
17
17
|
private val isFatalSendApduFailure: (SendApduResult.Failure) -> Boolean,
|
|
18
18
|
private val reconnectionTimeoutDuration: Duration,
|
|
@@ -26,10 +26,29 @@ 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 */
|
|
29
43
|
when (newState) {
|
|
30
44
|
is State.Connected -> {}
|
|
31
45
|
is State.SendingApdu -> {
|
|
32
|
-
sendApduFn(newState.requestContent.apdu)
|
|
46
|
+
sendApduFn(newState.requestContent.apdu, newState.requestContent.abortTimeoutDuration)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
is State.WaitingForDisconnection -> {
|
|
50
|
+
// TODO: send getAppAndVersion
|
|
51
|
+
sendApduFn ("b0010000".fromHexStringToBytesOrThrow(), Duration.INFINITE)
|
|
33
52
|
}
|
|
34
53
|
|
|
35
54
|
is State.WaitingForReconnection -> {
|
|
@@ -42,11 +61,16 @@ internal class DeviceConnectionStateMachine(
|
|
|
42
61
|
}
|
|
43
62
|
}
|
|
44
63
|
this.state = newState
|
|
64
|
+
loggerService.log(buildSimpleDebugLogInfo("DeviceConnectionStateMachine", "-> New state: $newState"))
|
|
45
65
|
}
|
|
46
66
|
|
|
47
67
|
private fun handleEvent(event: Event) {
|
|
48
|
-
val
|
|
49
|
-
|
|
68
|
+
val logMessage = """
|
|
69
|
+
-> Event received: $event
|
|
70
|
+
In state: $state
|
|
71
|
+
""".trimIndent()
|
|
72
|
+
loggerService.log(buildSimpleDebugLogInfo("DeviceConnectionStateMachine", logMessage))
|
|
73
|
+
when (val currentState = state) {
|
|
50
74
|
is State.Connected -> {
|
|
51
75
|
when (event) {
|
|
52
76
|
is Event.SendApduRequested -> {
|
|
@@ -71,31 +95,27 @@ internal class DeviceConnectionStateMachine(
|
|
|
71
95
|
when (event) {
|
|
72
96
|
is Event.ApduResultReceived -> {
|
|
73
97
|
when (event.result) {
|
|
74
|
-
is SendApduResult.
|
|
75
|
-
if
|
|
76
|
-
|
|
98
|
+
is SendApduResult.Success -> {
|
|
99
|
+
// check if last 2 bytes of APDU are [0x90,OxO0]
|
|
100
|
+
val apdu = event.result.apdu
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
if (isSuccessApdu(apdu) && currentState.requestContent.triggersDisconnection) {
|
|
104
|
+
pushState(State.WaitingForDisconnection(requestContent = currentState.requestContent, result = event.result))
|
|
77
105
|
} else {
|
|
78
106
|
pushState(State.Connected)
|
|
107
|
+
currentState.requestContent.resultCallback(event.result)
|
|
79
108
|
}
|
|
80
109
|
}
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
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)
|
|
110
|
+
is SendApduResult.Failure -> {
|
|
111
|
+
if (isFatalSendApduFailure(event.result)) {
|
|
112
|
+
pushState(State.Terminated)
|
|
93
113
|
} else {
|
|
94
114
|
pushState(State.Connected)
|
|
95
115
|
}
|
|
116
|
+
currentState.requestContent.resultCallback(event.result)
|
|
96
117
|
}
|
|
97
118
|
}
|
|
98
|
-
currentState.requestContent.resultCallback(event.result)
|
|
99
119
|
}
|
|
100
120
|
|
|
101
121
|
is Event.CloseConnectionRequested -> {
|
|
@@ -130,6 +150,42 @@ internal class DeviceConnectionStateMachine(
|
|
|
130
150
|
}
|
|
131
151
|
}
|
|
132
152
|
|
|
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
|
+
|
|
133
189
|
is State.WaitingForReconnection -> {
|
|
134
190
|
when (event) {
|
|
135
191
|
is Event.DeviceConnected -> {
|
|
@@ -234,13 +290,24 @@ internal class DeviceConnectionStateMachine(
|
|
|
234
290
|
onError(Exception("Unhandled event: $event in state: $currentState"))
|
|
235
291
|
}
|
|
236
292
|
}
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
private fun isSuccessApdu(apdu: ByteArray): Boolean {
|
|
296
|
+
val apduSize = apdu.size
|
|
297
|
+
return apduSize >= 2 &&
|
|
298
|
+
apdu[apduSize - 2] == 0x90.toByte() &&
|
|
299
|
+
apdu[apduSize - 1] == 0x00.toByte()
|
|
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)
|
|
244
311
|
}
|
|
245
312
|
|
|
246
313
|
private var timeoutJob: Job? = null
|
|
@@ -257,29 +324,30 @@ internal class DeviceConnectionStateMachine(
|
|
|
257
324
|
timeoutJob = null
|
|
258
325
|
}
|
|
259
326
|
|
|
260
|
-
|
|
327
|
+
fun requestSendApdu(requestContent: SendApduRequestContent) {
|
|
261
328
|
handleEvent(Event.SendApduRequested(requestContent))
|
|
262
329
|
}
|
|
263
330
|
|
|
264
|
-
|
|
331
|
+
fun requestCloseConnection() {
|
|
265
332
|
handleEvent(Event.CloseConnectionRequested)
|
|
266
333
|
}
|
|
267
334
|
|
|
268
|
-
|
|
335
|
+
fun handleApduResult(result: SendApduResult) {
|
|
269
336
|
handleEvent(Event.ApduResultReceived(result))
|
|
270
337
|
}
|
|
271
338
|
|
|
272
|
-
|
|
339
|
+
fun handleDeviceConnected() {
|
|
273
340
|
handleEvent(Event.DeviceConnected)
|
|
274
341
|
}
|
|
275
342
|
|
|
276
|
-
|
|
343
|
+
fun handleDeviceDisconnected() {
|
|
277
344
|
handleEvent(Event.DeviceDisconnected)
|
|
278
345
|
}
|
|
279
346
|
|
|
280
347
|
data class SendApduRequestContent(
|
|
281
348
|
val apdu: ByteArray,
|
|
282
349
|
val triggersDisconnection: Boolean,
|
|
350
|
+
val abortTimeoutDuration: Duration,
|
|
283
351
|
val resultCallback: (SendApduResult) -> Unit
|
|
284
352
|
)
|
|
285
353
|
|
|
@@ -306,6 +374,8 @@ internal class DeviceConnectionStateMachine(
|
|
|
306
374
|
|
|
307
375
|
data object WaitingForReconnection : State()
|
|
308
376
|
|
|
377
|
+
data class WaitingForDisconnection(val requestContent: SendApduRequestContent, val result: SendApduResult): State()
|
|
378
|
+
|
|
309
379
|
data class WaitingForReconnectionWithQueuedApdu(val requestContent: SendApduRequestContent) :
|
|
310
380
|
State()
|
|
311
381
|
|
|
@@ -44,4 +44,8 @@ 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()
|
|
47
51
|
}
|
|
@@ -5,6 +5,21 @@ public sealed class LedgerDevice(
|
|
|
5
5
|
public val usbInfo: UsbInfo,
|
|
6
6
|
public val bleInformation: BleInformation? = null,
|
|
7
7
|
) {
|
|
8
|
+
public data object Apex :
|
|
9
|
+
LedgerDevice(
|
|
10
|
+
name = "Ledger Apex",
|
|
11
|
+
usbInfo = UsbInfo(LEDGER_USB_VENDOR_ID, "0x80", "0x0008"),
|
|
12
|
+
bleInformation =
|
|
13
|
+
BleInformation(
|
|
14
|
+
serviceUuid = "13d63400-2c97-6004-0000-4c6564676572",
|
|
15
|
+
notifyCharacteristicUuid =
|
|
16
|
+
"13d63400-2c97-6004-0001-4c6564676572",
|
|
17
|
+
writeWithResponseCharacteristicUuid =
|
|
18
|
+
"13d63400-2c97-6004-0002-4c6564676572",
|
|
19
|
+
writeWithoutResponseCharacteristicUuid =
|
|
20
|
+
"13d63400-2c97-6004-0003-4c6564676572",
|
|
21
|
+
),
|
|
22
|
+
)
|
|
8
23
|
public data object Flex :
|
|
9
24
|
LedgerDevice(
|
|
10
25
|
name = "Ledger Flex",
|
|
@@ -77,6 +92,7 @@ public sealed class LedgerDevice(
|
|
|
77
92
|
add(NanoX)
|
|
78
93
|
add(NanoSPlus)
|
|
79
94
|
add(NanoS)
|
|
95
|
+
add(Apex)
|
|
80
96
|
}
|
|
81
97
|
|
|
82
98
|
public fun getAllDevices(): List<LedgerDevice> {
|
|
@@ -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 (ByteArray) -> SendApduResult,
|
|
12
|
+
val sendApduFn: suspend (apdu: ByteArray, triggersDisconnection: Boolean, abortTimeoutDuration: kotlin.time.Duration) -> SendApduResult,
|
|
13
13
|
)
|