@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,751 @@
|
|
|
1
|
+
package dev.bota.sdk.reactnative
|
|
2
|
+
|
|
3
|
+
import com.facebook.react.bridge.Arguments
|
|
4
|
+
import com.facebook.react.bridge.WritableMap
|
|
5
|
+
import dev.bota.sdk.BotaErrorCode
|
|
6
|
+
import dev.bota.sdk.BotaDeviceClient
|
|
7
|
+
import dev.bota.sdk.BotaSDKError
|
|
8
|
+
import dev.bota.sdk.EncryptedUploadV2CapabilitySnapshot
|
|
9
|
+
import dev.bota.sdk.EncryptedUploadV2Checkpoint
|
|
10
|
+
import dev.bota.sdk.EncryptedUploadV2Material
|
|
11
|
+
import dev.bota.sdk.EncryptedUploadV2ProfileProvider
|
|
12
|
+
import dev.bota.sdk.EncryptedUploadV2ProviderContext
|
|
13
|
+
import dev.bota.sdk.EncryptedUploadV2Recording
|
|
14
|
+
import dev.bota.sdk.RecordingSyncEvent
|
|
15
|
+
import dev.bota.sdk.RecordingTransferMetadata
|
|
16
|
+
import dev.bota.sdk.UploadOwnershipEvent
|
|
17
|
+
import dev.bota.sdk.UploadOwnershipResult
|
|
18
|
+
import dev.bota.sdk.model.ConnectedDevice
|
|
19
|
+
import dev.bota.sdk.model.DeviceRecording
|
|
20
|
+
import dev.bota.sdk.model.RecordingTransferProgress
|
|
21
|
+
import dev.bota.sdk.model.StreamingChunkDestinationProvider
|
|
22
|
+
import dev.bota.sdk.model.StreamingChunkRequest
|
|
23
|
+
import dev.bota.sdk.model.StreamingFinalizeHandler
|
|
24
|
+
import dev.bota.sdk.model.StreamingFinalizeMetadata
|
|
25
|
+
import dev.bota.sdk.model.StreamingRecordingEvent
|
|
26
|
+
import dev.bota.sdk.model.StreamingUploadDestination
|
|
27
|
+
import dev.bota.sdk.model.StreamingUploadMethod
|
|
28
|
+
import java.util.UUID
|
|
29
|
+
import java.util.concurrent.ConcurrentHashMap
|
|
30
|
+
import kotlinx.coroutines.CompletableDeferred
|
|
31
|
+
import kotlinx.coroutines.flow.Flow
|
|
32
|
+
import kotlinx.coroutines.flow.collect
|
|
33
|
+
|
|
34
|
+
internal interface BotaDeviceSDKAndroidRecordingClient {
|
|
35
|
+
suspend fun listRecordings(device: ConnectedDevice): List<DeviceRecording>
|
|
36
|
+
|
|
37
|
+
fun syncRecording(
|
|
38
|
+
device: ConnectedDevice,
|
|
39
|
+
recording: DeviceRecording,
|
|
40
|
+
sinkId: String,
|
|
41
|
+
): Flow<RecordingSyncEvent>
|
|
42
|
+
|
|
43
|
+
fun transferMetadata(sinkId: String): RecordingTransferMetadata?
|
|
44
|
+
|
|
45
|
+
suspend fun syncEncryptedRecordingV2(
|
|
46
|
+
device: ConnectedDevice,
|
|
47
|
+
recording: EncryptedUploadV2Recording,
|
|
48
|
+
provider: EncryptedUploadV2ProfileProvider,
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
suspend fun confirmRecording(device: ConnectedDevice, recordingUuid: String)
|
|
52
|
+
|
|
53
|
+
fun observeUploadOwnership(
|
|
54
|
+
device: ConnectedDevice,
|
|
55
|
+
recordingUuid: String,
|
|
56
|
+
uploadId: String,
|
|
57
|
+
destinationId: String,
|
|
58
|
+
): Flow<UploadOwnershipEvent>
|
|
59
|
+
|
|
60
|
+
fun streamRecording(
|
|
61
|
+
device: ConnectedDevice,
|
|
62
|
+
recordingUuid: String,
|
|
63
|
+
sinkId: String,
|
|
64
|
+
chunkSizeBytes: Int,
|
|
65
|
+
flushIntervalMilliseconds: ULong,
|
|
66
|
+
destinationProvider: StreamingChunkDestinationProvider,
|
|
67
|
+
finalize: StreamingFinalizeHandler,
|
|
68
|
+
): Flow<StreamingRecordingEvent>
|
|
69
|
+
|
|
70
|
+
suspend fun cancelCurrentOperation()
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
internal fun BotaDeviceSDKAndroidRecordings.BotaRecordingFile.toWritableMap(): WritableMap =
|
|
74
|
+
Arguments.createMap().apply {
|
|
75
|
+
putString("localPath", localPath)
|
|
76
|
+
putBoolean("e2eEncrypted", isE2EEncrypted)
|
|
77
|
+
contentSha256Hex?.let { putString("contentSha256", it) }
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
internal data class BotaStreamingProgress(
|
|
81
|
+
val sessionId: String,
|
|
82
|
+
val state: String,
|
|
83
|
+
val bytesReceived: ULong,
|
|
84
|
+
val chunksUploaded: UInt,
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
internal data class BotaEncryptedUploadV2Recording(
|
|
88
|
+
val uuid: String,
|
|
89
|
+
val generation: UInt,
|
|
90
|
+
val ciphertextLength: String,
|
|
91
|
+
val ciphertextSha256: String,
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
internal data class BotaEncryptedUploadV2Capability(
|
|
95
|
+
val encodingVersion: Int,
|
|
96
|
+
val transferProfileVersion: Int,
|
|
97
|
+
val rawValueHex: String,
|
|
98
|
+
val sha256Hex: String,
|
|
99
|
+
val flags: UInt,
|
|
100
|
+
val maximumSignedBlobBytes: UShort,
|
|
101
|
+
val maximumManifestBytes: UShort,
|
|
102
|
+
val maximumDataPayloadBytes: UShort,
|
|
103
|
+
val maximumWindowPackets: UShort,
|
|
104
|
+
val durableCheckpointIntervalBlocks: UInt,
|
|
105
|
+
val maximumMissingSequences: UShort,
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
internal data class BotaEncryptedUploadV2Checkpoint(
|
|
109
|
+
val version: Int,
|
|
110
|
+
val uploadSessionId: String,
|
|
111
|
+
val ownerRevision: UInt,
|
|
112
|
+
val revision: UInt,
|
|
113
|
+
val nextCiphertextOffset: String,
|
|
114
|
+
val prefixSha256: String,
|
|
115
|
+
val highestContiguousSequence: UInt?,
|
|
116
|
+
val transportSessionId: String,
|
|
117
|
+
val sinkRegistrationId: String,
|
|
118
|
+
val windowPackets: UShort,
|
|
119
|
+
val dataPayloadBytes: UShort,
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
internal data class BotaEncryptedUploadV2ProfileRequest(
|
|
123
|
+
val requestId: String,
|
|
124
|
+
val operationId: String,
|
|
125
|
+
val recording: BotaEncryptedUploadV2Recording,
|
|
126
|
+
val capability: BotaEncryptedUploadV2Capability,
|
|
127
|
+
val checkpoint: BotaEncryptedUploadV2Checkpoint?,
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
internal data class BotaEncryptedUploadV2Progress(
|
|
131
|
+
val operationId: String,
|
|
132
|
+
val recordingUuid: String,
|
|
133
|
+
val phase: String,
|
|
134
|
+
val completedBytes: String,
|
|
135
|
+
val totalBytes: String,
|
|
136
|
+
val checkpointRevision: UInt? = null,
|
|
137
|
+
val errorCode: String? = null,
|
|
138
|
+
val retryable: Boolean? = null,
|
|
139
|
+
val protocolStatus: UShort? = null,
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
internal data class BotaStreamingDestinationRequest(
|
|
143
|
+
val requestId: String,
|
|
144
|
+
val sessionId: String,
|
|
145
|
+
val sequence: UInt,
|
|
146
|
+
val encrypted: Boolean,
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
internal data class BotaStreamingFinalizeRequest(
|
|
150
|
+
val requestId: String,
|
|
151
|
+
val sessionId: String,
|
|
152
|
+
val totalChunks: UInt,
|
|
153
|
+
val durationMilliseconds: ULong,
|
|
154
|
+
val fileSizeBytes: ULong,
|
|
155
|
+
val encrypted: Boolean,
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
internal fun BotaStreamingProgress.toWritableMap(): WritableMap = Arguments.createMap().apply {
|
|
159
|
+
putString("sessionId", sessionId)
|
|
160
|
+
putString("state", state)
|
|
161
|
+
putDouble("bytesReceived", bytesReceived.toDouble())
|
|
162
|
+
putDouble("chunksUploaded", chunksUploaded.toDouble())
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
internal fun BotaStreamingDestinationRequest.toWritableMap(): WritableMap = Arguments.createMap().apply {
|
|
166
|
+
putString("requestId", requestId)
|
|
167
|
+
putString("sessionId", sessionId)
|
|
168
|
+
putDouble("sequence", sequence.toDouble())
|
|
169
|
+
putBoolean("encrypted", encrypted)
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
internal fun BotaStreamingFinalizeRequest.toWritableMap(): WritableMap = Arguments.createMap().apply {
|
|
173
|
+
putString("requestId", requestId)
|
|
174
|
+
putString("sessionId", sessionId)
|
|
175
|
+
putDouble("totalChunks", totalChunks.toDouble())
|
|
176
|
+
putDouble("durationMs", durationMilliseconds.toDouble())
|
|
177
|
+
putDouble("fileSizeBytes", fileSizeBytes.toDouble())
|
|
178
|
+
putBoolean("encrypted", encrypted)
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
internal fun BotaEncryptedUploadV2ProfileRequest.toWritableMap(): WritableMap =
|
|
182
|
+
Arguments.createMap().apply {
|
|
183
|
+
putString("requestId", requestId)
|
|
184
|
+
putString("operationId", operationId)
|
|
185
|
+
putMap("recording", recording.toWritableMap())
|
|
186
|
+
putMap("capability", capability.toWritableMap())
|
|
187
|
+
checkpoint?.let { putMap("checkpoint", it.toWritableMap()) }
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
internal fun BotaEncryptedUploadV2Progress.toWritableMap(): WritableMap = Arguments.createMap().apply {
|
|
191
|
+
putString("operationId", operationId)
|
|
192
|
+
putString("recordingUuid", recordingUuid)
|
|
193
|
+
putString("phase", phase)
|
|
194
|
+
putString("completedBytes", completedBytes)
|
|
195
|
+
putString("totalBytes", totalBytes)
|
|
196
|
+
checkpointRevision?.let { putDouble("checkpointRevision", it.toDouble()) }
|
|
197
|
+
errorCode?.let { putString("errorCode", it) }
|
|
198
|
+
retryable?.let { putBoolean("retryable", it) }
|
|
199
|
+
protocolStatus?.let { putDouble("protocolStatus", it.toDouble()) }
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
private fun BotaEncryptedUploadV2Recording.toWritableMap(): WritableMap = Arguments.createMap().apply {
|
|
203
|
+
putString("uuid", uuid)
|
|
204
|
+
putDouble("generation", generation.toDouble())
|
|
205
|
+
putString("ciphertextLength", ciphertextLength)
|
|
206
|
+
putString("ciphertextSha256", ciphertextSha256)
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
private fun BotaEncryptedUploadV2Capability.toWritableMap(): WritableMap = Arguments.createMap().apply {
|
|
210
|
+
putDouble("encodingVersion", encodingVersion.toDouble())
|
|
211
|
+
putDouble("transferProfileVersion", transferProfileVersion.toDouble())
|
|
212
|
+
putString("rawValueHex", rawValueHex)
|
|
213
|
+
putString("sha256Hex", sha256Hex)
|
|
214
|
+
putDouble("flags", flags.toDouble())
|
|
215
|
+
putDouble("maximumSignedBlobBytes", maximumSignedBlobBytes.toDouble())
|
|
216
|
+
putDouble("maximumManifestBytes", maximumManifestBytes.toDouble())
|
|
217
|
+
putDouble("maximumDataPayloadBytes", maximumDataPayloadBytes.toDouble())
|
|
218
|
+
putDouble("maximumWindowPackets", maximumWindowPackets.toDouble())
|
|
219
|
+
putDouble("durableCheckpointIntervalBlocks", durableCheckpointIntervalBlocks.toDouble())
|
|
220
|
+
putDouble("maximumMissingSequences", maximumMissingSequences.toDouble())
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
private fun BotaEncryptedUploadV2Checkpoint.toWritableMap(): WritableMap = Arguments.createMap().apply {
|
|
224
|
+
putDouble("version", version.toDouble())
|
|
225
|
+
putString("uploadSessionId", uploadSessionId)
|
|
226
|
+
putDouble("ownerRevision", ownerRevision.toDouble())
|
|
227
|
+
putDouble("revision", revision.toDouble())
|
|
228
|
+
putString("nextCiphertextOffset", nextCiphertextOffset)
|
|
229
|
+
putString("prefixSha256", prefixSha256)
|
|
230
|
+
highestContiguousSequence?.let { putDouble("highestContiguousSequence", it.toDouble()) }
|
|
231
|
+
putString("transportSessionId", transportSessionId)
|
|
232
|
+
putString("sinkRegistrationId", sinkRegistrationId)
|
|
233
|
+
putDouble("windowPackets", windowPackets.toDouble())
|
|
234
|
+
putDouble("dataPayloadBytes", dataPayloadBytes.toDouble())
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
internal class BotaDeviceSDKSharedAndroidRecordingClient(
|
|
238
|
+
private val client: BotaDeviceClient = BotaDeviceClient.shared,
|
|
239
|
+
) : BotaDeviceSDKAndroidRecordingClient {
|
|
240
|
+
override suspend fun listRecordings(device: ConnectedDevice): List<DeviceRecording> =
|
|
241
|
+
client.recordings.listRecordings(device)
|
|
242
|
+
|
|
243
|
+
override fun syncRecording(
|
|
244
|
+
device: ConnectedDevice,
|
|
245
|
+
recording: DeviceRecording,
|
|
246
|
+
sinkId: String,
|
|
247
|
+
): Flow<RecordingSyncEvent> = client.recordings.syncRecording(
|
|
248
|
+
device,
|
|
249
|
+
recording,
|
|
250
|
+
sinkId,
|
|
251
|
+
confirmOnCompletion = false,
|
|
252
|
+
)
|
|
253
|
+
|
|
254
|
+
override fun transferMetadata(sinkId: String): RecordingTransferMetadata? =
|
|
255
|
+
client.recordings.transferMetadata(sinkId)
|
|
256
|
+
|
|
257
|
+
override suspend fun syncEncryptedRecordingV2(
|
|
258
|
+
device: ConnectedDevice,
|
|
259
|
+
recording: EncryptedUploadV2Recording,
|
|
260
|
+
provider: EncryptedUploadV2ProfileProvider,
|
|
261
|
+
) {
|
|
262
|
+
client.recordings.syncEncryptedRecordingV2(device, recording, provider)
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
override suspend fun confirmRecording(device: ConnectedDevice, recordingUuid: String) {
|
|
266
|
+
client.recordings.confirmRecording(device, recordingUuid)
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
override fun observeUploadOwnership(
|
|
270
|
+
device: ConnectedDevice,
|
|
271
|
+
recordingUuid: String,
|
|
272
|
+
uploadId: String,
|
|
273
|
+
destinationId: String,
|
|
274
|
+
): Flow<UploadOwnershipEvent> = client.recordings.observeUploadOwnership(
|
|
275
|
+
device,
|
|
276
|
+
recordingUuid,
|
|
277
|
+
uploadId,
|
|
278
|
+
destinationId,
|
|
279
|
+
)
|
|
280
|
+
|
|
281
|
+
override fun streamRecording(
|
|
282
|
+
device: ConnectedDevice,
|
|
283
|
+
recordingUuid: String,
|
|
284
|
+
sinkId: String,
|
|
285
|
+
chunkSizeBytes: Int,
|
|
286
|
+
flushIntervalMilliseconds: ULong,
|
|
287
|
+
destinationProvider: StreamingChunkDestinationProvider,
|
|
288
|
+
finalize: StreamingFinalizeHandler,
|
|
289
|
+
): Flow<StreamingRecordingEvent> = client.recordings.streamRecording(
|
|
290
|
+
device,
|
|
291
|
+
recordingUuid,
|
|
292
|
+
sinkId,
|
|
293
|
+
chunkSizeBytes,
|
|
294
|
+
flushIntervalMilliseconds,
|
|
295
|
+
destinationProvider,
|
|
296
|
+
finalize,
|
|
297
|
+
)
|
|
298
|
+
|
|
299
|
+
override suspend fun cancelCurrentOperation() {
|
|
300
|
+
client.recordings.cancelCurrentOperation()
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
internal class BotaDeviceSDKAndroidRecordings(
|
|
305
|
+
private val client: BotaDeviceSDKAndroidRecordingClient =
|
|
306
|
+
BotaDeviceSDKSharedAndroidRecordingClient(),
|
|
307
|
+
) {
|
|
308
|
+
private val destinationRequests = ConcurrentHashMap<
|
|
309
|
+
String,
|
|
310
|
+
CompletableDeferred<StreamingUploadDestination>
|
|
311
|
+
>()
|
|
312
|
+
private data class EncryptedUploadV2Request(
|
|
313
|
+
val recording: EncryptedUploadV2Recording,
|
|
314
|
+
val material: CompletableDeferred<EncryptedUploadV2Material>,
|
|
315
|
+
)
|
|
316
|
+
private val encryptedUploadV2Requests =
|
|
317
|
+
ConcurrentHashMap<String, EncryptedUploadV2Request>()
|
|
318
|
+
private val finalizeRequests = ConcurrentHashMap<String, CompletableDeferred<Unit>>()
|
|
319
|
+
@Volatile private var activeStreamingSessionId: String? = null
|
|
320
|
+
|
|
321
|
+
internal data class BotaRecordingFile(
|
|
322
|
+
val localPath: String,
|
|
323
|
+
val isE2EEncrypted: Boolean,
|
|
324
|
+
val contentSha256Hex: String?,
|
|
325
|
+
)
|
|
326
|
+
|
|
327
|
+
suspend fun listRecordings(device: ConnectedDevice): List<DeviceRecording> =
|
|
328
|
+
client.listRecordings(device)
|
|
329
|
+
|
|
330
|
+
suspend fun syncRecording(
|
|
331
|
+
device: ConnectedDevice,
|
|
332
|
+
recording: DeviceRecording,
|
|
333
|
+
sinkId: String,
|
|
334
|
+
onProgress: (RecordingTransferProgress) -> Unit,
|
|
335
|
+
): BotaRecordingFile {
|
|
336
|
+
var path: String? = null
|
|
337
|
+
client.syncRecording(device, recording, sinkId).collect { event ->
|
|
338
|
+
when (event) {
|
|
339
|
+
is RecordingSyncEvent.Progress -> onProgress(event.progress)
|
|
340
|
+
is RecordingSyncEvent.Completed -> path = event.path.toString()
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
val localPath = path ?: error("recording transfer completed without a native file")
|
|
344
|
+
val metadata = client.transferMetadata(sinkId)
|
|
345
|
+
return BotaRecordingFile(
|
|
346
|
+
localPath,
|
|
347
|
+
metadata?.isE2EEncrypted ?: false,
|
|
348
|
+
metadata?.contentSha256Hex,
|
|
349
|
+
)
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
suspend fun syncEncryptedRecordingV2(
|
|
353
|
+
device: ConnectedDevice,
|
|
354
|
+
recording: EncryptedUploadV2Recording,
|
|
355
|
+
operationId: String,
|
|
356
|
+
onProfileRequest: (BotaEncryptedUploadV2ProfileRequest) -> Unit,
|
|
357
|
+
onProgress: (BotaEncryptedUploadV2Progress) -> Unit,
|
|
358
|
+
) {
|
|
359
|
+
try {
|
|
360
|
+
client.syncEncryptedRecordingV2(
|
|
361
|
+
device,
|
|
362
|
+
recording,
|
|
363
|
+
EncryptedUploadV2ProfileProvider { context ->
|
|
364
|
+
val completedBytes = context.checkpoint?.nextCiphertextOffset?.toString() ?: "0"
|
|
365
|
+
val checkpointRevision = context.checkpoint?.revision
|
|
366
|
+
onProgress(BotaEncryptedUploadV2Progress(
|
|
367
|
+
operationId,
|
|
368
|
+
recording.uuid,
|
|
369
|
+
"profile_requested",
|
|
370
|
+
completedBytes,
|
|
371
|
+
recording.ciphertextLength.toString(),
|
|
372
|
+
checkpointRevision,
|
|
373
|
+
))
|
|
374
|
+
val material = requestEncryptedUploadV2Profile(
|
|
375
|
+
operationId,
|
|
376
|
+
context,
|
|
377
|
+
onProfileRequest,
|
|
378
|
+
)
|
|
379
|
+
onProgress(BotaEncryptedUploadV2Progress(
|
|
380
|
+
operationId,
|
|
381
|
+
recording.uuid,
|
|
382
|
+
"transferring",
|
|
383
|
+
completedBytes,
|
|
384
|
+
recording.ciphertextLength.toString(),
|
|
385
|
+
checkpointRevision,
|
|
386
|
+
))
|
|
387
|
+
material
|
|
388
|
+
},
|
|
389
|
+
)
|
|
390
|
+
onProgress(BotaEncryptedUploadV2Progress(
|
|
391
|
+
operationId,
|
|
392
|
+
recording.uuid,
|
|
393
|
+
"completed",
|
|
394
|
+
recording.ciphertextLength.toString(),
|
|
395
|
+
recording.ciphertextLength.toString(),
|
|
396
|
+
))
|
|
397
|
+
} catch (error: Throwable) {
|
|
398
|
+
val failure = error.encryptedUploadV2Failure()
|
|
399
|
+
onProgress(BotaEncryptedUploadV2Progress(
|
|
400
|
+
operationId,
|
|
401
|
+
recording.uuid,
|
|
402
|
+
"failed",
|
|
403
|
+
"0",
|
|
404
|
+
recording.ciphertextLength.toString(),
|
|
405
|
+
errorCode = failure.code,
|
|
406
|
+
retryable = failure.retryable,
|
|
407
|
+
protocolStatus = failure.protocolStatus,
|
|
408
|
+
))
|
|
409
|
+
throw error
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
fun resolveEncryptedUploadV2Profile(
|
|
414
|
+
requestId: String,
|
|
415
|
+
profile: String,
|
|
416
|
+
uploadSessionId: String,
|
|
417
|
+
ownerRevision: UInt,
|
|
418
|
+
securityPolicy: String,
|
|
419
|
+
materialRegistrationId: String,
|
|
420
|
+
) {
|
|
421
|
+
val request = encryptedUploadV2Requests.remove(requestId)
|
|
422
|
+
?: error("encrypted upload v2 profile request is no longer pending")
|
|
423
|
+
runCatching {
|
|
424
|
+
require(profile == "encrypted_upload_v2") {
|
|
425
|
+
"encrypted upload v2 requires an explicit matching profile"
|
|
426
|
+
}
|
|
427
|
+
BotaDeviceSDKEncryptedUploadV2Materials.consume(
|
|
428
|
+
materialRegistrationId,
|
|
429
|
+
request.recording,
|
|
430
|
+
UUID.fromString(uploadSessionId),
|
|
431
|
+
ownerRevision,
|
|
432
|
+
securityPolicy,
|
|
433
|
+
)
|
|
434
|
+
}.onSuccess { request.material.complete(it) }
|
|
435
|
+
.onFailure { request.material.completeExceptionally(it) }
|
|
436
|
+
.getOrThrow()
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
fun rejectEncryptedUploadV2Profile(requestId: String, errorCode: String) {
|
|
440
|
+
val request = encryptedUploadV2Requests.remove(requestId)
|
|
441
|
+
?: error("encrypted upload v2 profile request is no longer pending")
|
|
442
|
+
val error = if (errorCode == "application_material_rejected") {
|
|
443
|
+
IllegalStateException("encrypted upload v2 material was rejected")
|
|
444
|
+
} else {
|
|
445
|
+
IllegalArgumentException("encrypted upload v2 rejection code is unsupported")
|
|
446
|
+
}
|
|
447
|
+
request.material.completeExceptionally(error)
|
|
448
|
+
if (errorCode != "application_material_rejected") throw error
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
suspend fun observeUploadOwnership(
|
|
452
|
+
device: ConnectedDevice,
|
|
453
|
+
recordingUuid: String,
|
|
454
|
+
uploadId: String,
|
|
455
|
+
destinationId: String,
|
|
456
|
+
onProgress: (RecordingTransferProgress) -> Unit,
|
|
457
|
+
): UploadOwnershipResult {
|
|
458
|
+
var result: UploadOwnershipResult? = null
|
|
459
|
+
client.observeUploadOwnership(device, recordingUuid, uploadId, destinationId).collect { event ->
|
|
460
|
+
when (event) {
|
|
461
|
+
is UploadOwnershipEvent.Progress -> onProgress(event.progress)
|
|
462
|
+
is UploadOwnershipEvent.Result -> result = event.result
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
return result ?: error("upload ownership completed without a result")
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
suspend fun confirmRecording(device: ConnectedDevice, recordingUuid: String) {
|
|
469
|
+
client.confirmRecording(device, recordingUuid)
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
suspend fun streamRecording(
|
|
473
|
+
device: ConnectedDevice,
|
|
474
|
+
recordingUuid: String,
|
|
475
|
+
sessionId: String,
|
|
476
|
+
chunkSizeBytes: Int,
|
|
477
|
+
flushIntervalMilliseconds: ULong,
|
|
478
|
+
onProgress: (BotaStreamingProgress) -> Unit,
|
|
479
|
+
onDestinationRequest: (BotaStreamingDestinationRequest) -> Unit,
|
|
480
|
+
onFinalizeRequest: (BotaStreamingFinalizeRequest) -> Unit,
|
|
481
|
+
): ULong {
|
|
482
|
+
activeStreamingSessionId = sessionId
|
|
483
|
+
var bytesReceived = 0uL
|
|
484
|
+
var chunksUploaded = 0u
|
|
485
|
+
onProgress(streamingProgress(sessionId, "streaming", bytesReceived, chunksUploaded))
|
|
486
|
+
try {
|
|
487
|
+
client.streamRecording(
|
|
488
|
+
device,
|
|
489
|
+
recordingUuid,
|
|
490
|
+
sessionId,
|
|
491
|
+
chunkSizeBytes,
|
|
492
|
+
flushIntervalMilliseconds,
|
|
493
|
+
StreamingChunkDestinationProvider { request ->
|
|
494
|
+
requestDestination(sessionId, request, onDestinationRequest)
|
|
495
|
+
},
|
|
496
|
+
StreamingFinalizeHandler { metadata ->
|
|
497
|
+
requestFinalize(sessionId, metadata, onFinalizeRequest)
|
|
498
|
+
},
|
|
499
|
+
).collect { event ->
|
|
500
|
+
when (event) {
|
|
501
|
+
is StreamingRecordingEvent.Paused -> {
|
|
502
|
+
bytesReceived = event.completedBytes
|
|
503
|
+
onProgress(streamingProgress(
|
|
504
|
+
sessionId,
|
|
505
|
+
"paused",
|
|
506
|
+
bytesReceived,
|
|
507
|
+
chunksUploaded,
|
|
508
|
+
))
|
|
509
|
+
}
|
|
510
|
+
StreamingRecordingEvent.Resumed -> onProgress(streamingProgress(
|
|
511
|
+
sessionId,
|
|
512
|
+
"streaming",
|
|
513
|
+
bytesReceived,
|
|
514
|
+
chunksUploaded,
|
|
515
|
+
))
|
|
516
|
+
is StreamingRecordingEvent.Completed -> {
|
|
517
|
+
bytesReceived = event.totalBytes
|
|
518
|
+
chunksUploaded = event.uploadedChunks
|
|
519
|
+
onProgress(streamingProgress(
|
|
520
|
+
sessionId,
|
|
521
|
+
"completing",
|
|
522
|
+
bytesReceived,
|
|
523
|
+
chunksUploaded,
|
|
524
|
+
))
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
return bytesReceived
|
|
529
|
+
} finally {
|
|
530
|
+
if (activeStreamingSessionId == sessionId) activeStreamingSessionId = null
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
fun resolveStreamingDestination(
|
|
535
|
+
requestId: String,
|
|
536
|
+
url: String,
|
|
537
|
+
method: String,
|
|
538
|
+
contentType: String,
|
|
539
|
+
bearerToken: String?,
|
|
540
|
+
) {
|
|
541
|
+
val destination = StreamingUploadDestination(
|
|
542
|
+
url,
|
|
543
|
+
StreamingUploadMethod.entries.firstOrNull { it.wireValue == method }
|
|
544
|
+
?: error("unsupported streaming upload method: $method"),
|
|
545
|
+
contentType,
|
|
546
|
+
bearerToken,
|
|
547
|
+
)
|
|
548
|
+
destinationRequests.remove(requestId)?.complete(destination)
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
fun rejectStreamingDestination(requestId: String, message: String) {
|
|
552
|
+
destinationRequests.remove(requestId)?.completeExceptionally(IllegalStateException(message))
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
fun resolveStreamingFinalize(requestId: String) {
|
|
556
|
+
finalizeRequests.remove(requestId)?.complete(Unit)
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
fun rejectStreamingFinalize(requestId: String, message: String) {
|
|
560
|
+
finalizeRequests.remove(requestId)?.completeExceptionally(IllegalStateException(message))
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
suspend fun abortStreaming(sessionId: String) {
|
|
564
|
+
if (activeStreamingSessionId != sessionId) return
|
|
565
|
+
rejectPendingRequests("streaming session was aborted")
|
|
566
|
+
runCatching { client.cancelCurrentOperation() }
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
suspend fun cancelAll() {
|
|
570
|
+
rejectPendingRequests("recording operations were cancelled")
|
|
571
|
+
runCatching { client.cancelCurrentOperation() }
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
private suspend fun requestDestination(
|
|
575
|
+
sessionId: String,
|
|
576
|
+
request: StreamingChunkRequest,
|
|
577
|
+
onRequest: (BotaStreamingDestinationRequest) -> Unit,
|
|
578
|
+
): StreamingUploadDestination {
|
|
579
|
+
val requestId = UUID.randomUUID().toString()
|
|
580
|
+
val deferred = CompletableDeferred<StreamingUploadDestination>()
|
|
581
|
+
destinationRequests[requestId] = deferred
|
|
582
|
+
onRequest(BotaStreamingDestinationRequest(
|
|
583
|
+
requestId,
|
|
584
|
+
sessionId,
|
|
585
|
+
request.sequence,
|
|
586
|
+
request.isEncrypted,
|
|
587
|
+
))
|
|
588
|
+
return try {
|
|
589
|
+
deferred.await()
|
|
590
|
+
} finally {
|
|
591
|
+
destinationRequests.remove(requestId, deferred)
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
private suspend fun requestEncryptedUploadV2Profile(
|
|
596
|
+
operationId: String,
|
|
597
|
+
context: EncryptedUploadV2ProviderContext,
|
|
598
|
+
onRequest: (BotaEncryptedUploadV2ProfileRequest) -> Unit,
|
|
599
|
+
): EncryptedUploadV2Material {
|
|
600
|
+
val requestId = UUID.randomUUID().toString()
|
|
601
|
+
val deferred = CompletableDeferred<EncryptedUploadV2Material>()
|
|
602
|
+
val request = EncryptedUploadV2Request(
|
|
603
|
+
context.recording,
|
|
604
|
+
deferred,
|
|
605
|
+
)
|
|
606
|
+
encryptedUploadV2Requests[requestId] = request
|
|
607
|
+
onRequest(BotaEncryptedUploadV2ProfileRequest(
|
|
608
|
+
requestId,
|
|
609
|
+
operationId,
|
|
610
|
+
context.recording.toBridgeValue(),
|
|
611
|
+
context.capability.toBridgeValue(),
|
|
612
|
+
context.checkpoint?.toBridgeValue(),
|
|
613
|
+
))
|
|
614
|
+
return try {
|
|
615
|
+
deferred.await()
|
|
616
|
+
} finally {
|
|
617
|
+
encryptedUploadV2Requests.remove(requestId, request)
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
private suspend fun requestFinalize(
|
|
622
|
+
sessionId: String,
|
|
623
|
+
metadata: StreamingFinalizeMetadata,
|
|
624
|
+
onRequest: (BotaStreamingFinalizeRequest) -> Unit,
|
|
625
|
+
) {
|
|
626
|
+
val requestId = UUID.randomUUID().toString()
|
|
627
|
+
val deferred = CompletableDeferred<Unit>()
|
|
628
|
+
finalizeRequests[requestId] = deferred
|
|
629
|
+
onRequest(BotaStreamingFinalizeRequest(
|
|
630
|
+
requestId,
|
|
631
|
+
sessionId,
|
|
632
|
+
metadata.totalChunks,
|
|
633
|
+
metadata.durationMilliseconds,
|
|
634
|
+
metadata.fileSizeBytes,
|
|
635
|
+
metadata.isEncrypted,
|
|
636
|
+
))
|
|
637
|
+
try {
|
|
638
|
+
deferred.await()
|
|
639
|
+
} finally {
|
|
640
|
+
finalizeRequests.remove(requestId, deferred)
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
private fun rejectPendingRequests(message: String) {
|
|
645
|
+
val error = IllegalStateException(message)
|
|
646
|
+
encryptedUploadV2Requests.values.forEach { it.material.completeExceptionally(error) }
|
|
647
|
+
destinationRequests.values.forEach { it.completeExceptionally(error) }
|
|
648
|
+
finalizeRequests.values.forEach { it.completeExceptionally(error) }
|
|
649
|
+
destinationRequests.clear()
|
|
650
|
+
finalizeRequests.clear()
|
|
651
|
+
encryptedUploadV2Requests.clear()
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
private fun EncryptedUploadV2Recording.toBridgeValue(): BotaEncryptedUploadV2Recording =
|
|
656
|
+
BotaEncryptedUploadV2Recording(
|
|
657
|
+
uuid,
|
|
658
|
+
generation,
|
|
659
|
+
ciphertextLength.toString(),
|
|
660
|
+
ciphertextSha256.toHex(),
|
|
661
|
+
)
|
|
662
|
+
|
|
663
|
+
private fun EncryptedUploadV2CapabilitySnapshot.toBridgeValue(): BotaEncryptedUploadV2Capability {
|
|
664
|
+
val values = capabilities
|
|
665
|
+
return BotaEncryptedUploadV2Capability(
|
|
666
|
+
rawValue.getOrElse(0) { 0 }.toUByte().toInt(),
|
|
667
|
+
rawValue.getOrElse(1) { 0 }.toUByte().toInt(),
|
|
668
|
+
rawValue.toHex(),
|
|
669
|
+
sha256.toHex(),
|
|
670
|
+
values.flags,
|
|
671
|
+
values.maximumSignedBlobBytes,
|
|
672
|
+
values.maximumManifestBytes,
|
|
673
|
+
values.maximumDataPayloadBytes,
|
|
674
|
+
values.maximumWindowPackets,
|
|
675
|
+
values.durableCheckpointIntervalBlocks,
|
|
676
|
+
values.maximumMissingSequences,
|
|
677
|
+
)
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
private fun EncryptedUploadV2Checkpoint.toBridgeValue(): BotaEncryptedUploadV2Checkpoint =
|
|
681
|
+
BotaEncryptedUploadV2Checkpoint(
|
|
682
|
+
1,
|
|
683
|
+
uploadSessionId.toString(),
|
|
684
|
+
ownerRevision,
|
|
685
|
+
revision,
|
|
686
|
+
nextCiphertextOffset.toString(),
|
|
687
|
+
prefixSha256.toHex(),
|
|
688
|
+
highestContiguousSequence,
|
|
689
|
+
transportSessionId.toString(),
|
|
690
|
+
sinkId,
|
|
691
|
+
windowPackets,
|
|
692
|
+
dataPayloadBytes,
|
|
693
|
+
)
|
|
694
|
+
|
|
695
|
+
private data class EncryptedUploadV2Failure(
|
|
696
|
+
val code: String,
|
|
697
|
+
val retryable: Boolean,
|
|
698
|
+
val protocolStatus: UShort?,
|
|
699
|
+
)
|
|
700
|
+
|
|
701
|
+
private fun Throwable.encryptedUploadV2Failure(): EncryptedUploadV2Failure = when (this) {
|
|
702
|
+
is BotaSDKError.Core -> EncryptedUploadV2Failure(
|
|
703
|
+
code.stableName(),
|
|
704
|
+
retryable,
|
|
705
|
+
protocolStatus,
|
|
706
|
+
)
|
|
707
|
+
is BotaSDKError.AuthorizationRequired ->
|
|
708
|
+
EncryptedUploadV2Failure("authorization_required", false, null)
|
|
709
|
+
is kotlinx.coroutines.CancellationException ->
|
|
710
|
+
EncryptedUploadV2Failure("cancelled", false, null)
|
|
711
|
+
else -> EncryptedUploadV2Failure("application_material_rejected", false, null)
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
private fun BotaErrorCode.stableName(): String = when (this) {
|
|
715
|
+
BotaErrorCode.InvalidInput -> "invalid_input"
|
|
716
|
+
BotaErrorCode.TruncatedPacket -> "truncated_packet"
|
|
717
|
+
BotaErrorCode.UnknownPacket -> "unknown_packet"
|
|
718
|
+
BotaErrorCode.PayloadTooLarge -> "payload_too_large"
|
|
719
|
+
BotaErrorCode.UnsupportedCapability -> "unsupported_capability"
|
|
720
|
+
BotaErrorCode.UnsupportedOperation -> "unsupported_operation"
|
|
721
|
+
BotaErrorCode.FeatureUnavailable -> "feature_unavailable"
|
|
722
|
+
BotaErrorCode.OperationInProgress -> "operation_in_progress"
|
|
723
|
+
BotaErrorCode.UnexpectedEvent -> "unexpected_event"
|
|
724
|
+
BotaErrorCode.DeviceNotFound -> "device_not_found"
|
|
725
|
+
BotaErrorCode.IdentityMismatch -> "identity_mismatch"
|
|
726
|
+
BotaErrorCode.ConnectionFailed -> "connection_failed"
|
|
727
|
+
BotaErrorCode.PersistenceFailed -> "persistence_failed"
|
|
728
|
+
BotaErrorCode.NotConnected -> "not_connected"
|
|
729
|
+
BotaErrorCode.Timeout -> "timeout"
|
|
730
|
+
BotaErrorCode.Cancelled -> "cancelled"
|
|
731
|
+
BotaErrorCode.ProtocolRejected -> "protocol_rejected"
|
|
732
|
+
BotaErrorCode.IntegrityFailed -> "integrity_failed"
|
|
733
|
+
BotaErrorCode.UploadOwnershipUnknown -> "upload_ownership_unknown"
|
|
734
|
+
BotaErrorCode.DownloadFailed -> "download_failed"
|
|
735
|
+
BotaErrorCode.Internal -> "internal"
|
|
736
|
+
is BotaErrorCode.Unknown -> "unknown"
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
private fun ByteArray.toHex(): String = joinToString("") { "%02x".format(it.toInt() and 0xff) }
|
|
740
|
+
|
|
741
|
+
private fun streamingProgress(
|
|
742
|
+
sessionId: String,
|
|
743
|
+
state: String,
|
|
744
|
+
bytesReceived: ULong,
|
|
745
|
+
chunksUploaded: UInt,
|
|
746
|
+
): BotaStreamingProgress = BotaStreamingProgress(
|
|
747
|
+
sessionId,
|
|
748
|
+
state,
|
|
749
|
+
bytesReceived,
|
|
750
|
+
chunksUploaded,
|
|
751
|
+
)
|