@ledgerhq/device-transport-kit-react-native-hid 0.0.0-rn-hid-20250221112139 → 0.0.0-rn-ble-logs-20250416162013

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 (73) hide show
  1. package/README.md +1 -1
  2. package/android/build.gradle +101 -0
  3. package/android/gradle.properties +1 -0
  4. package/android/src/main/AndroidManifest.xml +3 -0
  5. package/android/src/main/kotlin/com/ledger/androidtransporthid/BridgeEvents.kt +42 -0
  6. package/android/src/main/kotlin/com/ledger/androidtransporthid/TransportHidModule.kt +241 -0
  7. package/android/src/main/kotlin/com/ledger/androidtransporthid/TransportHidPackage.kt +25 -0
  8. package/android/src/main/kotlin/com/ledger/androidtransporthid/bridge/serialization.kt +124 -0
  9. package/android/src/main/kotlin/com/ledger/devicesdk/shared/androidMain/transport/usb/AndroidUsbTransport.kt +16 -0
  10. package/android/src/main/kotlin/com/ledger/devicesdk/shared/androidMain/transport/usb/DefaultAndroidUsbTransport.kt +298 -0
  11. package/android/src/main/kotlin/com/ledger/devicesdk/shared/androidMain/transport/usb/UsbPermissionRequester.kt +18 -0
  12. package/android/src/main/kotlin/com/ledger/devicesdk/shared/androidMain/transport/usb/connection/AndroidUsbApduSender.kt +133 -0
  13. package/android/src/main/kotlin/com/ledger/devicesdk/shared/androidMain/transport/usb/controller/UsbAttachedReceiverController.kt +59 -0
  14. package/android/src/main/kotlin/com/ledger/devicesdk/shared/androidMain/transport/usb/controller/UsbDetachedReceiverController.kt +58 -0
  15. package/android/src/main/kotlin/com/ledger/devicesdk/shared/androidMain/transport/usb/controller/UsbPermissionReceiver.kt +92 -0
  16. package/android/src/main/kotlin/com/ledger/devicesdk/shared/androidMain/transport/usb/model/LedgerUsbDevice.kt +16 -0
  17. package/android/src/main/kotlin/com/ledger/devicesdk/shared/androidMain/transport/usb/model/ProductId.kt +11 -0
  18. package/android/src/main/kotlin/com/ledger/devicesdk/shared/androidMain/transport/usb/model/UsbPermissionEvent.kt +14 -0
  19. package/android/src/main/kotlin/com/ledger/devicesdk/shared/androidMain/transport/usb/model/UsbState.kt +16 -0
  20. package/android/src/main/kotlin/com/ledger/devicesdk/shared/androidMain/transport/usb/model/VendorId.kt +11 -0
  21. package/android/src/main/kotlin/com/ledger/devicesdk/shared/androidMain/transport/usb/utils/UsbDeviceMapper.kt +46 -0
  22. package/android/src/main/kotlin/com/ledger/devicesdk/shared/androidMain/transport/usb/utils/UsbMapper.kt +56 -0
  23. package/android/src/main/kotlin/com/ledger/devicesdk/shared/androidMainInternal/transport/UsbConst.android.kt +8 -0
  24. package/android/src/main/kotlin/com/ledger/devicesdk/shared/androidMainInternal/transport/deviceconnection/DeviceApduSender.kt +13 -0
  25. package/android/src/main/kotlin/com/ledger/devicesdk/shared/androidMainInternal/transport/deviceconnection/DeviceConnection.kt +95 -0
  26. package/android/src/main/kotlin/com/ledger/devicesdk/shared/androidMainInternal/transport/deviceconnection/DeviceConnectionStateMachine.kt +314 -0
  27. package/android/src/main/kotlin/com/ledger/devicesdk/shared/api/apdu/Apdu.kt +44 -0
  28. package/android/src/main/kotlin/com/ledger/devicesdk/shared/api/apdu/ApduBuilder.kt +88 -0
  29. package/android/src/main/kotlin/com/ledger/devicesdk/shared/api/apdu/ApduParser.kt +37 -0
  30. package/android/src/main/kotlin/com/ledger/devicesdk/shared/api/apdu/ApduUtils.kt +37 -0
  31. package/android/src/main/kotlin/com/ledger/devicesdk/shared/api/apdu/SendApduResult.kt +47 -0
  32. package/android/src/main/kotlin/com/ledger/devicesdk/shared/api/connection/ConnectedDevice.kt +25 -0
  33. package/android/src/main/kotlin/com/ledger/devicesdk/shared/api/connection/ConnectionResult.kt +45 -0
  34. package/android/src/main/kotlin/com/ledger/devicesdk/shared/api/device/BleInformation.kt +8 -0
  35. package/android/src/main/kotlin/com/ledger/devicesdk/shared/api/device/LedgerDevice.kt +89 -0
  36. package/android/src/main/kotlin/com/ledger/devicesdk/shared/api/device/UsbInfo.kt +7 -0
  37. package/android/src/main/kotlin/com/ledger/devicesdk/shared/api/disconnection/DisconnectionResult.kt +10 -0
  38. package/android/src/main/kotlin/com/ledger/devicesdk/shared/api/discovery/ConnectivityType.kt +10 -0
  39. package/android/src/main/kotlin/com/ledger/devicesdk/shared/api/discovery/DiscoveryDevice.kt +18 -0
  40. package/android/src/main/kotlin/com/ledger/devicesdk/shared/api/discovery/DiscoveryResult.kt +28 -0
  41. package/android/src/main/kotlin/com/ledger/devicesdk/shared/api/utils/ByteArrayExtension.kt +116 -0
  42. package/android/src/main/kotlin/com/ledger/devicesdk/shared/api/utils/StringExtension.kt +21 -0
  43. package/android/src/main/kotlin/com/ledger/devicesdk/shared/internal/connection/InternalConnectedDevice.kt +13 -0
  44. package/android/src/main/kotlin/com/ledger/devicesdk/shared/internal/connection/InternalConnectionResult.kt +41 -0
  45. package/android/src/main/kotlin/com/ledger/devicesdk/shared/internal/coroutine/SDKScope.kt +25 -0
  46. package/android/src/main/kotlin/com/ledger/devicesdk/shared/internal/coroutine/SDKScopeHandler.kt +18 -0
  47. package/android/src/main/kotlin/com/ledger/devicesdk/shared/internal/event/SdkEventDispatcher.kt +19 -0
  48. package/android/src/main/kotlin/com/ledger/devicesdk/shared/internal/service/logger/DisableLoggerService.kt +12 -0
  49. package/android/src/main/kotlin/com/ledger/devicesdk/shared/internal/service/logger/LogInfo.kt +52 -0
  50. package/android/src/main/kotlin/com/ledger/devicesdk/shared/internal/service/logger/LogLevel.kt +13 -0
  51. package/android/src/main/kotlin/com/ledger/devicesdk/shared/internal/service/logger/LoggerService.kt +10 -0
  52. package/android/src/main/kotlin/com/ledger/devicesdk/shared/internal/transport/Transport.kt +21 -0
  53. package/android/src/main/kotlin/com/ledger/devicesdk/shared/internal/transport/TransportEvent.kt +18 -0
  54. package/android/src/main/kotlin/com/ledger/devicesdk/shared/internal/transport/framer/FramerService.kt +210 -0
  55. package/android/src/main/kotlin/com/ledger/devicesdk/shared/internal/transport/framer/FramerUtils.kt +35 -0
  56. package/android/src/main/kotlin/com/ledger/devicesdk/shared/internal/transport/framer/model/ApduConst.kt +9 -0
  57. package/android/src/main/kotlin/com/ledger/devicesdk/shared/internal/transport/framer/model/ApduFrame.kt +66 -0
  58. package/android/src/main/kotlin/com/ledger/devicesdk/shared/internal/transport/framer/model/ApduFramerHeader.kt +74 -0
  59. package/android/src/main/kotlin/com/ledger/devicesdk/shared/internal/transport/framer/model/FramerConst.kt +14 -0
  60. package/android/src/main/kotlin/com/ledger/devicesdk/shared/internal/transport/utils/ByteExtension.kt +21 -0
  61. package/android/src/main/kotlin/com/ledger/devicesdk/shared/internal/transport/utils/InternalByteArrayExtension.kt +18 -0
  62. package/android/src/main/kotlin/com/ledger/devicesdk/shared/internal/utils/Controller.kt +12 -0
  63. package/android/src/test/kotlin/com/ledger/devicesdk/shared/androidMainInternal/transport/deviceconnection/DeviceConnectionStateMachineTest.kt +713 -0
  64. package/android/src/test/kotlin/com/ledger/devicesdk/shared/androidMainInternal/transport/deviceconnection/DeviceConnectionTest.kt +218 -0
  65. package/lib/cjs/api/bridge/NativeTransportModule.js +1 -1
  66. package/lib/cjs/api/bridge/NativeTransportModule.js.map +1 -1
  67. package/lib/cjs/package.json +8 -2
  68. package/lib/esm/api/bridge/NativeTransportModule.js +1 -1
  69. package/lib/esm/api/bridge/NativeTransportModule.js.map +1 -1
  70. package/lib/esm/package.json +8 -2
  71. package/lib/types/api/bridge/NativeTransportModule.d.ts.map +1 -1
  72. package/lib/types/tsconfig.prod.tsbuildinfo +1 -1
  73. package/package.json +12 -6
package/README.md CHANGED
@@ -35,7 +35,7 @@ The transport itself is only compatible with Android devices but there is no add
35
35
 
36
36
  ### Pre-requisites
37
37
 
38
- To use this transport, ensure you have the Device Magement Kit installed in your project.
38
+ To use this transport, ensure you have the Device Management Kit installed in your project.
39
39
 
40
40
  #### AndroidManifest.xml
41
41
 
@@ -0,0 +1,101 @@
1
+ buildscript {
2
+ ext {
3
+ // Kotlin and JVM Versions
4
+ kotlin_version = '2.0.20'
5
+ coroutines_version = '1.7.3'
6
+ jvmTargetVersion = "17"
7
+
8
+ // Libraries Versions
9
+ androidCoreKtx_version = '1.13.1'
10
+ kotlin_stdlib_version = kotlin_version
11
+ kotlin_coroutines_version = coroutines_version
12
+ kotlinx_datetime_version = '0.6.1'
13
+ mockk_version = '1.13.5'
14
+ kotlin_junit_test_version = kotlin_version
15
+ kotlin_coroutines_test_version = coroutines_version
16
+ timber_version = '5.0.1'
17
+ buffer_version = '1.4.2'
18
+ }
19
+ repositories {
20
+ mavenCentral()
21
+ google()
22
+ maven { url "https://oss.sonatype.org/content/repositories/snapshots/" }
23
+ }
24
+
25
+ dependencies {
26
+ // Android Gradle Plugin
27
+ classpath "com.android.tools.build:gradle:8.3.2"
28
+
29
+ // Kotlin Gradle Plugin
30
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
31
+ }
32
+ }
33
+
34
+ allprojects {
35
+ repositories {
36
+ mavenCentral()
37
+ google()
38
+ maven { url "https://oss.sonatype.org/content/repositories/snapshots/" }
39
+ maven {
40
+ // React Native Android binaries
41
+ url "$rootDir/../node_modules/react-native/android"
42
+ }
43
+ }
44
+ }
45
+
46
+ apply plugin: 'com.android.library'
47
+ apply plugin: 'org.jetbrains.kotlin.android'
48
+
49
+ def safeExtGet(prop, fallback) {
50
+ rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
51
+ }
52
+
53
+ android {
54
+ namespace "com.ledger.androidtransporthid"
55
+ compileSdkVersion safeExtGet("compileSdkVersion", 34)
56
+
57
+ defaultConfig {
58
+ minSdkVersion safeExtGet("minSdkVersion", 30)
59
+ targetSdkVersion safeExtGet("targetSdkVersion", 34)
60
+ versionCode 1
61
+ versionName "1.0"
62
+ }
63
+
64
+ compileOptions {
65
+ // Use JavaVersion enums for compatibility
66
+ sourceCompatibility JavaVersion.VERSION_17
67
+ targetCompatibility JavaVersion.VERSION_17
68
+ }
69
+
70
+ kotlinOptions {
71
+ jvmTarget = jvmTargetVersion
72
+ }
73
+
74
+ lintOptions {
75
+ abortOnError false
76
+ warning 'InvalidPackage'
77
+ }
78
+ }
79
+
80
+ repositories {
81
+ mavenCentral()
82
+ google()
83
+ maven { url "https://oss.sonatype.org/content/repositories/snapshots/" }
84
+ maven {
85
+ // React Native Android binaries
86
+ url "$rootDir/../node_modules/react-native/android"
87
+ }
88
+ }
89
+
90
+ dependencies {
91
+ implementation 'com.facebook.react:react-native:+'
92
+ implementation "androidx.core:core-ktx:$androidCoreKtx_version"
93
+ implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_stdlib_version"
94
+ implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlin_coroutines_version"
95
+ implementation "org.jetbrains.kotlinx:kotlinx-datetime:$kotlinx_datetime_version"
96
+ implementation "com.jakewharton.timber:timber:$timber_version"
97
+ implementation "com.ditchoom:buffer:$buffer_version"
98
+ testImplementation "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
99
+ testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:$kotlin_coroutines_test_version"
100
+ testImplementation "junit:junit:4.13.2"
101
+ }
@@ -0,0 +1 @@
1
+ android.useAndroidX=true
@@ -0,0 +1,3 @@
1
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android">
2
+ <uses-feature android:name="android.hardware.usb.host" android:required="false"/>
3
+ </manifest>
@@ -0,0 +1,42 @@
1
+ package com.ledger.androidtransporthid
2
+
3
+ import com.facebook.react.bridge.ReactContext
4
+ import com.facebook.react.bridge.WritableArray
5
+ import com.facebook.react.bridge.WritableMap
6
+ import com.facebook.react.modules.core.DeviceEventManagerModule
7
+ import com.ledger.androidtransporthid.bridge.toWritableArray
8
+ import com.ledger.androidtransporthid.bridge.toWritableMap
9
+ import com.ledger.devicesdk.shared.api.discovery.DiscoveryDevice
10
+ import com.ledger.devicesdk.shared.internal.service.logger.LogInfo
11
+ import com.ledger.devicesdk.shared.internal.transport.TransportEvent
12
+
13
+ internal sealed class EventParams {
14
+ data class WMap(val map: WritableMap): EventParams()
15
+ data class WArray(val arr: WritableArray): EventParams()
16
+ data object Empty: EventParams()
17
+ }
18
+
19
+ internal sealed class BridgeEvents(val eventName: String, val params: EventParams) {
20
+ data class DiscoveredDevices(
21
+ val devices: List<DiscoveryDevice>,
22
+ ): BridgeEvents("DiscoveredDevices", EventParams.WArray(devices.toWritableArray()))
23
+ data class TransportLog(
24
+ val logInfo: LogInfo,
25
+ ): BridgeEvents("TransportLog", EventParams.WMap(logInfo.toWritableMap()));
26
+ data class DeviceDisconnected(
27
+ val deviceConnectionLost: TransportEvent.DeviceConnectionLost,
28
+ ): BridgeEvents("DeviceDisconnected", EventParams.WMap(deviceConnectionLost.toWritableMap()))
29
+ }
30
+
31
+ internal fun sendEvent(reactContext: ReactContext, bridgeEvent: BridgeEvents) {
32
+ reactContext
33
+ .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
34
+ .emit(
35
+ bridgeEvent.eventName,
36
+ when (bridgeEvent.params) {
37
+ is EventParams.WMap -> bridgeEvent.params.map
38
+ is EventParams.WArray -> bridgeEvent.params.arr
39
+ is EventParams.Empty -> null
40
+ }
41
+ )
42
+ }
@@ -0,0 +1,241 @@
1
+ package com.ledger.androidtransporthid
2
+
3
+ import android.app.PendingIntent
4
+ import android.content.Context
5
+ import android.content.Intent
6
+ import android.hardware.usb.UsbManager
7
+ import android.util.Base64
8
+ import com.facebook.react.bridge.LifecycleEventListener
9
+ import com.facebook.react.bridge.Promise
10
+ import com.facebook.react.bridge.ReactApplicationContext
11
+ import com.facebook.react.bridge.ReactContextBaseJavaModule
12
+ import com.facebook.react.bridge.ReactMethod
13
+ import com.ledger.androidtransporthid.bridge.toWritableMap
14
+ import com.ledger.devicesdk.shared.androidMain.transport.usb.AndroidUsbTransport
15
+ import com.ledger.devicesdk.shared.androidMain.transport.usb.DefaultAndroidUsbTransport
16
+ import com.ledger.devicesdk.shared.androidMain.transport.usb.controller.ACTION_USB_PERMISSION
17
+ import com.ledger.devicesdk.shared.androidMain.transport.usb.controller.UsbAttachedReceiverController
18
+ import com.ledger.devicesdk.shared.androidMain.transport.usb.controller.UsbDetachedReceiverController
19
+ import com.ledger.devicesdk.shared.androidMain.transport.usb.controller.UsbPermissionReceiver
20
+ import com.ledger.devicesdk.shared.api.discovery.DiscoveryDevice
21
+ import com.ledger.devicesdk.shared.internal.connection.InternalConnectedDevice
22
+ import com.ledger.devicesdk.shared.internal.connection.InternalConnectionResult
23
+ import com.ledger.devicesdk.shared.internal.event.SdkEventDispatcher
24
+ import com.ledger.devicesdk.shared.internal.service.logger.LoggerService
25
+ import com.ledger.devicesdk.shared.internal.transport.TransportEvent
26
+ import kotlinx.coroutines.CoroutineScope
27
+ import kotlinx.coroutines.Dispatchers
28
+ import kotlinx.coroutines.Job
29
+ import kotlinx.coroutines.flow.launchIn
30
+ import kotlinx.coroutines.flow.onEach
31
+ import kotlinx.coroutines.launch
32
+ import timber.log.Timber
33
+ import kotlin.random.Random
34
+ import kotlin.time.Duration.Companion.milliseconds
35
+
36
+ class TransportHidModule(
37
+ private val reactContext: ReactApplicationContext,
38
+ private val coroutineScope: CoroutineScope
39
+ ) :
40
+ ReactContextBaseJavaModule(reactContext), LifecycleEventListener {
41
+ override fun getName(): String = "LDMKTransportHIDModule"
42
+
43
+ private var usbPermissionReceiver: UsbPermissionReceiver? = null
44
+ private var usbAttachedReceiverController: UsbAttachedReceiverController? = null
45
+ private var usbDetachedReceiverController: UsbDetachedReceiverController? = null
46
+ private var sdkEventDispatcher: SdkEventDispatcher = SdkEventDispatcher()
47
+ private var eventDispatcherListeningJob: Job
48
+ private val loggerService: LoggerService =
49
+ LoggerService { info ->
50
+ Timber.tag("LDMKTransportHIDModule " + info.tag).d(info.message)
51
+ sendEvent(reactContext, BridgeEvents.TransportLog(info))
52
+ }
53
+
54
+ private val transport: AndroidUsbTransport? by lazy {
55
+ val currentActivity = reactContext.currentActivity
56
+ val currentApplication = currentActivity?.application
57
+
58
+ var transport: AndroidUsbTransport? = null
59
+ if (currentApplication != null) {
60
+ val usbManager = reactContext.getSystemService(Context.USB_SERVICE) as UsbManager
61
+ transport = DefaultAndroidUsbTransport(
62
+ application = currentApplication,
63
+ usbManager = usbManager,
64
+ permissionRequester = { context, manager, device ->
65
+ manager.requestPermission(
66
+ device,
67
+ PendingIntent.getBroadcast(
68
+ context,
69
+ Random.nextInt(),
70
+ Intent(ACTION_USB_PERMISSION).apply {
71
+ setPackage(context.packageName)
72
+ },
73
+ PendingIntent.FLAG_IMMUTABLE,
74
+ ),
75
+ )
76
+ },
77
+ eventDispatcher = sdkEventDispatcher,
78
+ coroutineDispatcher = Dispatchers.IO,
79
+ loggerService = loggerService,
80
+ scanDelay = 500.milliseconds,
81
+ )
82
+ usbPermissionReceiver = UsbPermissionReceiver(
83
+ context = reactContext,
84
+ androidUsbTransport = transport,
85
+ usbManager = usbManager,
86
+ loggerService = loggerService
87
+ )
88
+ usbDetachedReceiverController = UsbDetachedReceiverController(
89
+ context = reactContext,
90
+ androidUsbTransport = transport,
91
+ )
92
+ usbAttachedReceiverController = UsbAttachedReceiverController(
93
+ context = reactContext,
94
+ androidUsbTransport = transport,
95
+ )
96
+ usbPermissionReceiver!!.start()
97
+ usbAttachedReceiverController!!.start()
98
+ usbDetachedReceiverController!!.start()
99
+ }
100
+ transport
101
+ }
102
+
103
+ private val discoveryDevices: MutableList<DiscoveryDevice> = mutableListOf()
104
+ private val connectedDevices: MutableList<InternalConnectedDevice> = mutableListOf()
105
+
106
+ init {
107
+ reactContext.addLifecycleEventListener(this)
108
+ Timber.plant(Timber.DebugTree())
109
+ eventDispatcherListeningJob = sdkEventDispatcher.listen().onEach {
110
+ when (it) {
111
+ is TransportEvent.DeviceConnectionLost -> {
112
+ Timber.tag("RNHIDModule")
113
+ Timber.i("TransportEvent.DeviceConnectionLost ${it.id}")
114
+ connectedDevices.removeIf { device -> device.id == it.id }
115
+ sendEvent(reactContext, BridgeEvents.DeviceDisconnected(it))
116
+ }
117
+
118
+ else -> {}
119
+ }
120
+ }.launchIn(scope = coroutineScope)
121
+ }
122
+
123
+ override fun onHostResume() {}
124
+
125
+ override fun onHostPause() {}
126
+
127
+ override fun onHostDestroy() {
128
+ usbPermissionReceiver?.stop()
129
+ usbAttachedReceiverController?.stop()
130
+ usbDetachedReceiverController?.stop()
131
+ eventDispatcherListeningJob.cancel()
132
+ transport?.stopScan()
133
+ }
134
+
135
+ private var discoveryCount = 0
136
+
137
+ @ReactMethod
138
+ fun startScan(promise: Promise) {
139
+ discoveryCount += 1
140
+ if (discoveryCount > 1) {
141
+ promise.resolve(null)
142
+ return
143
+ }
144
+ try {
145
+ transport!!.startScan().onEach {
146
+ discoveryDevices.clear()
147
+ discoveryDevices += it
148
+ sendEvent(reactContext, BridgeEvents.DiscoveredDevices(it))
149
+ }.launchIn(scope = coroutineScope)
150
+ promise.resolve(null)
151
+ } catch (e: Exception) {
152
+ promise.reject(e);
153
+ }
154
+ }
155
+
156
+ @ReactMethod
157
+ fun stopScan(promise: Promise) {
158
+ discoveryCount -= 1
159
+ if (discoveryCount > 0) {
160
+ promise.resolve(null)
161
+ return
162
+ }
163
+ try {
164
+ transport!!.stopScan()
165
+ promise.resolve(null)
166
+ } catch (e: Exception) {
167
+ promise.reject(e);
168
+ }
169
+ }
170
+
171
+ @ReactMethod()
172
+ fun connectDevice(uid: String, promise: Promise) {
173
+ val device = discoveryDevices.firstOrNull { it.uid == uid }
174
+ if (device == null) {
175
+ promise.reject(Exception("[TransportHidModule][connectDevice] Device not found"))
176
+ return
177
+ }
178
+
179
+ coroutineScope.launch {
180
+ try {
181
+ val connectionResult = transport!!.connect(device)
182
+ when (connectionResult) {
183
+ is InternalConnectionResult.Connected -> {
184
+ connectedDevices.add(connectionResult.device)
185
+ }
186
+
187
+ else -> {}
188
+ }
189
+ promise.resolve(connectionResult.toWritableMap())
190
+ } catch (e: Exception) {
191
+ promise.reject(e)
192
+ }
193
+ }
194
+ }
195
+
196
+ @ReactMethod
197
+ fun disconnectDevice(sessionId: String, promise: Promise) {
198
+ coroutineScope.launch {
199
+ try {
200
+ transport!!.disconnect(sessionId)
201
+ promise.resolve(null);
202
+ } catch (e: Exception) {
203
+ promise.reject(e)
204
+ }
205
+ }
206
+ }
207
+
208
+ @ReactMethod
209
+ fun sendApdu(sessionId: String, apduBase64: String, promise: Promise) {
210
+ try {
211
+ val device = connectedDevices.firstOrNull() { it.id == sessionId }
212
+ if (device == null) {
213
+ promise.reject(Exception("[TransportHidModule][sendApdu] Device not found"))
214
+ return
215
+ }
216
+ coroutineScope.launch {
217
+ try {
218
+ val apdu: ByteArray = Base64.decode(apduBase64, Base64.DEFAULT)
219
+ val res = device.sendApduFn(apdu)
220
+ promise.resolve(res.toWritableMap())
221
+ } catch (e: Exception) {
222
+ Timber.i("$e, ${e.cause}")
223
+ promise.reject(e)
224
+ }
225
+ }
226
+ } catch (e: Exception) {
227
+ Timber.i("$e, ${e.cause}")
228
+ promise.reject(e)
229
+ }
230
+ }
231
+
232
+ @ReactMethod
233
+ fun addListener(eventName: String) {
234
+ // Nothing to do in our case, but React Native will issue a warning if this isn't implemented
235
+ }
236
+
237
+ @ReactMethod
238
+ fun removeListeners(count: Int) {
239
+ // Nothing to do in our case, but React Native will issue a warning if this isn't implemented
240
+ }
241
+ }
@@ -0,0 +1,25 @@
1
+ package com.ledger.androidtransporthid
2
+
3
+ import android.view.View
4
+ import com.facebook.react.ReactPackage
5
+ import com.facebook.react.bridge.NativeModule
6
+ import com.facebook.react.bridge.ReactApplicationContext
7
+ import com.facebook.react.uimanager.ReactShadowNode
8
+ import com.facebook.react.uimanager.ViewManager
9
+ import kotlinx.coroutines.CoroutineScope
10
+ import kotlinx.coroutines.Dispatchers
11
+
12
+ class TransportHidPackage() : ReactPackage {
13
+ override fun createNativeModules(reactContext: ReactApplicationContext): MutableList<NativeModule> {
14
+ return mutableListOf(
15
+ TransportHidModule(
16
+ reactContext = reactContext,
17
+ coroutineScope = CoroutineScope(Dispatchers.Default)
18
+ )
19
+ )
20
+ }
21
+
22
+ override fun createViewManagers(p0: ReactApplicationContext): MutableList<ViewManager<View, ReactShadowNode<*>>> {
23
+ return mutableListOf()
24
+ }
25
+ }
@@ -0,0 +1,124 @@
1
+ package com.ledger.androidtransporthid.bridge
2
+
3
+ import android.util.Base64
4
+ import com.facebook.react.bridge.Arguments
5
+ import com.facebook.react.bridge.WritableArray
6
+ import com.facebook.react.bridge.WritableMap
7
+ import com.ledger.devicesdk.shared.api.apdu.SendApduFailureReason
8
+ import com.ledger.devicesdk.shared.api.apdu.SendApduResult
9
+ import com.ledger.devicesdk.shared.api.device.LedgerDevice
10
+ import com.ledger.devicesdk.shared.api.discovery.ConnectivityType
11
+ import com.ledger.devicesdk.shared.api.discovery.DiscoveryDevice
12
+ import com.ledger.devicesdk.shared.internal.connection.InternalConnectionResult
13
+ import com.ledger.devicesdk.shared.internal.service.logger.LogInfo
14
+ import com.ledger.devicesdk.shared.internal.service.logger.LogLevel
15
+ import com.ledger.devicesdk.shared.internal.transport.TransportEvent
16
+ import kotlinx.datetime.Clock
17
+
18
+ fun LedgerDevice.toWritableMap(): WritableMap =
19
+ Arguments.createMap().apply {
20
+ putString("name", name)
21
+ putString("usbProductIdMask", usbInfo.productIdMask)
22
+ }
23
+
24
+ fun DiscoveryDevice.toWritableMap(): WritableMap =
25
+ Arguments.createMap().apply {
26
+ putString("uid", uid)
27
+ putString("name", name)
28
+ putMap("ledgerDevice", ledgerDevice.toWritableMap())
29
+ }
30
+
31
+
32
+ internal fun LogLevel.toSerializedString(): String =
33
+ when (this) {
34
+ LogLevel.DEBUG -> "debug"
35
+ LogLevel.INFO -> "info"
36
+ LogLevel.WARNING -> "warning"
37
+ LogLevel.ERROR -> "error"
38
+ }
39
+
40
+
41
+ internal fun LogInfo.toWritableMap(): WritableMap =
42
+ Arguments.createMap().apply {
43
+ putString("level", level.toSerializedString())
44
+ putString("tag", "[TransportHidModule][${tag}]")
45
+ putString("message", message)
46
+ putMap("jsonPayLoad", Arguments.makeNativeMap(jsonPayLoad))
47
+ putString("timestamp", Clock.System.now().toEpochMilliseconds().toString())
48
+ }
49
+
50
+ internal fun InternalConnectionResult.toWritableMap(): WritableMap =
51
+ when (this) {
52
+ is InternalConnectionResult.Connected -> {
53
+ Arguments.createMap().apply {
54
+ putBoolean("success", true)
55
+ putString("sessionId", sessionId)
56
+ putMap("ledgerDevice", device.ledgerDevice.toWritableMap())
57
+ putString("deviceName", device.name)
58
+ }
59
+ }
60
+ is InternalConnectionResult.ConnectionError -> {
61
+ Arguments.createMap().apply {
62
+ putString("error", when (error) {
63
+ /**
64
+ * Most of these errors are for the BLE lib so it does not really make sense
65
+ * to have them here but for the sake of being explicit and not using an "else"
66
+ * where a new error type could be added, we list everything.
67
+ */
68
+ InternalConnectionResult.Failure.BleNotSupported -> "BleNotSupported"
69
+ InternalConnectionResult.Failure.ConnectionTimeout -> "ConnectionTimeout"
70
+ InternalConnectionResult.Failure.DeviceConnectivityBluetoothDisabled -> "DeviceConnectivityBluetoothDisabled"
71
+ InternalConnectionResult.Failure.DeviceConnectivityLocationDisabled -> "DeviceConnectivityLocationDisabled"
72
+ InternalConnectionResult.Failure.DeviceNotFound -> "DeviceNotFound"
73
+ InternalConnectionResult.Failure.InitializingFailed -> "InitializingFailed"
74
+ InternalConnectionResult.Failure.InternalState -> "InternalState"
75
+ InternalConnectionResult.Failure.NoDeviceAddress -> "NoDeviceAddress"
76
+ InternalConnectionResult.Failure.PairingFailed -> "PairingFailed"
77
+ InternalConnectionResult.Failure.PermissionNotGranted -> "PermissionNotGranted"
78
+ InternalConnectionResult.Failure.ServiceNotFound -> "ServiceNotFound"
79
+ is InternalConnectionResult.Failure.Unknown -> "UnknownError: ${error.msg}"
80
+ })
81
+ putBoolean("success", false)
82
+ }
83
+ }
84
+ }
85
+
86
+ internal fun SendApduResult.toWritableMap(): WritableMap {
87
+ when(this) {
88
+ is SendApduResult.Success -> {
89
+ return Arguments.createMap().apply {
90
+ putBoolean("success", true)
91
+ putString("apdu", Base64.encodeToString(apdu, Base64.DEFAULT))
92
+ }
93
+ }
94
+ is SendApduResult.Failure -> {
95
+ return Arguments.createMap().apply {
96
+ putBoolean("success", false)
97
+ putString("error", when (reason) {
98
+ SendApduFailureReason.ApduNotWellFormatted -> "ApduNotWellFormatted"
99
+ SendApduFailureReason.DeviceBusy -> "DeviceBusy"
100
+ SendApduFailureReason.DeviceLocked -> "DeviceLocked"
101
+ SendApduFailureReason.DeviceNotFound -> "DeviceNotFound"
102
+ SendApduFailureReason.NoResponse -> "NoResponse"
103
+ SendApduFailureReason.NoUsbEndpointFound -> "NoUsbEndpointFound"
104
+ SendApduFailureReason.DeviceDisconnected -> "DeviceDisconnected"
105
+ SendApduFailureReason.Unknown -> "Unknown"
106
+ })
107
+ }
108
+ }
109
+ }
110
+ }
111
+
112
+ internal fun TransportEvent.DeviceConnectionLost.toWritableMap(): WritableMap =
113
+ Arguments.createMap().apply {
114
+ putString("id", id)
115
+ }
116
+
117
+ /* lists */
118
+
119
+ fun List<DiscoveryDevice>.toWritableArray(): WritableArray =
120
+ Arguments.createArray().apply {
121
+ forEach {
122
+ pushMap(it.toWritableMap())
123
+ }
124
+ }
@@ -0,0 +1,16 @@
1
+ /*
2
+ * SPDX-FileCopyrightText: 2023 Ledger SAS
3
+ * SPDX-License-Identifier: LicenseRef-LEDGER
4
+ */
5
+
6
+ package com.ledger.devicesdk.shared.androidMain.transport.usb
7
+
8
+ import com.ledger.devicesdk.shared.internal.transport.Transport
9
+ import com.ledger.devicesdk.shared.androidMain.transport.usb.model.UsbPermissionEvent
10
+ import com.ledger.devicesdk.shared.androidMain.transport.usb.model.UsbState
11
+
12
+ internal interface AndroidUsbTransport: Transport {
13
+ fun updateUsbState(state: UsbState)
14
+
15
+ fun updateUsbEvent(event: UsbPermissionEvent)
16
+ }