@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.
Files changed (57) hide show
  1. package/android/build.gradle +0 -9
  2. package/android/src/main/kotlin/com/ledger/androidtransporthid/TransportHidModule.kt +12 -3
  3. package/android/src/main/kotlin/com/ledger/androidtransporthid/bridge/serialization.kt +2 -0
  4. package/android/src/main/kotlin/com/ledger/devicesdk/shared/androidMain/transport/usb/DefaultAndroidUsbTransport.kt +85 -23
  5. package/android/src/main/kotlin/com/ledger/devicesdk/shared/androidMain/transport/usb/connection/AndroidUsbApduSender.kt +70 -29
  6. package/android/src/main/kotlin/com/ledger/devicesdk/shared/androidMain/transport/usb/utils/UsbDeviceMapper.kt +3 -0
  7. package/android/src/main/kotlin/com/ledger/devicesdk/shared/androidMainInternal/transport/deviceconnection/DeviceApduSender.kt +2 -1
  8. package/android/src/main/kotlin/com/ledger/devicesdk/shared/androidMainInternal/transport/deviceconnection/DeviceConnection.kt +5 -4
  9. package/android/src/main/kotlin/com/ledger/devicesdk/shared/androidMainInternal/transport/deviceconnection/DeviceConnectionStateMachine.kt +103 -33
  10. package/android/src/main/kotlin/com/ledger/devicesdk/shared/api/apdu/SendApduResult.kt +4 -0
  11. package/android/src/main/kotlin/com/ledger/devicesdk/shared/api/device/LedgerDevice.kt +16 -0
  12. package/android/src/main/kotlin/com/ledger/devicesdk/shared/internal/connection/InternalConnectedDevice.kt +1 -1
  13. package/android/src/test/kotlin/com/ledger/devicesdk/shared/androidMainInternal/transport/deviceconnection/DeviceConnectionStateMachineTest.kt +189 -46
  14. package/android/src/test/kotlin/com/ledger/devicesdk/shared/androidMainInternal/transport/deviceconnection/DeviceConnectionTest.kt +9 -5
  15. package/lib/cjs/api/bridge/DefaultNativeModuleWrapper.js +1 -1
  16. package/lib/cjs/api/bridge/DefaultNativeModuleWrapper.js.map +3 -3
  17. package/lib/cjs/api/bridge/mapper.js +1 -1
  18. package/lib/cjs/api/bridge/mapper.js.map +2 -2
  19. package/lib/cjs/api/bridge/mapper.test.js +1 -1
  20. package/lib/cjs/api/bridge/mapper.test.js.map +2 -2
  21. package/lib/cjs/api/bridge/types.js +1 -1
  22. package/lib/cjs/api/bridge/types.js.map +1 -1
  23. package/lib/cjs/api/transport/Errors.js +1 -1
  24. package/lib/cjs/api/transport/Errors.js.map +3 -3
  25. package/lib/cjs/api/transport/NativeModuleWrapper.js +1 -1
  26. package/lib/cjs/api/transport/NativeModuleWrapper.js.map +1 -1
  27. package/lib/cjs/api/transport/RNHidTransport.js +1 -1
  28. package/lib/cjs/api/transport/RNHidTransport.js.map +3 -3
  29. package/lib/cjs/api/transport/RNHidTransport.test.js +1 -1
  30. package/lib/cjs/api/transport/RNHidTransport.test.js.map +2 -2
  31. package/lib/cjs/package.json +11 -11
  32. package/lib/esm/api/bridge/DefaultNativeModuleWrapper.js +1 -1
  33. package/lib/esm/api/bridge/DefaultNativeModuleWrapper.js.map +3 -3
  34. package/lib/esm/api/bridge/mapper.js +1 -1
  35. package/lib/esm/api/bridge/mapper.js.map +3 -3
  36. package/lib/esm/api/bridge/mapper.test.js +1 -1
  37. package/lib/esm/api/bridge/mapper.test.js.map +3 -3
  38. package/lib/esm/api/bridge/types.js.map +1 -1
  39. package/lib/esm/api/transport/Errors.js +1 -1
  40. package/lib/esm/api/transport/Errors.js.map +3 -3
  41. package/lib/esm/api/transport/RNHidTransport.js +1 -1
  42. package/lib/esm/api/transport/RNHidTransport.js.map +3 -3
  43. package/lib/esm/api/transport/RNHidTransport.test.js +1 -1
  44. package/lib/esm/api/transport/RNHidTransport.test.js.map +3 -3
  45. package/lib/esm/package.json +11 -11
  46. package/lib/types/api/bridge/DefaultNativeModuleWrapper.d.ts +1 -1
  47. package/lib/types/api/bridge/DefaultNativeModuleWrapper.d.ts.map +1 -1
  48. package/lib/types/api/bridge/mapper.d.ts.map +1 -1
  49. package/lib/types/api/bridge/types.d.ts +2 -2
  50. package/lib/types/api/bridge/types.d.ts.map +1 -1
  51. package/lib/types/api/transport/Errors.d.ts +1 -1
  52. package/lib/types/api/transport/Errors.d.ts.map +1 -1
  53. package/lib/types/api/transport/NativeModuleWrapper.d.ts +1 -1
  54. package/lib/types/api/transport/NativeModuleWrapper.d.ts.map +1 -1
  55. package/lib/types/api/transport/RNHidTransport.d.ts.map +1 -1
  56. package/lib/types/tsconfig.prod.tsbuildinfo +1 -1
  57. 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 currentState = state
49
- when (currentState) {
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.Failure -> {
75
- if (isFatalSendApduFailure(event.result)) {
76
- pushState(State.Terminated)
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
- 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)
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
- val logMessage = """
238
- Received event:
239
- In state: $currentState
240
- -> Event: $event
241
- -> New state: $state
242
- """.trimIndent()
243
- loggerService.log(buildSimpleDebugLogInfo("DeviceConnectionStateMachine", logMessage))
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
- public fun requestSendApdu(requestContent: SendApduRequestContent) {
327
+ fun requestSendApdu(requestContent: SendApduRequestContent) {
261
328
  handleEvent(Event.SendApduRequested(requestContent))
262
329
  }
263
330
 
264
- public fun requestCloseConnection() {
331
+ fun requestCloseConnection() {
265
332
  handleEvent(Event.CloseConnectionRequested)
266
333
  }
267
334
 
268
- public fun handleApduResult(result: SendApduResult) {
335
+ fun handleApduResult(result: SendApduResult) {
269
336
  handleEvent(Event.ApduResultReceived(result))
270
337
  }
271
338
 
272
- public fun handleDeviceConnected() {
339
+ fun handleDeviceConnected() {
273
340
  handleEvent(Event.DeviceConnected)
274
341
  }
275
342
 
276
- public fun handleDeviceDisconnected() {
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
  )