@ledgerhq/device-transport-kit-react-native-hid 0.0.0-rn-hid-20250221112139 → 0.0.0-rnhid-transport-20250411151739
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/README.md +1 -1
- package/android/build.gradle +101 -0
- package/android/gradle/wrapper/gradle-wrapper.jar +0 -0
- package/android/gradle/wrapper/gradle-wrapper.properties +7 -0
- package/android/gradle.properties +1 -0
- package/android/gradlew +252 -0
- package/android/gradlew.bat +94 -0
- package/android/src/main/AndroidManifest.xml +3 -0
- package/android/src/main/kotlin/com/ledger/androidtransporthid/BridgeEvents.kt +42 -0
- package/android/src/main/kotlin/com/ledger/androidtransporthid/TransportHidModule.kt +241 -0
- package/android/src/main/kotlin/com/ledger/androidtransporthid/TransportHidPackage.kt +25 -0
- package/android/src/main/kotlin/com/ledger/androidtransporthid/bridge/serialization.kt +124 -0
- package/android/src/main/kotlin/com/ledger/devicesdk/shared/androidMain/transport/usb/AndroidUsbTransport.kt +16 -0
- package/android/src/main/kotlin/com/ledger/devicesdk/shared/androidMain/transport/usb/DefaultAndroidUsbTransport.kt +298 -0
- package/android/src/main/kotlin/com/ledger/devicesdk/shared/androidMain/transport/usb/UsbPermissionRequester.kt +18 -0
- package/android/src/main/kotlin/com/ledger/devicesdk/shared/androidMain/transport/usb/connection/AndroidUsbApduSender.kt +133 -0
- package/android/src/main/kotlin/com/ledger/devicesdk/shared/androidMain/transport/usb/controller/UsbAttachedReceiverController.kt +59 -0
- package/android/src/main/kotlin/com/ledger/devicesdk/shared/androidMain/transport/usb/controller/UsbDetachedReceiverController.kt +58 -0
- package/android/src/main/kotlin/com/ledger/devicesdk/shared/androidMain/transport/usb/controller/UsbPermissionReceiver.kt +92 -0
- package/android/src/main/kotlin/com/ledger/devicesdk/shared/androidMain/transport/usb/model/LedgerUsbDevice.kt +16 -0
- package/android/src/main/kotlin/com/ledger/devicesdk/shared/androidMain/transport/usb/model/ProductId.kt +11 -0
- package/android/src/main/kotlin/com/ledger/devicesdk/shared/androidMain/transport/usb/model/UsbPermissionEvent.kt +14 -0
- package/android/src/main/kotlin/com/ledger/devicesdk/shared/androidMain/transport/usb/model/UsbState.kt +16 -0
- package/android/src/main/kotlin/com/ledger/devicesdk/shared/androidMain/transport/usb/model/VendorId.kt +11 -0
- package/android/src/main/kotlin/com/ledger/devicesdk/shared/androidMain/transport/usb/utils/UsbDeviceMapper.kt +46 -0
- package/android/src/main/kotlin/com/ledger/devicesdk/shared/androidMain/transport/usb/utils/UsbMapper.kt +56 -0
- package/android/src/main/kotlin/com/ledger/devicesdk/shared/androidMainInternal/transport/UsbConst.android.kt +8 -0
- package/android/src/main/kotlin/com/ledger/devicesdk/shared/androidMainInternal/transport/deviceconnection/DeviceApduSender.kt +13 -0
- package/android/src/main/kotlin/com/ledger/devicesdk/shared/androidMainInternal/transport/deviceconnection/DeviceConnection.kt +95 -0
- package/android/src/main/kotlin/com/ledger/devicesdk/shared/androidMainInternal/transport/deviceconnection/DeviceConnectionStateMachine.kt +314 -0
- package/android/src/main/kotlin/com/ledger/devicesdk/shared/api/apdu/Apdu.kt +44 -0
- package/android/src/main/kotlin/com/ledger/devicesdk/shared/api/apdu/ApduBuilder.kt +88 -0
- package/android/src/main/kotlin/com/ledger/devicesdk/shared/api/apdu/ApduParser.kt +37 -0
- package/android/src/main/kotlin/com/ledger/devicesdk/shared/api/apdu/ApduUtils.kt +37 -0
- package/android/src/main/kotlin/com/ledger/devicesdk/shared/api/apdu/SendApduResult.kt +47 -0
- package/android/src/main/kotlin/com/ledger/devicesdk/shared/api/connection/ConnectedDevice.kt +25 -0
- package/android/src/main/kotlin/com/ledger/devicesdk/shared/api/connection/ConnectionResult.kt +45 -0
- package/android/src/main/kotlin/com/ledger/devicesdk/shared/api/device/BleInformation.kt +8 -0
- package/android/src/main/kotlin/com/ledger/devicesdk/shared/api/device/LedgerDevice.kt +89 -0
- package/android/src/main/kotlin/com/ledger/devicesdk/shared/api/device/UsbInfo.kt +7 -0
- package/android/src/main/kotlin/com/ledger/devicesdk/shared/api/disconnection/DisconnectionResult.kt +10 -0
- package/android/src/main/kotlin/com/ledger/devicesdk/shared/api/discovery/ConnectivityType.kt +10 -0
- package/android/src/main/kotlin/com/ledger/devicesdk/shared/api/discovery/DiscoveryDevice.kt +18 -0
- package/android/src/main/kotlin/com/ledger/devicesdk/shared/api/discovery/DiscoveryResult.kt +28 -0
- package/android/src/main/kotlin/com/ledger/devicesdk/shared/api/utils/ByteArrayExtension.kt +116 -0
- package/android/src/main/kotlin/com/ledger/devicesdk/shared/api/utils/StringExtension.kt +21 -0
- package/android/src/main/kotlin/com/ledger/devicesdk/shared/internal/connection/InternalConnectedDevice.kt +13 -0
- package/android/src/main/kotlin/com/ledger/devicesdk/shared/internal/connection/InternalConnectionResult.kt +41 -0
- package/android/src/main/kotlin/com/ledger/devicesdk/shared/internal/coroutine/SDKScope.kt +25 -0
- package/android/src/main/kotlin/com/ledger/devicesdk/shared/internal/coroutine/SDKScopeHandler.kt +18 -0
- package/android/src/main/kotlin/com/ledger/devicesdk/shared/internal/event/SdkEventDispatcher.kt +19 -0
- package/android/src/main/kotlin/com/ledger/devicesdk/shared/internal/service/logger/DisableLoggerService.kt +12 -0
- package/android/src/main/kotlin/com/ledger/devicesdk/shared/internal/service/logger/LogInfo.kt +52 -0
- package/android/src/main/kotlin/com/ledger/devicesdk/shared/internal/service/logger/LogLevel.kt +13 -0
- package/android/src/main/kotlin/com/ledger/devicesdk/shared/internal/service/logger/LoggerService.kt +10 -0
- package/android/src/main/kotlin/com/ledger/devicesdk/shared/internal/transport/Transport.kt +21 -0
- package/android/src/main/kotlin/com/ledger/devicesdk/shared/internal/transport/TransportEvent.kt +18 -0
- package/android/src/main/kotlin/com/ledger/devicesdk/shared/internal/transport/framer/FramerService.kt +210 -0
- package/android/src/main/kotlin/com/ledger/devicesdk/shared/internal/transport/framer/FramerUtils.kt +35 -0
- package/android/src/main/kotlin/com/ledger/devicesdk/shared/internal/transport/framer/model/ApduConst.kt +9 -0
- package/android/src/main/kotlin/com/ledger/devicesdk/shared/internal/transport/framer/model/ApduFrame.kt +66 -0
- package/android/src/main/kotlin/com/ledger/devicesdk/shared/internal/transport/framer/model/ApduFramerHeader.kt +74 -0
- package/android/src/main/kotlin/com/ledger/devicesdk/shared/internal/transport/framer/model/FramerConst.kt +14 -0
- package/android/src/main/kotlin/com/ledger/devicesdk/shared/internal/transport/utils/ByteExtension.kt +21 -0
- package/android/src/main/kotlin/com/ledger/devicesdk/shared/internal/transport/utils/InternalByteArrayExtension.kt +18 -0
- package/android/src/main/kotlin/com/ledger/devicesdk/shared/internal/utils/Controller.kt +12 -0
- package/android/src/test/kotlin/com/ledger/devicesdk/shared/androidMainInternal/transport/deviceconnection/DeviceConnectionStateMachineTest.kt +713 -0
- package/android/src/test/kotlin/com/ledger/devicesdk/shared/androidMainInternal/transport/deviceconnection/DeviceConnectionTest.kt +218 -0
- package/lib/cjs/package.json +2 -1
- package/lib/esm/package.json +2 -1
- package/lib/types/tsconfig.prod.tsbuildinfo +1 -1
- package/package.json +6 -5
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
package com.ledger.devicesdk.shared.androidMainInternal.transport.deviceconnection
|
|
2
|
+
|
|
3
|
+
import com.ledger.devicesdk.shared.api.apdu.SendApduResult
|
|
4
|
+
import com.ledger.devicesdk.shared.internal.service.logger.LogInfo
|
|
5
|
+
import com.ledger.devicesdk.shared.internal.service.logger.LoggerService
|
|
6
|
+
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
|
7
|
+
import kotlinx.coroutines.async
|
|
8
|
+
import kotlinx.coroutines.test.StandardTestDispatcher
|
|
9
|
+
import kotlinx.coroutines.test.advanceTimeBy
|
|
10
|
+
import kotlinx.coroutines.test.runTest
|
|
11
|
+
import kotlin.time.Duration.Companion.seconds
|
|
12
|
+
import kotlin.test.Test
|
|
13
|
+
import org.junit.Assert.*
|
|
14
|
+
import kotlin.time.Duration.Companion.milliseconds
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Those tests focus mainly on calling all public methods, and ensuring that the correct apduSender
|
|
18
|
+
* is used after reconnections.
|
|
19
|
+
* For full coverage of the possible scenarios, check the unit tests of DeviceConnectionStateMachine.
|
|
20
|
+
*/
|
|
21
|
+
class DeviceConnectionTest {
|
|
22
|
+
@Test
|
|
23
|
+
fun `GIVEN a device connection with an APDU sender WHEN requestSendApdu is called THEN the apduSender is used and the result is obtained`() = runTest {
|
|
24
|
+
val apduSender = MockedApduSender()
|
|
25
|
+
|
|
26
|
+
val deviceConnection = DeviceConnection(
|
|
27
|
+
sessionId = "mockId",
|
|
28
|
+
deviceApduSender = apduSender,
|
|
29
|
+
onTerminated = { },
|
|
30
|
+
isFatalSendApduFailure = { false },
|
|
31
|
+
reconnectionTimeoutDuration = 5.seconds,
|
|
32
|
+
loggerService = FakeLoggerService(),
|
|
33
|
+
coroutineDispatcher = StandardTestDispatcher(testScheduler),
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
// Request sending an APDU
|
|
37
|
+
deviceConnection.requestSendApdu(mockedApdu)
|
|
38
|
+
|
|
39
|
+
// Send APDU should have been called once with the correct apdu
|
|
40
|
+
assertEquals(1, apduSender.sendCalls.size)
|
|
41
|
+
assertArrayEquals(mockedApdu, apduSender.sendCalls[0])
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
@Test
|
|
45
|
+
fun `GIVEN a device connection with an initial APDU sender and an APDU that triggers disconnection WHEN requestSendApdu is called and a reconnection occurs THEN the second apduSender is used and the result is obtained`() =
|
|
46
|
+
runTest {
|
|
47
|
+
apdusTriggeringDisconnection.forEach { apduTriggeringDisconnection ->
|
|
48
|
+
val apduSender1 = MockedApduSender()
|
|
49
|
+
apduSender1.nextResult = mockedSuccessApduResult
|
|
50
|
+
|
|
51
|
+
val deviceConnection = DeviceConnection(
|
|
52
|
+
sessionId = "mockId",
|
|
53
|
+
deviceApduSender = apduSender1,
|
|
54
|
+
onTerminated = { },
|
|
55
|
+
isFatalSendApduFailure = { false },
|
|
56
|
+
reconnectionTimeoutDuration = 5.seconds,
|
|
57
|
+
loggerService = FakeLoggerService(),
|
|
58
|
+
coroutineDispatcher = StandardTestDispatcher(testScheduler),
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
// Request sending an apdu
|
|
62
|
+
val result1 = deviceConnection.requestSendApdu(apduTriggeringDisconnection)
|
|
63
|
+
|
|
64
|
+
// apduSender1.sendApdu should have been called once with the correct apdu
|
|
65
|
+
assertEquals(1, apduSender1.sendCalls.size)
|
|
66
|
+
assertArrayEquals(apduTriggeringDisconnection, apduSender1.sendCalls[0])
|
|
67
|
+
|
|
68
|
+
// The result should have been obtained
|
|
69
|
+
assertEquals(mockedSuccessApduResult, result1)
|
|
70
|
+
|
|
71
|
+
// Request sending a second apdu
|
|
72
|
+
val result2 = async {
|
|
73
|
+
deviceConnection.requestSendApdu(mockedApdu)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// apduSender1.sendApdu shouldn't have been called again
|
|
77
|
+
assertEquals(1, apduSender1.sendCalls.size)
|
|
78
|
+
|
|
79
|
+
// Simulate reconnection
|
|
80
|
+
val apduSender2 = MockedApduSender()
|
|
81
|
+
apduSender2.nextResult = mockedSuccessApduResult
|
|
82
|
+
deviceConnection.handleDeviceConnected(apduSender2)
|
|
83
|
+
|
|
84
|
+
// The result should have been obtained
|
|
85
|
+
assertEquals(mockedSuccessApduResult, result2.await())
|
|
86
|
+
|
|
87
|
+
// apduSender2.sendApdu should have been called once with the correct apdu
|
|
88
|
+
assertEquals(1, apduSender2.sendCalls.size)
|
|
89
|
+
assertArrayEquals(mockedApdu, apduSender2.sendCalls[0])
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
@OptIn(ExperimentalCoroutinesApi::class)
|
|
94
|
+
@Test
|
|
95
|
+
fun `GIVEN a device connection WHEN handleDeviceDisconnected is called and the timeout elapses THEN onTerminated is triggered`() =
|
|
96
|
+
runTest {
|
|
97
|
+
var terminated = false
|
|
98
|
+
val apduSender = MockedApduSender()
|
|
99
|
+
|
|
100
|
+
val deviceConnection = DeviceConnection(
|
|
101
|
+
sessionId = "mockId",
|
|
102
|
+
deviceApduSender = apduSender,
|
|
103
|
+
onTerminated = {
|
|
104
|
+
terminated = true
|
|
105
|
+
},
|
|
106
|
+
isFatalSendApduFailure = { false },
|
|
107
|
+
reconnectionTimeoutDuration = 5.seconds,
|
|
108
|
+
loggerService = FakeLoggerService(),
|
|
109
|
+
coroutineDispatcher = StandardTestDispatcher(testScheduler),
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
// Simulate disconnection
|
|
113
|
+
deviceConnection.handleDeviceDisconnected()
|
|
114
|
+
|
|
115
|
+
// Timeout
|
|
116
|
+
advanceTimeBy(5.seconds)
|
|
117
|
+
assertFalse(terminated)
|
|
118
|
+
advanceTimeBy(1.milliseconds)
|
|
119
|
+
assertTrue(terminated)
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
@Test
|
|
123
|
+
fun `GIVEN a disconnected device connection WHEN a device gets reconnected and an APDU is requested THEN the correct apduSender is used and the result is obtained`() =
|
|
124
|
+
runTest {
|
|
125
|
+
val apduSender1 = MockedApduSender()
|
|
126
|
+
|
|
127
|
+
val deviceConnection = DeviceConnection(
|
|
128
|
+
sessionId = "mockId",
|
|
129
|
+
deviceApduSender = apduSender1,
|
|
130
|
+
onTerminated = { },
|
|
131
|
+
isFatalSendApduFailure = { false },
|
|
132
|
+
reconnectionTimeoutDuration = 5.seconds,
|
|
133
|
+
loggerService = FakeLoggerService(),
|
|
134
|
+
coroutineDispatcher = StandardTestDispatcher(testScheduler),
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
// Simulate disconnection
|
|
138
|
+
deviceConnection.handleDeviceDisconnected()
|
|
139
|
+
|
|
140
|
+
// Request sending an APDU
|
|
141
|
+
val result = async {
|
|
142
|
+
deviceConnection.requestSendApdu(mockedApdu)
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Simulate reconnection
|
|
146
|
+
val apduSender2 = MockedApduSender()
|
|
147
|
+
deviceConnection.handleDeviceConnected(apduSender2)
|
|
148
|
+
|
|
149
|
+
// The result should have been obtained
|
|
150
|
+
assertEquals(mockedSuccessApduResult, result.await())
|
|
151
|
+
|
|
152
|
+
// apduSender1.sendApdu should not have been called
|
|
153
|
+
assertEquals(0, apduSender1.sendCalls.size)
|
|
154
|
+
|
|
155
|
+
// apduSender2.sendApdu should have been called once with the correct apdu
|
|
156
|
+
assertEquals(1, apduSender2.sendCalls.size)
|
|
157
|
+
assertArrayEquals(mockedApdu, apduSender2.sendCalls[0])
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
@Test
|
|
161
|
+
fun `GIVEN a device connection WHEN requestCloseConnection is called THEN onTerminated is triggered`() = runTest {
|
|
162
|
+
var terminated = false
|
|
163
|
+
|
|
164
|
+
val deviceConnection = DeviceConnection(
|
|
165
|
+
sessionId = "mockId",
|
|
166
|
+
deviceApduSender = MockedApduSender(),
|
|
167
|
+
onTerminated = {
|
|
168
|
+
terminated = true
|
|
169
|
+
},
|
|
170
|
+
isFatalSendApduFailure = { false },
|
|
171
|
+
reconnectionTimeoutDuration = 5.seconds,
|
|
172
|
+
loggerService = FakeLoggerService(),
|
|
173
|
+
coroutineDispatcher = StandardTestDispatcher(testScheduler),
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
// Request closing connection
|
|
177
|
+
deviceConnection.requestCloseConnection()
|
|
178
|
+
|
|
179
|
+
// onTerminated should have been called
|
|
180
|
+
assertTrue(terminated)
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// Helpers
|
|
184
|
+
companion object {
|
|
185
|
+
|
|
186
|
+
val mockedApdu: ByteArray = byteArrayOf(0x01, 0x02)
|
|
187
|
+
|
|
188
|
+
val apdusTriggeringDisconnection: List<ByteArray> = listOf(
|
|
189
|
+
byteArrayOf(0xe0.toByte(), 0xd8.toByte(), 0x01), // Open app
|
|
190
|
+
byteArrayOf(0xe0.toByte(), 0xd8.toByte(), 0x01) // Close app
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
val mockedSuccessApduResult =
|
|
194
|
+
SendApduResult.Success(byteArrayOf(0x05, 0x06, 0x90.toByte(), 0x00))
|
|
195
|
+
|
|
196
|
+
class MockedApduSender() : DeviceApduSender<String> {
|
|
197
|
+
// can be set from the outside for easy mocking
|
|
198
|
+
var nextResult: SendApduResult = mockedSuccessApduResult
|
|
199
|
+
|
|
200
|
+
private val _sendCalls: MutableList<ByteArray> = mutableListOf()
|
|
201
|
+
// to easily check from the outside the apdus sent
|
|
202
|
+
val sendCalls: MutableList<ByteArray>
|
|
203
|
+
get() = _sendCalls
|
|
204
|
+
|
|
205
|
+
override suspend fun send(apdu: ByteArray): SendApduResult {
|
|
206
|
+
_sendCalls += apdu
|
|
207
|
+
return nextResult
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
override val dependencies: String
|
|
211
|
+
get() = ""
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
internal class FakeLoggerService : LoggerService {
|
|
215
|
+
override fun log(info: LogInfo) {}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
package/lib/cjs/package.json
CHANGED
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
}
|
|
19
19
|
},
|
|
20
20
|
"files": [
|
|
21
|
+
"./android",
|
|
21
22
|
"./lib"
|
|
22
23
|
],
|
|
23
24
|
"scripts": {
|
|
@@ -32,7 +33,7 @@
|
|
|
32
33
|
"prettier": "prettier . --check",
|
|
33
34
|
"prettier:fix": "prettier . --write",
|
|
34
35
|
"typecheck": "tsc --noEmit",
|
|
35
|
-
"test": "vitest run --passWithNoTests",
|
|
36
|
+
"test": "vitest run --passWithNoTests && cd android && ./gradlew testDebugUnitTest --tests \"*\" --info",
|
|
36
37
|
"test:watch": "vitest --passWithNoTests",
|
|
37
38
|
"test:coverage": "vitest run --coverage --passWithNoTests"
|
|
38
39
|
},
|
package/lib/esm/package.json
CHANGED
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
}
|
|
19
19
|
},
|
|
20
20
|
"files": [
|
|
21
|
+
"./android",
|
|
21
22
|
"./lib"
|
|
22
23
|
],
|
|
23
24
|
"scripts": {
|
|
@@ -32,7 +33,7 @@
|
|
|
32
33
|
"prettier": "prettier . --check",
|
|
33
34
|
"prettier:fix": "prettier . --write",
|
|
34
35
|
"typecheck": "tsc --noEmit",
|
|
35
|
-
"test": "vitest run --passWithNoTests",
|
|
36
|
+
"test": "vitest run --passWithNoTests && cd android && ./gradlew testDebugUnitTest --tests \"*\" --info",
|
|
36
37
|
"test:watch": "vitest --passWithNoTests",
|
|
37
38
|
"test:coverage": "vitest run --coverage --passWithNoTests"
|
|
38
39
|
},
|