@ledgerhq/device-transport-kit-react-native-hid 0.0.0-rn-hid-fixed-device-id-20250521115949 → 0.0.0-rn-hid-improvements-20250522101701
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 +4 -0
- package/android/src/main/kotlin/com/ledger/devicesdk/shared/androidMain/transport/usb/DefaultAndroidUsbTransport.kt +10 -9
- package/android/src/main/kotlin/com/ledger/devicesdk/shared/androidMain/transport/usb/connection/AndroidUsbApduSender.kt +33 -34
- package/android/src/main/kotlin/com/ledger/devicesdk/shared/internal/transport/Transport.kt +2 -1
- package/package.json +5 -5
|
@@ -172,6 +172,10 @@ class TransportHidModule(
|
|
|
172
172
|
@ReactMethod()
|
|
173
173
|
fun connectDevice(uid: String, promise: Promise) {
|
|
174
174
|
val device = discoveryDevices.firstOrNull { it.uid == uid }
|
|
175
|
+
if (device == null) {
|
|
176
|
+
promise.reject(Exception("[TransportHidModule][connectDevice] Device not found"))
|
|
177
|
+
return
|
|
178
|
+
}
|
|
175
179
|
|
|
176
180
|
coroutineScope.launch {
|
|
177
181
|
try {
|
|
@@ -17,7 +17,6 @@ import com.ledger.devicesdk.shared.androidMain.transport.usb.utils.toLedgerUsbDe
|
|
|
17
17
|
import com.ledger.devicesdk.shared.androidMain.transport.usb.utils.toScannedDevice
|
|
18
18
|
import com.ledger.devicesdk.shared.androidMain.transport.usb.utils.toUsbDevices
|
|
19
19
|
import com.ledger.devicesdk.shared.androidMainInternal.transport.deviceconnection.DeviceConnection
|
|
20
|
-
import com.ledger.devicesdk.shared.api.discovery.ConnectivityType
|
|
21
20
|
import com.ledger.devicesdk.shared.api.discovery.DiscoveryDevice
|
|
22
21
|
import com.ledger.devicesdk.shared.internal.connection.InternalConnectedDevice
|
|
23
22
|
import com.ledger.devicesdk.shared.internal.connection.InternalConnectionResult
|
|
@@ -124,9 +123,10 @@ internal class DefaultAndroidUsbTransport(
|
|
|
124
123
|
"Device disconnected (sessionId=${deviceConnection.sessionId})"
|
|
125
124
|
)
|
|
126
125
|
)
|
|
127
|
-
deviceConnection.handleDeviceDisconnected()
|
|
128
126
|
usbConnections.remove(key)
|
|
129
127
|
usbConnectionsPendingReconnection.add(deviceConnection)
|
|
128
|
+
deviceConnection.handleDeviceDisconnected()
|
|
129
|
+
(deviceConnection.getApduSender() as AndroidUsbApduSender).release()
|
|
130
130
|
}
|
|
131
131
|
}
|
|
132
132
|
}
|
|
@@ -274,7 +274,7 @@ internal class DefaultAndroidUsbTransport(
|
|
|
274
274
|
}
|
|
275
275
|
}
|
|
276
276
|
|
|
277
|
-
override suspend fun connect(discoveryDevice: DiscoveryDevice
|
|
277
|
+
override suspend fun connect(discoveryDevice: DiscoveryDevice): InternalConnectionResult {
|
|
278
278
|
|
|
279
279
|
loggerService.log(
|
|
280
280
|
buildSimpleDebugLogInfo(
|
|
@@ -286,13 +286,13 @@ internal class DefaultAndroidUsbTransport(
|
|
|
286
286
|
val usbDevices = usbManager.deviceList.values
|
|
287
287
|
|
|
288
288
|
var usbDevice: UsbDevice? =
|
|
289
|
-
usbDevices.firstOrNull { it.deviceId == discoveryDevice
|
|
289
|
+
usbDevices.firstOrNull { it.deviceId == discoveryDevice.uid.toInt() }
|
|
290
290
|
|
|
291
291
|
if (usbDevice == null) {
|
|
292
292
|
// This is useful for LL during the OS update
|
|
293
293
|
loggerService.log(buildSimpleDebugLogInfo("AndroidUsbTransport", "[connect] No device found with matching id, looking for device with matching model"))
|
|
294
294
|
usbDevice =
|
|
295
|
-
usbDevices.firstOrNull { it.toLedgerUsbDevice()
|
|
295
|
+
usbDevices.firstOrNull { it.toLedgerUsbDevice()?.ledgerDevice == discoveryDevice.ledgerDevice }
|
|
296
296
|
} else {
|
|
297
297
|
loggerService.log(buildSimpleDebugLogInfo("AndroidUsbTransport", "[connect] Found device with matching id"))
|
|
298
298
|
}
|
|
@@ -326,9 +326,10 @@ internal class DefaultAndroidUsbTransport(
|
|
|
326
326
|
val deviceConnection = DeviceConnection(
|
|
327
327
|
sessionId = sessionId,
|
|
328
328
|
deviceApduSender = apduSender,
|
|
329
|
-
isFatalSendApduFailure = { false },
|
|
329
|
+
isFatalSendApduFailure = { false },
|
|
330
330
|
reconnectionTimeoutDuration = 5.seconds,
|
|
331
331
|
onTerminated = {
|
|
332
|
+
(it.getApduSender() as AndroidUsbApduSender).release()
|
|
332
333
|
usbConnections.remove(sessionId)
|
|
333
334
|
usbConnectionsPendingReconnection.remove(it)
|
|
334
335
|
eventDispatcher.dispatch(TransportEvent.DeviceConnectionLost(sessionId))
|
|
@@ -340,9 +341,9 @@ internal class DefaultAndroidUsbTransport(
|
|
|
340
341
|
val connectedDevice =
|
|
341
342
|
InternalConnectedDevice(
|
|
342
343
|
sessionId,
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
344
|
+
discoveryDevice.name,
|
|
345
|
+
discoveryDevice.ledgerDevice,
|
|
346
|
+
discoveryDevice.connectivityType,
|
|
346
347
|
sendApduFn = { apdu: ByteArray, triggersDisconnection: Boolean, abortTimeoutDuration: Duration ->
|
|
347
348
|
deviceConnection.requestSendApdu(apdu, triggersDisconnection, abortTimeoutDuration)
|
|
348
349
|
}
|
|
@@ -22,7 +22,6 @@ import com.ledger.devicesdk.shared.internal.service.logger.buildSimpleErrorLogIn
|
|
|
22
22
|
import com.ledger.devicesdk.shared.internal.transport.framer.FramerService
|
|
23
23
|
import com.ledger.devicesdk.shared.internal.transport.framer.to2BytesArray
|
|
24
24
|
import kotlinx.coroutines.CoroutineDispatcher
|
|
25
|
-
import kotlinx.coroutines.cancel
|
|
26
25
|
import kotlinx.coroutines.delay
|
|
27
26
|
import kotlinx.coroutines.launch
|
|
28
27
|
import kotlinx.coroutines.withContext
|
|
@@ -36,7 +35,7 @@ private const val DEFAULT_USB_INTERFACE = 0
|
|
|
36
35
|
|
|
37
36
|
internal class AndroidUsbApduSender(
|
|
38
37
|
override val dependencies: Dependencies,
|
|
39
|
-
|
|
38
|
+
usbManager: UsbManager,
|
|
40
39
|
private val framerService: FramerService,
|
|
41
40
|
private val request: UsbRequest,
|
|
42
41
|
private val ioDispatcher: CoroutineDispatcher,
|
|
@@ -47,49 +46,48 @@ internal class AndroidUsbApduSender(
|
|
|
47
46
|
val ledgerUsbDevice: LedgerUsbDevice,
|
|
48
47
|
)
|
|
49
48
|
|
|
49
|
+
private val usbDevice = dependencies.usbDevice
|
|
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
|
+
|
|
50
64
|
override suspend fun send(apdu: ByteArray, abortTimeoutDuration: Duration): SendApduResult =
|
|
51
65
|
try {
|
|
52
|
-
val usbDevice = dependencies.usbDevice
|
|
53
66
|
withContext(context = ioDispatcher) {
|
|
54
|
-
val usbInterface = usbDevice.getInterface(DEFAULT_USB_INTERFACE)
|
|
55
|
-
val androidToUsbEndpoint =
|
|
56
|
-
usbInterface.firstEndpointOrThrow { it == UsbConstants.USB_DIR_OUT }
|
|
57
|
-
val usbToAndroidEndpoint =
|
|
58
|
-
usbInterface.firstEndpointOrThrow { it == UsbConstants.USB_DIR_IN }
|
|
59
|
-
val usbConnection = usbManager.openDevice(usbDevice)
|
|
60
|
-
.apply { claimInterface(usbInterface, true) }
|
|
61
67
|
|
|
62
68
|
val timeoutJob = launch {
|
|
63
69
|
delay(abortTimeoutDuration)
|
|
64
|
-
usbConnection.releaseInterface(usbInterface)
|
|
65
|
-
usbConnection.close()
|
|
66
70
|
throw SendApduTimeoutException
|
|
67
71
|
}
|
|
68
72
|
|
|
69
|
-
|
|
73
|
+
transmitApdu(
|
|
74
|
+
usbConnection = usbConnection,
|
|
75
|
+
androidToUsbEndpoint = androidToUsbEndpoint,
|
|
76
|
+
rawApdu = apdu,
|
|
77
|
+
)
|
|
70
78
|
|
|
71
|
-
|
|
79
|
+
val apduResponse =
|
|
80
|
+
receiveApdu(
|
|
72
81
|
usbConnection = usbConnection,
|
|
73
|
-
|
|
74
|
-
rawApdu = apdu,
|
|
82
|
+
usbToAndroidEndpoint = usbToAndroidEndpoint,
|
|
75
83
|
)
|
|
84
|
+
timeoutJob.cancel()
|
|
76
85
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
usbConnection = usbConnection,
|
|
80
|
-
usbToAndroidEndpoint = usbToAndroidEndpoint,
|
|
81
|
-
)
|
|
82
|
-
timeoutJob.cancel()
|
|
83
|
-
|
|
84
|
-
if (apduResponse.isEmpty()) {
|
|
85
|
-
return@withContext SendApduResult.Failure(reason = SendApduFailureReason.EmptyResponse)
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
return@withContext SendApduResult.Success(apdu = apduResponse)
|
|
89
|
-
} finally {
|
|
90
|
-
usbConnection.releaseInterface(usbInterface)
|
|
91
|
-
usbConnection.close()
|
|
86
|
+
if (apduResponse.isEmpty()) {
|
|
87
|
+
return@withContext SendApduResult.Failure(reason = SendApduFailureReason.EmptyResponse)
|
|
92
88
|
}
|
|
89
|
+
|
|
90
|
+
return@withContext SendApduResult.Success(apdu = apduResponse)
|
|
93
91
|
}
|
|
94
92
|
} catch (e: SendApduTimeoutException) {
|
|
95
93
|
loggerService.log(
|
|
@@ -132,8 +130,8 @@ internal class AndroidUsbApduSender(
|
|
|
132
130
|
private fun receiveApdu(
|
|
133
131
|
usbConnection: UsbDeviceConnection,
|
|
134
132
|
usbToAndroidEndpoint: UsbEndpoint,
|
|
135
|
-
): ByteArray
|
|
136
|
-
if (!request.initialize(usbConnection, usbToAndroidEndpoint)) {
|
|
133
|
+
): ByteArray {
|
|
134
|
+
return if (!request.initialize(usbConnection, usbToAndroidEndpoint)) {
|
|
137
135
|
request.close()
|
|
138
136
|
byteArrayOf()
|
|
139
137
|
} else {
|
|
@@ -154,6 +152,7 @@ internal class AndroidUsbApduSender(
|
|
|
154
152
|
}
|
|
155
153
|
framerService.deserialize(mtu = USB_MTU, frames)
|
|
156
154
|
}
|
|
155
|
+
}
|
|
157
156
|
|
|
158
157
|
private fun UsbInterface.firstEndpointOrThrow(predicate: (Int) -> Boolean): UsbEndpoint {
|
|
159
158
|
for (endp in 0..this.endpointCount) {
|
|
@@ -169,7 +168,7 @@ internal class AndroidUsbApduSender(
|
|
|
169
168
|
private fun generateChannelId(): ByteArray =
|
|
170
169
|
Random.nextInt(0, until = Int.MAX_VALUE).to2BytesArray()
|
|
171
170
|
|
|
172
|
-
private data object SendApduTimeoutException: Exception() {
|
|
171
|
+
private data object SendApduTimeoutException : Exception() {
|
|
173
172
|
private fun readResolve(): Any = SendApduTimeoutException
|
|
174
173
|
}
|
|
175
174
|
}
|
|
@@ -14,7 +14,8 @@ internal interface Transport {
|
|
|
14
14
|
|
|
15
15
|
fun stopScan()
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
// TODO change by Flow<ConnectedDeviceState> or add observe device connection for listening device state flow through?
|
|
18
|
+
suspend fun connect(discoveryDevice: DiscoveryDevice): InternalConnectionResult
|
|
18
19
|
|
|
19
20
|
suspend fun disconnect(deviceId: String)
|
|
20
21
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ledgerhq/device-transport-kit-react-native-hid",
|
|
3
|
-
"version": "0.0.0-rn-hid-
|
|
3
|
+
"version": "0.0.0-rn-hid-improvements-20250522101701",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"private": false,
|
|
6
6
|
"react-native": "src/index.ts",
|
|
@@ -35,17 +35,17 @@
|
|
|
35
35
|
"@types/uuid": "^10.0.0",
|
|
36
36
|
"react-native": "0.76.6",
|
|
37
37
|
"rxjs": "^7.8.2",
|
|
38
|
-
"@ledgerhq/device-management-kit": "0.0.0-rn-hid-
|
|
38
|
+
"@ledgerhq/device-management-kit": "0.0.0-rn-hid-improvements-20250522101701",
|
|
39
39
|
"@ledgerhq/eslint-config-dsdk": "0.0.2",
|
|
40
40
|
"@ledgerhq/ldmk-tool": "0.0.1",
|
|
41
41
|
"@ledgerhq/prettier-config-dsdk": "0.0.2",
|
|
42
|
-
"@ledgerhq/
|
|
43
|
-
"@ledgerhq/
|
|
42
|
+
"@ledgerhq/vitest-config-dmk": "0.0.0",
|
|
43
|
+
"@ledgerhq/tsconfig-dsdk": "1.0.1"
|
|
44
44
|
},
|
|
45
45
|
"peerDependencies": {
|
|
46
46
|
"react-native": ">0.74.1",
|
|
47
47
|
"rxjs": "^7.8.2",
|
|
48
|
-
"@ledgerhq/device-management-kit": "0.0.0-rn-hid-
|
|
48
|
+
"@ledgerhq/device-management-kit": "0.0.0-rn-hid-improvements-20250522101701"
|
|
49
49
|
},
|
|
50
50
|
"scripts": {
|
|
51
51
|
"prebuild": "rimraf lib",
|