@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.
Files changed (72) hide show
  1. package/README.md +1 -1
  2. package/android/build.gradle +101 -0
  3. package/android/gradle/wrapper/gradle-wrapper.jar +0 -0
  4. package/android/gradle/wrapper/gradle-wrapper.properties +7 -0
  5. package/android/gradle.properties +1 -0
  6. package/android/gradlew +252 -0
  7. package/android/gradlew.bat +94 -0
  8. package/android/src/main/AndroidManifest.xml +3 -0
  9. package/android/src/main/kotlin/com/ledger/androidtransporthid/BridgeEvents.kt +42 -0
  10. package/android/src/main/kotlin/com/ledger/androidtransporthid/TransportHidModule.kt +241 -0
  11. package/android/src/main/kotlin/com/ledger/androidtransporthid/TransportHidPackage.kt +25 -0
  12. package/android/src/main/kotlin/com/ledger/androidtransporthid/bridge/serialization.kt +124 -0
  13. package/android/src/main/kotlin/com/ledger/devicesdk/shared/androidMain/transport/usb/AndroidUsbTransport.kt +16 -0
  14. package/android/src/main/kotlin/com/ledger/devicesdk/shared/androidMain/transport/usb/DefaultAndroidUsbTransport.kt +298 -0
  15. package/android/src/main/kotlin/com/ledger/devicesdk/shared/androidMain/transport/usb/UsbPermissionRequester.kt +18 -0
  16. package/android/src/main/kotlin/com/ledger/devicesdk/shared/androidMain/transport/usb/connection/AndroidUsbApduSender.kt +133 -0
  17. package/android/src/main/kotlin/com/ledger/devicesdk/shared/androidMain/transport/usb/controller/UsbAttachedReceiverController.kt +59 -0
  18. package/android/src/main/kotlin/com/ledger/devicesdk/shared/androidMain/transport/usb/controller/UsbDetachedReceiverController.kt +58 -0
  19. package/android/src/main/kotlin/com/ledger/devicesdk/shared/androidMain/transport/usb/controller/UsbPermissionReceiver.kt +92 -0
  20. package/android/src/main/kotlin/com/ledger/devicesdk/shared/androidMain/transport/usb/model/LedgerUsbDevice.kt +16 -0
  21. package/android/src/main/kotlin/com/ledger/devicesdk/shared/androidMain/transport/usb/model/ProductId.kt +11 -0
  22. package/android/src/main/kotlin/com/ledger/devicesdk/shared/androidMain/transport/usb/model/UsbPermissionEvent.kt +14 -0
  23. package/android/src/main/kotlin/com/ledger/devicesdk/shared/androidMain/transport/usb/model/UsbState.kt +16 -0
  24. package/android/src/main/kotlin/com/ledger/devicesdk/shared/androidMain/transport/usb/model/VendorId.kt +11 -0
  25. package/android/src/main/kotlin/com/ledger/devicesdk/shared/androidMain/transport/usb/utils/UsbDeviceMapper.kt +46 -0
  26. package/android/src/main/kotlin/com/ledger/devicesdk/shared/androidMain/transport/usb/utils/UsbMapper.kt +56 -0
  27. package/android/src/main/kotlin/com/ledger/devicesdk/shared/androidMainInternal/transport/UsbConst.android.kt +8 -0
  28. package/android/src/main/kotlin/com/ledger/devicesdk/shared/androidMainInternal/transport/deviceconnection/DeviceApduSender.kt +13 -0
  29. package/android/src/main/kotlin/com/ledger/devicesdk/shared/androidMainInternal/transport/deviceconnection/DeviceConnection.kt +95 -0
  30. package/android/src/main/kotlin/com/ledger/devicesdk/shared/androidMainInternal/transport/deviceconnection/DeviceConnectionStateMachine.kt +314 -0
  31. package/android/src/main/kotlin/com/ledger/devicesdk/shared/api/apdu/Apdu.kt +44 -0
  32. package/android/src/main/kotlin/com/ledger/devicesdk/shared/api/apdu/ApduBuilder.kt +88 -0
  33. package/android/src/main/kotlin/com/ledger/devicesdk/shared/api/apdu/ApduParser.kt +37 -0
  34. package/android/src/main/kotlin/com/ledger/devicesdk/shared/api/apdu/ApduUtils.kt +37 -0
  35. package/android/src/main/kotlin/com/ledger/devicesdk/shared/api/apdu/SendApduResult.kt +47 -0
  36. package/android/src/main/kotlin/com/ledger/devicesdk/shared/api/connection/ConnectedDevice.kt +25 -0
  37. package/android/src/main/kotlin/com/ledger/devicesdk/shared/api/connection/ConnectionResult.kt +45 -0
  38. package/android/src/main/kotlin/com/ledger/devicesdk/shared/api/device/BleInformation.kt +8 -0
  39. package/android/src/main/kotlin/com/ledger/devicesdk/shared/api/device/LedgerDevice.kt +89 -0
  40. package/android/src/main/kotlin/com/ledger/devicesdk/shared/api/device/UsbInfo.kt +7 -0
  41. package/android/src/main/kotlin/com/ledger/devicesdk/shared/api/disconnection/DisconnectionResult.kt +10 -0
  42. package/android/src/main/kotlin/com/ledger/devicesdk/shared/api/discovery/ConnectivityType.kt +10 -0
  43. package/android/src/main/kotlin/com/ledger/devicesdk/shared/api/discovery/DiscoveryDevice.kt +18 -0
  44. package/android/src/main/kotlin/com/ledger/devicesdk/shared/api/discovery/DiscoveryResult.kt +28 -0
  45. package/android/src/main/kotlin/com/ledger/devicesdk/shared/api/utils/ByteArrayExtension.kt +116 -0
  46. package/android/src/main/kotlin/com/ledger/devicesdk/shared/api/utils/StringExtension.kt +21 -0
  47. package/android/src/main/kotlin/com/ledger/devicesdk/shared/internal/connection/InternalConnectedDevice.kt +13 -0
  48. package/android/src/main/kotlin/com/ledger/devicesdk/shared/internal/connection/InternalConnectionResult.kt +41 -0
  49. package/android/src/main/kotlin/com/ledger/devicesdk/shared/internal/coroutine/SDKScope.kt +25 -0
  50. package/android/src/main/kotlin/com/ledger/devicesdk/shared/internal/coroutine/SDKScopeHandler.kt +18 -0
  51. package/android/src/main/kotlin/com/ledger/devicesdk/shared/internal/event/SdkEventDispatcher.kt +19 -0
  52. package/android/src/main/kotlin/com/ledger/devicesdk/shared/internal/service/logger/DisableLoggerService.kt +12 -0
  53. package/android/src/main/kotlin/com/ledger/devicesdk/shared/internal/service/logger/LogInfo.kt +52 -0
  54. package/android/src/main/kotlin/com/ledger/devicesdk/shared/internal/service/logger/LogLevel.kt +13 -0
  55. package/android/src/main/kotlin/com/ledger/devicesdk/shared/internal/service/logger/LoggerService.kt +10 -0
  56. package/android/src/main/kotlin/com/ledger/devicesdk/shared/internal/transport/Transport.kt +21 -0
  57. package/android/src/main/kotlin/com/ledger/devicesdk/shared/internal/transport/TransportEvent.kt +18 -0
  58. package/android/src/main/kotlin/com/ledger/devicesdk/shared/internal/transport/framer/FramerService.kt +210 -0
  59. package/android/src/main/kotlin/com/ledger/devicesdk/shared/internal/transport/framer/FramerUtils.kt +35 -0
  60. package/android/src/main/kotlin/com/ledger/devicesdk/shared/internal/transport/framer/model/ApduConst.kt +9 -0
  61. package/android/src/main/kotlin/com/ledger/devicesdk/shared/internal/transport/framer/model/ApduFrame.kt +66 -0
  62. package/android/src/main/kotlin/com/ledger/devicesdk/shared/internal/transport/framer/model/ApduFramerHeader.kt +74 -0
  63. package/android/src/main/kotlin/com/ledger/devicesdk/shared/internal/transport/framer/model/FramerConst.kt +14 -0
  64. package/android/src/main/kotlin/com/ledger/devicesdk/shared/internal/transport/utils/ByteExtension.kt +21 -0
  65. package/android/src/main/kotlin/com/ledger/devicesdk/shared/internal/transport/utils/InternalByteArrayExtension.kt +18 -0
  66. package/android/src/main/kotlin/com/ledger/devicesdk/shared/internal/utils/Controller.kt +12 -0
  67. package/android/src/test/kotlin/com/ledger/devicesdk/shared/androidMainInternal/transport/deviceconnection/DeviceConnectionStateMachineTest.kt +713 -0
  68. package/android/src/test/kotlin/com/ledger/devicesdk/shared/androidMainInternal/transport/deviceconnection/DeviceConnectionTest.kt +218 -0
  69. package/lib/cjs/package.json +2 -1
  70. package/lib/esm/package.json +2 -1
  71. package/lib/types/tsconfig.prod.tsbuildinfo +1 -1
  72. package/package.json +6 -5
@@ -0,0 +1,88 @@
1
+ /*
2
+ * SPDX-FileCopyrightText: 2024 Ledger SAS
3
+ * SPDX-License-Identifier: LicenseRef-LEDGER
4
+ */
5
+
6
+ package com.ledger.devicesdk.shared.api.apdu
7
+
8
+ import com.ledger.devicesdk.shared.api.utils.isHexadecimal
9
+ import com.ledger.devicesdk.shared.internal.transport.framer.model.MINIMUM_APDU_SIZE
10
+ import com.ledger.devicesdk.shared.internal.transport.utils.isHexadecimal
11
+ import kotlin.contracts.ExperimentalContracts
12
+ import kotlin.contracts.InvocationKind
13
+ import kotlin.contracts.contract
14
+ import kotlin.properties.Delegates
15
+
16
+ @OptIn(ExperimentalContracts::class)
17
+ public fun apdu(init: ApduBuilder.() -> Unit): Apdu {
18
+ contract { callsInPlace(init, InvocationKind.EXACTLY_ONCE) }
19
+ val builder = ApduBuilder()
20
+ init.invoke(builder)
21
+ return builder.build()
22
+ }
23
+
24
+ @OptIn(ExperimentalContracts::class)
25
+ public fun rawApdu(init: ApduRawBuilder.() -> Unit): Apdu {
26
+ contract { callsInPlace(init, InvocationKind.EXACTLY_ONCE) }
27
+ val builder = ApduRawBuilder()
28
+ init.invoke(builder)
29
+ return builder.build()
30
+ }
31
+
32
+ public class ApduBuilder internal constructor() {
33
+ public var classInstruction: Byte by Delegates.notNull()
34
+ public var instructionMethod: Byte by Delegates.notNull()
35
+ public var parameter1: Byte by Delegates.notNull()
36
+ public var parameter2: Byte by Delegates.notNull()
37
+ public var data: ByteArray? = null
38
+
39
+ internal fun build(): Apdu {
40
+ check(classInstruction.isHexadecimal()) { "classInstruction must be a hexadecimal value" }
41
+ check(instructionMethod.isHexadecimal()) { "instructionMethod must be a hexadecimal value" }
42
+ check(parameter1.isHexadecimal()) { "parameter1 must be a hexadecimal value" }
43
+ check(parameter2.isHexadecimal()) { "parameter2 must be a hexadecimal value" }
44
+ if (data != null) {
45
+ val internalData = data!!
46
+ check(internalData.isHexadecimal()) { "data must be filled with a hexadecimal values" }
47
+ }
48
+
49
+ return Apdu(
50
+ classInstruction = classInstruction,
51
+ instructionMethod = instructionMethod,
52
+ parameter1 = parameter1,
53
+ parameter2 = parameter2,
54
+ data = data,
55
+ dataLength = data?.size ?: 0,
56
+ )
57
+ }
58
+ }
59
+
60
+ public class ApduRawBuilder internal constructor() {
61
+ public var rawApdu: ByteArray? = null
62
+
63
+ internal fun build(): Apdu {
64
+ check(rawApdu != null) { "RawAPDU must be filled" }
65
+ val apdu = rawApdu!!
66
+ check(apdu.isHexadecimal()) { "APDU must be filled with hexadecimal values" }
67
+ check(
68
+ apdu.size >= MINIMUM_APDU_SIZE,
69
+ ) {
70
+ "APDU size is not correct : current size = ${apdu.size} / " +
71
+ "minimum size must be = 4 (CLA / INS / P1 / P2)"
72
+ }
73
+ val data =
74
+ if (apdu.size == MINIMUM_APDU_SIZE) {
75
+ null
76
+ } else {
77
+ apdu.sliceArray(MINIMUM_APDU_SIZE..<apdu.size)
78
+ }
79
+ return Apdu(
80
+ classInstruction = apdu[0],
81
+ instructionMethod = apdu[1],
82
+ parameter1 = apdu[2],
83
+ parameter2 = apdu[3],
84
+ data = data,
85
+ dataLength = data?.size ?: 0,
86
+ )
87
+ }
88
+ }
@@ -0,0 +1,37 @@
1
+ /*
2
+ * SPDX-FileCopyrightText: 2024 Ledger SAS
3
+ * SPDX-License-Identifier: LicenseRef-LEDGER
4
+ */
5
+
6
+ package com.ledger.devicesdk.shared.api.apdu
7
+
8
+ import com.ledger.devicesdk.shared.api.utils.extractField
9
+
10
+ public class ApduParser(
11
+ private val response: ByteArray,
12
+ ) {
13
+ private var currentIndex = 0
14
+
15
+ public fun extractByteValue(): Byte = extract1BytesValue().first()
16
+
17
+ public fun extract1BytesValue(): ByteArray = extractBytesValue(nbrBytes = 1)
18
+
19
+ public fun extract2BytesValue(): ByteArray = extractBytesValue(nbrBytes = 2)
20
+
21
+ public fun extract3BytesValue(): ByteArray = extractBytesValue(nbrBytes = 3)
22
+
23
+ public fun extract4BytesValue(): ByteArray = extractBytesValue(nbrBytes = 4)
24
+
25
+ public fun extractValueString(nbrBytes: Int): String = extractBytesValue(nbrBytes = nbrBytes).decodeToString()
26
+
27
+ public fun extractRemainingBytesValue(): ByteArray = extractBytesValue(nbrBytes = response.size - currentIndex)
28
+
29
+ public fun extractBytesValue(nbrBytes: Int): ByteArray {
30
+ val index = currentIndex + nbrBytes
31
+ val result = response.extractField(from = currentIndex, to = index, toInclusive = false)
32
+ currentIndex += nbrBytes
33
+ return result
34
+ }
35
+
36
+ public fun getCurrentIndex(): Int = currentIndex
37
+ }
@@ -0,0 +1,37 @@
1
+ /*
2
+ * SPDX-FileCopyrightText: 2024 Ledger SAS
3
+ * SPDX-License-Identifier: LicenseRef-LEDGER
4
+ */
5
+
6
+ package com.ledger.devicesdk.shared.api.apdu
7
+
8
+ import com.ledger.devicesdk.shared.api.utils.isHexadecimal
9
+ import com.ledger.devicesdk.shared.internal.transport.utils.isHexadecimal
10
+
11
+ internal fun Apdu.toRawApdu(): ByteArray {
12
+ val dataLength = this.dataLength.toByte()
13
+ return byteArrayOf(
14
+ this.classInstruction,
15
+ this.instructionMethod,
16
+ this.parameter1,
17
+ this.parameter2,
18
+ ) + dataLength + (this.data ?: byteArrayOf())
19
+ }
20
+
21
+ public fun Apdu.isNotWellFormatted(): Boolean = !this.isWellFormatted()
22
+
23
+ public fun Apdu.isWellFormatted(): Boolean = this.isHexadecimalFields()
24
+
25
+ public fun Apdu.isHexadecimalFields(): Boolean =
26
+ try {
27
+ check(this.classInstruction.isHexadecimal())
28
+ check(this.instructionMethod.isHexadecimal())
29
+ check(this.parameter1.isHexadecimal())
30
+ check(this.parameter2.isHexadecimal())
31
+ if (this.data != null) {
32
+ check(this.data.isHexadecimal())
33
+ }
34
+ true
35
+ } catch (e: IllegalStateException) {
36
+ false
37
+ }
@@ -0,0 +1,47 @@
1
+ /*
2
+ * SPDX-FileCopyrightText: 2024 Ledger SAS
3
+ * SPDX-License-Identifier: LicenseRef-LEDGER
4
+ */
5
+
6
+ package com.ledger.devicesdk.shared.api.apdu
7
+
8
+ public sealed class SendApduResult {
9
+ public data class Success(
10
+ val apdu: ByteArray,
11
+ ) : SendApduResult() {
12
+ override fun equals(other: Any?): Boolean {
13
+ if (this === other) return true
14
+ if (other == null || this::class != other::class) return false
15
+
16
+ other as Success
17
+
18
+ return apdu.contentEquals(other.apdu)
19
+ }
20
+
21
+ override fun hashCode(): Int {
22
+ return apdu.contentHashCode()
23
+ }
24
+ }
25
+
26
+ public data class Failure(
27
+ val reason: SendApduFailureReason,
28
+ ) : SendApduResult()
29
+ }
30
+
31
+ public sealed class SendApduFailureReason {
32
+ public data object DeviceNotFound : SendApduFailureReason()
33
+
34
+ public data object NoUsbEndpointFound : SendApduFailureReason()
35
+
36
+ public data object ApduNotWellFormatted : SendApduFailureReason()
37
+
38
+ public data object DeviceLocked : SendApduFailureReason()
39
+
40
+ public data object DeviceBusy : SendApduFailureReason()
41
+
42
+ public data object NoResponse : SendApduFailureReason()
43
+
44
+ public data object DeviceDisconnected : SendApduFailureReason()
45
+
46
+ public data object Unknown : SendApduFailureReason()
47
+ }
@@ -0,0 +1,25 @@
1
+ /*
2
+ * SPDX-FileCopyrightText: 2023 Ledger SAS
3
+ * SPDX-License-Identifier: LicenseRef-LEDGER
4
+ */
5
+
6
+ package com.ledger.devicesdk.shared.api.connection
7
+
8
+ import com.ledger.devicesdk.shared.api.device.LedgerDevice
9
+ import com.ledger.devicesdk.shared.api.discovery.ConnectivityType
10
+ import com.ledger.devicesdk.shared.internal.connection.InternalConnectedDevice
11
+
12
+ public data class ConnectedDevice(
13
+ public val uid: String,
14
+ public val name: String,
15
+ public val ledgerDevice: LedgerDevice,
16
+ public val connectivityType: ConnectivityType,
17
+ )
18
+
19
+ internal fun InternalConnectedDevice.toConnectedDevice(): ConnectedDevice =
20
+ ConnectedDevice(
21
+ uid = this.id,
22
+ name = this.name,
23
+ ledgerDevice = this.ledgerDevice,
24
+ connectivityType = this.connectivity,
25
+ )
@@ -0,0 +1,45 @@
1
+ /*
2
+ * SPDX-FileCopyrightText: 2023 Ledger SAS
3
+ * SPDX-License-Identifier: LicenseRef-LEDGER
4
+ */
5
+
6
+ package com.ledger.devicesdk.shared.api.connection
7
+
8
+ public sealed class ConnectionResult {
9
+ public data class Connected(
10
+ val device: ConnectedDevice,
11
+ ) : ConnectionResult()
12
+
13
+ public data class Disconnected(
14
+ val failure: Failure,
15
+ ) : ConnectionResult()
16
+
17
+ // Most of these failures are mapped from the ble library but could be cleaned
18
+ public sealed class Failure {
19
+ public data object PairingFailed : Failure()
20
+
21
+ public data object ConnectionTimeout : Failure()
22
+
23
+ public data object DeviceNotFound : Failure()
24
+
25
+ public data object NoDeviceAddress : Failure()
26
+
27
+ public data object ServiceNotFound : Failure()
28
+
29
+ public data object InternalState : Failure()
30
+
31
+ public data object InitializingFailed : Failure()
32
+
33
+ public data object PermissionNotGranted : Failure()
34
+
35
+ public data object DeviceConnectivityBluetoothDisabled : Failure()
36
+
37
+ public data object DeviceConnectivityLocationDisabled : Failure()
38
+
39
+ public data object BleNotSupported : Failure()
40
+
41
+ public data class Unknown(
42
+ val msg: String?,
43
+ ) : Failure()
44
+ }
45
+ }
@@ -0,0 +1,8 @@
1
+ package com.ledger.devicesdk.shared.api.device
2
+
3
+ public data class BleInformation(
4
+ val serviceUuid: String,
5
+ val notifyCharacteristicUuid: String,
6
+ val writeWithResponseCharacteristicUuid: String,
7
+ val writeWithoutResponseCharacteristicUuid: String,
8
+ )
@@ -0,0 +1,89 @@
1
+ package com.ledger.devicesdk.shared.api.device
2
+
3
+ public sealed class LedgerDevice(
4
+ public val name: String,
5
+ public val usbInfo: UsbInfo,
6
+ public val bleInformation: BleInformation? = null,
7
+ ) {
8
+ public data object Flex :
9
+ LedgerDevice(
10
+ name = "Ledger Flex",
11
+ usbInfo = UsbInfo(LEDGER_USB_VENDOR_ID, "0x70", "0x0007"),
12
+ bleInformation =
13
+ BleInformation(
14
+ serviceUuid = "13d63400-2c97-3004-0000-4c6564676572",
15
+ notifyCharacteristicUuid =
16
+ "13d63400-2c97-3004-0001-4c6564676572",
17
+ writeWithResponseCharacteristicUuid =
18
+ "13d63400-2c97-3004-0002-4c6564676572",
19
+ writeWithoutResponseCharacteristicUuid =
20
+ "13d63400-2c97-3004-0003-4c6564676572",
21
+ ),
22
+ )
23
+
24
+ public data object Stax :
25
+ LedgerDevice(
26
+ name = "Ledger Stax",
27
+ usbInfo = UsbInfo(LEDGER_USB_VENDOR_ID, "0x60", "0x0006"),
28
+ bleInformation =
29
+ BleInformation(
30
+ serviceUuid = "13d63400-2c97-6004-0000-4c6564676572",
31
+ notifyCharacteristicUuid =
32
+ "13d63400-2c97-6004-0001-4c6564676572",
33
+ writeWithResponseCharacteristicUuid =
34
+ "13d63400-2c97-6004-0002-4c6564676572",
35
+ writeWithoutResponseCharacteristicUuid =
36
+ "13d63400-2c97-6004-0003-4c6564676572",
37
+ ),
38
+ )
39
+
40
+ public data object NanoX :
41
+ LedgerDevice(
42
+ name = "Nano X",
43
+ usbInfo = UsbInfo(LEDGER_USB_VENDOR_ID, "0x40", "0x0004"),
44
+ bleInformation =
45
+ BleInformation(
46
+ serviceUuid = "13d63400-2c97-0004-0000-4c6564676572",
47
+ notifyCharacteristicUuid =
48
+ "13d63400-2c97-0004-0001-4c6564676572",
49
+ writeWithResponseCharacteristicUuid =
50
+ "13d63400-2c97-0004-0002-4c6564676572",
51
+ writeWithoutResponseCharacteristicUuid =
52
+ "13d63400-2c97-0004-0003-4c6564676572",
53
+ ),
54
+ )
55
+
56
+ public data object NanoSPlus :
57
+ LedgerDevice(
58
+ name = "Nano S Plus",
59
+ usbInfo = UsbInfo(LEDGER_USB_VENDOR_ID, "0x50", "0x0005"),
60
+ bleInformation = null,
61
+ )
62
+
63
+ public data object NanoS :
64
+ LedgerDevice(
65
+ name = "Nano S",
66
+ usbInfo = UsbInfo(LEDGER_USB_VENDOR_ID, "0x10", "0x0001"),
67
+ bleInformation = null,
68
+ )
69
+
70
+ public companion object {
71
+ public const val LEDGER_USB_VENDOR_ID: String = "0x2c97"
72
+
73
+ // Cannot use reflexion here to get all subclasses as it depends of the JVM
74
+ private val subclasses = buildList {
75
+ add(Flex)
76
+ add(Stax)
77
+ add(NanoX)
78
+ add(NanoSPlus)
79
+ add(NanoS)
80
+ }
81
+
82
+ public fun getAllDevices(): List<LedgerDevice> {
83
+ return subclasses
84
+ }
85
+
86
+ public fun getAllDevicesWithBluetooth(): List<LedgerDevice> =
87
+ getAllDevices().filter { it.bleInformation != null }
88
+ }
89
+ }
@@ -0,0 +1,7 @@
1
+ package com.ledger.devicesdk.shared.api.device
2
+
3
+ public data class UsbInfo(
4
+ val vendorId: String,
5
+ val productIdMask: String,
6
+ val bootloaderProductId: String,
7
+ )
@@ -0,0 +1,10 @@
1
+ /*
2
+ * SPDX-FileCopyrightText: 2023 Ledger SAS
3
+ * SPDX-License-Identifier: LicenseRef-LEDGER
4
+ */
5
+
6
+ package com.ledger.devicesdk.shared.api.disconnection
7
+
8
+ public sealed class DisconnectionResult {
9
+ public data object Success : DisconnectionResult()
10
+ }
@@ -0,0 +1,10 @@
1
+ /*
2
+ * SPDX-FileCopyrightText: 2023 Ledger SAS
3
+ * SPDX-License-Identifier: LicenseRef-LEDGER
4
+ */
5
+
6
+ package com.ledger.devicesdk.shared.api.discovery
7
+
8
+ public sealed class ConnectivityType {
9
+ public data object Usb : ConnectivityType()
10
+ }
@@ -0,0 +1,18 @@
1
+ /*
2
+ * SPDX-FileCopyrightText: 2023 Ledger SAS
3
+ * SPDX-License-Identifier: LicenseRef-LEDGER
4
+ */
5
+
6
+ package com.ledger.devicesdk.shared.api.discovery
7
+
8
+ import com.ledger.devicesdk.shared.api.device.LedgerDevice
9
+ import kotlinx.datetime.Clock
10
+
11
+ public class DiscoveryDevice(
12
+ public val uid: String,
13
+ public val name: String,
14
+ public val ledgerDevice: LedgerDevice,
15
+ public val connectivityType: ConnectivityType,
16
+ ) {
17
+ public val timestamp: Long = Clock.System.now().toEpochMilliseconds()
18
+ }
@@ -0,0 +1,28 @@
1
+ /*
2
+ * SPDX-FileCopyrightText: 2023 Ledger SAS
3
+ * SPDX-License-Identifier: LicenseRef-LEDGER
4
+ */
5
+
6
+ package com.ledger.devicesdk.shared.api.discovery
7
+
8
+ public sealed class DiscoveryResult {
9
+ public data class DeviceDiscovered(
10
+ val devices: List<DiscoveryDevice>,
11
+ ) : DiscoveryResult()
12
+
13
+ public data object Ended : DiscoveryResult()
14
+
15
+ public sealed class Failure : DiscoveryResult() {
16
+ public data object LocationDisabled : Failure()
17
+
18
+ public data object BluetoothDisabled : Failure()
19
+
20
+ public data object BluetoothPermissionNotGranted : Failure()
21
+
22
+ public data object BluetoothBleNotSupported : Failure()
23
+
24
+ public data class Unknown(
25
+ val message: String,
26
+ ) : Failure()
27
+ }
28
+ }
@@ -0,0 +1,116 @@
1
+ /*
2
+ * SPDX-FileCopyrightText: 2024 Ledger SAS
3
+ * SPDX-License-Identifier: LicenseRef-LEDGER
4
+ */
5
+
6
+ package com.ledger.devicesdk.shared.api.utils
7
+
8
+ import com.ledger.devicesdk.shared.internal.transport.framer.toInt
9
+ import com.ledger.devicesdk.shared.internal.transport.framer.toIntOn2Bytes
10
+ import com.ledger.devicesdk.shared.internal.transport.framer.toUInt
11
+ import com.ledger.devicesdk.shared.api.utils.isNotHexadecimal
12
+ import com.ledger.devicesdk.shared.internal.transport.utils.isNotHexadecimal
13
+
14
+ public fun ByteArray.isHexadecimal(): Boolean {
15
+ return if (isEmpty()) {
16
+ false
17
+ } else {
18
+ var result = true
19
+ this.forEach {
20
+ if (it.isNotHexadecimal()) {
21
+ result = false
22
+ return@forEach
23
+ }
24
+ }
25
+ result
26
+ }
27
+ }
28
+
29
+ @OptIn(ExperimentalStdlibApi::class)
30
+ public fun ByteArray.toHexadecimalString(uppercase: Boolean = true): String {
31
+ var result = ""
32
+ forEach { result += it.toHexString() }
33
+ return if (uppercase) result.uppercase() else result
34
+ }
35
+
36
+ public fun ByteArray.extractField(
37
+ from: Int,
38
+ to: Int,
39
+ fromInclusive: Boolean = true,
40
+ toInclusive: Boolean = true,
41
+ ): ByteArray =
42
+ when (fromInclusive) {
43
+ true -> {
44
+ when (toInclusive) {
45
+ true -> this.sliceArray(from..to)
46
+ false -> this.sliceArray(from..<to)
47
+ }
48
+ }
49
+
50
+ false -> {
51
+ when (toInclusive) {
52
+ true -> this.sliceArray(from + 1..to)
53
+ false -> this.sliceArray(from + 1..<to)
54
+ }
55
+ }
56
+ }
57
+
58
+ public fun ByteArray.extractFieldAsIntOn2Bytes(
59
+ from: Int,
60
+ to: Int,
61
+ fromInclusive: Boolean = true,
62
+ toInclusive: Boolean = true,
63
+ ): Int =
64
+ this
65
+ .extractField(
66
+ from = from,
67
+ to = to,
68
+ fromInclusive = fromInclusive,
69
+ toInclusive = toInclusive,
70
+ ).toIntOn2Bytes()
71
+
72
+ public fun ByteArray.extractFieldAsUInt(
73
+ from: Int,
74
+ to: Int,
75
+ fromInclusive: Boolean = true,
76
+ toInclusive: Boolean = true,
77
+ ): UInt =
78
+ this
79
+ .extractField(
80
+ from = from,
81
+ to = to,
82
+ fromInclusive = fromInclusive,
83
+ toInclusive = toInclusive,
84
+ ).toUInt()
85
+
86
+ public fun ByteArray.extractFieldAsInt(
87
+ from: Int,
88
+ to: Int,
89
+ fromInclusive: Boolean = true,
90
+ toInclusive: Boolean = true,
91
+ ): Int =
92
+ this
93
+ .extractField(
94
+ from = from,
95
+ to = to,
96
+ fromInclusive = fromInclusive,
97
+ toInclusive = toInclusive,
98
+ ).toInt()
99
+
100
+ public fun ByteArray.decodeString(
101
+ from: Int,
102
+ to: Int,
103
+ fromInclusive: Boolean = true,
104
+ toInclusive: Boolean = true,
105
+ ): String =
106
+ this
107
+ .extractField(
108
+ from = from,
109
+ to = to,
110
+ fromInclusive = fromInclusive,
111
+ toInclusive = toInclusive,
112
+ ).decodeToString()
113
+
114
+ public fun ByteArray.extractFieldAtIndexAsInt(at: Int): Int = this[at].toInt()
115
+
116
+ public fun ByteArray.extractFieldAtIndexAsString(at: Int): String = this[at].toString()
@@ -0,0 +1,21 @@
1
+ /*
2
+ * SPDX-FileCopyrightText: 2024 Ledger SAS
3
+ * SPDX-License-Identifier: LicenseRef-LEDGER
4
+ */
5
+
6
+ package com.ledger.devicesdk.shared.api.utils
7
+
8
+ @Throws(IllegalStateException::class)
9
+ public fun String.fromHexStringToBytesOrThrow(): ByteArray {
10
+ check(this.isHexadecimal()) { "$this is not in hexadecimal format" }
11
+ return chunked(2)
12
+ .map(String::fromHexStringToByteOrThrow)
13
+ .toByteArray()
14
+ }
15
+
16
+ @Throws(NumberFormatException::class)
17
+ public fun String.fromHexStringToByteOrThrow(): Byte = this.toInt(16).toByte()
18
+
19
+ public fun String.isHexadecimal(): Boolean = this.isNotEmpty() && this.matches(Regex("^[A-Fa-f0-9]+$"))
20
+
21
+ public fun String.isNotHexadecimal(): Boolean = !this.isHexadecimal()
@@ -0,0 +1,13 @@
1
+ package com.ledger.devicesdk.shared.internal.connection
2
+
3
+ import com.ledger.devicesdk.shared.api.apdu.SendApduResult
4
+ import com.ledger.devicesdk.shared.api.device.LedgerDevice
5
+ import com.ledger.devicesdk.shared.api.discovery.ConnectivityType
6
+
7
+ internal data class InternalConnectedDevice(
8
+ val id: String,
9
+ val name: String,
10
+ val ledgerDevice: LedgerDevice,
11
+ val connectivity: ConnectivityType,
12
+ val sendApduFn: suspend (ByteArray) -> SendApduResult,
13
+ )
@@ -0,0 +1,41 @@
1
+ package com.ledger.devicesdk.shared.internal.connection
2
+
3
+ internal sealed class InternalConnectionResult {
4
+ data class Connected(
5
+ val device: InternalConnectedDevice,
6
+ val sessionId: String,
7
+ ) : InternalConnectionResult()
8
+
9
+ data class ConnectionError(
10
+ val error: Failure,
11
+ ) : InternalConnectionResult()
12
+
13
+ // Most of these failures are mapped from the ble library but could be cleaned
14
+ sealed class Failure {
15
+ data object PairingFailed : Failure()
16
+
17
+ data object ConnectionTimeout : Failure()
18
+
19
+ data object DeviceNotFound : Failure()
20
+
21
+ data object NoDeviceAddress : Failure()
22
+
23
+ data object ServiceNotFound : Failure()
24
+
25
+ data object InternalState : Failure()
26
+
27
+ data object InitializingFailed : Failure()
28
+
29
+ data object PermissionNotGranted : Failure()
30
+
31
+ data object DeviceConnectivityBluetoothDisabled : Failure()
32
+
33
+ data object DeviceConnectivityLocationDisabled : Failure()
34
+
35
+ data object BleNotSupported : Failure()
36
+
37
+ data class Unknown(
38
+ val msg: String?,
39
+ ) : Failure()
40
+ }
41
+ }
@@ -0,0 +1,25 @@
1
+ /*
2
+ * SPDX-FileCopyrightText: 2024 Ledger SAS
3
+ * SPDX-License-Identifier: LicenseRef-LEDGER
4
+ */
5
+
6
+ package com.ledger.devicesdk.shared.internal.coroutine
7
+
8
+ import kotlin.coroutines.CoroutineContext
9
+ import kotlinx.coroutines.CoroutineScope
10
+ import kotlinx.coroutines.cancelChildren
11
+
12
+ internal val sdkScope: SdkCloseableScope
13
+ get() = getSdkScope()
14
+
15
+ internal class SdkCloseableScope(
16
+ context: CoroutineContext,
17
+ ) : CoroutineScope{
18
+ override val coroutineContext: CoroutineContext = context
19
+
20
+ override fun toString(): String = "CoroutineScope(context = $coroutineContext)"
21
+
22
+ fun close() {
23
+ coroutineContext.cancelChildren()
24
+ }
25
+ }