@bota.dev/react-native-app-sdk 2.0.0-beta.0
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/BotaDeviceSDK.podspec +55 -0
- package/LICENSE +21 -0
- package/README.md +101 -0
- package/android/build.gradle +76 -0
- package/android/src/main/AndroidManifest.xml +1 -0
- package/android/src/main/java/dev/bota/sdk/reactnative/BotaDeviceSDKAndroidDevices.kt +172 -0
- package/android/src/main/java/dev/bota/sdk/reactnative/BotaDeviceSDKAndroidLifecycle.kt +100 -0
- package/android/src/main/java/dev/bota/sdk/reactnative/BotaDeviceSDKAndroidLogs.kt +82 -0
- package/android/src/main/java/dev/bota/sdk/reactnative/BotaDeviceSDKAndroidModels.kt +476 -0
- package/android/src/main/java/dev/bota/sdk/reactnative/BotaDeviceSDKAndroidOTA.kt +58 -0
- package/android/src/main/java/dev/bota/sdk/reactnative/BotaDeviceSDKAndroidRecordingUploads.kt +180 -0
- package/android/src/main/java/dev/bota/sdk/reactnative/BotaDeviceSDKAndroidRecordings.kt +751 -0
- package/android/src/main/java/dev/bota/sdk/reactnative/BotaDeviceSDKAndroidSecurity.kt +509 -0
- package/android/src/main/java/dev/bota/sdk/reactnative/BotaDeviceSDKAndroidWiFi.kt +125 -0
- package/android/src/main/java/dev/bota/sdk/reactnative/BotaDeviceSDKEncryptedUploadV2Materials.kt +46 -0
- package/android/src/main/java/dev/bota/sdk/reactnative/BotaDeviceSDKModule.kt +723 -0
- package/android/src/main/java/dev/bota/sdk/reactnative/BotaDeviceSDKPackage.kt +33 -0
- package/android/src/test/java/dev/bota/sdk/reactnative/BotaDeviceSDKAndroidDevicesTest.kt +211 -0
- package/android/src/test/java/dev/bota/sdk/reactnative/BotaDeviceSDKAndroidLifecycleTest.kt +132 -0
- package/android/src/test/java/dev/bota/sdk/reactnative/BotaDeviceSDKAndroidLogsTest.kt +57 -0
- package/android/src/test/java/dev/bota/sdk/reactnative/BotaDeviceSDKAndroidOTATest.kt +75 -0
- package/android/src/test/java/dev/bota/sdk/reactnative/BotaDeviceSDKAndroidRecordingUploadsTest.kt +147 -0
- package/android/src/test/java/dev/bota/sdk/reactnative/BotaDeviceSDKAndroidRecordingsTest.kt +355 -0
- package/android/src/test/java/dev/bota/sdk/reactnative/BotaDeviceSDKAndroidSecurityTest.kt +523 -0
- package/android/src/test/java/dev/bota/sdk/reactnative/BotaDeviceSDKAndroidWiFiTest.kt +100 -0
- package/generated/codegen-contract.json +3593 -0
- package/ios/BotaDeviceSDK.mm +1300 -0
- package/ios/BotaDeviceSDKAppleBridge.swift +2260 -0
- package/ios/BotaDeviceSDKAppleDevices.swift +191 -0
- package/ios/BotaDeviceSDKAppleLifecycle.swift +167 -0
- package/ios/BotaDeviceSDKAppleLogs.swift +79 -0
- package/ios/BotaDeviceSDKAppleOTA.swift +73 -0
- package/ios/BotaDeviceSDKAppleRecordingUploads.swift +178 -0
- package/ios/BotaDeviceSDKAppleRecordings.swift +670 -0
- package/ios/BotaDeviceSDKAppleSecurity.swift +586 -0
- package/ios/BotaDeviceSDKAppleWiFi.swift +128 -0
- package/ios/BotaDeviceSDKEncryptedUploadV2Materials.swift +99 -0
- package/lib/commonjs/BotaClient.js +194 -0
- package/lib/commonjs/BotaClient.js.map +1 -0
- package/lib/commonjs/ble/constants.js +10 -0
- package/lib/commonjs/ble/constants.js.map +1 -0
- package/lib/commonjs/ble/deviceLogs.js +50 -0
- package/lib/commonjs/ble/deviceLogs.js.map +1 -0
- package/lib/commonjs/cache/DeviceStateCache.js +6 -0
- package/lib/commonjs/cache/DeviceStateCache.js.map +1 -0
- package/lib/commonjs/client.js +746 -0
- package/lib/commonjs/client.js.map +1 -0
- package/lib/commonjs/compatibility/runtime.js +44 -0
- package/lib/commonjs/compatibility/runtime.js.map +1 -0
- package/lib/commonjs/index.js +123 -0
- package/lib/commonjs/index.js.map +1 -0
- package/lib/commonjs/managers/DeviceManager.js +647 -0
- package/lib/commonjs/managers/DeviceManager.js.map +1 -0
- package/lib/commonjs/managers/OTAManager.js +140 -0
- package/lib/commonjs/managers/OTAManager.js.map +1 -0
- package/lib/commonjs/managers/RecordingManager.js +444 -0
- package/lib/commonjs/managers/RecordingManager.js.map +1 -0
- package/lib/commonjs/managers/StreamingSession.js +175 -0
- package/lib/commonjs/managers/StreamingSession.js.map +1 -0
- package/lib/commonjs/managers/types.js +6 -0
- package/lib/commonjs/managers/types.js.map +1 -0
- package/lib/commonjs/models/Device.js +2 -0
- package/lib/commonjs/models/Device.js.map +1 -0
- package/lib/commonjs/models/Recording.js +2 -0
- package/lib/commonjs/models/Recording.js.map +1 -0
- package/lib/commonjs/models/Status.js +6 -0
- package/lib/commonjs/models/Status.js.map +1 -0
- package/lib/commonjs/package.json +1 -0
- package/lib/commonjs/specs/NativeBotaDeviceSDK.js +9 -0
- package/lib/commonjs/specs/NativeBotaDeviceSDK.js.map +1 -0
- package/lib/commonjs/sync/syncStatus.js +178 -0
- package/lib/commonjs/sync/syncStatus.js.map +1 -0
- package/lib/commonjs/utils/errors.js +194 -0
- package/lib/commonjs/utils/errors.js.map +1 -0
- package/lib/commonjs/utils/logger.js +84 -0
- package/lib/commonjs/utils/logger.js.map +1 -0
- package/lib/module/BotaClient.js +189 -0
- package/lib/module/BotaClient.js.map +1 -0
- package/lib/module/ble/constants.js +6 -0
- package/lib/module/ble/constants.js.map +1 -0
- package/lib/module/ble/deviceLogs.js +45 -0
- package/lib/module/ble/deviceLogs.js.map +1 -0
- package/lib/module/cache/DeviceStateCache.js +4 -0
- package/lib/module/cache/DeviceStateCache.js.map +1 -0
- package/lib/module/client.js +740 -0
- package/lib/module/client.js.map +1 -0
- package/lib/module/compatibility/runtime.js +35 -0
- package/lib/module/compatibility/runtime.js.map +1 -0
- package/lib/module/index.js +17 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/managers/DeviceManager.js +641 -0
- package/lib/module/managers/DeviceManager.js.map +1 -0
- package/lib/module/managers/OTAManager.js +134 -0
- package/lib/module/managers/OTAManager.js.map +1 -0
- package/lib/module/managers/RecordingManager.js +438 -0
- package/lib/module/managers/RecordingManager.js.map +1 -0
- package/lib/module/managers/StreamingSession.js +168 -0
- package/lib/module/managers/StreamingSession.js.map +1 -0
- package/lib/module/managers/types.js +4 -0
- package/lib/module/managers/types.js.map +1 -0
- package/lib/module/models/Device.js +2 -0
- package/lib/module/models/Device.js.map +1 -0
- package/lib/module/models/Recording.js +2 -0
- package/lib/module/models/Recording.js.map +1 -0
- package/lib/module/models/Status.js +4 -0
- package/lib/module/models/Status.js.map +1 -0
- package/lib/module/specs/NativeBotaDeviceSDK.js +5 -0
- package/lib/module/specs/NativeBotaDeviceSDK.js.map +1 -0
- package/lib/module/sync/syncStatus.js +174 -0
- package/lib/module/sync/syncStatus.js.map +1 -0
- package/lib/module/utils/errors.js +183 -0
- package/lib/module/utils/errors.js.map +1 -0
- package/lib/module/utils/logger.js +80 -0
- package/lib/module/utils/logger.js.map +1 -0
- package/lib/typescript/BotaClient.d.ts +40 -0
- package/lib/typescript/BotaClient.d.ts.map +1 -0
- package/lib/typescript/ble/constants.d.ts +4 -0
- package/lib/typescript/ble/constants.d.ts.map +1 -0
- package/lib/typescript/ble/deviceLogs.d.ts +10 -0
- package/lib/typescript/ble/deviceLogs.d.ts.map +1 -0
- package/lib/typescript/cache/DeviceStateCache.d.ts +14 -0
- package/lib/typescript/cache/DeviceStateCache.d.ts.map +1 -0
- package/lib/typescript/client.d.ts +208 -0
- package/lib/typescript/client.d.ts.map +1 -0
- package/lib/typescript/compatibility/runtime.d.ts +11 -0
- package/lib/typescript/compatibility/runtime.d.ts.map +1 -0
- package/lib/typescript/index.d.ts +19 -0
- package/lib/typescript/index.d.ts.map +1 -0
- package/lib/typescript/managers/DeviceManager.d.ts +117 -0
- package/lib/typescript/managers/DeviceManager.d.ts.map +1 -0
- package/lib/typescript/managers/OTAManager.d.ts +25 -0
- package/lib/typescript/managers/OTAManager.d.ts.map +1 -0
- package/lib/typescript/managers/RecordingManager.d.ts +54 -0
- package/lib/typescript/managers/RecordingManager.d.ts.map +1 -0
- package/lib/typescript/managers/StreamingSession.d.ts +35 -0
- package/lib/typescript/managers/StreamingSession.d.ts.map +1 -0
- package/lib/typescript/managers/types.d.ts +19 -0
- package/lib/typescript/managers/types.d.ts.map +1 -0
- package/lib/typescript/models/Device.d.ts +392 -0
- package/lib/typescript/models/Device.d.ts.map +1 -0
- package/lib/typescript/models/Recording.d.ts +259 -0
- package/lib/typescript/models/Recording.d.ts.map +1 -0
- package/lib/typescript/models/Status.d.ts +106 -0
- package/lib/typescript/models/Status.d.ts.map +1 -0
- package/lib/typescript/specs/NativeBotaDeviceSDK.d.ts +393 -0
- package/lib/typescript/specs/NativeBotaDeviceSDK.d.ts.map +1 -0
- package/lib/typescript/sync/syncStatus.d.ts +123 -0
- package/lib/typescript/sync/syncStatus.d.ts.map +1 -0
- package/lib/typescript/utils/errors.d.ts +83 -0
- package/lib/typescript/utils/errors.d.ts.map +1 -0
- package/lib/typescript/utils/logger.d.ts +36 -0
- package/lib/typescript/utils/logger.d.ts.map +1 -0
- package/package.json +100 -0
- package/scripts/bota_device_sdk_spm_workaround.rb +53 -0
- package/src/BotaClient.ts +231 -0
- package/src/ble/constants.ts +3 -0
- package/src/ble/deviceLogs.ts +61 -0
- package/src/cache/DeviceStateCache.ts +20 -0
- package/src/client.ts +1410 -0
- package/src/compatibility/runtime.ts +55 -0
- package/src/index.ts +146 -0
- package/src/managers/DeviceManager.ts +927 -0
- package/src/managers/OTAManager.ts +195 -0
- package/src/managers/RecordingManager.ts +578 -0
- package/src/managers/StreamingSession.ts +212 -0
- package/src/managers/types.ts +36 -0
- package/src/models/Device.ts +484 -0
- package/src/models/Recording.ts +281 -0
- package/src/models/Status.ts +128 -0
- package/src/specs/NativeBotaDeviceSDK.ts +542 -0
- package/src/sync/syncStatus.ts +243 -0
- package/src/utils/errors.ts +327 -0
- package/src/utils/logger.ts +122 -0
|
@@ -0,0 +1,509 @@
|
|
|
1
|
+
package dev.bota.sdk.reactnative
|
|
2
|
+
|
|
3
|
+
import dev.bota.sdk.BotaDeviceClient
|
|
4
|
+
import dev.bota.sdk.DeviceApiEnvironment
|
|
5
|
+
import dev.bota.sdk.model.ConnectedDevice
|
|
6
|
+
import dev.bota.sdk.model.DeviceConnectionSettings
|
|
7
|
+
import dev.bota.sdk.model.DeprovisionResult
|
|
8
|
+
import dev.bota.sdk.model.FactoryResetCompletion
|
|
9
|
+
import dev.bota.sdk.model.FactoryResetGrantRequest
|
|
10
|
+
import dev.bota.sdk.model.FactoryResetPersistenceResult
|
|
11
|
+
import dev.bota.sdk.model.ProvisioningMaterial
|
|
12
|
+
import dev.bota.sdk.model.ProvisioningMaterialRequest
|
|
13
|
+
import dev.bota.sdk.model.RecordingControlResult
|
|
14
|
+
import dev.bota.sdk.model.RecordingState
|
|
15
|
+
import java.util.Base64
|
|
16
|
+
import java.util.UUID
|
|
17
|
+
import kotlinx.coroutines.CancellationException
|
|
18
|
+
import kotlinx.coroutines.CompletableDeferred
|
|
19
|
+
import kotlinx.coroutines.CoroutineScope
|
|
20
|
+
import kotlinx.coroutines.CoroutineStart
|
|
21
|
+
import kotlinx.coroutines.Dispatchers
|
|
22
|
+
import kotlinx.coroutines.Job
|
|
23
|
+
import kotlinx.coroutines.SupervisorJob
|
|
24
|
+
import kotlinx.coroutines.cancelAndJoin
|
|
25
|
+
import kotlinx.coroutines.flow.Flow
|
|
26
|
+
import kotlinx.coroutines.flow.collect
|
|
27
|
+
import kotlinx.coroutines.launch
|
|
28
|
+
import kotlinx.coroutines.sync.Mutex
|
|
29
|
+
import kotlinx.coroutines.sync.withLock
|
|
30
|
+
|
|
31
|
+
internal data class BotaDeviceSDKAndroidProvisioningRequest(
|
|
32
|
+
val requestId: String,
|
|
33
|
+
val serialNumber: String,
|
|
34
|
+
val nonce: String,
|
|
35
|
+
val devicePublicKey: String,
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
internal data class BotaDeviceSDKAndroidFactoryResetRequest(
|
|
39
|
+
val requestId: String,
|
|
40
|
+
val serialNumber: String,
|
|
41
|
+
val nonce: String,
|
|
42
|
+
val commandId: String,
|
|
43
|
+
val bindingGeneration: ULong,
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
internal data class BotaDeviceSDKAndroidFactoryResetPersistenceRequest(
|
|
47
|
+
val requestId: String,
|
|
48
|
+
val localRecordingsDeleted: UShort,
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
internal interface BotaDeviceSDKAndroidSecurityClient {
|
|
52
|
+
suspend fun isProvisioned(device: ConnectedDevice): Boolean
|
|
53
|
+
suspend fun readPublicKey(device: ConnectedDevice): String?
|
|
54
|
+
suspend fun readAuthNonce(device: ConnectedDevice): String?
|
|
55
|
+
suspend fun setApiEndpoint(environment: DeviceApiEnvironment, device: ConnectedDevice)
|
|
56
|
+
suspend fun deliverCertificate(certificatePem: String, privateKeyPem: String, device: ConnectedDevice)
|
|
57
|
+
suspend fun deliverBackendPublicKey(publicKey: ByteArray, device: ConnectedDevice)
|
|
58
|
+
suspend fun writeGrant(grantBlob: String, device: ConnectedDevice)
|
|
59
|
+
suspend fun syncTime(device: ConnectedDevice)
|
|
60
|
+
suspend fun requestStartRecording(
|
|
61
|
+
device: ConnectedDevice,
|
|
62
|
+
grantBlob: String,
|
|
63
|
+
): RecordingControlResult
|
|
64
|
+
|
|
65
|
+
suspend fun requestStopRecording(
|
|
66
|
+
device: ConnectedDevice,
|
|
67
|
+
grantBlob: String,
|
|
68
|
+
): RecordingControlResult
|
|
69
|
+
|
|
70
|
+
suspend fun readRecordingState(device: ConnectedDevice): RecordingState
|
|
71
|
+
|
|
72
|
+
fun recordingStateUpdates(device: ConnectedDevice): Flow<RecordingState>
|
|
73
|
+
suspend fun provision(
|
|
74
|
+
device: ConnectedDevice,
|
|
75
|
+
provider: suspend (ProvisioningMaterialRequest) -> ProvisioningMaterial,
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
suspend fun deprovision(device: ConnectedDevice, grantBlob: String): DeprovisionResult
|
|
79
|
+
|
|
80
|
+
suspend fun readConnectionSettings(device: ConnectedDevice): DeviceConnectionSettings
|
|
81
|
+
|
|
82
|
+
suspend fun writeConnectionSettings(
|
|
83
|
+
settings: DeviceConnectionSettings,
|
|
84
|
+
device: ConnectedDevice,
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
suspend fun factoryReset(
|
|
88
|
+
device: ConnectedDevice,
|
|
89
|
+
commandId: String,
|
|
90
|
+
bindingGeneration: ULong,
|
|
91
|
+
persistResult: (suspend (FactoryResetPersistenceResult) -> Unit)?,
|
|
92
|
+
provider: suspend (FactoryResetGrantRequest) -> ByteArray,
|
|
93
|
+
): FactoryResetCompletion
|
|
94
|
+
|
|
95
|
+
suspend fun resumePendingFactoryReset(
|
|
96
|
+
device: ConnectedDevice,
|
|
97
|
+
currentBindingGeneration: ULong,
|
|
98
|
+
persistResult: (suspend (FactoryResetPersistenceResult) -> Unit)?,
|
|
99
|
+
): FactoryResetCompletion?
|
|
100
|
+
|
|
101
|
+
suspend fun resumeUnjournaledFactoryReset(
|
|
102
|
+
device: ConnectedDevice,
|
|
103
|
+
commandId: String,
|
|
104
|
+
bindingGeneration: ULong,
|
|
105
|
+
persistResult: suspend (FactoryResetPersistenceResult) -> Unit,
|
|
106
|
+
): FactoryResetCompletion
|
|
107
|
+
|
|
108
|
+
suspend fun cancelCurrentOperation()
|
|
109
|
+
|
|
110
|
+
suspend fun cancelFactoryReset()
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
internal class BotaDeviceSDKSharedAndroidSecurityClient(
|
|
114
|
+
private val client: BotaDeviceClient = BotaDeviceClient.shared,
|
|
115
|
+
) : BotaDeviceSDKAndroidSecurityClient {
|
|
116
|
+
override suspend fun isProvisioned(device: ConnectedDevice): Boolean =
|
|
117
|
+
client.controls.isProvisioned(device)
|
|
118
|
+
|
|
119
|
+
override suspend fun readPublicKey(device: ConnectedDevice): String? =
|
|
120
|
+
client.controls.readPublicKey(device)
|
|
121
|
+
|
|
122
|
+
override suspend fun readAuthNonce(device: ConnectedDevice): String? =
|
|
123
|
+
client.controls.readAuthNonce(device)
|
|
124
|
+
|
|
125
|
+
override suspend fun setApiEndpoint(environment: DeviceApiEnvironment, device: ConnectedDevice) {
|
|
126
|
+
client.controls.setApiEndpoint(environment, device)
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
override suspend fun deliverCertificate(
|
|
130
|
+
certificatePem: String,
|
|
131
|
+
privateKeyPem: String,
|
|
132
|
+
device: ConnectedDevice,
|
|
133
|
+
) {
|
|
134
|
+
client.controls.deliverCertificate(certificatePem, privateKeyPem, device)
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
override suspend fun deliverBackendPublicKey(publicKey: ByteArray, device: ConnectedDevice) {
|
|
138
|
+
client.controls.deliverBackendPublicKey(publicKey, device)
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
override suspend fun writeGrant(grantBlob: String, device: ConnectedDevice) {
|
|
142
|
+
client.controls.writeGrant(grantBlob, device)
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
override suspend fun syncTime(device: ConnectedDevice) {
|
|
146
|
+
client.controls.syncTime(device = device)
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
override suspend fun requestStartRecording(
|
|
150
|
+
device: ConnectedDevice,
|
|
151
|
+
grantBlob: String,
|
|
152
|
+
): RecordingControlResult = client.controls.requestStartRecording(device, grantBlob)
|
|
153
|
+
|
|
154
|
+
override suspend fun requestStopRecording(
|
|
155
|
+
device: ConnectedDevice,
|
|
156
|
+
grantBlob: String,
|
|
157
|
+
): RecordingControlResult = client.controls.requestStopRecording(device, grantBlob)
|
|
158
|
+
|
|
159
|
+
override suspend fun readRecordingState(device: ConnectedDevice): RecordingState =
|
|
160
|
+
client.controls.readRecordingState(device)
|
|
161
|
+
|
|
162
|
+
override fun recordingStateUpdates(device: ConnectedDevice): Flow<RecordingState> =
|
|
163
|
+
client.controls.recordingStateUpdates(device)
|
|
164
|
+
|
|
165
|
+
override suspend fun provision(
|
|
166
|
+
device: ConnectedDevice,
|
|
167
|
+
provider: suspend (ProvisioningMaterialRequest) -> ProvisioningMaterial,
|
|
168
|
+
) {
|
|
169
|
+
client.provisioning.provision(device, provider)
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
override suspend fun deprovision(
|
|
173
|
+
device: ConnectedDevice,
|
|
174
|
+
grantBlob: String,
|
|
175
|
+
): DeprovisionResult = client.provisioning.deprovision(device, grantBlob)
|
|
176
|
+
|
|
177
|
+
override suspend fun readConnectionSettings(device: ConnectedDevice): DeviceConnectionSettings =
|
|
178
|
+
client.provisioning.readConnectionSettings(device)
|
|
179
|
+
|
|
180
|
+
override suspend fun writeConnectionSettings(
|
|
181
|
+
settings: DeviceConnectionSettings,
|
|
182
|
+
device: ConnectedDevice,
|
|
183
|
+
) {
|
|
184
|
+
client.provisioning.writeConnectionSettings(settings, device)
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
override suspend fun factoryReset(
|
|
188
|
+
device: ConnectedDevice,
|
|
189
|
+
commandId: String,
|
|
190
|
+
bindingGeneration: ULong,
|
|
191
|
+
persistResult: (suspend (FactoryResetPersistenceResult) -> Unit)?,
|
|
192
|
+
provider: suspend (FactoryResetGrantRequest) -> ByteArray,
|
|
193
|
+
): FactoryResetCompletion = client.factoryReset.factoryReset(
|
|
194
|
+
device,
|
|
195
|
+
commandId,
|
|
196
|
+
bindingGeneration,
|
|
197
|
+
persistResult,
|
|
198
|
+
provider,
|
|
199
|
+
)
|
|
200
|
+
|
|
201
|
+
override suspend fun resumePendingFactoryReset(
|
|
202
|
+
device: ConnectedDevice,
|
|
203
|
+
currentBindingGeneration: ULong,
|
|
204
|
+
persistResult: (suspend (FactoryResetPersistenceResult) -> Unit)?,
|
|
205
|
+
): FactoryResetCompletion? = client.factoryReset.resumePendingFactoryReset(
|
|
206
|
+
device,
|
|
207
|
+
currentBindingGeneration,
|
|
208
|
+
persistResult,
|
|
209
|
+
)
|
|
210
|
+
|
|
211
|
+
override suspend fun resumeUnjournaledFactoryReset(
|
|
212
|
+
device: ConnectedDevice,
|
|
213
|
+
commandId: String,
|
|
214
|
+
bindingGeneration: ULong,
|
|
215
|
+
persistResult: suspend (FactoryResetPersistenceResult) -> Unit,
|
|
216
|
+
): FactoryResetCompletion = client.factoryReset.resumeUnjournaledFactoryReset(
|
|
217
|
+
device,
|
|
218
|
+
commandId,
|
|
219
|
+
bindingGeneration,
|
|
220
|
+
persistResult,
|
|
221
|
+
)
|
|
222
|
+
|
|
223
|
+
override suspend fun cancelCurrentOperation() {
|
|
224
|
+
client.provisioning.cancelCurrentOperation()
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
override suspend fun cancelFactoryReset() {
|
|
228
|
+
client.factoryReset.cancelCurrentOperation()
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
internal class BotaDeviceSDKAndroidSecurity(
|
|
233
|
+
private val client: BotaDeviceSDKAndroidSecurityClient = BotaDeviceSDKSharedAndroidSecurityClient(),
|
|
234
|
+
private val scope: CoroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.Default),
|
|
235
|
+
) {
|
|
236
|
+
private val lock = Any()
|
|
237
|
+
private val streamOperations = Mutex()
|
|
238
|
+
private val streamLock = Any()
|
|
239
|
+
private val provisioningRequests = mutableMapOf<String, CompletableDeferred<ProvisioningMaterial>>()
|
|
240
|
+
private val factoryResetRequests = mutableMapOf<String, CompletableDeferred<ByteArray>>()
|
|
241
|
+
private val factoryResetPersistenceRequests = mutableMapOf<String, CompletableDeferred<Unit>>()
|
|
242
|
+
private var recordingStateStream: Job? = null
|
|
243
|
+
|
|
244
|
+
suspend fun provision(
|
|
245
|
+
device: ConnectedDevice,
|
|
246
|
+
onMaterialRequest: (BotaDeviceSDKAndroidProvisioningRequest) -> Unit,
|
|
247
|
+
) {
|
|
248
|
+
client.provision(device) { request ->
|
|
249
|
+
requestProvisioningMaterial(request, onMaterialRequest)
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
suspend fun deprovision(device: ConnectedDevice, grantBlob: String): DeprovisionResult =
|
|
254
|
+
client.deprovision(device, grantBlob)
|
|
255
|
+
|
|
256
|
+
suspend fun isProvisioned(device: ConnectedDevice): Boolean = client.isProvisioned(device)
|
|
257
|
+
suspend fun readPublicKey(device: ConnectedDevice): String? = client.readPublicKey(device)
|
|
258
|
+
suspend fun readAuthNonce(device: ConnectedDevice): String? = client.readAuthNonce(device)
|
|
259
|
+
suspend fun setApiEndpoint(environment: DeviceApiEnvironment, device: ConnectedDevice) =
|
|
260
|
+
client.setApiEndpoint(environment, device)
|
|
261
|
+
suspend fun deliverCertificate(certificatePem: String, privateKeyPem: String, device: ConnectedDevice) =
|
|
262
|
+
client.deliverCertificate(certificatePem, privateKeyPem, device)
|
|
263
|
+
suspend fun deliverBackendPublicKey(publicKey: ByteArray, device: ConnectedDevice) =
|
|
264
|
+
client.deliverBackendPublicKey(publicKey, device)
|
|
265
|
+
suspend fun writeGrant(grantBlob: String, device: ConnectedDevice) = client.writeGrant(grantBlob, device)
|
|
266
|
+
suspend fun syncTime(device: ConnectedDevice) = client.syncTime(device)
|
|
267
|
+
suspend fun requestStartRecording(
|
|
268
|
+
device: ConnectedDevice,
|
|
269
|
+
grantBlob: String,
|
|
270
|
+
): RecordingControlResult = client.requestStartRecording(device, grantBlob)
|
|
271
|
+
suspend fun requestStopRecording(
|
|
272
|
+
device: ConnectedDevice,
|
|
273
|
+
grantBlob: String,
|
|
274
|
+
): RecordingControlResult = client.requestStopRecording(device, grantBlob)
|
|
275
|
+
suspend fun readRecordingState(device: ConnectedDevice): RecordingState =
|
|
276
|
+
client.readRecordingState(device)
|
|
277
|
+
|
|
278
|
+
suspend fun startRecordingStateUpdates(
|
|
279
|
+
device: ConnectedDevice,
|
|
280
|
+
onError: (Throwable) -> Unit = {},
|
|
281
|
+
onState: (RecordingState) -> Unit,
|
|
282
|
+
) = streamOperations.withLock {
|
|
283
|
+
stopOwnedRecordingStateStream()
|
|
284
|
+
val updates = client.recordingStateUpdates(device)
|
|
285
|
+
lateinit var task: Job
|
|
286
|
+
task = scope.launch(start = CoroutineStart.LAZY) {
|
|
287
|
+
try {
|
|
288
|
+
updates.collect(onState)
|
|
289
|
+
} catch (_: CancellationException) {
|
|
290
|
+
// Explicit stop is not a recording-state stream failure.
|
|
291
|
+
} catch (error: Throwable) {
|
|
292
|
+
onError(error)
|
|
293
|
+
} finally {
|
|
294
|
+
synchronized(streamLock) {
|
|
295
|
+
if (recordingStateStream === task) recordingStateStream = null
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
synchronized(streamLock) { recordingStateStream = task }
|
|
300
|
+
task.start()
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
suspend fun stopRecordingStateUpdates() = streamOperations.withLock {
|
|
304
|
+
stopOwnedRecordingStateStream()
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
suspend fun readConnectionSettings(device: ConnectedDevice): DeviceConnectionSettings =
|
|
308
|
+
client.readConnectionSettings(device)
|
|
309
|
+
|
|
310
|
+
suspend fun writeConnectionSettings(
|
|
311
|
+
settings: DeviceConnectionSettings,
|
|
312
|
+
device: ConnectedDevice,
|
|
313
|
+
) {
|
|
314
|
+
client.writeConnectionSettings(settings, device)
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
suspend fun factoryReset(
|
|
318
|
+
device: ConnectedDevice,
|
|
319
|
+
commandId: String,
|
|
320
|
+
bindingGeneration: ULong,
|
|
321
|
+
onGrantRequest: (BotaDeviceSDKAndroidFactoryResetRequest) -> Unit,
|
|
322
|
+
onPersistenceRequest: ((BotaDeviceSDKAndroidFactoryResetPersistenceRequest) -> Unit)?,
|
|
323
|
+
): FactoryResetCompletion = client.factoryReset(
|
|
324
|
+
device = device,
|
|
325
|
+
commandId = commandId,
|
|
326
|
+
bindingGeneration = bindingGeneration,
|
|
327
|
+
persistResult = onPersistenceRequest?.let { onRequest ->
|
|
328
|
+
{ result -> requestFactoryResetPersistence(result, onRequest) }
|
|
329
|
+
},
|
|
330
|
+
provider = { request -> requestFactoryResetGrant(request, onGrantRequest) },
|
|
331
|
+
)
|
|
332
|
+
|
|
333
|
+
suspend fun resumePendingFactoryReset(
|
|
334
|
+
device: ConnectedDevice,
|
|
335
|
+
currentBindingGeneration: ULong,
|
|
336
|
+
onPersistenceRequest: ((BotaDeviceSDKAndroidFactoryResetPersistenceRequest) -> Unit)?,
|
|
337
|
+
): FactoryResetCompletion? {
|
|
338
|
+
val persister: (suspend (FactoryResetPersistenceResult) -> Unit)? =
|
|
339
|
+
if (onPersistenceRequest == null) {
|
|
340
|
+
null
|
|
341
|
+
} else {
|
|
342
|
+
{ result ->
|
|
343
|
+
requestFactoryResetPersistence(result, onPersistenceRequest)
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
val pending = client.resumePendingFactoryReset(
|
|
347
|
+
device = device,
|
|
348
|
+
currentBindingGeneration = currentBindingGeneration,
|
|
349
|
+
persistResult = persister,
|
|
350
|
+
)
|
|
351
|
+
if (pending != null || persister == null) return pending
|
|
352
|
+
return client.resumeUnjournaledFactoryReset(
|
|
353
|
+
device = device,
|
|
354
|
+
commandId = "rn-resume-${UUID.randomUUID()}",
|
|
355
|
+
bindingGeneration = currentBindingGeneration,
|
|
356
|
+
persistResult = persister,
|
|
357
|
+
)
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
fun resolveProvisioningMaterial(
|
|
361
|
+
requestId: String,
|
|
362
|
+
apiEndpoint: String,
|
|
363
|
+
deviceToken: String,
|
|
364
|
+
mtu: ULong,
|
|
365
|
+
) {
|
|
366
|
+
pendingRequest(requestId).complete(
|
|
367
|
+
ProvisioningMaterial(
|
|
368
|
+
apiEndpoint = apiEndpoint.encodeToByteArray(),
|
|
369
|
+
deviceToken = deviceToken.encodeToByteArray(),
|
|
370
|
+
mtu = mtu,
|
|
371
|
+
),
|
|
372
|
+
)
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
fun rejectApplicationMaterial(requestId: String, message: String) {
|
|
376
|
+
synchronized(lock) { provisioningRequests.remove(requestId) }
|
|
377
|
+
?.completeExceptionally(IllegalStateException(message))
|
|
378
|
+
?: synchronized(lock) { factoryResetRequests.remove(requestId) }
|
|
379
|
+
?.completeExceptionally(IllegalStateException(message))
|
|
380
|
+
?: synchronized(lock) { factoryResetPersistenceRequests.remove(requestId) }
|
|
381
|
+
?.completeExceptionally(IllegalStateException(message))
|
|
382
|
+
?: error("application material request is no longer pending")
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
fun resolveFactoryResetGrant(requestId: String, grantBlob: String) {
|
|
386
|
+
val pending = synchronized(lock) { factoryResetRequests.remove(requestId) }
|
|
387
|
+
?: error("application material request is no longer pending")
|
|
388
|
+
val grant = runCatching { Base64.getDecoder().decode(grantBlob) }
|
|
389
|
+
.getOrElse {
|
|
390
|
+
pending.completeExceptionally(
|
|
391
|
+
IllegalArgumentException("factory reset grant is not valid encoded data"),
|
|
392
|
+
)
|
|
393
|
+
return
|
|
394
|
+
}
|
|
395
|
+
if (grant.isEmpty()) {
|
|
396
|
+
pending.completeExceptionally(
|
|
397
|
+
IllegalArgumentException("factory reset grant is not valid encoded data"),
|
|
398
|
+
)
|
|
399
|
+
return
|
|
400
|
+
}
|
|
401
|
+
pending.complete(grant)
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
fun resolveFactoryResetResultPersistence(requestId: String) {
|
|
405
|
+
val pending = synchronized(lock) { factoryResetPersistenceRequests.remove(requestId) }
|
|
406
|
+
?: error("application material request is no longer pending")
|
|
407
|
+
pending.complete(Unit)
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
suspend fun cancelAll() {
|
|
411
|
+
runCatching { stopRecordingStateUpdates() }
|
|
412
|
+
val pending = synchronized(lock) {
|
|
413
|
+
provisioningRequests.values.toList().also { provisioningRequests.clear() }
|
|
414
|
+
}
|
|
415
|
+
pending.forEach { it.cancel() }
|
|
416
|
+
val pendingResets = synchronized(lock) {
|
|
417
|
+
factoryResetRequests.values.toList().also { factoryResetRequests.clear() }
|
|
418
|
+
}
|
|
419
|
+
pendingResets.forEach { it.cancel() }
|
|
420
|
+
val pendingPersistence = synchronized(lock) {
|
|
421
|
+
factoryResetPersistenceRequests.values.toList().also {
|
|
422
|
+
factoryResetPersistenceRequests.clear()
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
pendingPersistence.forEach { it.cancel() }
|
|
426
|
+
runCatching { client.cancelCurrentOperation() }
|
|
427
|
+
runCatching { client.cancelFactoryReset() }
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
private suspend fun stopOwnedRecordingStateStream() {
|
|
431
|
+
val stream = synchronized(streamLock) {
|
|
432
|
+
recordingStateStream.also { recordingStateStream = null }
|
|
433
|
+
} ?: return
|
|
434
|
+
stream.cancelAndJoin()
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
private suspend fun requestProvisioningMaterial(
|
|
438
|
+
request: ProvisioningMaterialRequest,
|
|
439
|
+
onRequest: (BotaDeviceSDKAndroidProvisioningRequest) -> Unit,
|
|
440
|
+
): ProvisioningMaterial {
|
|
441
|
+
val requestId = UUID.randomUUID().toString()
|
|
442
|
+
val pending = CompletableDeferred<ProvisioningMaterial>()
|
|
443
|
+
synchronized(lock) { provisioningRequests[requestId] = pending }
|
|
444
|
+
return try {
|
|
445
|
+
onRequest(
|
|
446
|
+
BotaDeviceSDKAndroidProvisioningRequest(
|
|
447
|
+
requestId = requestId,
|
|
448
|
+
serialNumber = request.serialNumber,
|
|
449
|
+
nonce = request.nonce.hexString(),
|
|
450
|
+
devicePublicKey = request.devicePublicKey.hexString(),
|
|
451
|
+
),
|
|
452
|
+
)
|
|
453
|
+
pending.await()
|
|
454
|
+
} finally {
|
|
455
|
+
synchronized(lock) { provisioningRequests.remove(requestId, pending) }
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
private fun pendingRequest(requestId: String): CompletableDeferred<ProvisioningMaterial> =
|
|
460
|
+
synchronized(lock) { provisioningRequests.remove(requestId) }
|
|
461
|
+
?: error("application material request is no longer pending")
|
|
462
|
+
|
|
463
|
+
private suspend fun requestFactoryResetGrant(
|
|
464
|
+
request: FactoryResetGrantRequest,
|
|
465
|
+
onRequest: (BotaDeviceSDKAndroidFactoryResetRequest) -> Unit,
|
|
466
|
+
): ByteArray {
|
|
467
|
+
val requestId = UUID.randomUUID().toString()
|
|
468
|
+
val pending = CompletableDeferred<ByteArray>()
|
|
469
|
+
synchronized(lock) { factoryResetRequests[requestId] = pending }
|
|
470
|
+
return try {
|
|
471
|
+
onRequest(
|
|
472
|
+
BotaDeviceSDKAndroidFactoryResetRequest(
|
|
473
|
+
requestId = requestId,
|
|
474
|
+
serialNumber = request.serialNumber,
|
|
475
|
+
nonce = request.nonce.hexString(),
|
|
476
|
+
commandId = request.commandId,
|
|
477
|
+
bindingGeneration = request.bindingGeneration,
|
|
478
|
+
),
|
|
479
|
+
)
|
|
480
|
+
pending.await()
|
|
481
|
+
} finally {
|
|
482
|
+
synchronized(lock) { factoryResetRequests.remove(requestId, pending) }
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
private suspend fun requestFactoryResetPersistence(
|
|
487
|
+
result: FactoryResetPersistenceResult,
|
|
488
|
+
onRequest: (BotaDeviceSDKAndroidFactoryResetPersistenceRequest) -> Unit,
|
|
489
|
+
) {
|
|
490
|
+
val requestId = UUID.randomUUID().toString()
|
|
491
|
+
val pending = CompletableDeferred<Unit>()
|
|
492
|
+
synchronized(lock) { factoryResetPersistenceRequests[requestId] = pending }
|
|
493
|
+
try {
|
|
494
|
+
onRequest(
|
|
495
|
+
BotaDeviceSDKAndroidFactoryResetPersistenceRequest(
|
|
496
|
+
requestId = requestId,
|
|
497
|
+
localRecordingsDeleted = result.localRecordingsDeleted,
|
|
498
|
+
),
|
|
499
|
+
)
|
|
500
|
+
pending.await()
|
|
501
|
+
} finally {
|
|
502
|
+
synchronized(lock) { factoryResetPersistenceRequests.remove(requestId, pending) }
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
private fun ByteArray.hexString(): String = joinToString("") { byte ->
|
|
508
|
+
"%02x".format(byte.toInt() and 0xFF)
|
|
509
|
+
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
package dev.bota.sdk.reactnative
|
|
2
|
+
|
|
3
|
+
import dev.bota.sdk.BotaDeviceClient
|
|
4
|
+
import dev.bota.sdk.model.ConnectedDevice
|
|
5
|
+
import dev.bota.sdk.model.DeviceWiFiScanResult
|
|
6
|
+
import dev.bota.sdk.model.WiFiConfigResult
|
|
7
|
+
import dev.bota.sdk.model.WiFiStatusInfo
|
|
8
|
+
import kotlinx.coroutines.CancellationException
|
|
9
|
+
import kotlinx.coroutines.CoroutineScope
|
|
10
|
+
import kotlinx.coroutines.CoroutineStart
|
|
11
|
+
import kotlinx.coroutines.Job
|
|
12
|
+
import kotlinx.coroutines.cancelAndJoin
|
|
13
|
+
import kotlinx.coroutines.flow.Flow
|
|
14
|
+
import kotlinx.coroutines.flow.collect
|
|
15
|
+
import kotlinx.coroutines.launch
|
|
16
|
+
import kotlinx.coroutines.sync.Mutex
|
|
17
|
+
import kotlinx.coroutines.sync.withLock
|
|
18
|
+
|
|
19
|
+
internal interface BotaDeviceSDKAndroidWiFiClient {
|
|
20
|
+
suspend fun configure(
|
|
21
|
+
device: ConnectedDevice,
|
|
22
|
+
ssid: String,
|
|
23
|
+
password: String,
|
|
24
|
+
grantBlob: String,
|
|
25
|
+
): WiFiConfigResult
|
|
26
|
+
|
|
27
|
+
suspend fun disconnect(device: ConnectedDevice): WiFiConfigResult
|
|
28
|
+
|
|
29
|
+
suspend fun readStatus(device: ConnectedDevice): WiFiStatusInfo
|
|
30
|
+
|
|
31
|
+
fun statusUpdates(device: ConnectedDevice): Flow<WiFiStatusInfo>
|
|
32
|
+
|
|
33
|
+
suspend fun scanNetworks(device: ConnectedDevice): DeviceWiFiScanResult
|
|
34
|
+
|
|
35
|
+
suspend fun cancelCurrentOperation()
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
internal class BotaDeviceSDKSharedAndroidWiFiClient(
|
|
39
|
+
private val client: BotaDeviceClient = BotaDeviceClient.shared,
|
|
40
|
+
) : BotaDeviceSDKAndroidWiFiClient {
|
|
41
|
+
override suspend fun configure(
|
|
42
|
+
device: ConnectedDevice,
|
|
43
|
+
ssid: String,
|
|
44
|
+
password: String,
|
|
45
|
+
grantBlob: String,
|
|
46
|
+
): WiFiConfigResult = client.wifi.configure(device, ssid, password, grantBlob)
|
|
47
|
+
|
|
48
|
+
override suspend fun disconnect(device: ConnectedDevice): WiFiConfigResult =
|
|
49
|
+
client.wifi.disconnect(device)
|
|
50
|
+
|
|
51
|
+
override suspend fun readStatus(device: ConnectedDevice): WiFiStatusInfo =
|
|
52
|
+
client.wifi.readStatus(device)
|
|
53
|
+
|
|
54
|
+
override fun statusUpdates(device: ConnectedDevice): Flow<WiFiStatusInfo> =
|
|
55
|
+
client.wifi.statusUpdates(device)
|
|
56
|
+
|
|
57
|
+
override suspend fun scanNetworks(device: ConnectedDevice): DeviceWiFiScanResult =
|
|
58
|
+
client.wifi.scanNetworks(device)
|
|
59
|
+
|
|
60
|
+
override suspend fun cancelCurrentOperation() {
|
|
61
|
+
client.wifi.cancelCurrentOperation()
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
internal class BotaDeviceSDKAndroidWiFi(
|
|
66
|
+
private val client: BotaDeviceSDKAndroidWiFiClient,
|
|
67
|
+
private val scope: CoroutineScope,
|
|
68
|
+
) {
|
|
69
|
+
private val operations = Mutex()
|
|
70
|
+
private val streamLock = Any()
|
|
71
|
+
private var statusStream: Job? = null
|
|
72
|
+
|
|
73
|
+
suspend fun configure(
|
|
74
|
+
device: ConnectedDevice,
|
|
75
|
+
ssid: String,
|
|
76
|
+
password: String,
|
|
77
|
+
grantBlob: String,
|
|
78
|
+
): WiFiConfigResult = client.configure(device, ssid, password, grantBlob)
|
|
79
|
+
|
|
80
|
+
suspend fun disconnect(device: ConnectedDevice): WiFiConfigResult = client.disconnect(device)
|
|
81
|
+
|
|
82
|
+
suspend fun readStatus(device: ConnectedDevice): WiFiStatusInfo = client.readStatus(device)
|
|
83
|
+
|
|
84
|
+
suspend fun scanNetworks(device: ConnectedDevice): DeviceWiFiScanResult =
|
|
85
|
+
client.scanNetworks(device)
|
|
86
|
+
|
|
87
|
+
suspend fun startStatusUpdates(
|
|
88
|
+
device: ConnectedDevice,
|
|
89
|
+
onError: (Throwable) -> Unit = {},
|
|
90
|
+
onStatus: (WiFiStatusInfo) -> Unit,
|
|
91
|
+
) = operations.withLock {
|
|
92
|
+
stopOwned()
|
|
93
|
+
val updates = client.statusUpdates(device)
|
|
94
|
+
lateinit var task: Job
|
|
95
|
+
task = scope.launch(start = CoroutineStart.LAZY) {
|
|
96
|
+
try {
|
|
97
|
+
updates.collect(onStatus)
|
|
98
|
+
} catch (_: CancellationException) {
|
|
99
|
+
// Explicit stop is not a WiFi status-stream failure.
|
|
100
|
+
} catch (error: Throwable) {
|
|
101
|
+
onError(error)
|
|
102
|
+
} finally {
|
|
103
|
+
synchronized(streamLock) {
|
|
104
|
+
if (statusStream === task) statusStream = null
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
synchronized(streamLock) { statusStream = task }
|
|
109
|
+
task.start()
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
suspend fun stopStatusUpdates() = operations.withLock {
|
|
113
|
+
stopOwned()
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
suspend fun cancelAll() {
|
|
117
|
+
runCatching { stopStatusUpdates() }
|
|
118
|
+
runCatching { client.cancelCurrentOperation() }
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
private suspend fun stopOwned() {
|
|
122
|
+
val stream = synchronized(streamLock) { statusStream.also { statusStream = null } } ?: return
|
|
123
|
+
stream.cancelAndJoin()
|
|
124
|
+
}
|
|
125
|
+
}
|
package/android/src/main/java/dev/bota/sdk/reactnative/BotaDeviceSDKEncryptedUploadV2Materials.kt
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
package dev.bota.sdk.reactnative
|
|
2
|
+
|
|
3
|
+
import dev.bota.sdk.EncryptedUploadV2Material
|
|
4
|
+
import dev.bota.sdk.EncryptedUploadV2Recording
|
|
5
|
+
import dev.bota.sdk.EncryptedUploadV2SecurityPolicy
|
|
6
|
+
import java.util.UUID
|
|
7
|
+
import java.util.concurrent.ConcurrentHashMap
|
|
8
|
+
|
|
9
|
+
/** Holds v2 application material in native memory and exposes only opaque IDs to JavaScript. */
|
|
10
|
+
public object BotaDeviceSDKEncryptedUploadV2Materials {
|
|
11
|
+
private val materials = ConcurrentHashMap<String, EncryptedUploadV2Material>()
|
|
12
|
+
|
|
13
|
+
public fun register(material: EncryptedUploadV2Material): String =
|
|
14
|
+
UUID.randomUUID().toString().also { materials[it] = material }
|
|
15
|
+
|
|
16
|
+
public fun remove(registrationId: String): EncryptedUploadV2Material? =
|
|
17
|
+
materials.remove(registrationId)
|
|
18
|
+
|
|
19
|
+
internal fun consume(
|
|
20
|
+
registrationId: String,
|
|
21
|
+
recording: EncryptedUploadV2Recording,
|
|
22
|
+
uploadSessionId: UUID,
|
|
23
|
+
ownerRevision: UInt,
|
|
24
|
+
securityPolicy: String,
|
|
25
|
+
): EncryptedUploadV2Material {
|
|
26
|
+
val material = materials[registrationId]
|
|
27
|
+
?: error("encrypted upload v2 material registration is unavailable")
|
|
28
|
+
require(
|
|
29
|
+
material.recordingId == recording.uuid &&
|
|
30
|
+
material.uploadSessionId == uploadSessionId &&
|
|
31
|
+
material.ownerRevision == ownerRevision &&
|
|
32
|
+
material.policy == securityPolicy.toNativePolicy(),
|
|
33
|
+
) { "encrypted upload v2 material does not match the selected profile" }
|
|
34
|
+
check(materials.remove(registrationId, material)) {
|
|
35
|
+
"encrypted upload v2 material registration is unavailable"
|
|
36
|
+
}
|
|
37
|
+
return material
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
private fun String.toNativePolicy(): EncryptedUploadV2SecurityPolicy = when (this) {
|
|
42
|
+
"legacy_allowed" -> EncryptedUploadV2SecurityPolicy.LegacyAllowed
|
|
43
|
+
"v2_preferred" -> EncryptedUploadV2SecurityPolicy.V2Preferred
|
|
44
|
+
"v2_required" -> EncryptedUploadV2SecurityPolicy.V2Required
|
|
45
|
+
else -> error("encrypted upload v2 security policy is unsupported")
|
|
46
|
+
}
|