@onekeyfe/react-native-range-downloader 3.0.81 → 3.0.82

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 (146) hide show
  1. package/ReactNativeRangeDownloader.podspec +7 -0
  2. package/android/src/main/java/com/margelo/nitro/reactnativerangedownloader/FirmwareArchiveRules.kt +291 -0
  3. package/android/src/main/java/com/margelo/nitro/reactnativerangedownloader/FirmwareArtifactOrphanSweep.kt +111 -0
  4. package/android/src/main/java/com/margelo/nitro/reactnativerangedownloader/FirmwareArtifactStore.kt +822 -0
  5. package/android/src/main/java/com/margelo/nitro/reactnativerangedownloader/ReactNativeRangeDownloader.kt +148 -0
  6. package/android/src/test/java/com/margelo/nitro/reactnativerangedownloader/FirmwareArchiveRulesTest.kt +114 -0
  7. package/android/src/test/java/com/margelo/nitro/reactnativerangedownloader/FirmwareArtifactDeadlineTest.kt +26 -0
  8. package/android/src/test/java/com/margelo/nitro/reactnativerangedownloader/FirmwareArtifactOrphanSweepTest.kt +107 -0
  9. package/ios/FirmwareArchiveMinizipBridge.h +33 -0
  10. package/ios/FirmwareArchiveMinizipBridge.mm +213 -0
  11. package/ios/FirmwareArtifactStore.swift +1508 -0
  12. package/ios/RangeDownloadLogic.swift +317 -0
  13. package/ios/ReactNativeRangeDownloader.swift +214 -6
  14. package/lib/module/index.js +2 -1
  15. package/lib/module/index.js.map +1 -1
  16. package/lib/typescript/src/ReactNativeRangeDownloader.nitro.d.ts +92 -1
  17. package/lib/typescript/src/ReactNativeRangeDownloader.nitro.d.ts.map +1 -1
  18. package/lib/typescript/src/index.d.ts +1 -0
  19. package/lib/typescript/src/index.d.ts.map +1 -1
  20. package/nitrogen/generated/android/c++/JDownloadChannel.hpp +8 -6
  21. package/nitrogen/generated/android/c++/JFirmwareArchiveExpectedEntry.hpp +69 -0
  22. package/nitrogen/generated/android/c++/JFirmwareArchiveMaterializeParams.hpp +87 -0
  23. package/nitrogen/generated/android/c++/JFirmwareArchiveMaterializeResult.hpp +80 -0
  24. package/nitrogen/generated/android/c++/JFirmwareArchiveMaterializedArtifact.hpp +63 -0
  25. package/nitrogen/generated/android/c++/JFirmwareArtifactCancelParams.hpp +57 -0
  26. package/nitrogen/generated/android/c++/JFirmwareArtifactCapabilities.hpp +88 -0
  27. package/nitrogen/generated/android/c++/JFirmwareArtifactDownloadParams.hpp +98 -0
  28. package/nitrogen/generated/android/c++/JFirmwareArtifactLease.hpp +57 -0
  29. package/nitrogen/generated/android/c++/JFirmwareArtifactLeaseCreateParams.hpp +57 -0
  30. package/nitrogen/generated/android/c++/JFirmwareArtifactLeaseReleaseParams.hpp +61 -0
  31. package/nitrogen/generated/android/c++/JFirmwareArtifactLeaseRetainParams.hpp +61 -0
  32. package/nitrogen/generated/android/c++/JFirmwareArtifactReaderCloseParams.hpp +57 -0
  33. package/nitrogen/generated/android/c++/JFirmwareArtifactReaderInfo.hpp +61 -0
  34. package/nitrogen/generated/android/c++/JFirmwareArtifactReaderReadParams.hpp +65 -0
  35. package/nitrogen/generated/android/c++/JFirmwareArtifactReceipt.hpp +69 -0
  36. package/nitrogen/generated/android/c++/JFirmwareArtifactRefParams.hpp +57 -0
  37. package/nitrogen/generated/android/c++/JFirmwareArtifactSweepResult.hpp +61 -0
  38. package/nitrogen/generated/android/c++/JFunc_void_RangeDownloadEvent.hpp +3 -3
  39. package/nitrogen/generated/android/c++/JHybridReactNativeRangeDownloaderSpec.cpp +274 -25
  40. package/nitrogen/generated/android/c++/JHybridReactNativeRangeDownloaderSpec.hpp +32 -22
  41. package/nitrogen/generated/android/c++/JRangeDownloadEvent.hpp +2 -2
  42. package/nitrogen/generated/android/c++/JRangeDownloadOutcome.hpp +6 -7
  43. package/nitrogen/generated/android/c++/JRangeDownloadParams.hpp +2 -2
  44. package/nitrogen/generated/android/c++/JRangeDownloadResult.hpp +2 -2
  45. package/nitrogen/generated/android/c++/JRangeFallbackKind.hpp +12 -13
  46. package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativerangedownloader/DownloadChannel.kt +5 -2
  47. package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativerangedownloader/FirmwareArchiveExpectedEntry.kt +47 -0
  48. package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativerangedownloader/FirmwareArchiveMaterializeParams.kt +44 -0
  49. package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativerangedownloader/FirmwareArchiveMaterializeResult.kt +38 -0
  50. package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativerangedownloader/FirmwareArchiveMaterializedArtifact.kt +41 -0
  51. package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativerangedownloader/FirmwareArtifactCancelParams.kt +38 -0
  52. package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativerangedownloader/FirmwareArtifactCapabilities.kt +47 -0
  53. package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativerangedownloader/FirmwareArtifactDownloadParams.kt +68 -0
  54. package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativerangedownloader/FirmwareArtifactLease.kt +38 -0
  55. package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativerangedownloader/FirmwareArtifactLeaseCreateParams.kt +38 -0
  56. package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativerangedownloader/FirmwareArtifactLeaseReleaseParams.kt +41 -0
  57. package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativerangedownloader/FirmwareArtifactLeaseRetainParams.kt +41 -0
  58. package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativerangedownloader/FirmwareArtifactReaderCloseParams.kt +38 -0
  59. package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativerangedownloader/FirmwareArtifactReaderInfo.kt +41 -0
  60. package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativerangedownloader/FirmwareArtifactReaderReadParams.kt +44 -0
  61. package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativerangedownloader/FirmwareArtifactReceipt.kt +47 -0
  62. package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativerangedownloader/FirmwareArtifactRefParams.kt +38 -0
  63. package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativerangedownloader/FirmwareArtifactSweepResult.kt +41 -0
  64. package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativerangedownloader/Func_void_RangeDownloadEvent.kt +1 -1
  65. package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativerangedownloader/HybridReactNativeRangeDownloaderSpec.kt +65 -19
  66. package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativerangedownloader/RangeDownloadEvent.kt +2 -2
  67. package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativerangedownloader/RangeDownloadOutcome.kt +3 -1
  68. package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativerangedownloader/RangeDownloadParams.kt +2 -2
  69. package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativerangedownloader/RangeDownloadResult.kt +2 -2
  70. package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativerangedownloader/RangeFallbackKind.kt +3 -1
  71. package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativerangedownloader/reactnativerangedownloaderOnLoad.kt +1 -1
  72. package/nitrogen/generated/android/reactnativerangedownloader+autolinking.cmake +1 -1
  73. package/nitrogen/generated/android/reactnativerangedownloader+autolinking.gradle +1 -1
  74. package/nitrogen/generated/android/reactnativerangedownloaderOnLoad.cpp +28 -18
  75. package/nitrogen/generated/android/reactnativerangedownloaderOnLoad.hpp +14 -5
  76. package/nitrogen/generated/ios/ReactNativeRangeDownloader+autolinking.rb +2 -2
  77. package/nitrogen/generated/ios/ReactNativeRangeDownloader-Swift-Cxx-Bridge.cpp +49 -1
  78. package/nitrogen/generated/ios/ReactNativeRangeDownloader-Swift-Cxx-Bridge.hpp +348 -4
  79. package/nitrogen/generated/ios/ReactNativeRangeDownloader-Swift-Cxx-Umbrella.hpp +54 -1
  80. package/nitrogen/generated/ios/ReactNativeRangeDownloaderAutolinking.mm +1 -1
  81. package/nitrogen/generated/ios/ReactNativeRangeDownloaderAutolinking.swift +9 -8
  82. package/nitrogen/generated/ios/c++/HybridReactNativeRangeDownloaderSpecSwift.cpp +1 -1
  83. package/nitrogen/generated/ios/c++/HybridReactNativeRangeDownloaderSpecSwift.hpp +159 -1
  84. package/nitrogen/generated/ios/swift/DownloadChannel.swift +5 -1
  85. package/nitrogen/generated/ios/swift/FirmwareArchiveExpectedEntry.swift +44 -0
  86. package/nitrogen/generated/ios/swift/FirmwareArchiveMaterializeParams.swift +58 -0
  87. package/nitrogen/generated/ios/swift/FirmwareArchiveMaterializeResult.swift +35 -0
  88. package/nitrogen/generated/ios/swift/FirmwareArchiveMaterializedArtifact.swift +34 -0
  89. package/nitrogen/generated/ios/swift/FirmwareArtifactCancelParams.swift +29 -0
  90. package/nitrogen/generated/ios/swift/FirmwareArtifactCapabilities.swift +50 -0
  91. package/nitrogen/generated/ios/swift/FirmwareArtifactDownloadParams.swift +131 -0
  92. package/nitrogen/generated/ios/swift/FirmwareArtifactLease.swift +29 -0
  93. package/nitrogen/generated/ios/swift/FirmwareArtifactLeaseCreateParams.swift +29 -0
  94. package/nitrogen/generated/ios/swift/FirmwareArtifactLeaseReleaseParams.swift +34 -0
  95. package/nitrogen/generated/ios/swift/FirmwareArtifactLeaseRetainParams.swift +34 -0
  96. package/nitrogen/generated/ios/swift/FirmwareArtifactReaderCloseParams.swift +29 -0
  97. package/nitrogen/generated/ios/swift/FirmwareArtifactReaderInfo.swift +34 -0
  98. package/nitrogen/generated/ios/swift/FirmwareArtifactReaderReadParams.swift +39 -0
  99. package/nitrogen/generated/ios/swift/FirmwareArtifactReceipt.swift +44 -0
  100. package/nitrogen/generated/ios/swift/FirmwareArtifactRefParams.swift +29 -0
  101. package/nitrogen/generated/ios/swift/FirmwareArtifactSweepResult.swift +34 -0
  102. package/nitrogen/generated/ios/swift/Func_void.swift +1 -2
  103. package/nitrogen/generated/ios/swift/Func_void_FirmwareArchiveMaterializeResult.swift +46 -0
  104. package/nitrogen/generated/ios/swift/Func_void_FirmwareArtifactLease.swift +46 -0
  105. package/nitrogen/generated/ios/swift/Func_void_FirmwareArtifactReaderInfo.swift +46 -0
  106. package/nitrogen/generated/ios/swift/Func_void_FirmwareArtifactReceipt.swift +46 -0
  107. package/nitrogen/generated/ios/swift/Func_void_FirmwareArtifactSweepResult.swift +46 -0
  108. package/nitrogen/generated/ios/swift/Func_void_RangeDownloadEvent.swift +1 -2
  109. package/nitrogen/generated/ios/swift/Func_void_RangeDownloadResult.swift +1 -2
  110. package/nitrogen/generated/ios/swift/Func_void_std__exception_ptr.swift +1 -2
  111. package/nitrogen/generated/ios/swift/Func_void_std__shared_ptr_ArrayBuffer_.swift +46 -0
  112. package/nitrogen/generated/ios/swift/HybridReactNativeRangeDownloaderSpec.swift +15 -4
  113. package/nitrogen/generated/ios/swift/HybridReactNativeRangeDownloaderSpec_cxx.swift +230 -2
  114. package/nitrogen/generated/ios/swift/RangeDownloadEvent.swift +11 -42
  115. package/nitrogen/generated/ios/swift/RangeDownloadOutcome.swift +1 -1
  116. package/nitrogen/generated/ios/swift/RangeDownloadParams.swift +72 -139
  117. package/nitrogen/generated/ios/swift/RangeDownloadResult.swift +16 -53
  118. package/nitrogen/generated/ios/swift/RangeFallbackKind.swift +1 -1
  119. package/nitrogen/generated/shared/c++/DownloadChannel.hpp +5 -1
  120. package/nitrogen/generated/shared/c++/FirmwareArchiveExpectedEntry.hpp +95 -0
  121. package/nitrogen/generated/shared/c++/FirmwareArchiveMaterializeParams.hpp +95 -0
  122. package/nitrogen/generated/shared/c++/FirmwareArchiveMaterializeResult.hpp +85 -0
  123. package/nitrogen/generated/shared/c++/FirmwareArchiveMaterializedArtifact.hpp +89 -0
  124. package/nitrogen/generated/shared/c++/FirmwareArtifactCancelParams.hpp +83 -0
  125. package/nitrogen/generated/shared/c++/FirmwareArtifactCapabilities.hpp +96 -0
  126. package/nitrogen/generated/shared/c++/FirmwareArtifactDownloadParams.hpp +124 -0
  127. package/nitrogen/generated/shared/c++/FirmwareArtifactLease.hpp +83 -0
  128. package/nitrogen/generated/shared/c++/FirmwareArtifactLeaseCreateParams.hpp +83 -0
  129. package/nitrogen/generated/shared/c++/FirmwareArtifactLeaseReleaseParams.hpp +87 -0
  130. package/nitrogen/generated/shared/c++/FirmwareArtifactLeaseRetainParams.hpp +87 -0
  131. package/nitrogen/generated/shared/c++/FirmwareArtifactReaderCloseParams.hpp +83 -0
  132. package/nitrogen/generated/shared/c++/FirmwareArtifactReaderInfo.hpp +87 -0
  133. package/nitrogen/generated/shared/c++/FirmwareArtifactReaderReadParams.hpp +91 -0
  134. package/nitrogen/generated/shared/c++/FirmwareArtifactReceipt.hpp +95 -0
  135. package/nitrogen/generated/shared/c++/FirmwareArtifactRefParams.hpp +83 -0
  136. package/nitrogen/generated/shared/c++/FirmwareArtifactSweepResult.hpp +87 -0
  137. package/nitrogen/generated/shared/c++/HybridReactNativeRangeDownloaderSpec.cpp +13 -1
  138. package/nitrogen/generated/shared/c++/HybridReactNativeRangeDownloaderSpec.hpp +59 -1
  139. package/nitrogen/generated/shared/c++/RangeDownloadEvent.hpp +25 -17
  140. package/nitrogen/generated/shared/c++/RangeDownloadOutcome.hpp +1 -1
  141. package/nitrogen/generated/shared/c++/RangeDownloadParams.hpp +43 -35
  142. package/nitrogen/generated/shared/c++/RangeDownloadResult.hpp +22 -14
  143. package/nitrogen/generated/shared/c++/RangeFallbackKind.hpp +1 -1
  144. package/package.json +4 -4
  145. package/src/ReactNativeRangeDownloader.nitro.ts +130 -1
  146. package/src/index.tsx +3 -1
@@ -2,12 +2,16 @@ package com.margelo.nitro.reactnativerangedownloader
2
2
 
3
3
  import com.facebook.proguard.annotations.DoNotStrip
4
4
  import com.margelo.nitro.NitroModules
5
+ import com.margelo.nitro.core.ArrayBuffer
5
6
  import com.margelo.nitro.core.Promise
6
7
  import com.margelo.nitro.nativelogger.OneKeyLog
7
8
  import java.io.File
8
9
  import java.security.MessageDigest
9
10
  import java.util.concurrent.CopyOnWriteArrayList
10
11
  import java.util.concurrent.atomic.AtomicLong
12
+ import kotlinx.coroutines.CoroutineScope
13
+ import kotlinx.coroutines.Dispatchers
14
+ import kotlinx.coroutines.SupervisorJob
11
15
 
12
16
  // P1: Nitro adapter for the Android concurrent multi-range downloader.
13
17
  //
@@ -33,6 +37,7 @@ class ReactNativeRangeDownloader : HybridReactNativeRangeDownloaderSpec() {
33
37
 
34
38
  private val listeners = CopyOnWriteArrayList<Listener>()
35
39
  private val nextListenerId = AtomicLong(1)
40
+ private val firmwareArtifactScope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
36
41
 
37
42
  // Active downloads keyed by "channel|taskId" so cancel/discardArtifacts can
38
43
  // flip the abort flag + stop the worker pool BEFORE deleting files, instead of
@@ -265,6 +270,149 @@ class ReactNativeRangeDownloader : HybridReactNativeRangeDownloaderSpec() {
265
270
  return ctx.cacheDir.absolutePath
266
271
  }
267
272
 
273
+ override fun getFirmwareArtifactCapabilities(): FirmwareArtifactCapabilities {
274
+ return FirmwareArtifactCapabilities(
275
+ firmwareArtifactProtocolVersion = 4.0,
276
+ supportedRouteTypes = arrayOf("domain"),
277
+ supportsArchiveMaterialization = true,
278
+ maxReadBytes = FirmwareArtifactStore.MAX_READ_BYTES.toDouble(),
279
+ )
280
+ }
281
+
282
+ override fun downloadFirmwareArtifact(
283
+ params: FirmwareArtifactDownloadParams,
284
+ ): Promise<FirmwareArtifactReceipt> {
285
+ return Promise.async(firmwareArtifactScope) {
286
+ val artifact = FirmwareArtifactStore.download(params)
287
+ FirmwareArtifactReceipt(
288
+ artifactRef = artifact.artifactRef,
289
+ size = artifact.size.toDouble(),
290
+ sha256 = artifact.sha256,
291
+ expectedSha256Verified = params.expectedSha256 != null,
292
+ )
293
+ }
294
+ }
295
+
296
+ override fun cancelFirmwareArtifactDownloads(
297
+ params: FirmwareArtifactCancelParams,
298
+ ): Promise<Unit> {
299
+ return Promise.async(firmwareArtifactScope) {
300
+ FirmwareArtifactStore.cancelDownloads(params.transactionId)
301
+ }
302
+ }
303
+
304
+ override fun discardFirmwareArtifact(
305
+ params: FirmwareArtifactRefParams,
306
+ ): Promise<Unit> {
307
+ return Promise.async(firmwareArtifactScope) {
308
+ FirmwareArtifactStore.discard(params.artifactRef)
309
+ }
310
+ }
311
+
312
+ override fun openFirmwareArtifact(
313
+ params: FirmwareArtifactRefParams,
314
+ ): Promise<FirmwareArtifactReaderInfo> {
315
+ return Promise.async(firmwareArtifactScope) {
316
+ val (readerId, size) = FirmwareArtifactStore.open(params.artifactRef)
317
+ FirmwareArtifactReaderInfo(readerId = readerId, size = size.toDouble())
318
+ }
319
+ }
320
+
321
+ override fun readFirmwareArtifact(
322
+ params: FirmwareArtifactReaderReadParams,
323
+ ): Promise<ArrayBuffer> {
324
+ return Promise.async(firmwareArtifactScope) {
325
+ require(
326
+ params.offset.isFinite() &&
327
+ params.offset >= 0 &&
328
+ params.offset <= FirmwareArtifactStore.MAX_ARTIFACT_BYTES.toDouble() &&
329
+ params.offset.toLong().toDouble() == params.offset &&
330
+ params.length.isFinite() &&
331
+ params.length > 0 &&
332
+ params.length <= FirmwareArtifactStore.MAX_READ_BYTES.toDouble() &&
333
+ params.length.toInt().toDouble() == params.length
334
+ ) {
335
+ "Invalid firmware artifact read"
336
+ }
337
+ ArrayBuffer.copy(
338
+ FirmwareArtifactStore.read(
339
+ readerId = params.readerId,
340
+ offset = params.offset.toLong(),
341
+ length = params.length.toInt(),
342
+ ),
343
+ )
344
+ }
345
+ }
346
+
347
+ override fun closeFirmwareArtifact(
348
+ params: FirmwareArtifactReaderCloseParams,
349
+ ): Promise<Unit> {
350
+ return Promise.async(firmwareArtifactScope) {
351
+ FirmwareArtifactStore.close(params.readerId)
352
+ }
353
+ }
354
+
355
+ override fun materializeFirmwareArchive(
356
+ params: FirmwareArchiveMaterializeParams,
357
+ ): Promise<FirmwareArchiveMaterializeResult> {
358
+ return Promise.async(firmwareArtifactScope) {
359
+ val artifacts = FirmwareArtifactStore.materializeArchive(
360
+ params.leaseRef,
361
+ params.archiveArtifactRef,
362
+ params.expectedEntries,
363
+ )
364
+ FirmwareArchiveMaterializeResult(
365
+ artifacts = artifacts.map { entry ->
366
+ FirmwareArchiveMaterializedArtifact(
367
+ entryName = entry.entryName,
368
+ receipt = FirmwareArtifactReceipt(
369
+ artifactRef = entry.artifact.artifactRef,
370
+ size = entry.artifact.size.toDouble(),
371
+ sha256 = entry.artifact.sha256,
372
+ expectedSha256Verified = params.expectedEntries != null,
373
+ ),
374
+ )
375
+ }.toTypedArray(),
376
+ )
377
+ }
378
+ }
379
+
380
+ override fun createFirmwareArtifactLease(
381
+ params: FirmwareArtifactLeaseCreateParams,
382
+ ): Promise<FirmwareArtifactLease> {
383
+ return Promise.async(firmwareArtifactScope) {
384
+ FirmwareArtifactLease(
385
+ leaseRef = FirmwareArtifactStore.createLease(params.transactionId),
386
+ )
387
+ }
388
+ }
389
+
390
+ override fun retainFirmwareArtifact(
391
+ params: FirmwareArtifactLeaseRetainParams,
392
+ ): Promise<Unit> {
393
+ return Promise.async(firmwareArtifactScope) {
394
+ FirmwareArtifactStore.retain(params.leaseRef, params.artifactRef)
395
+ }
396
+ }
397
+
398
+ override fun releaseFirmwareArtifactLease(
399
+ params: FirmwareArtifactLeaseReleaseParams,
400
+ ): Promise<Unit> {
401
+ return Promise.async(firmwareArtifactScope) {
402
+ FirmwareArtifactStore.releaseLease(params.leaseRef, params.disposition)
403
+ }
404
+ }
405
+
406
+ override fun sweepFirmwareArtifactOrphans(): Promise<FirmwareArtifactSweepResult> {
407
+ return Promise.async(firmwareArtifactScope) {
408
+ val (deletedFiles, deletedBytes) = FirmwareArtifactStore.sweepOrphans()
409
+ FirmwareArtifactSweepResult(
410
+ deletedFiles = deletedFiles.toDouble(),
411
+ deletedBytes = deletedBytes.toDouble(),
412
+ )
413
+ }
414
+ }
415
+
268
416
  // Broadcast one event to every registered listener. Listeners filter by
269
417
  // channel/taskId on their side (shared registry, per the design).
270
418
  private fun sendEvent(
@@ -0,0 +1,114 @@
1
+ package com.margelo.nitro.reactnativerangedownloader
2
+
3
+ import java.io.File
4
+ import java.io.FileOutputStream
5
+ import java.util.zip.ZipEntry
6
+ import java.util.zip.ZipOutputStream
7
+ import org.junit.Assert.assertEquals
8
+ import org.junit.Assert.assertThrows
9
+ import org.junit.Rule
10
+ import org.junit.Test
11
+ import org.junit.rules.TemporaryFolder
12
+
13
+ class FirmwareArchiveRulesTest {
14
+ @get:Rule
15
+ val temporaryFolder = TemporaryFolder()
16
+
17
+ private fun entry(
18
+ artifactId: String = "resource-entry",
19
+ entryName: String = "assets/icon.png",
20
+ expectedSize: Double = 128.0,
21
+ expectedSha256: String = "a".repeat(64),
22
+ ) = FirmwareArchiveExpectedEntry(
23
+ artifactId = artifactId,
24
+ entryName = entryName,
25
+ expectedSize = expectedSize,
26
+ expectedSha256 = expectedSha256,
27
+ )
28
+
29
+ @Test
30
+ fun acceptsAnExactPortableAllowlist() {
31
+ val requirements = FirmwareArchiveRules.validateRequirements(
32
+ arrayOf(
33
+ entry(),
34
+ entry(
35
+ artifactId = "resource-entry-2",
36
+ entryName = "assets/sub/icon-2.png",
37
+ expectedSha256 = "b".repeat(64),
38
+ ),
39
+ ),
40
+ )
41
+
42
+ assertEquals(2, requirements.size)
43
+ }
44
+
45
+ @Test
46
+ fun rejectsTraversalAndNestedArchives() {
47
+ for (entryName in listOf("../icon.png", "assets/../icon.png", "assets.zip")) {
48
+ assertThrows(IllegalArgumentException::class.java) {
49
+ FirmwareArchiveRules.validateRequirements(
50
+ arrayOf(entry(entryName = entryName)),
51
+ )
52
+ }
53
+ }
54
+ }
55
+
56
+ @Test
57
+ fun rejectsCaseFoldedDuplicateNames() {
58
+ assertThrows(IllegalArgumentException::class.java) {
59
+ FirmwareArchiveRules.validateRequirements(
60
+ arrayOf(
61
+ entry(entryName = "assets/Icon.png"),
62
+ entry(
63
+ artifactId = "resource-entry-2",
64
+ entryName = "assets/icon.png",
65
+ ),
66
+ ),
67
+ )
68
+ }
69
+ }
70
+
71
+ @Test
72
+ fun rejectsNonIntegralSizesAndInvalidDigests() {
73
+ assertThrows(IllegalArgumentException::class.java) {
74
+ FirmwareArchiveRules.validateRequirements(
75
+ arrayOf(entry(expectedSize = 1.5)),
76
+ )
77
+ }
78
+ assertThrows(IllegalArgumentException::class.java) {
79
+ FirmwareArchiveRules.validateRequirements(
80
+ arrayOf(entry(expectedSha256 = "not-a-digest")),
81
+ )
82
+ }
83
+ }
84
+
85
+ @Test
86
+ fun acceptsPortableEntriesWithoutExpectedIntegrityMetadata() {
87
+ val archive = createArchive("assets/icon.png", byteArrayOf(1, 2, 3))
88
+
89
+ val entries = FirmwareArchiveRules.validateCentralDirectory(archive, null)
90
+
91
+ assertEquals(1, entries.size)
92
+ assertEquals("assets/icon.png", entries.single().name)
93
+ assertEquals(3, entries.single().uncompressedSize)
94
+ }
95
+
96
+ @Test
97
+ fun rejectsTraversalWithoutExpectedIntegrityMetadata() {
98
+ val archive = createArchive("../icon.png", byteArrayOf(1))
99
+
100
+ assertThrows(IllegalArgumentException::class.java) {
101
+ FirmwareArchiveRules.validateCentralDirectory(archive, null)
102
+ }
103
+ }
104
+
105
+ private fun createArchive(entryName: String, content: ByteArray): File {
106
+ val archive = temporaryFolder.newFile("firmware.zip")
107
+ ZipOutputStream(FileOutputStream(archive)).use { zip ->
108
+ zip.putNextEntry(ZipEntry(entryName))
109
+ zip.write(content)
110
+ zip.closeEntry()
111
+ }
112
+ return archive
113
+ }
114
+ }
@@ -0,0 +1,26 @@
1
+ package com.margelo.nitro.reactnativerangedownloader
2
+
3
+ import org.junit.Assert.assertEquals
4
+ import org.junit.Assert.assertThrows
5
+ import org.junit.Test
6
+
7
+ class FirmwareArtifactDeadlineTest {
8
+ @Test
9
+ fun defaultsToABoundedThreeMinuteDeadline() {
10
+ assertEquals(180.0, validateFirmwareDownloadDeadlineSeconds(null), 0.0)
11
+ }
12
+
13
+ @Test
14
+ fun preservesFractionalDeadlines() {
15
+ assertEquals(2.75, validateFirmwareDownloadDeadlineSeconds(2.75), 0.0)
16
+ }
17
+
18
+ @Test
19
+ fun rejectsUnboundedOrInvalidDeadlines() {
20
+ listOf(0.0, -1.0, Double.NaN, Double.POSITIVE_INFINITY, 86_400.1).forEach {
21
+ assertThrows(IllegalArgumentException::class.java) {
22
+ validateFirmwareDownloadDeadlineSeconds(it)
23
+ }
24
+ }
25
+ }
26
+ }
@@ -0,0 +1,107 @@
1
+ package com.margelo.nitro.reactnativerangedownloader
2
+
3
+ import java.io.File
4
+ import org.junit.Assert.assertEquals
5
+ import org.junit.Assert.assertFalse
6
+ import org.junit.Assert.assertTrue
7
+ import org.junit.Rule
8
+ import org.junit.Test
9
+ import org.junit.rules.TemporaryFolder
10
+
11
+ class FirmwareArtifactOrphanSweepTest {
12
+ @get:Rule
13
+ val temporaryFolder = TemporaryFolder()
14
+
15
+ @Test
16
+ fun removesOnlyStaleRootScratchEntriesWithExactNamesAndTypes() {
17
+ val root = temporaryFolder.newFolder("firmware-artifact-sweep")
18
+ val nowMs = 2_000_000_000_000L
19
+ val staleAt = nowMs - FIRMWARE_ARTIFACT_SCRATCH_GRACE_MS - 1
20
+ val freshAt = nowMs - FIRMWARE_ARTIFACT_SCRATCH_GRACE_MS + 1
21
+ val staleArchive = File(
22
+ root,
23
+ "archive-00000000-0000-4000-8000-000000000001",
24
+ ).apply {
25
+ assertTrue(mkdirs())
26
+ File(this, "0.entry").writeText("archive")
27
+ }
28
+ val stalePromote = File(
29
+ root,
30
+ ".promote-00000000-0000-4000-8000-000000000002",
31
+ ).apply { writeText("promote") }
32
+ val freshArchive = File(
33
+ root,
34
+ "archive-00000000-0000-4000-8000-000000000003",
35
+ ).apply { assertTrue(mkdirs()) }
36
+ val freshPromote = File(
37
+ root,
38
+ ".promote-00000000-0000-4000-8000-000000000004",
39
+ ).apply { writeText("promote") }
40
+ val malformedArchive = File(
41
+ root,
42
+ "archive-00000000-0000-4000-8000-000000000005.extra",
43
+ ).apply { assertTrue(mkdirs()) }
44
+ val malformedPromote = File(
45
+ root,
46
+ ".promote-00000000-0000-4000-8000-000000000006.tmp",
47
+ ).apply { writeText("promote") }
48
+ val archiveNamedFile = File(
49
+ root,
50
+ "archive-00000000-0000-4000-8000-000000000007",
51
+ ).apply { writeText("promote") }
52
+ val promoteNamedDirectory = File(
53
+ root,
54
+ ".promote-00000000-0000-4000-8000-000000000008",
55
+ ).apply { assertTrue(mkdirs()) }
56
+ val nestedArchive = File(
57
+ File(root, "nested"),
58
+ "archive-00000000-0000-4000-8000-000000000009",
59
+ ).apply { assertTrue(mkdirs()) }
60
+
61
+ for (
62
+ entry in listOf(
63
+ staleArchive,
64
+ stalePromote,
65
+ malformedArchive,
66
+ malformedPromote,
67
+ archiveNamedFile,
68
+ promoteNamedDirectory,
69
+ nestedArchive,
70
+ )
71
+ ) {
72
+ assertTrue(entry.setLastModified(staleAt))
73
+ }
74
+ for (entry in listOf(freshArchive, freshPromote)) {
75
+ assertTrue(entry.setLastModified(freshAt))
76
+ }
77
+
78
+ val result = sweepFirmwareArtifactOrphansAtRoot(
79
+ root = root,
80
+ retainedSha256 = emptySet(),
81
+ activeSha256 = emptySet(),
82
+ openPaths = emptySet(),
83
+ nowMs = nowMs,
84
+ )
85
+
86
+ assertEquals(2, result.first)
87
+ assertEquals(14L, result.second)
88
+ assertFalse(staleArchive.exists())
89
+ assertFalse(stalePromote.exists())
90
+ for (
91
+ retainedEntry in listOf(
92
+ freshArchive,
93
+ freshPromote,
94
+ malformedArchive,
95
+ malformedPromote,
96
+ archiveNamedFile,
97
+ promoteNamedDirectory,
98
+ nestedArchive,
99
+ )
100
+ ) {
101
+ assertTrue(
102
+ "Unexpectedly removed ${retainedEntry.name}",
103
+ retainedEntry.exists(),
104
+ )
105
+ }
106
+ }
107
+ }
@@ -0,0 +1,33 @@
1
+ #import <Foundation/Foundation.h>
2
+
3
+ NS_ASSUME_NONNULL_BEGIN
4
+
5
+ @interface FirmwareArchiveEntryInfo : NSObject
6
+
7
+ @property(nonatomic, readonly) NSString *name;
8
+ @property(nonatomic, readonly) int64_t compressedSize;
9
+ @property(nonatomic, readonly) int64_t uncompressedSize;
10
+ @property(nonatomic, readonly) uint32_t crc32;
11
+ @property(nonatomic, readonly) uint16_t flags;
12
+ @property(nonatomic, readonly) uint16_t compressionMethod;
13
+ @property(nonatomic, readonly) uint16_t versionMadeBy;
14
+ @property(nonatomic, readonly) uint32_t externalAttributes;
15
+ @property(nonatomic, readonly) uint32_t diskNumber;
16
+ @property(nonatomic, readonly, nullable) NSString *linkName;
17
+
18
+ @end
19
+
20
+ @interface FirmwareArchiveMinizipBridge : NSObject
21
+
22
+ + (nullable NSArray<FirmwareArchiveEntryInfo *> *)scanArchiveAtPath:
23
+ (NSString *)path
24
+ error:(NSError * _Nullable * _Nullable)error;
25
+
26
+ + (BOOL)extractEntryNamed:(NSString *)entryName
27
+ archivePath:(NSString *)archivePath
28
+ consumer:(BOOL (^)(NSData *chunk))consumer
29
+ error:(NSError * _Nullable * _Nullable)error;
30
+
31
+ @end
32
+
33
+ NS_ASSUME_NONNULL_END
@@ -0,0 +1,213 @@
1
+ #import "FirmwareArchiveMinizipBridge.h"
2
+
3
+ #import "mz.h"
4
+ #import "mz_strm.h"
5
+ #import "mz_zip.h"
6
+ #import "mz_zip_rw.h"
7
+
8
+ #include <cstring>
9
+
10
+ static NSString *const FirmwareArchiveBridgeErrorDomain =
11
+ @"so.onekey.firmware.archive.minizip";
12
+
13
+ static NSError *FirmwareArchiveBridgeError(NSString *message, int32_t code) {
14
+ return [NSError errorWithDomain:FirmwareArchiveBridgeErrorDomain
15
+ code:code
16
+ userInfo:@{NSLocalizedDescriptionKey : message}];
17
+ }
18
+
19
+ @interface FirmwareArchiveEntryInfo ()
20
+
21
+ @property(nonatomic, readwrite) NSString *name;
22
+ @property(nonatomic, readwrite) int64_t compressedSize;
23
+ @property(nonatomic, readwrite) int64_t uncompressedSize;
24
+ @property(nonatomic, readwrite) uint32_t crc32;
25
+ @property(nonatomic, readwrite) uint16_t flags;
26
+ @property(nonatomic, readwrite) uint16_t compressionMethod;
27
+ @property(nonatomic, readwrite) uint16_t versionMadeBy;
28
+ @property(nonatomic, readwrite) uint32_t externalAttributes;
29
+ @property(nonatomic, readwrite) uint32_t diskNumber;
30
+ @property(nonatomic, readwrite, nullable) NSString *linkName;
31
+
32
+ @end
33
+
34
+ @implementation FirmwareArchiveEntryInfo
35
+ @end
36
+
37
+ @implementation FirmwareArchiveMinizipBridge
38
+
39
+ + (nullable NSArray<FirmwareArchiveEntryInfo *> *)scanArchiveAtPath:
40
+ (NSString *)path
41
+ error:(NSError * _Nullable * _Nullable)error {
42
+ void *reader = nullptr;
43
+ mz_zip_reader_create(&reader);
44
+ if (reader == nullptr) {
45
+ if (error != nullptr) {
46
+ *error = FirmwareArchiveBridgeError(@"Archive reader allocation failed",
47
+ MZ_MEM_ERROR);
48
+ }
49
+ return nil;
50
+ }
51
+
52
+ int32_t result = mz_zip_reader_open_file(reader, path.fileSystemRepresentation);
53
+ if (result != MZ_OK) {
54
+ mz_zip_reader_delete(&reader);
55
+ if (error != nullptr) {
56
+ *error = FirmwareArchiveBridgeError(@"Archive open failed", result);
57
+ }
58
+ return nil;
59
+ }
60
+
61
+ NSMutableArray<FirmwareArchiveEntryInfo *> *entries =
62
+ [NSMutableArray array];
63
+ result = mz_zip_reader_goto_first_entry(reader);
64
+ while (result == MZ_OK) {
65
+ mz_zip_file *fileInfo = nullptr;
66
+ result = mz_zip_reader_entry_get_info(reader, &fileInfo);
67
+ if (result != MZ_OK || fileInfo == nullptr ||
68
+ fileInfo->filename == nullptr) {
69
+ result = result == MZ_OK ? MZ_FORMAT_ERROR : result;
70
+ break;
71
+ }
72
+ const size_t filenameLength = std::strlen(fileInfo->filename);
73
+ if (filenameLength == 0 || filenameLength > 4096) {
74
+ result = MZ_FORMAT_ERROR;
75
+ break;
76
+ }
77
+ if ((fileInfo->flag & (1 << 11)) == 0) {
78
+ const unsigned char *filenameBytes =
79
+ reinterpret_cast<const unsigned char *>(fileInfo->filename);
80
+ bool isAscii = true;
81
+ for (size_t index = 0; index < filenameLength; index += 1) {
82
+ if (filenameBytes[index] >= 0x80) {
83
+ isAscii = false;
84
+ break;
85
+ }
86
+ }
87
+ if (!isAscii) {
88
+ result = MZ_FORMAT_ERROR;
89
+ break;
90
+ }
91
+ }
92
+ NSString *name = [NSString stringWithUTF8String:fileInfo->filename];
93
+ if (name == nil) {
94
+ result = MZ_FORMAT_ERROR;
95
+ break;
96
+ }
97
+ NSString *linkName = nil;
98
+ if (fileInfo->linkname != nullptr) {
99
+ linkName = [NSString stringWithUTF8String:fileInfo->linkname];
100
+ if (linkName == nil) {
101
+ result = MZ_FORMAT_ERROR;
102
+ break;
103
+ }
104
+ }
105
+
106
+ FirmwareArchiveEntryInfo *entry =
107
+ [[FirmwareArchiveEntryInfo alloc] init];
108
+ entry.name = name;
109
+ entry.compressedSize = fileInfo->compressed_size;
110
+ entry.uncompressedSize = fileInfo->uncompressed_size;
111
+ entry.crc32 = fileInfo->crc;
112
+ entry.flags = fileInfo->flag;
113
+ entry.compressionMethod = fileInfo->compression_method;
114
+ entry.versionMadeBy = fileInfo->version_madeby;
115
+ entry.externalAttributes = fileInfo->external_fa;
116
+ entry.diskNumber = fileInfo->disk_number;
117
+ entry.linkName = linkName;
118
+ [entries addObject:entry];
119
+ if (entries.count > 4096) {
120
+ result = MZ_FORMAT_ERROR;
121
+ break;
122
+ }
123
+ result = mz_zip_reader_goto_next_entry(reader);
124
+ }
125
+
126
+ if (result == MZ_END_OF_LIST) {
127
+ result = MZ_OK;
128
+ }
129
+ int32_t closeResult = mz_zip_reader_close(reader);
130
+ mz_zip_reader_delete(&reader);
131
+ if (result != MZ_OK || closeResult != MZ_OK) {
132
+ if (error != nullptr) {
133
+ *error = FirmwareArchiveBridgeError(@"Archive scan failed",
134
+ result != MZ_OK ? result
135
+ : closeResult);
136
+ }
137
+ return nil;
138
+ }
139
+ return entries;
140
+ }
141
+
142
+ + (BOOL)extractEntryNamed:(NSString *)entryName
143
+ archivePath:(NSString *)archivePath
144
+ consumer:(BOOL (^)(NSData *chunk))consumer
145
+ error:(NSError * _Nullable * _Nullable)error {
146
+ void *reader = nullptr;
147
+ bool archiveOpened = false;
148
+ bool entryOpened = false;
149
+ mz_zip_reader_create(&reader);
150
+ if (reader == nullptr) {
151
+ if (error != nullptr) {
152
+ *error = FirmwareArchiveBridgeError(@"Archive reader allocation failed",
153
+ MZ_MEM_ERROR);
154
+ }
155
+ return NO;
156
+ }
157
+
158
+ int32_t result =
159
+ mz_zip_reader_open_file(reader, archivePath.fileSystemRepresentation);
160
+ if (result == MZ_OK) {
161
+ archiveOpened = true;
162
+ }
163
+ if (result == MZ_OK) {
164
+ result = mz_zip_reader_locate_entry(
165
+ reader, entryName.UTF8String, 0);
166
+ }
167
+ if (result == MZ_OK) {
168
+ result = mz_zip_reader_entry_open(reader);
169
+ if (result == MZ_OK) {
170
+ entryOpened = true;
171
+ }
172
+ }
173
+
174
+ uint8_t buffer[64 * 1024];
175
+ while (result == MZ_OK) {
176
+ int32_t count =
177
+ mz_zip_reader_entry_read(reader, buffer, (int32_t)sizeof(buffer));
178
+ if (count < 0) {
179
+ result = count;
180
+ break;
181
+ }
182
+ if (count == 0) {
183
+ break;
184
+ }
185
+ NSData *chunk = [NSData dataWithBytes:buffer length:(NSUInteger)count];
186
+ if (!consumer(chunk)) {
187
+ result = MZ_WRITE_ERROR;
188
+ break;
189
+ }
190
+ }
191
+
192
+ int32_t entryCloseResult =
193
+ entryOpened ? mz_zip_reader_entry_close(reader) : MZ_OK;
194
+ int32_t archiveCloseResult =
195
+ archiveOpened ? mz_zip_reader_close(reader) : MZ_OK;
196
+ mz_zip_reader_delete(&reader);
197
+ if (result != MZ_OK || entryCloseResult != MZ_OK ||
198
+ archiveCloseResult != MZ_OK) {
199
+ if (error != nullptr) {
200
+ int32_t failure = result != MZ_OK
201
+ ? result
202
+ : (entryCloseResult != MZ_OK
203
+ ? entryCloseResult
204
+ : archiveCloseResult);
205
+ *error = FirmwareArchiveBridgeError(
206
+ @"Archive entry extraction or CRC validation failed", failure);
207
+ }
208
+ return NO;
209
+ }
210
+ return YES;
211
+ }
212
+
213
+ @end