react-native-update 10.59.2 → 10.61.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.
Files changed (877) hide show
  1. package/README-CN.md +13 -7
  2. package/README.md +15 -6
  3. package/android/build.gradle +25 -33
  4. package/android/jni/Android.mk +18 -24
  5. package/android/jni/Application.mk +3 -1
  6. package/android/lib/arm64-v8a/librnpushy.so +0 -0
  7. package/android/lib/armeabi-v7a/librnpushy.so +0 -0
  8. package/android/lib/x86/librnpushy.so +0 -0
  9. package/android/lib/x86_64/librnpushy.so +0 -0
  10. package/android/proguard.pro +9 -7
  11. package/android/src/main/java/cn/reactnative/modules/update/ApkInstaller.java +3 -8
  12. package/android/src/main/java/cn/reactnative/modules/update/ArchiveLimits.java +24 -6
  13. package/android/src/main/java/cn/reactnative/modules/update/BundlePreparationResult.java +2 -0
  14. package/android/src/main/java/cn/reactnative/modules/update/BundledResourceCopier.java +50 -51
  15. package/android/src/main/java/cn/reactnative/modules/update/CleanupKeepNames.java +12 -13
  16. package/android/src/main/java/cn/reactnative/modules/update/CopyGroupResult.java +3 -0
  17. package/android/src/main/java/cn/reactnative/modules/update/CrashHold.java +22 -30
  18. package/android/src/main/java/cn/reactnative/modules/update/DownloadTask.java +284 -452
  19. package/android/src/main/java/cn/reactnative/modules/update/DownloadTaskParams.java +8 -4
  20. package/android/src/main/java/cn/reactnative/modules/update/ErrorCodes.java +32 -3
  21. package/android/src/main/java/cn/reactnative/modules/update/FileOperationException.java +1 -1
  22. package/android/src/main/java/cn/reactnative/modules/update/FlowBridge.java +39 -8
  23. package/android/src/main/java/cn/reactnative/modules/update/HttpClients.java +63 -0
  24. package/android/src/main/java/cn/reactnative/modules/update/HttpUtils.java +0 -45
  25. package/android/src/main/java/cn/reactnative/modules/update/InstallRecord.java +68 -116
  26. package/android/src/main/java/cn/reactnative/modules/update/NativeCore.java +10 -2
  27. package/android/src/main/java/cn/reactnative/modules/update/PatchFailedException.java +1 -1
  28. package/android/src/main/java/cn/reactnative/modules/update/PushyRuntime.java +53 -62
  29. package/android/src/main/java/cn/reactnative/modules/update/ReactReloadManager.java +9 -24
  30. package/android/src/main/java/cn/reactnative/modules/update/ResumePlan.java +31 -0
  31. package/android/src/main/java/cn/reactnative/modules/update/SafeZipFile.java +42 -80
  32. package/android/src/main/java/cn/reactnative/modules/update/StateSerialRunner.java +22 -39
  33. package/android/src/main/java/cn/reactnative/modules/update/SyncCoordinator.java +474 -500
  34. package/android/src/main/java/cn/reactnative/modules/update/Texts.java +15 -1
  35. package/android/src/main/java/cn/reactnative/modules/update/Threads.java +44 -0
  36. package/android/src/main/java/cn/reactnative/modules/update/UpdateContext.java +265 -188
  37. package/android/src/main/java/cn/reactnative/modules/update/UpdateEventEmitter.java +1 -1
  38. package/android/src/main/java/cn/reactnative/modules/update/UpdateFileUtils.java +75 -17
  39. package/android/src/main/java/cn/reactnative/modules/update/{UpdateModuleImpl.java → UpdateModule.java} +114 -96
  40. package/android/src/main/java/cn/reactnative/modules/update/UpdatePackage.java +16 -19
  41. package/android/src/main/java/cn/reactnative/modules/update/VersionScopedKeys.java +64 -0
  42. package/android/src/oldarch/cn/reactnative/modules/update/NativePushySpec.java +71 -0
  43. package/cpp/android_jni/archive_limits_jni.cpp +83 -0
  44. package/cpp/android_jni/install_record_jni.cpp +89 -0
  45. package/cpp/android_jni/jni_log.h +30 -0
  46. package/cpp/android_jni/jni_natives.h +46 -0
  47. package/cpp/android_jni/jni_registration.cpp +146 -0
  48. package/cpp/android_jni/jni_util.h +160 -0
  49. package/cpp/android_jni/resume_plan_jni.cpp +58 -0
  50. package/cpp/android_jni/update_core_android.cpp +448 -0
  51. package/cpp/android_jni/update_flow_jni.cpp +150 -0
  52. package/{android/jni → cpp/hpatch}/hpatch.c +5 -5
  53. package/{android/jni → cpp/hpatch}/hpatch.h +4 -1
  54. package/cpp/patch_core/archive_limits.cpp +194 -0
  55. package/cpp/patch_core/archive_limits.h +101 -10
  56. package/cpp/patch_core/archive_patch_core.cpp +98 -15
  57. package/cpp/patch_core/archive_patch_core.h +51 -0
  58. package/cpp/patch_core/digest.h +5 -4
  59. package/cpp/patch_core/error_codes.h +15 -11
  60. package/cpp/patch_core/install_record.cpp +430 -0
  61. package/cpp/patch_core/install_record.h +165 -9
  62. package/cpp/patch_core/native_texts.h +102 -0
  63. package/cpp/patch_core/patch_core.cpp +62 -16
  64. package/cpp/patch_core/patch_core.h +27 -7
  65. package/cpp/patch_core/patch_manifest.cpp +201 -0
  66. package/cpp/patch_core/patch_manifest.h +74 -0
  67. package/cpp/patch_core/resume_plan.cpp +226 -0
  68. package/cpp/patch_core/resume_plan.h +88 -0
  69. package/cpp/patch_core/state_ops.h +5 -3
  70. package/cpp/update_flow_core/flow_json.cpp +82 -8
  71. package/cpp/update_flow_core/flow_json.h +21 -0
  72. package/cpp/update_flow_core/native_config.cpp +436 -0
  73. package/cpp/update_flow_core/native_config.h +84 -0
  74. package/cpp/update_flow_core/native_round.cpp +321 -0
  75. package/cpp/update_flow_core/native_round.h +94 -0
  76. package/cpp/update_flow_core/round_engine.cpp +456 -0
  77. package/cpp/update_flow_core/round_engine.h +106 -0
  78. package/cpp/update_flow_core/update_flow_core.cpp +5 -14
  79. package/cpp/update_flow_core/update_flow_core.h +2 -2
  80. package/harmony/hvigor-plugin.ts +11 -19
  81. package/harmony/pushy/src/main/cpp/CMakeLists.txt +19 -26
  82. package/harmony/pushy/src/main/cpp/PushyPackage.h +2 -2
  83. package/harmony/pushy/src/main/cpp/PushyTurboModule.cpp +57 -94
  84. package/harmony/pushy/src/main/cpp/pushy.cpp +817 -518
  85. package/harmony/pushy/src/main/ets/ArchiveLimits.ts +55 -75
  86. package/harmony/pushy/src/main/ets/DownloadTask.ts +279 -539
  87. package/harmony/pushy/src/main/ets/DownloadTaskParams.ts +6 -6
  88. package/harmony/pushy/src/main/ets/ErrorCodes.ts +3 -15
  89. package/harmony/pushy/src/main/ets/EventHub.ts +1 -1
  90. package/harmony/pushy/src/main/ets/InstallRecord.ts +54 -151
  91. package/harmony/pushy/src/main/ets/Logger.ts +20 -34
  92. package/harmony/pushy/src/main/ets/NativePatchCore.ts +150 -41
  93. package/harmony/pushy/src/main/ets/PushyConfiguration.ts +8 -85
  94. package/harmony/pushy/src/main/ets/PushyFileJSBundleProvider.ets +8 -3
  95. package/harmony/pushy/src/main/ets/PushyTurboModule.ts +11 -25
  96. package/harmony/pushy/src/main/ets/SaveFile.ts +99 -18
  97. package/harmony/pushy/src/main/ets/SyncCoordinator.ts +291 -543
  98. package/harmony/pushy/src/main/ets/Texts.ts +8 -7
  99. package/harmony/pushy/src/main/ets/UpdateContext.ts +245 -152
  100. package/harmony/pushy.har +0 -0
  101. package/ios/RCTPushy/RCTPushy+Launch.mm +190 -0
  102. package/ios/RCTPushy/RCTPushy.h +12 -0
  103. package/ios/RCTPushy/RCTPushy.mm +135 -2666
  104. package/ios/RCTPushy/RCTPushyCommon.h +28 -0
  105. package/ios/RCTPushy/RCTPushyConfiguration.h +0 -8
  106. package/ios/RCTPushy/RCTPushyConfiguration.mm +40 -123
  107. package/ios/RCTPushy/RCTPushyDownloader.mm +152 -198
  108. package/ios/RCTPushy/RCTPushyInstallRecord.mm +101 -0
  109. package/ios/RCTPushy/RCTPushyInstaller.mm +732 -0
  110. package/ios/RCTPushy/RCTPushyInternal.h +185 -0
  111. package/ios/RCTPushy/RCTPushyOrchestrator.mm +969 -0
  112. package/ios/RCTPushy/RCTPushyState.mm +382 -0
  113. package/package.json +46 -25
  114. package/react-native-update.podspec +73 -82
  115. package/src/client.ts +126 -1633
  116. package/src/clientActivation.ts +324 -0
  117. package/src/clientCheck.ts +405 -0
  118. package/src/clientDownload.ts +448 -0
  119. package/src/clientNativeConfig.ts +171 -0
  120. package/src/clientReporter.ts +398 -0
  121. package/src/clientState.ts +51 -0
  122. package/src/context.ts +1 -1
  123. package/src/core.ts +39 -10
  124. package/src/endpoint.ts +22 -8
  125. package/src/error.ts +18 -35
  126. package/src/i18n.ts +18 -83
  127. package/src/locales/en.ts +1 -5
  128. package/src/locales/zh.ts +1 -5
  129. package/src/provider.tsx +86 -67
  130. package/src/telemetry.ts +0 -13
  131. package/src/type.ts +0 -15
  132. package/src/updateFlowCore.ts +116 -10
  133. package/src/utils.ts +49 -42
  134. package/android/jni/HDiffPatch/.travis.yml +0 -33
  135. package/android/jni/HDiffPatch/CHANGELOG.md +0 -153
  136. package/android/jni/HDiffPatch/Makefile +0 -157
  137. package/android/jni/HDiffPatch/_atosize.h +0 -83
  138. package/android/jni/HDiffPatch/_clock_for_demo.h +0 -47
  139. package/android/jni/HDiffPatch/_dir_ignore.h +0 -191
  140. package/android/jni/HDiffPatch/builds/android_ndk_jni_mk/Android.mk +0 -37
  141. package/android/jni/HDiffPatch/builds/android_ndk_jni_mk/Application.mk +0 -7
  142. package/android/jni/HDiffPatch/builds/android_ndk_jni_mk/build_libs.bat +0 -1
  143. package/android/jni/HDiffPatch/builds/android_ndk_jni_mk/build_libs.sh +0 -1
  144. package/android/jni/HDiffPatch/builds/android_ndk_jni_mk/hpatch.c +0 -18
  145. package/android/jni/HDiffPatch/builds/android_ndk_jni_mk/hpatch.h +0 -20
  146. package/android/jni/HDiffPatch/builds/android_ndk_jni_mk/hpatch_jni.c +0 -28
  147. package/android/jni/HDiffPatch/builds/android_ndk_jni_mk/java/com/github/sisong/HPatch.java +0 -8
  148. package/android/jni/HDiffPatch/builds/codeblocks/HDiffPatch.workspace +0 -8
  149. package/android/jni/HDiffPatch/builds/codeblocks/HDiffZ.cbp +0 -219
  150. package/android/jni/HDiffPatch/builds/codeblocks/HPatchZ.cbp +0 -156
  151. package/android/jni/HDiffPatch/builds/codeblocks/unitTest.cbp +0 -65
  152. package/android/jni/HDiffPatch/builds/vc/HDiffPatch.sln +0 -105
  153. package/android/jni/HDiffPatch/builds/vc/HDiffZ.vcxproj +0 -237
  154. package/android/jni/HDiffPatch/builds/vc/HDiffZ.vcxproj.user +0 -3
  155. package/android/jni/HDiffPatch/builds/vc/HPatchZ.vcxproj +0 -318
  156. package/android/jni/HDiffPatch/builds/vc/HPatchZ.vcxproj.user +0 -3
  157. package/android/jni/HDiffPatch/builds/vc/unitTest.vcxproj +0 -182
  158. package/android/jni/HDiffPatch/builds/vc/unitTest.vcxproj.user +0 -3
  159. package/android/jni/HDiffPatch/builds/xcode/HDiffPatch.xcworkspace/contents.xcworkspacedata +0 -30
  160. package/android/jni/HDiffPatch/builds/xcode/HDiffPatch.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +0 -8
  161. package/android/jni/HDiffPatch/builds/xcode/bestParams.xcodeproj/project.pbxproj +0 -408
  162. package/android/jni/HDiffPatch/builds/xcode/bestParams.xcodeproj/project.xcworkspace/contents.xcworkspacedata +0 -7
  163. package/android/jni/HDiffPatch/builds/xcode/hdiffz.xcodeproj/project.pbxproj +0 -573
  164. package/android/jni/HDiffPatch/builds/xcode/hdiffz.xcodeproj/project.xcworkspace/contents.xcworkspacedata +0 -7
  165. package/android/jni/HDiffPatch/builds/xcode/hdiffz.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +0 -8
  166. package/android/jni/HDiffPatch/builds/xcode/hpatchz.xcodeproj/project.pbxproj +0 -396
  167. package/android/jni/HDiffPatch/builds/xcode/hpatchz.xcodeproj/project.xcworkspace/contents.xcworkspacedata +0 -7
  168. package/android/jni/HDiffPatch/builds/xcode/lz4.xcodeproj/project.pbxproj +0 -268
  169. package/android/jni/HDiffPatch/builds/xcode/lz4.xcodeproj/project.xcworkspace/contents.xcworkspacedata +0 -7
  170. package/android/jni/HDiffPatch/builds/xcode/lzma.xcodeproj/project.pbxproj +0 -361
  171. package/android/jni/HDiffPatch/builds/xcode/lzma.xcodeproj/project.xcworkspace/contents.xcworkspacedata +0 -7
  172. package/android/jni/HDiffPatch/builds/xcode/testHashClash.xcodeproj/project.pbxproj +0 -301
  173. package/android/jni/HDiffPatch/builds/xcode/testHashClash.xcodeproj/project.xcworkspace/contents.xcworkspacedata +0 -7
  174. package/android/jni/HDiffPatch/builds/xcode/testHashClash.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +0 -8
  175. package/android/jni/HDiffPatch/builds/xcode/unitTest.xcodeproj/project.pbxproj +0 -411
  176. package/android/jni/HDiffPatch/builds/xcode/unitTest.xcodeproj/project.xcworkspace/contents.xcworkspacedata +0 -7
  177. package/android/jni/HDiffPatch/builds/xcode/zstd.xcodeproj/project.pbxproj +0 -504
  178. package/android/jni/HDiffPatch/builds/xcode/zstd.xcodeproj/project.xcworkspace/contents.xcworkspacedata +0 -7
  179. package/android/jni/HDiffPatch/checksum_plugin_demo.h +0 -344
  180. package/android/jni/HDiffPatch/compress_parallel.cpp +0 -166
  181. package/android/jni/HDiffPatch/compress_parallel.h +0 -59
  182. package/android/jni/HDiffPatch/compress_plugin_demo.h +0 -1152
  183. package/android/jni/HDiffPatch/dirDiffPatch/dir_diff/dir_diff.cpp +0 -797
  184. package/android/jni/HDiffPatch/dirDiffPatch/dir_diff/dir_diff.h +0 -65
  185. package/android/jni/HDiffPatch/dirDiffPatch/dir_diff/dir_diff_tools.cpp +0 -200
  186. package/android/jni/HDiffPatch/dirDiffPatch/dir_diff/dir_diff_tools.h +0 -178
  187. package/android/jni/HDiffPatch/dirDiffPatch/dir_diff/dir_manifest.cpp +0 -357
  188. package/android/jni/HDiffPatch/dirDiffPatch/dir_diff/dir_manifest.h +0 -87
  189. package/android/jni/HDiffPatch/dirDiffPatch/dir_diff/file_for_dirDiff.h +0 -151
  190. package/android/jni/HDiffPatch/dirDiffPatch/dir_patch/dir_patch.c +0 -770
  191. package/android/jni/HDiffPatch/dirDiffPatch/dir_patch/dir_patch.h +0 -236
  192. package/android/jni/HDiffPatch/dirDiffPatch/dir_patch/dir_patch_private.h +0 -47
  193. package/android/jni/HDiffPatch/dirDiffPatch/dir_patch/dir_patch_tools.c +0 -112
  194. package/android/jni/HDiffPatch/dirDiffPatch/dir_patch/dir_patch_tools.h +0 -53
  195. package/android/jni/HDiffPatch/dirDiffPatch/dir_patch/new_dir_output.c +0 -331
  196. package/android/jni/HDiffPatch/dirDiffPatch/dir_patch/new_dir_output.h +0 -144
  197. package/android/jni/HDiffPatch/dirDiffPatch/dir_patch/new_stream.c +0 -176
  198. package/android/jni/HDiffPatch/dirDiffPatch/dir_patch/new_stream.h +0 -84
  199. package/android/jni/HDiffPatch/dirDiffPatch/dir_patch/ref_stream.c +0 -146
  200. package/android/jni/HDiffPatch/dirDiffPatch/dir_patch/ref_stream.h +0 -61
  201. package/android/jni/HDiffPatch/dirDiffPatch/dir_patch/res_handle_limit.c +0 -152
  202. package/android/jni/HDiffPatch/dirDiffPatch/dir_patch/res_handle_limit.h +0 -76
  203. package/android/jni/HDiffPatch/hdiffz.cpp +0 -1570
  204. package/android/jni/HDiffPatch/hpatch_dir_listener.h +0 -290
  205. package/android/jni/HDiffPatch/hpatchz.c +0 -1221
  206. package/android/jni/HDiffPatch/libHDiffPatch/HDiff/diff.cpp +0 -1025
  207. package/android/jni/HDiffPatch/libHDiffPatch/HDiff/diff.h +0 -116
  208. package/android/jni/HDiffPatch/libHDiffPatch/HDiff/diff_types.h +0 -72
  209. package/android/jni/HDiffPatch/libHDiffPatch/HDiff/private_diff/bytes_rle.cpp +0 -213
  210. package/android/jni/HDiffPatch/libHDiffPatch/HDiff/private_diff/bytes_rle.h +0 -63
  211. package/android/jni/HDiffPatch/libHDiffPatch/HDiff/private_diff/compress_detect.cpp +0 -170
  212. package/android/jni/HDiffPatch/libHDiffPatch/HDiff/private_diff/compress_detect.h +0 -86
  213. package/android/jni/HDiffPatch/libHDiffPatch/HDiff/private_diff/libdivsufsort/config.h +0 -89
  214. package/android/jni/HDiffPatch/libHDiffPatch/HDiff/private_diff/libdivsufsort/divsufsort.c +0 -13
  215. package/android/jni/HDiffPatch/libHDiffPatch/HDiff/private_diff/libdivsufsort/divsufsort.c.inc.h +0 -398
  216. package/android/jni/HDiffPatch/libHDiffPatch/HDiff/private_diff/libdivsufsort/divsufsort.h +0 -194
  217. package/android/jni/HDiffPatch/libHDiffPatch/HDiff/private_diff/libdivsufsort/divsufsort64.c +0 -13
  218. package/android/jni/HDiffPatch/libHDiffPatch/HDiff/private_diff/libdivsufsort/divsufsort64.h +0 -204
  219. package/android/jni/HDiffPatch/libHDiffPatch/HDiff/private_diff/libdivsufsort/divsufsort_private.h +0 -207
  220. package/android/jni/HDiffPatch/libHDiffPatch/HDiff/private_diff/libdivsufsort/sssort.c.inc.h +0 -815
  221. package/android/jni/HDiffPatch/libHDiffPatch/HDiff/private_diff/libdivsufsort/trsort.c.inc.h +0 -586
  222. package/android/jni/HDiffPatch/libHDiffPatch/HDiff/private_diff/libdivsufsort/utils.c.inc.h +0 -381
  223. package/android/jni/HDiffPatch/libHDiffPatch/HDiff/private_diff/limit_mem_diff/adler_roll.c +0 -352
  224. package/android/jni/HDiffPatch/libHDiffPatch/HDiff/private_diff/limit_mem_diff/adler_roll.h +0 -137
  225. package/android/jni/HDiffPatch/libHDiffPatch/HDiff/private_diff/limit_mem_diff/bloom_filter.h +0 -137
  226. package/android/jni/HDiffPatch/libHDiffPatch/HDiff/private_diff/limit_mem_diff/covers.h +0 -70
  227. package/android/jni/HDiffPatch/libHDiffPatch/HDiff/private_diff/limit_mem_diff/digest_matcher.cpp +0 -628
  228. package/android/jni/HDiffPatch/libHDiffPatch/HDiff/private_diff/limit_mem_diff/digest_matcher.h +0 -87
  229. package/android/jni/HDiffPatch/libHDiffPatch/HDiff/private_diff/limit_mem_diff/stream_serialize.cpp +0 -363
  230. package/android/jni/HDiffPatch/libHDiffPatch/HDiff/private_diff/limit_mem_diff/stream_serialize.h +0 -139
  231. package/android/jni/HDiffPatch/libHDiffPatch/HDiff/private_diff/mem_buf.h +0 -68
  232. package/android/jni/HDiffPatch/libHDiffPatch/HDiff/private_diff/pack_uint.h +0 -115
  233. package/android/jni/HDiffPatch/libHDiffPatch/HDiff/private_diff/suffix_string.cpp +0 -342
  234. package/android/jni/HDiffPatch/libHDiffPatch/HDiff/private_diff/suffix_string.h +0 -94
  235. package/android/jni/HDiffPatch/libHDiffPatch/HPatch/checksum_plugin.h +0 -52
  236. package/android/jni/HDiffPatch/libParallel/parallel_channel.cpp +0 -174
  237. package/android/jni/HDiffPatch/libParallel/parallel_channel.h +0 -73
  238. package/android/jni/HDiffPatch/libParallel/parallel_import.cpp +0 -367
  239. package/android/jni/HDiffPatch/libParallel/parallel_import.h +0 -92
  240. package/android/jni/lzma/Asm/arm/7zCrcOpt.asm +0 -100
  241. package/android/jni/lzma/Asm/x86/7zAsm.asm +0 -147
  242. package/android/jni/lzma/Asm/x86/7zCrcOpt.asm +0 -147
  243. package/android/jni/lzma/Asm/x86/AesOpt.asm +0 -237
  244. package/android/jni/lzma/Asm/x86/LzmaDecOpt.asm +0 -1258
  245. package/android/jni/lzma/Asm/x86/XzCrc64Opt.asm +0 -205
  246. package/android/jni/lzma/C/7z.h +0 -202
  247. package/android/jni/lzma/C/7zAlloc.c +0 -80
  248. package/android/jni/lzma/C/7zAlloc.h +0 -19
  249. package/android/jni/lzma/C/7zArcIn.c +0 -1771
  250. package/android/jni/lzma/C/7zBuf.c +0 -36
  251. package/android/jni/lzma/C/7zBuf.h +0 -35
  252. package/android/jni/lzma/C/7zBuf2.c +0 -52
  253. package/android/jni/lzma/C/7zCrc.c +0 -128
  254. package/android/jni/lzma/C/7zCrc.h +0 -25
  255. package/android/jni/lzma/C/7zCrcOpt.c +0 -115
  256. package/android/jni/lzma/C/7zDec.c +0 -591
  257. package/android/jni/lzma/C/7zFile.c +0 -286
  258. package/android/jni/lzma/C/7zFile.h +0 -83
  259. package/android/jni/lzma/C/7zStream.c +0 -176
  260. package/android/jni/lzma/C/7zVersion.h +0 -27
  261. package/android/jni/lzma/C/7zVersion.rc +0 -55
  262. package/android/jni/lzma/C/Aes.c +0 -306
  263. package/android/jni/lzma/C/Aes.h +0 -38
  264. package/android/jni/lzma/C/AesOpt.c +0 -184
  265. package/android/jni/lzma/C/Alloc.c +0 -455
  266. package/android/jni/lzma/C/Alloc.h +0 -51
  267. package/android/jni/lzma/C/Bcj2.c +0 -257
  268. package/android/jni/lzma/C/Bcj2.h +0 -146
  269. package/android/jni/lzma/C/Bcj2Enc.c +0 -311
  270. package/android/jni/lzma/C/Bra.c +0 -230
  271. package/android/jni/lzma/C/Bra.h +0 -64
  272. package/android/jni/lzma/C/Bra86.c +0 -82
  273. package/android/jni/lzma/C/BraIA64.c +0 -53
  274. package/android/jni/lzma/C/CpuArch.c +0 -218
  275. package/android/jni/lzma/C/CpuArch.h +0 -336
  276. package/android/jni/lzma/C/Delta.c +0 -64
  277. package/android/jni/lzma/C/Delta.h +0 -19
  278. package/android/jni/lzma/C/DllSecur.c +0 -108
  279. package/android/jni/lzma/C/DllSecur.h +0 -20
  280. package/android/jni/lzma/C/LzFind.c +0 -1127
  281. package/android/jni/lzma/C/LzFind.h +0 -121
  282. package/android/jni/lzma/C/LzFindMt.c +0 -853
  283. package/android/jni/lzma/C/LzFindMt.h +0 -101
  284. package/android/jni/lzma/C/LzHash.h +0 -57
  285. package/android/jni/lzma/C/Lzma2DecMt.c +0 -1082
  286. package/android/jni/lzma/C/Lzma2DecMt.h +0 -79
  287. package/android/jni/lzma/C/Lzma2Enc.c +0 -803
  288. package/android/jni/lzma/C/Lzma2Enc.h +0 -55
  289. package/android/jni/lzma/C/Lzma86.h +0 -111
  290. package/android/jni/lzma/C/Lzma86Dec.c +0 -54
  291. package/android/jni/lzma/C/Lzma86Enc.c +0 -106
  292. package/android/jni/lzma/C/LzmaEnc.c +0 -2976
  293. package/android/jni/lzma/C/LzmaEnc.h +0 -76
  294. package/android/jni/lzma/C/LzmaLib.c +0 -40
  295. package/android/jni/lzma/C/LzmaLib.h +0 -131
  296. package/android/jni/lzma/C/MtCoder.c +0 -601
  297. package/android/jni/lzma/C/MtCoder.h +0 -141
  298. package/android/jni/lzma/C/MtDec.c +0 -1138
  299. package/android/jni/lzma/C/MtDec.h +0 -201
  300. package/android/jni/lzma/C/Ppmd.h +0 -85
  301. package/android/jni/lzma/C/Ppmd7.c +0 -712
  302. package/android/jni/lzma/C/Ppmd7.h +0 -142
  303. package/android/jni/lzma/C/Ppmd7Dec.c +0 -191
  304. package/android/jni/lzma/C/Ppmd7Enc.c +0 -187
  305. package/android/jni/lzma/C/RotateDefs.h +0 -30
  306. package/android/jni/lzma/C/Sha256.c +0 -248
  307. package/android/jni/lzma/C/Sha256.h +0 -26
  308. package/android/jni/lzma/C/Sort.c +0 -141
  309. package/android/jni/lzma/C/Sort.h +0 -18
  310. package/android/jni/lzma/C/Threads.c +0 -95
  311. package/android/jni/lzma/C/Threads.h +0 -70
  312. package/android/jni/lzma/C/ThreadsP.c +0 -396
  313. package/android/jni/lzma/C/ThreadsP.h +0 -31
  314. package/android/jni/lzma/C/Util/7z/7z.dsp +0 -241
  315. package/android/jni/lzma/C/Util/7z/7z.dsw +0 -29
  316. package/android/jni/lzma/C/Util/7z/7zMain.c +0 -686
  317. package/android/jni/lzma/C/Util/7z/Precomp.c +0 -4
  318. package/android/jni/lzma/C/Util/7z/Precomp.h +0 -10
  319. package/android/jni/lzma/C/Util/7z/makefile +0 -40
  320. package/android/jni/lzma/C/Util/7z/makefile.gcc +0 -75
  321. package/android/jni/lzma/C/Util/Lzma/LzmaUtil.c +0 -258
  322. package/android/jni/lzma/C/Util/Lzma/LzmaUtil.dsp +0 -168
  323. package/android/jni/lzma/C/Util/Lzma/LzmaUtil.dsw +0 -29
  324. package/android/jni/lzma/C/Util/Lzma/makefile +0 -28
  325. package/android/jni/lzma/C/Util/Lzma/makefile.gcc +0 -44
  326. package/android/jni/lzma/C/Util/LzmaLib/LzmaLib.def +0 -4
  327. package/android/jni/lzma/C/Util/LzmaLib/LzmaLib.dsp +0 -178
  328. package/android/jni/lzma/C/Util/LzmaLib/LzmaLib.dsw +0 -29
  329. package/android/jni/lzma/C/Util/LzmaLib/LzmaLibExports.c +0 -14
  330. package/android/jni/lzma/C/Util/LzmaLib/makefile +0 -34
  331. package/android/jni/lzma/C/Util/LzmaLib/resource.rc +0 -3
  332. package/android/jni/lzma/C/Util/SfxSetup/Precomp.c +0 -4
  333. package/android/jni/lzma/C/Util/SfxSetup/Precomp.h +0 -10
  334. package/android/jni/lzma/C/Util/SfxSetup/SfxSetup.c +0 -640
  335. package/android/jni/lzma/C/Util/SfxSetup/SfxSetup.dsp +0 -231
  336. package/android/jni/lzma/C/Util/SfxSetup/SfxSetup.dsw +0 -29
  337. package/android/jni/lzma/C/Util/SfxSetup/makefile +0 -37
  338. package/android/jni/lzma/C/Util/SfxSetup/makefile_con +0 -38
  339. package/android/jni/lzma/C/Util/SfxSetup/resource.rc +0 -5
  340. package/android/jni/lzma/C/Util/SfxSetup/setup.ico +0 -0
  341. package/android/jni/lzma/C/Xz.c +0 -90
  342. package/android/jni/lzma/C/Xz.h +0 -460
  343. package/android/jni/lzma/C/XzCrc64.c +0 -86
  344. package/android/jni/lzma/C/XzCrc64.h +0 -26
  345. package/android/jni/lzma/C/XzCrc64Opt.c +0 -69
  346. package/android/jni/lzma/C/XzDec.c +0 -2766
  347. package/android/jni/lzma/C/XzEnc.c +0 -1329
  348. package/android/jni/lzma/C/XzEnc.h +0 -60
  349. package/android/jni/lzma/C/XzIn.c +0 -319
  350. package/android/jni/lzma/CPP/7zip/7zip.mak +0 -240
  351. package/android/jni/lzma/CPP/7zip/Aes.mak +0 -7
  352. package/android/jni/lzma/CPP/7zip/Archive/7z/7zCompressionMode.cpp +0 -3
  353. package/android/jni/lzma/CPP/7zip/Archive/7z/7zCompressionMode.h +0 -76
  354. package/android/jni/lzma/CPP/7zip/Archive/7z/7zDecode.cpp +0 -569
  355. package/android/jni/lzma/CPP/7zip/Archive/7z/7zDecode.h +0 -70
  356. package/android/jni/lzma/CPP/7zip/Archive/7z/7zEncode.cpp +0 -678
  357. package/android/jni/lzma/CPP/7zip/Archive/7z/7zEncode.h +0 -92
  358. package/android/jni/lzma/CPP/7zip/Archive/7z/7zExtract.cpp +0 -423
  359. package/android/jni/lzma/CPP/7zip/Archive/7z/7zFolderInStream.cpp +0 -139
  360. package/android/jni/lzma/CPP/7zip/Archive/7z/7zFolderInStream.h +0 -61
  361. package/android/jni/lzma/CPP/7zip/Archive/7z/7zHandler.cpp +0 -763
  362. package/android/jni/lzma/CPP/7zip/Archive/7z/7zHandler.h +0 -181
  363. package/android/jni/lzma/CPP/7zip/Archive/7z/7zHandlerOut.cpp +0 -942
  364. package/android/jni/lzma/CPP/7zip/Archive/7z/7zHeader.cpp +0 -19
  365. package/android/jni/lzma/CPP/7zip/Archive/7z/7zHeader.h +0 -148
  366. package/android/jni/lzma/CPP/7zip/Archive/7z/7zIn.cpp +0 -1666
  367. package/android/jni/lzma/CPP/7zip/Archive/7z/7zIn.h +0 -445
  368. package/android/jni/lzma/CPP/7zip/Archive/7z/7zItem.h +0 -202
  369. package/android/jni/lzma/CPP/7zip/Archive/7z/7zOut.cpp +0 -901
  370. package/android/jni/lzma/CPP/7zip/Archive/7z/7zOut.h +0 -335
  371. package/android/jni/lzma/CPP/7zip/Archive/7z/7zProperties.cpp +0 -174
  372. package/android/jni/lzma/CPP/7zip/Archive/7z/7zProperties.h +0 -22
  373. package/android/jni/lzma/CPP/7zip/Archive/7z/7zRegister.cpp +0 -21
  374. package/android/jni/lzma/CPP/7zip/Archive/7z/7zSpecStream.cpp +0 -22
  375. package/android/jni/lzma/CPP/7zip/Archive/7z/7zSpecStream.h +0 -35
  376. package/android/jni/lzma/CPP/7zip/Archive/7z/7zUpdate.cpp +0 -2500
  377. package/android/jni/lzma/CPP/7zip/Archive/7z/7zUpdate.h +0 -139
  378. package/android/jni/lzma/CPP/7zip/Archive/7z/StdAfx.cpp +0 -3
  379. package/android/jni/lzma/CPP/7zip/Archive/7z/StdAfx.h +0 -8
  380. package/android/jni/lzma/CPP/7zip/Archive/Archive.def +0 -12
  381. package/android/jni/lzma/CPP/7zip/Archive/Archive2.def +0 -19
  382. package/android/jni/lzma/CPP/7zip/Archive/ArchiveExports.cpp +0 -151
  383. package/android/jni/lzma/CPP/7zip/Archive/Common/CoderMixer2.cpp +0 -1125
  384. package/android/jni/lzma/CPP/7zip/Archive/Common/CoderMixer2.h +0 -447
  385. package/android/jni/lzma/CPP/7zip/Archive/Common/DummyOutStream.cpp +0 -17
  386. package/android/jni/lzma/CPP/7zip/Archive/Common/DummyOutStream.h +0 -25
  387. package/android/jni/lzma/CPP/7zip/Archive/Common/HandlerOut.cpp +0 -232
  388. package/android/jni/lzma/CPP/7zip/Archive/Common/HandlerOut.h +0 -110
  389. package/android/jni/lzma/CPP/7zip/Archive/Common/InStreamWithCRC.cpp +0 -46
  390. package/android/jni/lzma/CPP/7zip/Archive/Common/InStreamWithCRC.h +0 -67
  391. package/android/jni/lzma/CPP/7zip/Archive/Common/ItemNameUtils.cpp +0 -88
  392. package/android/jni/lzma/CPP/7zip/Archive/Common/ItemNameUtils.h +0 -28
  393. package/android/jni/lzma/CPP/7zip/Archive/Common/MultiStream.cpp +0 -191
  394. package/android/jni/lzma/CPP/7zip/Archive/Common/MultiStream.h +0 -89
  395. package/android/jni/lzma/CPP/7zip/Archive/Common/OutStreamWithCRC.cpp +0 -18
  396. package/android/jni/lzma/CPP/7zip/Archive/Common/OutStreamWithCRC.h +0 -37
  397. package/android/jni/lzma/CPP/7zip/Archive/Common/ParseProperties.cpp +0 -3
  398. package/android/jni/lzma/CPP/7zip/Archive/Common/ParseProperties.h +0 -6
  399. package/android/jni/lzma/CPP/7zip/Archive/Common/StdAfx.h +0 -8
  400. package/android/jni/lzma/CPP/7zip/Archive/DllExports2.cpp +0 -122
  401. package/android/jni/lzma/CPP/7zip/Archive/IArchive.h +0 -608
  402. package/android/jni/lzma/CPP/7zip/Archive/Icons/7z.ico +0 -0
  403. package/android/jni/lzma/CPP/7zip/Archive/LzmaHandler.cpp +0 -629
  404. package/android/jni/lzma/CPP/7zip/Archive/SplitHandler.cpp +0 -359
  405. package/android/jni/lzma/CPP/7zip/Archive/StdAfx.h +0 -8
  406. package/android/jni/lzma/CPP/7zip/Archive/XzHandler.cpp +0 -1308
  407. package/android/jni/lzma/CPP/7zip/Archive/XzHandler.h +0 -11
  408. package/android/jni/lzma/CPP/7zip/Asm.mak +0 -9
  409. package/android/jni/lzma/CPP/7zip/Bundles/Alone7z/Alone.dsp +0 -1910
  410. package/android/jni/lzma/CPP/7zip/Bundles/Alone7z/Alone.dsw +0 -29
  411. package/android/jni/lzma/CPP/7zip/Bundles/Alone7z/StdAfx.cpp +0 -3
  412. package/android/jni/lzma/CPP/7zip/Bundles/Alone7z/StdAfx.h +0 -8
  413. package/android/jni/lzma/CPP/7zip/Bundles/Alone7z/makefile +0 -154
  414. package/android/jni/lzma/CPP/7zip/Bundles/Alone7z/resource.rc +0 -7
  415. package/android/jni/lzma/CPP/7zip/Bundles/Format7zExtractR/StdAfx.cpp +0 -3
  416. package/android/jni/lzma/CPP/7zip/Bundles/Format7zExtractR/StdAfx.h +0 -8
  417. package/android/jni/lzma/CPP/7zip/Bundles/Format7zExtractR/makefile +0 -96
  418. package/android/jni/lzma/CPP/7zip/Bundles/Format7zExtractR/resource.rc +0 -5
  419. package/android/jni/lzma/CPP/7zip/Bundles/Format7zR/StdAfx.cpp +0 -3
  420. package/android/jni/lzma/CPP/7zip/Bundles/Format7zR/StdAfx.h +0 -8
  421. package/android/jni/lzma/CPP/7zip/Bundles/Format7zR/makefile +0 -116
  422. package/android/jni/lzma/CPP/7zip/Bundles/Format7zR/resource.rc +0 -5
  423. package/android/jni/lzma/CPP/7zip/Bundles/LzmaCon/LzmaAlone.cpp +0 -799
  424. package/android/jni/lzma/CPP/7zip/Bundles/LzmaCon/LzmaCon.dsp +0 -477
  425. package/android/jni/lzma/CPP/7zip/Bundles/LzmaCon/LzmaCon.dsw +0 -29
  426. package/android/jni/lzma/CPP/7zip/Bundles/LzmaCon/StdAfx.cpp +0 -3
  427. package/android/jni/lzma/CPP/7zip/Bundles/LzmaCon/StdAfx.h +0 -8
  428. package/android/jni/lzma/CPP/7zip/Bundles/LzmaCon/makefile +0 -59
  429. package/android/jni/lzma/CPP/7zip/Bundles/LzmaCon/makefile.gcc +0 -195
  430. package/android/jni/lzma/CPP/7zip/Bundles/LzmaCon/resource.rc +0 -3
  431. package/android/jni/lzma/CPP/7zip/Bundles/LzmaSpec/LzmaSpec.cpp +0 -715
  432. package/android/jni/lzma/CPP/7zip/Bundles/SFXCon/7z.ico +0 -0
  433. package/android/jni/lzma/CPP/7zip/Bundles/SFXCon/SFXCon.dsp +0 -912
  434. package/android/jni/lzma/CPP/7zip/Bundles/SFXCon/SFXCon.dsw +0 -29
  435. package/android/jni/lzma/CPP/7zip/Bundles/SFXCon/SfxCon.cpp +0 -482
  436. package/android/jni/lzma/CPP/7zip/Bundles/SFXCon/StdAfx.cpp +0 -3
  437. package/android/jni/lzma/CPP/7zip/Bundles/SFXCon/StdAfx.h +0 -8
  438. package/android/jni/lzma/CPP/7zip/Bundles/SFXCon/makefile +0 -134
  439. package/android/jni/lzma/CPP/7zip/Bundles/SFXCon/resource.rc +0 -5
  440. package/android/jni/lzma/CPP/7zip/Bundles/SFXSetup/ExtractCallbackSfx.cpp +0 -246
  441. package/android/jni/lzma/CPP/7zip/Bundles/SFXSetup/ExtractCallbackSfx.h +0 -86
  442. package/android/jni/lzma/CPP/7zip/Bundles/SFXSetup/ExtractEngine.cpp +0 -137
  443. package/android/jni/lzma/CPP/7zip/Bundles/SFXSetup/ExtractEngine.h +0 -11
  444. package/android/jni/lzma/CPP/7zip/Bundles/SFXSetup/SFXSetup.dsp +0 -803
  445. package/android/jni/lzma/CPP/7zip/Bundles/SFXSetup/SFXSetup.dsw +0 -29
  446. package/android/jni/lzma/CPP/7zip/Bundles/SFXSetup/SfxSetup.cpp +0 -364
  447. package/android/jni/lzma/CPP/7zip/Bundles/SFXSetup/StdAfx.cpp +0 -3
  448. package/android/jni/lzma/CPP/7zip/Bundles/SFXSetup/StdAfx.h +0 -13
  449. package/android/jni/lzma/CPP/7zip/Bundles/SFXSetup/makefile +0 -117
  450. package/android/jni/lzma/CPP/7zip/Bundles/SFXSetup/resource.h +0 -6
  451. package/android/jni/lzma/CPP/7zip/Bundles/SFXSetup/resource.rc +0 -16
  452. package/android/jni/lzma/CPP/7zip/Bundles/SFXSetup/setup.ico +0 -0
  453. package/android/jni/lzma/CPP/7zip/Bundles/SFXWin/7z.ico +0 -0
  454. package/android/jni/lzma/CPP/7zip/Bundles/SFXWin/SFXWin.dsp +0 -988
  455. package/android/jni/lzma/CPP/7zip/Bundles/SFXWin/SFXWin.dsw +0 -29
  456. package/android/jni/lzma/CPP/7zip/Bundles/SFXWin/SfxWin.cpp +0 -241
  457. package/android/jni/lzma/CPP/7zip/Bundles/SFXWin/StdAfx.cpp +0 -3
  458. package/android/jni/lzma/CPP/7zip/Bundles/SFXWin/StdAfx.h +0 -14
  459. package/android/jni/lzma/CPP/7zip/Bundles/SFXWin/makefile +0 -153
  460. package/android/jni/lzma/CPP/7zip/Bundles/SFXWin/resource.h +0 -1
  461. package/android/jni/lzma/CPP/7zip/Bundles/SFXWin/resource.rc +0 -50
  462. package/android/jni/lzma/CPP/7zip/Common/CWrappers.cpp +0 -250
  463. package/android/jni/lzma/CPP/7zip/Common/CWrappers.h +0 -120
  464. package/android/jni/lzma/CPP/7zip/Common/CreateCoder.cpp +0 -536
  465. package/android/jni/lzma/CPP/7zip/Common/CreateCoder.h +0 -192
  466. package/android/jni/lzma/CPP/7zip/Common/FilePathAutoRename.cpp +0 -46
  467. package/android/jni/lzma/CPP/7zip/Common/FilePathAutoRename.h +0 -10
  468. package/android/jni/lzma/CPP/7zip/Common/FileStreams.cpp +0 -475
  469. package/android/jni/lzma/CPP/7zip/Common/FileStreams.h +0 -166
  470. package/android/jni/lzma/CPP/7zip/Common/FilterCoder.cpp +0 -435
  471. package/android/jni/lzma/CPP/7zip/Common/FilterCoder.h +0 -205
  472. package/android/jni/lzma/CPP/7zip/Common/InBuffer.cpp +0 -163
  473. package/android/jni/lzma/CPP/7zip/Common/InBuffer.h +0 -92
  474. package/android/jni/lzma/CPP/7zip/Common/InOutTempBuffer.cpp +0 -127
  475. package/android/jni/lzma/CPP/7zip/Common/InOutTempBuffer.h +0 -48
  476. package/android/jni/lzma/CPP/7zip/Common/LimitedStreams.cpp +0 -367
  477. package/android/jni/lzma/CPP/7zip/Common/LimitedStreams.h +0 -252
  478. package/android/jni/lzma/CPP/7zip/Common/LockedStream.cpp +0 -3
  479. package/android/jni/lzma/CPP/7zip/Common/LockedStream.h +0 -6
  480. package/android/jni/lzma/CPP/7zip/Common/MethodId.cpp +0 -3
  481. package/android/jni/lzma/CPP/7zip/Common/MethodId.h +0 -10
  482. package/android/jni/lzma/CPP/7zip/Common/MethodProps.cpp +0 -509
  483. package/android/jni/lzma/CPP/7zip/Common/MethodProps.h +0 -264
  484. package/android/jni/lzma/CPP/7zip/Common/OffsetStream.cpp +0 -39
  485. package/android/jni/lzma/CPP/7zip/Common/OffsetStream.h +0 -26
  486. package/android/jni/lzma/CPP/7zip/Common/OutBuffer.cpp +0 -111
  487. package/android/jni/lzma/CPP/7zip/Common/OutBuffer.h +0 -66
  488. package/android/jni/lzma/CPP/7zip/Common/ProgressUtils.cpp +0 -51
  489. package/android/jni/lzma/CPP/7zip/Common/ProgressUtils.h +0 -35
  490. package/android/jni/lzma/CPP/7zip/Common/PropId.cpp +0 -108
  491. package/android/jni/lzma/CPP/7zip/Common/RegisterArc.h +0 -78
  492. package/android/jni/lzma/CPP/7zip/Common/RegisterCodec.h +0 -106
  493. package/android/jni/lzma/CPP/7zip/Common/StdAfx.h +0 -8
  494. package/android/jni/lzma/CPP/7zip/Common/StreamBinder.cpp +0 -156
  495. package/android/jni/lzma/CPP/7zip/Common/StreamBinder.h +0 -60
  496. package/android/jni/lzma/CPP/7zip/Common/StreamObjects.cpp +0 -285
  497. package/android/jni/lzma/CPP/7zip/Common/StreamObjects.h +0 -157
  498. package/android/jni/lzma/CPP/7zip/Common/StreamUtils.cpp +0 -56
  499. package/android/jni/lzma/CPP/7zip/Common/StreamUtils.h +0 -13
  500. package/android/jni/lzma/CPP/7zip/Common/UniqBlocks.cpp +0 -57
  501. package/android/jni/lzma/CPP/7zip/Common/UniqBlocks.h +0 -26
  502. package/android/jni/lzma/CPP/7zip/Common/VirtThread.cpp +0 -48
  503. package/android/jni/lzma/CPP/7zip/Common/VirtThread.h +0 -24
  504. package/android/jni/lzma/CPP/7zip/Compress/Bcj2Coder.cpp +0 -666
  505. package/android/jni/lzma/CPP/7zip/Compress/Bcj2Coder.h +0 -120
  506. package/android/jni/lzma/CPP/7zip/Compress/Bcj2Register.cpp +0 -24
  507. package/android/jni/lzma/CPP/7zip/Compress/BcjCoder.cpp +0 -24
  508. package/android/jni/lzma/CPP/7zip/Compress/BcjCoder.h +0 -31
  509. package/android/jni/lzma/CPP/7zip/Compress/BcjRegister.cpp +0 -17
  510. package/android/jni/lzma/CPP/7zip/Compress/BranchMisc.cpp +0 -23
  511. package/android/jni/lzma/CPP/7zip/Compress/BranchMisc.h +0 -35
  512. package/android/jni/lzma/CPP/7zip/Compress/BranchRegister.cpp +0 -41
  513. package/android/jni/lzma/CPP/7zip/Compress/ByteSwap.cpp +0 -92
  514. package/android/jni/lzma/CPP/7zip/Compress/CodecExports.cpp +0 -344
  515. package/android/jni/lzma/CPP/7zip/Compress/CopyCoder.cpp +0 -120
  516. package/android/jni/lzma/CPP/7zip/Compress/CopyCoder.h +0 -49
  517. package/android/jni/lzma/CPP/7zip/Compress/CopyRegister.cpp +0 -15
  518. package/android/jni/lzma/CPP/7zip/Compress/DeltaFilter.cpp +0 -128
  519. package/android/jni/lzma/CPP/7zip/Compress/Lzma2Decoder.cpp +0 -265
  520. package/android/jni/lzma/CPP/7zip/Compress/Lzma2Decoder.h +0 -96
  521. package/android/jni/lzma/CPP/7zip/Compress/Lzma2Encoder.cpp +0 -122
  522. package/android/jni/lzma/CPP/7zip/Compress/Lzma2Encoder.h +0 -42
  523. package/android/jni/lzma/CPP/7zip/Compress/Lzma2Register.cpp +0 -22
  524. package/android/jni/lzma/CPP/7zip/Compress/LzmaDecoder.cpp +0 -343
  525. package/android/jni/lzma/CPP/7zip/Compress/LzmaDecoder.h +0 -113
  526. package/android/jni/lzma/CPP/7zip/Compress/LzmaEncoder.cpp +0 -182
  527. package/android/jni/lzma/CPP/7zip/Compress/LzmaEncoder.h +0 -46
  528. package/android/jni/lzma/CPP/7zip/Compress/LzmaRegister.cpp +0 -22
  529. package/android/jni/lzma/CPP/7zip/Compress/PpmdDecoder.cpp +0 -170
  530. package/android/jni/lzma/CPP/7zip/Compress/PpmdDecoder.h +0 -86
  531. package/android/jni/lzma/CPP/7zip/Compress/PpmdEncoder.cpp +0 -152
  532. package/android/jni/lzma/CPP/7zip/Compress/PpmdEncoder.h +0 -58
  533. package/android/jni/lzma/CPP/7zip/Compress/PpmdRegister.cpp +0 -22
  534. package/android/jni/lzma/CPP/7zip/Compress/StdAfx.h +0 -8
  535. package/android/jni/lzma/CPP/7zip/Compress/XzDecoder.cpp +0 -150
  536. package/android/jni/lzma/CPP/7zip/Compress/XzDecoder.h +0 -92
  537. package/android/jni/lzma/CPP/7zip/Compress/XzEncoder.cpp +0 -245
  538. package/android/jni/lzma/CPP/7zip/Compress/XzEncoder.h +0 -46
  539. package/android/jni/lzma/CPP/7zip/Crc.mak +0 -8
  540. package/android/jni/lzma/CPP/7zip/Crc64.mak +0 -8
  541. package/android/jni/lzma/CPP/7zip/Crypto/7zAes.cpp +0 -280
  542. package/android/jni/lzma/CPP/7zip/Crypto/7zAes.h +0 -118
  543. package/android/jni/lzma/CPP/7zip/Crypto/7zAesRegister.cpp +0 -17
  544. package/android/jni/lzma/CPP/7zip/Crypto/MyAes.cpp +0 -112
  545. package/android/jni/lzma/CPP/7zip/Crypto/MyAes.h +0 -57
  546. package/android/jni/lzma/CPP/7zip/Crypto/MyAesReg.cpp +0 -16
  547. package/android/jni/lzma/CPP/7zip/Crypto/RandGen.cpp +0 -233
  548. package/android/jni/lzma/CPP/7zip/Crypto/RandGen.h +0 -40
  549. package/android/jni/lzma/CPP/7zip/Crypto/StdAfx.h +0 -8
  550. package/android/jni/lzma/CPP/7zip/GuiCommon.rc +0 -84
  551. package/android/jni/lzma/CPP/7zip/Guid.txt +0 -220
  552. package/android/jni/lzma/CPP/7zip/ICoder.h +0 -399
  553. package/android/jni/lzma/CPP/7zip/IDecl.h +0 -28
  554. package/android/jni/lzma/CPP/7zip/IPassword.h +0 -23
  555. package/android/jni/lzma/CPP/7zip/IProgress.h +0 -19
  556. package/android/jni/lzma/CPP/7zip/IStream.h +0 -127
  557. package/android/jni/lzma/CPP/7zip/LzmaDec.mak +0 -5
  558. package/android/jni/lzma/CPP/7zip/MyVersion.h +0 -2
  559. package/android/jni/lzma/CPP/7zip/MyVersionInfo.rc +0 -2
  560. package/android/jni/lzma/CPP/7zip/PropID.h +0 -127
  561. package/android/jni/lzma/CPP/7zip/SubBuild.mak +0 -3
  562. package/android/jni/lzma/CPP/7zip/UI/Client7z/Client7z.cpp +0 -993
  563. package/android/jni/lzma/CPP/7zip/UI/Client7z/Client7z.dsp +0 -235
  564. package/android/jni/lzma/CPP/7zip/UI/Client7z/Client7z.dsw +0 -29
  565. package/android/jni/lzma/CPP/7zip/UI/Client7z/StdAfx.cpp +0 -3
  566. package/android/jni/lzma/CPP/7zip/UI/Client7z/StdAfx.h +0 -8
  567. package/android/jni/lzma/CPP/7zip/UI/Client7z/makefile +0 -28
  568. package/android/jni/lzma/CPP/7zip/UI/Client7z/resource.rc +0 -3
  569. package/android/jni/lzma/CPP/7zip/UI/Common/ArchiveCommandLine.cpp +0 -1295
  570. package/android/jni/lzma/CPP/7zip/UI/Common/ArchiveCommandLine.h +0 -136
  571. package/android/jni/lzma/CPP/7zip/UI/Common/ArchiveExtractCallback.cpp +0 -1715
  572. package/android/jni/lzma/CPP/7zip/UI/Common/ArchiveExtractCallback.h +0 -403
  573. package/android/jni/lzma/CPP/7zip/UI/Common/ArchiveName.cpp +0 -155
  574. package/android/jni/lzma/CPP/7zip/UI/Common/ArchiveName.h +0 -10
  575. package/android/jni/lzma/CPP/7zip/UI/Common/ArchiveOpenCallback.cpp +0 -161
  576. package/android/jni/lzma/CPP/7zip/UI/Common/ArchiveOpenCallback.h +0 -112
  577. package/android/jni/lzma/CPP/7zip/UI/Common/Bench.cpp +0 -3618
  578. package/android/jni/lzma/CPP/7zip/UI/Common/Bench.h +0 -77
  579. package/android/jni/lzma/CPP/7zip/UI/Common/DefaultName.cpp +0 -37
  580. package/android/jni/lzma/CPP/7zip/UI/Common/DefaultName.h +0 -11
  581. package/android/jni/lzma/CPP/7zip/UI/Common/DirItem.h +0 -190
  582. package/android/jni/lzma/CPP/7zip/UI/Common/EnumDirItems.cpp +0 -1096
  583. package/android/jni/lzma/CPP/7zip/UI/Common/EnumDirItems.h +0 -42
  584. package/android/jni/lzma/CPP/7zip/UI/Common/ExitCode.h +0 -27
  585. package/android/jni/lzma/CPP/7zip/UI/Common/Extract.cpp +0 -482
  586. package/android/jni/lzma/CPP/7zip/UI/Common/Extract.h +0 -94
  587. package/android/jni/lzma/CPP/7zip/UI/Common/ExtractMode.h +0 -34
  588. package/android/jni/lzma/CPP/7zip/UI/Common/ExtractingFilePath.cpp +0 -280
  589. package/android/jni/lzma/CPP/7zip/UI/Common/ExtractingFilePath.h +0 -31
  590. package/android/jni/lzma/CPP/7zip/UI/Common/HashCalc.cpp +0 -347
  591. package/android/jni/lzma/CPP/7zip/UI/Common/HashCalc.h +0 -110
  592. package/android/jni/lzma/CPP/7zip/UI/Common/IFileExtractCallback.h +0 -114
  593. package/android/jni/lzma/CPP/7zip/UI/Common/LoadCodecs.cpp +0 -1074
  594. package/android/jni/lzma/CPP/7zip/UI/Common/LoadCodecs.h +0 -424
  595. package/android/jni/lzma/CPP/7zip/UI/Common/OpenArchive.cpp +0 -3553
  596. package/android/jni/lzma/CPP/7zip/UI/Common/OpenArchive.h +0 -436
  597. package/android/jni/lzma/CPP/7zip/UI/Common/PropIDUtils.cpp +0 -668
  598. package/android/jni/lzma/CPP/7zip/UI/Common/PropIDUtils.h +0 -18
  599. package/android/jni/lzma/CPP/7zip/UI/Common/Property.h +0 -14
  600. package/android/jni/lzma/CPP/7zip/UI/Common/SetProperties.cpp +0 -80
  601. package/android/jni/lzma/CPP/7zip/UI/Common/SetProperties.h +0 -10
  602. package/android/jni/lzma/CPP/7zip/UI/Common/SortUtils.cpp +0 -25
  603. package/android/jni/lzma/CPP/7zip/UI/Common/SortUtils.h +0 -10
  604. package/android/jni/lzma/CPP/7zip/UI/Common/StdAfx.h +0 -8
  605. package/android/jni/lzma/CPP/7zip/UI/Common/TempFiles.cpp +0 -19
  606. package/android/jni/lzma/CPP/7zip/UI/Common/TempFiles.h +0 -16
  607. package/android/jni/lzma/CPP/7zip/UI/Common/Update.cpp +0 -1704
  608. package/android/jni/lzma/CPP/7zip/UI/Common/Update.h +0 -200
  609. package/android/jni/lzma/CPP/7zip/UI/Common/UpdateAction.cpp +0 -64
  610. package/android/jni/lzma/CPP/7zip/UI/Common/UpdateAction.h +0 -66
  611. package/android/jni/lzma/CPP/7zip/UI/Common/UpdateCallback.cpp +0 -771
  612. package/android/jni/lzma/CPP/7zip/UI/Common/UpdateCallback.h +0 -162
  613. package/android/jni/lzma/CPP/7zip/UI/Common/UpdatePair.cpp +0 -233
  614. package/android/jni/lzma/CPP/7zip/UI/Common/UpdatePair.h +0 -27
  615. package/android/jni/lzma/CPP/7zip/UI/Common/UpdateProduce.cpp +0 -70
  616. package/android/jni/lzma/CPP/7zip/UI/Common/UpdateProduce.h +0 -55
  617. package/android/jni/lzma/CPP/7zip/UI/Common/WorkDir.cpp +0 -94
  618. package/android/jni/lzma/CPP/7zip/UI/Common/WorkDir.h +0 -26
  619. package/android/jni/lzma/CPP/7zip/UI/Common/ZipRegistry.h +0 -130
  620. package/android/jni/lzma/CPP/7zip/UI/Console/BenchCon.cpp +0 -41
  621. package/android/jni/lzma/CPP/7zip/UI/Console/BenchCon.h +0 -14
  622. package/android/jni/lzma/CPP/7zip/UI/Console/Console.mak +0 -43
  623. package/android/jni/lzma/CPP/7zip/UI/Console/Console.manifest +0 -13
  624. package/android/jni/lzma/CPP/7zip/UI/Console/ConsoleClose.cpp +0 -69
  625. package/android/jni/lzma/CPP/7zip/UI/Console/ConsoleClose.h +0 -33
  626. package/android/jni/lzma/CPP/7zip/UI/Console/ExtractCallbackConsole.cpp +0 -825
  627. package/android/jni/lzma/CPP/7zip/UI/Console/ExtractCallbackConsole.h +0 -164
  628. package/android/jni/lzma/CPP/7zip/UI/Console/HashCon.cpp +0 -367
  629. package/android/jni/lzma/CPP/7zip/UI/Console/HashCon.h +0 -48
  630. package/android/jni/lzma/CPP/7zip/UI/Console/List.cpp +0 -1359
  631. package/android/jni/lzma/CPP/7zip/UI/Console/List.h +0 -27
  632. package/android/jni/lzma/CPP/7zip/UI/Console/Main.cpp +0 -1154
  633. package/android/jni/lzma/CPP/7zip/UI/Console/MainAr.cpp +0 -175
  634. package/android/jni/lzma/CPP/7zip/UI/Console/OpenCallbackConsole.cpp +0 -115
  635. package/android/jni/lzma/CPP/7zip/UI/Console/OpenCallbackConsole.h +0 -66
  636. package/android/jni/lzma/CPP/7zip/UI/Console/PercentPrinter.cpp +0 -183
  637. package/android/jni/lzma/CPP/7zip/UI/Console/PercentPrinter.h +0 -62
  638. package/android/jni/lzma/CPP/7zip/UI/Console/StdAfx.cpp +0 -3
  639. package/android/jni/lzma/CPP/7zip/UI/Console/StdAfx.h +0 -8
  640. package/android/jni/lzma/CPP/7zip/UI/Console/UpdateCallbackConsole.cpp +0 -702
  641. package/android/jni/lzma/CPP/7zip/UI/Console/UpdateCallbackConsole.h +0 -124
  642. package/android/jni/lzma/CPP/7zip/UI/Console/UserInputUtils.cpp +0 -110
  643. package/android/jni/lzma/CPP/7zip/UI/Console/UserInputUtils.h +0 -27
  644. package/android/jni/lzma/CPP/7zip/UI/Console/makefile +0 -64
  645. package/android/jni/lzma/CPP/7zip/UI/Console/resource.rc +0 -7
  646. package/android/jni/lzma/CPP/7zip/UI/Explorer/MyMessages.cpp +0 -37
  647. package/android/jni/lzma/CPP/7zip/UI/Explorer/MyMessages.h +0 -16
  648. package/android/jni/lzma/CPP/7zip/UI/FileManager/BrowseDialog.cpp +0 -1025
  649. package/android/jni/lzma/CPP/7zip/UI/FileManager/BrowseDialog.h +0 -21
  650. package/android/jni/lzma/CPP/7zip/UI/FileManager/BrowseDialogRes.h +0 -9
  651. package/android/jni/lzma/CPP/7zip/UI/FileManager/ComboDialog.cpp +0 -64
  652. package/android/jni/lzma/CPP/7zip/UI/FileManager/ComboDialog.h +0 -28
  653. package/android/jni/lzma/CPP/7zip/UI/FileManager/ComboDialogRes.h +0 -4
  654. package/android/jni/lzma/CPP/7zip/UI/FileManager/DialogSize.h +0 -16
  655. package/android/jni/lzma/CPP/7zip/UI/FileManager/ExtractCallback.cpp +0 -1037
  656. package/android/jni/lzma/CPP/7zip/UI/FileManager/ExtractCallback.h +0 -328
  657. package/android/jni/lzma/CPP/7zip/UI/FileManager/FormatUtils.cpp +0 -28
  658. package/android/jni/lzma/CPP/7zip/UI/FileManager/FormatUtils.h +0 -14
  659. package/android/jni/lzma/CPP/7zip/UI/FileManager/LangUtils.h +0 -40
  660. package/android/jni/lzma/CPP/7zip/UI/FileManager/MyWindowsNew.h +0 -76
  661. package/android/jni/lzma/CPP/7zip/UI/FileManager/OverwriteDialog.cpp +0 -122
  662. package/android/jni/lzma/CPP/7zip/UI/FileManager/OverwriteDialog.h +0 -69
  663. package/android/jni/lzma/CPP/7zip/UI/FileManager/OverwriteDialog.rc +0 -91
  664. package/android/jni/lzma/CPP/7zip/UI/FileManager/OverwriteDialogRes.h +0 -17
  665. package/android/jni/lzma/CPP/7zip/UI/FileManager/PasswordDialog.cpp +0 -58
  666. package/android/jni/lzma/CPP/7zip/UI/FileManager/PasswordDialog.h +0 -28
  667. package/android/jni/lzma/CPP/7zip/UI/FileManager/PasswordDialog.rc +0 -14
  668. package/android/jni/lzma/CPP/7zip/UI/FileManager/PasswordDialogRes.h +0 -5
  669. package/android/jni/lzma/CPP/7zip/UI/FileManager/ProgressDialog.cpp +0 -196
  670. package/android/jni/lzma/CPP/7zip/UI/FileManager/ProgressDialog.h +0 -170
  671. package/android/jni/lzma/CPP/7zip/UI/FileManager/ProgressDialog.rc +0 -12
  672. package/android/jni/lzma/CPP/7zip/UI/FileManager/ProgressDialog2.cpp +0 -1337
  673. package/android/jni/lzma/CPP/7zip/UI/FileManager/ProgressDialog2.h +0 -351
  674. package/android/jni/lzma/CPP/7zip/UI/FileManager/ProgressDialog2.rc +0 -40
  675. package/android/jni/lzma/CPP/7zip/UI/FileManager/ProgressDialog2Res.h +0 -48
  676. package/android/jni/lzma/CPP/7zip/UI/FileManager/ProgressDialog2a.rc +0 -80
  677. package/android/jni/lzma/CPP/7zip/UI/FileManager/ProgressDialogRes.h +0 -3
  678. package/android/jni/lzma/CPP/7zip/UI/FileManager/PropertyName.cpp +0 -23
  679. package/android/jni/lzma/CPP/7zip/UI/FileManager/PropertyName.h +0 -10
  680. package/android/jni/lzma/CPP/7zip/UI/FileManager/PropertyNameRes.h +0 -95
  681. package/android/jni/lzma/CPP/7zip/UI/FileManager/SysIconUtils.cpp +0 -255
  682. package/android/jni/lzma/CPP/7zip/UI/FileManager/SysIconUtils.h +0 -62
  683. package/android/jni/lzma/CPP/7zip/UI/FileManager/resource.h +0 -177
  684. package/android/jni/lzma/CPP/7zip/UI/FileManager/resourceGui.h +0 -15
  685. package/android/jni/lzma/CPP/7zip/UI/GUI/Extract.rc +0 -59
  686. package/android/jni/lzma/CPP/7zip/UI/GUI/ExtractDialog.cpp +0 -418
  687. package/android/jni/lzma/CPP/7zip/UI/GUI/ExtractDialog.h +0 -113
  688. package/android/jni/lzma/CPP/7zip/UI/GUI/ExtractDialog.rc +0 -98
  689. package/android/jni/lzma/CPP/7zip/UI/GUI/ExtractDialogRes.h +0 -24
  690. package/android/jni/lzma/CPP/7zip/UI/GUI/ExtractGUI.cpp +0 -280
  691. package/android/jni/lzma/CPP/7zip/UI/GUI/ExtractGUI.h +0 -38
  692. package/android/jni/lzma/CPP/7zip/UI/GUI/ExtractRes.h +0 -51
  693. package/android/jni/lzma/CPP/7zip/UI/GUI/HashGUI.h +0 -27
  694. package/android/jni/lzma/CPP/7zip/UI/GUI/resource2.h +0 -2
  695. package/android/jni/lzma/CPP/Build.mak +0 -156
  696. package/android/jni/lzma/CPP/Common/AutoPtr.h +0 -35
  697. package/android/jni/lzma/CPP/Common/CRC.cpp +0 -7
  698. package/android/jni/lzma/CPP/Common/C_FileIO.cpp +0 -92
  699. package/android/jni/lzma/CPP/Common/C_FileIO.h +0 -53
  700. package/android/jni/lzma/CPP/Common/ComTry.h +0 -21
  701. package/android/jni/lzma/CPP/Common/CommandLineParser.cpp +0 -197
  702. package/android/jni/lzma/CPP/Common/CommandLineParser.h +0 -63
  703. package/android/jni/lzma/CPP/Common/Common.h +0 -43
  704. package/android/jni/lzma/CPP/Common/CrcReg.cpp +0 -98
  705. package/android/jni/lzma/CPP/Common/Defs.h +0 -15
  706. package/android/jni/lzma/CPP/Common/DynamicBuffer.h +0 -64
  707. package/android/jni/lzma/CPP/Common/IntToString.cpp +0 -193
  708. package/android/jni/lzma/CPP/Common/IntToString.h +0 -28
  709. package/android/jni/lzma/CPP/Common/Lang.h +0 -23
  710. package/android/jni/lzma/CPP/Common/ListFileUtils.cpp +0 -132
  711. package/android/jni/lzma/CPP/Common/ListFileUtils.h +0 -18
  712. package/android/jni/lzma/CPP/Common/MyBuffer.h +0 -259
  713. package/android/jni/lzma/CPP/Common/MyBuffer2.h +0 -100
  714. package/android/jni/lzma/CPP/Common/MyCom.h +0 -277
  715. package/android/jni/lzma/CPP/Common/MyException.h +0 -14
  716. package/android/jni/lzma/CPP/Common/MyGuidDef.h +0 -54
  717. package/android/jni/lzma/CPP/Common/MyInitGuid.h +0 -45
  718. package/android/jni/lzma/CPP/Common/MyLinux.h +0 -42
  719. package/android/jni/lzma/CPP/Common/MyString.cpp +0 -1659
  720. package/android/jni/lzma/CPP/Common/MyString.h +0 -868
  721. package/android/jni/lzma/CPP/Common/MyTypes.h +0 -35
  722. package/android/jni/lzma/CPP/Common/MyUnknown.h +0 -17
  723. package/android/jni/lzma/CPP/Common/MyVector.cpp +0 -3
  724. package/android/jni/lzma/CPP/Common/MyVector.h +0 -634
  725. package/android/jni/lzma/CPP/Common/MyWindows.cpp +0 -145
  726. package/android/jni/lzma/CPP/Common/MyWindows.h +0 -231
  727. package/android/jni/lzma/CPP/Common/NewHandler.cpp +0 -163
  728. package/android/jni/lzma/CPP/Common/NewHandler.h +0 -88
  729. package/android/jni/lzma/CPP/Common/Sha256Reg.cpp +0 -40
  730. package/android/jni/lzma/CPP/Common/StdAfx.h +0 -8
  731. package/android/jni/lzma/CPP/Common/StdInStream.cpp +0 -89
  732. package/android/jni/lzma/CPP/Common/StdInStream.h +0 -38
  733. package/android/jni/lzma/CPP/Common/StdOutStream.cpp +0 -163
  734. package/android/jni/lzma/CPP/Common/StdOutStream.h +0 -71
  735. package/android/jni/lzma/CPP/Common/StringConvert.cpp +0 -319
  736. package/android/jni/lzma/CPP/Common/StringConvert.h +0 -88
  737. package/android/jni/lzma/CPP/Common/StringToInt.cpp +0 -144
  738. package/android/jni/lzma/CPP/Common/StringToInt.h +0 -21
  739. package/android/jni/lzma/CPP/Common/TextConfig.cpp +0 -124
  740. package/android/jni/lzma/CPP/Common/TextConfig.h +0 -19
  741. package/android/jni/lzma/CPP/Common/UTFConvert.cpp +0 -288
  742. package/android/jni/lzma/CPP/Common/UTFConvert.h +0 -12
  743. package/android/jni/lzma/CPP/Common/Wildcard.cpp +0 -676
  744. package/android/jni/lzma/CPP/Common/Wildcard.h +0 -149
  745. package/android/jni/lzma/CPP/Common/XzCrc64Init.cpp +0 -7
  746. package/android/jni/lzma/CPP/Common/XzCrc64Reg.cpp +0 -42
  747. package/android/jni/lzma/CPP/Windows/COM.h +0 -70
  748. package/android/jni/lzma/CPP/Windows/CommonDialog.cpp +0 -185
  749. package/android/jni/lzma/CPP/Windows/CommonDialog.h +0 -23
  750. package/android/jni/lzma/CPP/Windows/Control/ComboBox.cpp +0 -66
  751. package/android/jni/lzma/CPP/Windows/Control/ComboBox.h +0 -65
  752. package/android/jni/lzma/CPP/Windows/Control/CommandBar.h +0 -52
  753. package/android/jni/lzma/CPP/Windows/Control/Dialog.cpp +0 -251
  754. package/android/jni/lzma/CPP/Windows/Control/Dialog.h +0 -170
  755. package/android/jni/lzma/CPP/Windows/Control/Edit.h +0 -19
  756. package/android/jni/lzma/CPP/Windows/Control/ImageList.cpp +0 -10
  757. package/android/jni/lzma/CPP/Windows/Control/ImageList.h +0 -87
  758. package/android/jni/lzma/CPP/Windows/Control/ListView.cpp +0 -155
  759. package/android/jni/lzma/CPP/Windows/Control/ListView.h +0 -146
  760. package/android/jni/lzma/CPP/Windows/Control/ProgressBar.h +0 -35
  761. package/android/jni/lzma/CPP/Windows/Control/PropertyPage.cpp +0 -143
  762. package/android/jni/lzma/CPP/Windows/Control/PropertyPage.h +0 -50
  763. package/android/jni/lzma/CPP/Windows/Control/ReBar.h +0 -34
  764. package/android/jni/lzma/CPP/Windows/Control/Static.h +0 -28
  765. package/android/jni/lzma/CPP/Windows/Control/StatusBar.h +0 -42
  766. package/android/jni/lzma/CPP/Windows/Control/StdAfx.h +0 -8
  767. package/android/jni/lzma/CPP/Windows/Control/ToolBar.h +0 -43
  768. package/android/jni/lzma/CPP/Windows/Control/Trackbar.h +0 -27
  769. package/android/jni/lzma/CPP/Windows/Control/Window2.cpp +0 -200
  770. package/android/jni/lzma/CPP/Windows/Control/Window2.h +0 -51
  771. package/android/jni/lzma/CPP/Windows/DLL.cpp +0 -109
  772. package/android/jni/lzma/CPP/Windows/DLL.h +0 -58
  773. package/android/jni/lzma/CPP/Windows/Defs.h +0 -17
  774. package/android/jni/lzma/CPP/Windows/ErrorMsg.cpp +0 -66
  775. package/android/jni/lzma/CPP/Windows/ErrorMsg.h +0 -15
  776. package/android/jni/lzma/CPP/Windows/FileDir.cpp +0 -714
  777. package/android/jni/lzma/CPP/Windows/FileDir.h +0 -117
  778. package/android/jni/lzma/CPP/Windows/FileFind.cpp +0 -749
  779. package/android/jni/lzma/CPP/Windows/FileFind.h +0 -161
  780. package/android/jni/lzma/CPP/Windows/FileIO.cpp +0 -432
  781. package/android/jni/lzma/CPP/Windows/FileIO.h +0 -212
  782. package/android/jni/lzma/CPP/Windows/FileLink.cpp +0 -440
  783. package/android/jni/lzma/CPP/Windows/FileMapping.cpp +0 -12
  784. package/android/jni/lzma/CPP/Windows/FileMapping.h +0 -66
  785. package/android/jni/lzma/CPP/Windows/FileName.cpp +0 -839
  786. package/android/jni/lzma/CPP/Windows/FileName.h +0 -115
  787. package/android/jni/lzma/CPP/Windows/FileSystem.cpp +0 -131
  788. package/android/jni/lzma/CPP/Windows/FileSystem.h +0 -27
  789. package/android/jni/lzma/CPP/Windows/Handle.h +0 -37
  790. package/android/jni/lzma/CPP/Windows/MemoryLock.cpp +0 -112
  791. package/android/jni/lzma/CPP/Windows/MemoryLock.h +0 -40
  792. package/android/jni/lzma/CPP/Windows/NtCheck.h +0 -46
  793. package/android/jni/lzma/CPP/Windows/PropVariant.cpp +0 -347
  794. package/android/jni/lzma/CPP/Windows/PropVariant.h +0 -114
  795. package/android/jni/lzma/CPP/Windows/PropVariantConv.cpp +0 -138
  796. package/android/jni/lzma/CPP/Windows/PropVariantConv.h +0 -37
  797. package/android/jni/lzma/CPP/Windows/Registry.cpp +0 -390
  798. package/android/jni/lzma/CPP/Windows/Registry.h +0 -84
  799. package/android/jni/lzma/CPP/Windows/ResourceString.cpp +0 -103
  800. package/android/jni/lzma/CPP/Windows/ResourceString.h +0 -16
  801. package/android/jni/lzma/CPP/Windows/SecurityUtils.cpp +0 -181
  802. package/android/jni/lzma/CPP/Windows/SecurityUtils.h +0 -167
  803. package/android/jni/lzma/CPP/Windows/Shell.cpp +0 -358
  804. package/android/jni/lzma/CPP/Windows/Shell.h +0 -94
  805. package/android/jni/lzma/CPP/Windows/StdAfx.h +0 -8
  806. package/android/jni/lzma/CPP/Windows/Synchronization.cpp +0 -10
  807. package/android/jni/lzma/CPP/Windows/Synchronization.h +0 -164
  808. package/android/jni/lzma/CPP/Windows/System.cpp +0 -142
  809. package/android/jni/lzma/CPP/Windows/System.h +0 -40
  810. package/android/jni/lzma/CPP/Windows/Thread.h +0 -38
  811. package/android/jni/lzma/CPP/Windows/TimeUtils.cpp +0 -213
  812. package/android/jni/lzma/CPP/Windows/TimeUtils.h +0 -32
  813. package/android/jni/lzma/CPP/Windows/Window.cpp +0 -179
  814. package/android/jni/lzma/CPP/Windows/Window.h +0 -284
  815. package/android/jni/lzma/CS/7zip/Common/CRC.cs +0 -55
  816. package/android/jni/lzma/CS/7zip/Common/CommandLineParser.cs +0 -274
  817. package/android/jni/lzma/CS/7zip/Common/InBuffer.cs +0 -72
  818. package/android/jni/lzma/CS/7zip/Common/OutBuffer.cs +0 -47
  819. package/android/jni/lzma/CS/7zip/Compress/LZ/IMatchFinder.cs +0 -24
  820. package/android/jni/lzma/CS/7zip/Compress/LZ/LzBinTree.cs +0 -367
  821. package/android/jni/lzma/CS/7zip/Compress/LZ/LzInWindow.cs +0 -132
  822. package/android/jni/lzma/CS/7zip/Compress/LZ/LzOutWindow.cs +0 -110
  823. package/android/jni/lzma/CS/7zip/Compress/LZMA/LzmaBase.cs +0 -76
  824. package/android/jni/lzma/CS/7zip/Compress/LZMA/LzmaDecoder.cs +0 -398
  825. package/android/jni/lzma/CS/7zip/Compress/LZMA/LzmaEncoder.cs +0 -1480
  826. package/android/jni/lzma/CS/7zip/Compress/LzmaAlone/LzmaAlone.cs +0 -364
  827. package/android/jni/lzma/CS/7zip/Compress/LzmaAlone/LzmaAlone.csproj +0 -90
  828. package/android/jni/lzma/CS/7zip/Compress/LzmaAlone/LzmaAlone.sln +0 -20
  829. package/android/jni/lzma/CS/7zip/Compress/LzmaAlone/LzmaBench.cs +0 -340
  830. package/android/jni/lzma/CS/7zip/Compress/LzmaAlone/Properties/AssemblyInfo.cs +0 -29
  831. package/android/jni/lzma/CS/7zip/Compress/LzmaAlone/Properties/Resources.cs +0 -70
  832. package/android/jni/lzma/CS/7zip/Compress/LzmaAlone/Properties/Settings.cs +0 -42
  833. package/android/jni/lzma/CS/7zip/Compress/RangeCoder/RangeCoder.cs +0 -234
  834. package/android/jni/lzma/CS/7zip/Compress/RangeCoder/RangeCoderBit.cs +0 -117
  835. package/android/jni/lzma/CS/7zip/Compress/RangeCoder/RangeCoderBitTree.cs +0 -157
  836. package/android/jni/lzma/CS/7zip/ICoder.cs +0 -157
  837. package/android/jni/lzma/DOC/7zC.txt +0 -187
  838. package/android/jni/lzma/DOC/7zFormat.txt +0 -469
  839. package/android/jni/lzma/DOC/Methods.txt +0 -173
  840. package/android/jni/lzma/DOC/installer.txt +0 -166
  841. package/android/jni/lzma/DOC/lzma-history.txt +0 -446
  842. package/android/jni/lzma/DOC/lzma-specification.txt +0 -1176
  843. package/android/jni/lzma/DOC/lzma.txt +0 -328
  844. package/android/jni/lzma/Java/SevenZip/CRC.java +0 -52
  845. package/android/jni/lzma/Java/SevenZip/Compression/LZ/BinTree.java +0 -382
  846. package/android/jni/lzma/Java/SevenZip/Compression/LZ/InWindow.java +0 -131
  847. package/android/jni/lzma/Java/SevenZip/Compression/LZ/OutWindow.java +0 -85
  848. package/android/jni/lzma/Java/SevenZip/Compression/LZMA/Base.java +0 -88
  849. package/android/jni/lzma/Java/SevenZip/Compression/LZMA/Decoder.java +0 -329
  850. package/android/jni/lzma/Java/SevenZip/Compression/LZMA/Encoder.java +0 -1416
  851. package/android/jni/lzma/Java/SevenZip/Compression/RangeCoder/BitTreeDecoder.java +0 -55
  852. package/android/jni/lzma/Java/SevenZip/Compression/RangeCoder/BitTreeEncoder.java +0 -99
  853. package/android/jni/lzma/Java/SevenZip/Compression/RangeCoder/Decoder.java +0 -88
  854. package/android/jni/lzma/Java/SevenZip/Compression/RangeCoder/Encoder.java +0 -151
  855. package/android/jni/lzma/Java/SevenZip/ICodeProgress.java +0 -6
  856. package/android/jni/lzma/Java/SevenZip/LzmaAlone.java +0 -253
  857. package/android/jni/lzma/Java/SevenZip/LzmaBench.java +0 -392
  858. package/android/jni/lzma/bin/7zS2.sfx +0 -0
  859. package/android/jni/lzma/bin/7zS2con.sfx +0 -0
  860. package/android/jni/lzma/bin/7zSD.sfx +0 -0
  861. package/android/jni/lzma/bin/7zdec.exe +0 -0
  862. package/android/jni/lzma/bin/7zr.exe +0 -0
  863. package/android/jni/lzma/bin/installer/config.txt +0 -5
  864. package/android/jni/lzma/bin/installer/cr.bat +0 -5
  865. package/android/jni/lzma/bin/lzma.exe +0 -0
  866. package/android/jni/lzma/bin/x64/7zr.exe +0 -0
  867. package/android/src/main/java/cn/reactnative/modules/update/ArchivePatchPlanResult.java +0 -6
  868. package/android/src/main/java/cn/reactnative/modules/update/PushyConfiguration.java +0 -104
  869. package/android/src/main/java/cn/reactnative/modules/update/UiThreadRunner.java +0 -37
  870. package/android/src/newarch/cn/reactnative/modules/update/UpdateModule.java +0 -121
  871. package/android/src/oldarch/cn/reactnative/modules/update/UpdateModule.java +0 -125
  872. package/cpp/patch_core/jni_natives.h +0 -27
  873. package/cpp/patch_core/jni_registration.cpp +0 -103
  874. package/cpp/patch_core/jni_util.h +0 -60
  875. package/cpp/patch_core/patch_core_android.cpp +0 -97
  876. package/cpp/patch_core/update_core_android.cpp +0 -485
  877. package/cpp/update_flow_core/update_flow_jni.cpp +0 -150
@@ -1,971 +1,82 @@
1
- #import "RCTPushy.h"
1
+ #import "RCTPushyInternal.h"
2
2
  #import "RCTPushyConfiguration.h"
3
- #import "RCTPushyDownloader.h"
4
- #import "ZipArchive.h"
5
- #include "../../cpp/patch_core/archive_limits.h"
6
- #include "../../cpp/patch_core/archive_patch_core.h"
7
- #include "../../cpp/patch_core/digest.h"
8
- #include "../../cpp/patch_core/hbc_transform_wire.h"
9
- #include "../../cpp/patch_core/install_record.h"
10
3
  #include "../../cpp/patch_core/error_codes.h"
4
+ #include "../../cpp/patch_core/hbc_transform_wire.h"
11
5
  #include "../../cpp/patch_core/patch_core.h"
12
- #include "../../cpp/patch_core/state_core.h"
13
- #include "../../cpp/update_flow_core/flow_json.h"
14
- #include "../../cpp/update_flow_core/update_flow_core.h"
15
-
16
- #import <UIKit/UIKit.h>
17
6
 
18
7
  // use_frameworks! 构建里 React 头文件只有 <React/…> 框架形式可见,引号形式的
19
- // __has_include 会探测失败;漏掉这里会静默编入 [self.bridge reload] 兜底,而
20
- // bridgeless 下 bridge 是 nil/代理,reload 变成无声 no-op(切版本后不重启)。
8
+ // __has_include 会探测失败;漏掉框架形式会让重启变成无声 no-op(切版本后不
9
+ // 重启),所以两种形式都探测不到时直接编译失败。
21
10
  #if __has_include("RCTReloadCommand.h")
22
11
  #import "RCTReloadCommand.h"
23
- #define PUSHY_HAS_RELOAD_COMMAND 1
24
12
  #elif __has_include(<React/RCTReloadCommand.h>)
25
13
  #import <React/RCTReloadCommand.h>
26
- #define PUSHY_HAS_RELOAD_COMMAND 1
14
+ #else
15
+ #error React Native 0.62 or newer is required: RCTReloadCommand.h was not found
27
16
  #endif
28
17
  #ifdef RCT_NEW_ARCH_ENABLED
29
18
  #import "RCTPushySpec.h"
30
19
  #endif
31
20
 
32
- #import <React/RCTConvert.h>
33
- #import <React/RCTLog.h>
34
- // RN's log macros pass __FILE__, which would embed this file's absolute build
35
- // path in the binary; log with the bare file name instead.
36
- #if RCTLOG_ENABLED
37
- #undef _RCTLog
38
- #define _RCTLog(lvl, ...) _RCTLogNativeInternal(lvl, __FILE_NAME__, __LINE__, __VA_ARGS__)
39
- #endif
40
- #import <objc/runtime.h>
41
- #import <os/lock.h>
42
-
43
- #include <atomic>
44
- #include <sys/stat.h>
45
-
46
- // Immutable host-facing snapshot; activated always means NEXT launch.
47
- static NSDictionary *PushyHostResult(NSString *status, NSString *reason,
48
- NSString *hash, BOOL activated) {
49
- return @{@"status": status, @"reason": reason ?: @"",
50
- @"hash": hash ?: @"", @"activated": @(activated)};
51
- }
52
-
53
- static NSString *const keyPushyInfo = @"REACTNATIVECN_PUSHY_INFO_KEY";
54
- // Binary identity (SyncBinaryVersion input). Prefixed like every other key:
55
- // the unprefixed names collided with generic host-app/SDK defaults, and a
56
- // foreign value there made every launch look like a rebuilt binary and wipe
57
- // the installed update.
58
- static NSString *const paramPackageVersion = @"REACTNATIVECN_PUSHY_PACKAGEVERSION";
59
- static NSString *const paramBuildTime = @"REACTNATIVECN_PUSHY_BUILDTIME";
60
- // The unprefixed names earlier SDKs wrote. Read only as a fallback until the
61
- // first state write migrates them (PushyStateFromDefaults /
62
- // PushyApplyStateToDefaults).
63
- static NSString *const legacyParamPackageVersion = @"packageVersion";
64
- static NSString *const legacyParamBuildTime = @"buildTime";
65
- static NSString *const paramLastVersion = @"lastVersion";
66
- static NSString *const paramCurrentVersion = @"currentVersion";
67
- static NSString *const paramIsFirstTime = @"isFirstTime";
68
- static NSString *const paramIsFirstLoadOk = @"isFirstLoadOK";
69
- static NSString *const keyUuid = @"REACTNATIVECN_PUSHY_UUID";
70
- static NSString *const keyHashInfo = @"REACTNATIVECN_PUSHY_HASH_";
71
- static NSString *const keyFirstLoadMarked = @"REACTNATIVECN_PUSHY_FIRSTLOADMARKED_KEY";
72
- static NSString *const keyRolledBackMarked = @"REACTNATIVECN_PUSHY_ROLLEDBACKMARKED_KEY";
73
- static NSString *const KeyPackageUpdatedMarked = RCTPushyRevealText("0832d5f29aa549710b0939daf88ca058790f3dded7e888b451640d2ec3f69099bb43711523cafa8ea7bb4372132a");
74
- // bundleHash cache: "<cacheKey>|<sha256hex>" where cacheKey identifies the
75
- // installed binary (packageVersion + embedded bundle size + mtime). Recomputed
76
- // only when the key changes, i.e. once per install.
77
- static NSString *const keyBundleHashCache = @"REACTNATIVECN_PUSHY_BUNDLEHASH_KEY";
78
- // Raw JSON persisted by JS (syncNativeConfig) for the native cold-start
79
- // update check; parsed on read by the orchestrator. Absent = check disabled.
80
- static NSString *const keyNativeConfig = @"REACTNATIVECN_PUSHY_NATIVE_CONFIG_KEY";
81
- // Raw response cache written by the native cold-start check for the JS side
82
- // to reuse (§10.3), scoped to the request and config that produced it.
83
- static NSString *const keyNativeCheckCache = @"REACTNATIVECN_PUSHY_NATIVE_CHECK_RESP_KEY";
84
- // Set when a native check round starts, cleared when it ends (§11.4). Residue
85
- // on the next launch means the previous process died mid-round (a crash
86
- // rescue was truncated): that launch resumes immediately instead of waiting.
87
- static NSString *const keyNativeCheckIncomplete = @"REACTNATIVECN_PUSHY_NATIVE_CHECK_INCOMPLETE_KEY";
88
- static NSString *const PushyErrorDomain = @"cn.reactnative.pushy";
89
-
90
- // file def
91
- static NSString * const BUNDLE_FILE_NAME = @"index.bundlejs";
92
- static NSString * const SOURCE_PATCH_NAME = @"__diff.json";
93
- static NSString * const BUNDLE_PATCH_NAME = RCTPushyRevealText("3319f0d4b6c56a502c3b10fcdca0de7d4b3307e9");
94
- // Protocol strings and version-info flags read by JS, stored encoded
95
- // (RCTPushyRevealText) so they do not appear as plain binary strings.
96
- static NSString * const PushyOptionUrlKey = RCTPushyRevealText("2f07f0d0ba8e5d572e");
97
- static NSString * const PushyIpaDeltaSuffix = RCTPushyRevealText("741ee4d0e09b69512137");
98
- static NSString * const PushyPpkDeltaSuffix = RCTPushyRevealText("7407e4dae09b69512137");
99
- static NSString * const PushyInfoCrashHold = RCTPushyRevealText("3905f5c2a6b96d56212a19");
100
- static NSString * const PushyInfoForceBoot = RCTPushyRevealText("3c18e6d2aba9674a360d19ead5a695");
101
- static NSString * const PushyStatusNone = RCTPushyRevealText("3418c1c1aa8a7c40");
102
- #define VERSION_COMPLETE_FILE_NAME_LITERAL ".pushy-complete"
103
- static NSString * const VERSION_COMPLETE_FILE_NAME = @VERSION_COMPLETE_FILE_NAME_LITERAL;
104
-
105
- // error def — messages are human-readable; the stable cross-platform codes
106
- // live in cpp/patch_core/error_codes.h and travel in PushyErrorCodeKey.
107
- static NSString * const ERROR_OPTIONS = @"options error";
108
- static NSString * const ERROR_FILE_OPERATION = @"file operation error";
109
- static NSString * const PushyErrorCodeKey = @"PushyErrorCode";
110
-
111
- static NSString *PushyCode(const char *code) {
112
- return [NSString stringWithUTF8String:code];
113
- }
114
-
115
- // cInfo.os label prefix; must match the JS side (core.ts) so the server
116
- // buckets tvOS devices apart from iOS ones.
117
- #if TARGET_OS_TV
118
- static NSString * const PushyOsName = @"tvos";
119
- #else
120
- static NSString * const PushyOsName = @"ios";
121
- #endif
122
-
123
21
  // event def
124
22
  static NSString * const EVENT_PROGRESS_DOWNLOAD = @"RCTPushyDownloadProgress";
125
23
  static NSString * const PARAM_PROGRESS_HASH = @"hash";
126
24
  static NSString * const PARAM_PROGRESS_RECEIVED = @"received";
127
25
  static NSString * const PARAM_PROGRESS_TOTAL = @"total";
128
26
 
129
- static NSTimeInterval PushyMonotonicNow(void) {
130
- return [NSProcessInfo processInfo].systemUptime;
131
- }
132
-
133
- static std::string PushyToStdString(NSString *value);
134
-
135
- static NSString *PushyStagingDirForVersionDir(NSString *versionDir) {
136
- return [versionDir stringByAppendingString:@(pushy::install_record::kStagingSuffix)];
137
- }
138
-
139
- // Parsed completion record (cpp/patch_core/install_record.h): nil when the
140
- // file is absent or malformed; an empty dictionary for the legacy empty
141
- // marker written by SDK < 10.53.
142
- static NSDictionary *PushyReadInstallRecord(NSString *versionDir) {
143
- NSString *path = [versionDir stringByAppendingPathComponent:VERSION_COMPLETE_FILE_NAME];
144
- NSData *data = [NSData dataWithContentsOfFile:path];
145
- if (data == nil) {
146
- return nil;
147
- }
148
- if (data.length == 0) {
149
- return @{};
150
- }
151
- if (data.length > 64 * 1024) {
152
- return nil;
153
- }
154
- id object = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
155
- return [object isKindOfClass:[NSDictionary class]] ? (NSDictionary *)object : nil;
156
- }
157
-
158
- // Presence check (launch / dedup paths, no digest work): bundle present and
159
- // the record exists and, unless legacy-empty, names this version.
160
- static BOOL PushyHasCompletedVersionAtPath(NSString *versionDir, NSString *hash) {
161
- NSString *bundlePath = [versionDir stringByAppendingPathComponent:BUNDLE_FILE_NAME];
162
- if (![[NSFileManager defaultManager] fileExistsAtPath:bundlePath]) {
163
- return NO;
164
- }
165
- NSDictionary *record = PushyReadInstallRecord(versionDir);
166
- if (record == nil) {
167
- return NO;
168
- }
169
- if (record.count == 0) {
170
- return YES;
171
- }
172
- return [record[@"schema"] isKindOfClass:[NSNumber class]]
173
- && [record[@"schema"] intValue] == pushy::install_record::kSchema
174
- && [record[@"versionHash"] isKindOfClass:[NSString class]]
175
- && [record[@"versionHash"] isEqualToString:hash];
176
- }
177
-
178
- // Activation check: re-hashes the bundle when the record carries a digest.
179
- // Returns nil when the directory may be activated, the reason otherwise.
180
- static NSString *PushyVerifyInstallForActivation(NSString *versionDir, NSString *hash) {
181
- NSDictionary *record = PushyReadInstallRecord(versionDir);
182
- if (record == nil) {
183
- return [NSString stringWithFormat:@"Bundle version %@ has no valid completion record.", hash];
184
- }
185
- if (record.count == 0) {
186
- return nil;
187
- }
188
- if (![record[@"schema"] isKindOfClass:[NSNumber class]]
189
- || [record[@"schema"] intValue] != pushy::install_record::kSchema
190
- || ![record[@"versionHash"] isKindOfClass:[NSString class]]
191
- || ![record[@"versionHash"] isEqualToString:hash]) {
192
- return [NSString stringWithFormat:@"Bundle version %@ completion record mismatch.", hash];
193
- }
194
- NSString *expected = record[@"bundleSha256"];
195
- if (![expected isKindOfClass:[NSString class]] || expected.length == 0) {
196
- return nil;
197
- }
198
- std::string actual = pushy::digest::Sha256File(
199
- PushyToStdString([versionDir stringByAppendingPathComponent:BUNDLE_FILE_NAME]));
200
- if ([[expected lowercaseString] isEqualToString:[NSString stringWithUTF8String:actual.c_str()]]) {
201
- return nil;
202
- }
203
- return [NSString stringWithFormat:@"Bundle version %@ bundle digest mismatch.", hash];
204
- }
205
-
206
- // SHA-256 of each in-flight download's archive, keyed by hash; consumed by
207
- // the completion record write. Guarded by @synchronized on the table.
208
- static NSMutableDictionary<NSString *, NSString *> *PushyArtifactDigests(void) {
209
- static NSMutableDictionary *table;
210
- static dispatch_once_t once;
211
- dispatch_once(&once, ^{
212
- table = [NSMutableDictionary dictionary];
213
- });
214
- return table;
215
- }
216
-
217
- static NSError *PushyErrorWithCode(const char *code, NSString *message);
218
-
219
- // nil when the volume holding `path` can take `bytesToWrite` plus the safety
220
- // margin (or the free space is unknown); a PATCH_FAILED error otherwise. The
221
- // probe is the downloader's (RCTPushyFreeSpaceShortfall), which applies the
222
- // same rule to the announced Content-Length before the first byte lands.
223
- static NSError *PushyEnsureFreeSpace(NSString *path, long long bytesToWrite) {
224
- NSString *shortfall = RCTPushyFreeSpaceShortfall(path, bytesToWrite);
225
- if (shortfall == nil) {
226
- return nil;
227
- }
228
- return PushyErrorWithCode(pushy::error_codes::kDeltaFailed, shortfall);
229
- }
230
-
231
- // SSZipArchive delegate enforcing cpp/patch_core/archive_limits.h while the
232
- // archive is walked (entry count, per-entry size, total size, compression
233
- // ratio) plus two entry-shape rules Android's java.util.zip gets for free:
234
- // - no symlinks: SSZipArchive materialises them with any target regardless
235
- // of preserveAttributes, so `assets -> ../../Documents` followed by
236
- // `assets/x` would write outside the version directory;
237
- // - no ".pushy-" reserved names at any depth: the completion marker is
238
- // written by this SDK only, after the whole install succeeded. An archive
239
- // shipping its own would be trusted as a finished install even if the
240
- // patch step that follows fails.
241
- // A NO from zipArchiveShouldUnzipFileAtIndex: cancels the whole unzip (the
242
- // 2.x contract the podspec pins), and the completion handler turns the
243
- // recorded violation into a hard PATCH_FAILED; the caller then discards the
244
- // staging directory.
245
- @interface PushyUnzipGuard : NSObject <SSZipArchiveDelegate>
246
- @property (nonatomic, copy) NSString *violation;
247
- @property (nonatomic, assign) long long totalUncompressed;
248
- @end
249
-
250
- @implementation PushyUnzipGuard
251
-
252
- - (void)zipArchiveWillUnzipArchiveAtPath:(NSString *)path zipInfo:(unz_global_info)zipInfo {
253
- if ((long long)zipInfo.number_entry > pushy::archive_limits::kMaxEntries) {
254
- self.violation = [NSString stringWithFormat:@"archive has too many entries (%lu)",
255
- (unsigned long)zipInfo.number_entry];
256
- }
257
- }
258
-
259
- - (BOOL)zipArchiveShouldUnzipFileAtIndex:(NSInteger)fileIndex
260
- totalFiles:(NSInteger)totalFiles
261
- archivePath:(NSString *)archivePath
262
- fileInfo:(unz_file_info)fileInfo {
263
- if (self.violation != nil) {
264
- return NO;
265
- }
266
- // Unix mode bits travel in the high 16 bits of the external attributes
267
- // (the same field SSZipArchive keys its own symlink branch on).
268
- if (((fileInfo.external_fa >> 16) & S_IFMT) == S_IFLNK) {
269
- self.violation = [NSString stringWithFormat:@"archive entry #%ld is a symlink",
270
- (long)fileIndex];
271
- return NO;
272
- }
273
- long long size = (long long)fileInfo.uncompressed_size;
274
- long long compressed = (long long)fileInfo.compressed_size;
275
- if (size > pushy::archive_limits::kMaxEntryBytes) {
276
- self.violation = [NSString stringWithFormat:@"archive entry #%ld too large (%lld bytes)",
277
- (long)fileIndex, size];
278
- return NO;
279
- }
280
- self.totalUncompressed += size;
281
- if (self.totalUncompressed > pushy::archive_limits::kMaxTotalUncompressedBytes) {
282
- self.violation = [NSString stringWithFormat:@"archive expands beyond %lld bytes",
283
- pushy::archive_limits::kMaxTotalUncompressedBytes];
284
- return NO;
285
- }
286
- if (compressed > 0 && size > pushy::archive_limits::kRatioCheckMinBytes
287
- && size / compressed > pushy::archive_limits::kMaxCompressionRatio) {
288
- self.violation = [NSString stringWithFormat:@"archive entry #%ld compression ratio too high",
289
- (long)fileIndex];
290
- return NO;
291
- }
292
- return YES;
293
- }
294
-
295
- // The entry name is only exposed once the entry is on disk (shouldUnzip
296
- // runs before the name is read). A reserved name is flagged here; the next
297
- // shouldUnzip refuses (cancelling the unzip) or, for the last entry, the
298
- // completion handler picks the violation up. Either way the caller drops
299
- // the whole staging directory, so the written file never survives.
300
- - (void)zipArchiveDidUnzipFileAtIndex:(NSInteger)fileIndex
301
- totalFiles:(NSInteger)totalFiles
302
- archivePath:(NSString *)archivePath
303
- unzippedFilePath:(NSString *)unzippedFilePath {
304
- NSString *name = unzippedFilePath.lastPathComponent;
305
- if (self.violation == nil && [name hasPrefix:@".pushy-"]) {
306
- self.violation = [NSString stringWithFormat:@"archive contains reserved entry %@", name];
307
- }
308
- }
309
-
310
- @end
311
-
312
- static NSError *PushyDownloadDeadlineExpiredError(void) {
313
- return [NSError errorWithDomain:PushyErrorDomain
314
- code:-1
315
- userInfo:@{
316
- NSLocalizedDescriptionKey: @"download deadline expired before start",
317
- PushyErrorCodeKey: PushyCode(pushy::error_codes::kDownloadFailed),
318
- }];
319
- }
320
-
321
-
322
- typedef NS_ENUM(NSInteger, PushyType) {
323
- PushyTypeFullDownload = 1,
324
- PushyTypePatchFromPackage = 2,
325
- PushyTypePatchFromPpk = 3,
326
- };
327
-
328
- static std::atomic<bool> ignoreRollback{false};
329
27
  // Whether the host asked +bundleURL for the bundle to load (the
330
28
  // isUsingBundleUrl constant, mirroring Android/Harmony). NO means the app
331
29
  // runs a bundle this SDK never resolved, so updates cannot take effect.
332
30
  static std::atomic<bool> pushyIsUsingBundleUrl{false};
333
- // Bumped by resetToPackagedBundle. The cold-start check runs for minutes and
334
- // may already hold a decision when the app resets to the packaged bundle; it
335
- // samples this counter and abandons activation (and its response cache) when
336
- // the value moved, so an in-flight rescue can never resurrect the version the
337
- // app just reset away from.
338
- static std::atomic<uint64_t> pushyResetGeneration{0};
339
- // The version whose bundle this process actually loaded (resolved in
340
- // +bundleURL). resetToPackagedBundle must not delete its directory: update
341
- // assets (images/fonts) are read from it on demand at runtime, so wiping it
342
- // under a silent (no-restart) reset would break every image the running app
343
- // has not loaded yet. Guarded by the state lock.
344
- static NSString *pushyLaunchVersion = nil;
345
31
 
346
- // JS and the bridge-free cold-start engine use different RCTPushy instances,
347
- // but they must still share one download per target hash. Without this
348
- // process-wide registry two NSURLSessionDownloadTasks race over the same
349
- // archive path and the later unzip can delete the first task's valid output.
350
- static NSMutableDictionary<NSString *, NSMutableDictionary *> *PushyInFlightDownloads(void) {
351
- static NSMutableDictionary<NSString *, NSMutableDictionary *> *downloads;
352
- static dispatch_once_t onceToken;
353
- dispatch_once(&onceToken, ^{
354
- downloads = [NSMutableDictionary dictionary];
32
+ // Triggers a JS reload. React Native re-resolves the bundle URL itself on
33
+ // reload (RCTHost asks its bundleURLProvider, RCTBridge.setUp re-asks
34
+ // sourceURLForBridge:), so +bundleURL is deliberately NOT called here: it
35
+ // consumes one-shot launch state (first_time, the first-load mark,
36
+ // ignoreRollback), and consuming it for a reload that never happens would
37
+ // make the next cold start roll the freshly switched version back.
38
+ static void PushyTriggerReload(NSString *reason) {
39
+ dispatch_async(dispatch_get_main_queue(), ^{
40
+ RCTTriggerReloadCommandListeners(reason);
355
41
  });
356
- return downloads;
357
- }
358
-
359
- typedef NS_ENUM(NSInteger, PushyDownloadRegistration) {
360
- PushyDownloadRegistrationOwner,
361
- PushyDownloadRegistrationJoined,
362
- PushyDownloadRegistrationDeferred,
363
- };
364
-
365
- static PushyDownloadRegistration PushyRegisterDownload(
366
- NSString *hash,
367
- NSInteger type,
368
- NSTimeInterval deadlineUptime,
369
- void (^callback)(NSError *),
370
- void (^progress)(long long, long long),
371
- void (^deferredStart)(void)
372
- ) {
373
- NSMutableDictionary *downloads = PushyInFlightDownloads();
374
- @synchronized (downloads) {
375
- NSMutableDictionary *entry = downloads[hash];
376
- if (entry != nil) {
377
- if ([entry[@"type"] integerValue] == type) {
378
- NSTimeInterval ownerDeadline = [entry[@"deadlineUptime"] doubleValue];
379
- // A caller with substantially more time must not inherit an
380
- // owner's nearly-exhausted timeout: it observes the current
381
- // transfer and restarts after the owner settles (the
382
- // completion-marker preflight makes a successful owner free).
383
- // The comparison is on remaining budget, not on the absolute
384
- // deadline: a JS caller always computes now+600 a few seconds
385
- // after the owner did, so a strict `>` would defer every
386
- // second caller and turn the shared download back into
387
- // serialized re-downloads. Only a genuinely starved owner
388
- // (less than half the newcomer's budget left) defers.
389
- const NSTimeInterval now = PushyMonotonicNow();
390
- if (2 * (ownerDeadline - now) < (deadlineUptime - now)) {
391
- if (progress != nil) {
392
- [entry[@"progress"] addObject:[progress copy]];
393
- }
394
- [entry[@"deferred"] addObject:[deferredStart copy]];
395
- return PushyDownloadRegistrationDeferred;
396
- }
397
- [entry[@"callbacks"] addObject:[callback copy]];
398
- if (progress != nil) {
399
- [entry[@"progress"] addObject:[progress copy]];
400
- }
401
- return PushyDownloadRegistrationJoined;
402
- }
403
- // A diff failure must not settle a joined full request. Queue the
404
- // different artifact type behind the owner; once restarted it
405
- // registers normally (and re-checks the completion marker).
406
- if (progress != nil) {
407
- // The artifact type differs, but it still installs the same
408
- // target hash. Keep the waiting JS UI moving while its own
409
- // transfer is queued behind the current owner.
410
- [entry[@"progress"] addObject:[progress copy]];
411
- }
412
- [entry[@"deferred"] addObject:[deferredStart copy]];
413
- return PushyDownloadRegistrationDeferred;
414
- }
415
- NSMutableArray *progressHandlers = [NSMutableArray array];
416
- if (progress != nil) {
417
- [progressHandlers addObject:[progress copy]];
418
- }
419
- downloads[hash] = [@{
420
- @"type": @(type),
421
- @"deadlineUptime": @(deadlineUptime),
422
- @"callbacks": [NSMutableArray arrayWithObject:[callback copy]],
423
- @"progress": progressHandlers,
424
- @"deferred": [NSMutableArray array],
425
- } mutableCopy];
426
- return PushyDownloadRegistrationOwner;
427
- }
428
- }
429
-
430
- static void PushyReportDownloadProgress(
431
- NSString *hash, long long received, long long total
432
- ) {
433
- NSMutableDictionary *downloads = PushyInFlightDownloads();
434
- NSArray *handlers = nil;
435
- @synchronized (downloads) {
436
- handlers = [downloads[hash][@"progress"] copy];
437
- }
438
- for (id value in handlers) {
439
- void (^handler)(long long, long long) =
440
- (void (^)(long long, long long))value;
441
- handler(received, total);
442
- }
443
- }
444
-
445
- static void PushyFinishDownload(NSString *hash, NSError *error) {
446
- NSMutableDictionary *downloads = PushyInFlightDownloads();
447
- NSArray *callbacks = nil;
448
- NSArray *deferred = nil;
449
- @synchronized (downloads) {
450
- callbacks = [downloads[hash][@"callbacks"] copy];
451
- deferred = [downloads[hash][@"deferred"] copy];
452
- [downloads removeObjectForKey:hash];
453
- }
454
- // Establish the next (different-type) owner before waking the completed
455
- // owner's callbacks; otherwise its strategy loop could race the waiter.
456
- for (id value in deferred) {
457
- void (^start)(void) = (void (^)(void))value;
458
- start();
459
- }
460
- for (id value in callbacks) {
461
- void (^callback)(NSError *) = (void (^)(NSError *))value;
462
- callback(error);
463
- }
464
- }
465
-
466
- // Serializes every read-modify-write of the persisted update state. The state
467
- // machine itself is a pure function (state_core), but callers run on different
468
- // threads (main thread bundleURL, module method queue, _fileQueue), so the
469
- // read→transform→write sequence must be atomic to avoid e.g. markSuccess being
470
- // overwritten by a concurrent bundleURL and the version being rolled back.
471
- static os_unfair_lock pushyStateLock = OS_UNFAIR_LOCK_INIT;
472
-
473
- static void PushyWithStateLock(void (NS_NOESCAPE ^block)(void)) {
474
- #if DEBUG
475
- // Not re-entrant: a nested acquisition deadlocks in release, so catch it
476
- // where it is written.
477
- os_unfair_lock_assert_not_owner(&pushyStateLock);
478
- #endif
479
- os_unfair_lock_lock(&pushyStateLock);
480
- @try {
481
- block();
482
- } @finally {
483
- os_unfair_lock_unlock(&pushyStateLock);
484
- }
485
- }
486
-
487
- static std::string PushyToStdString(NSString *value) {
488
- if (value == nil) {
489
- return std::string();
490
- }
491
- return std::string([value UTF8String]);
492
- }
493
-
494
- static NSError *PushyNSErrorFromStatus(const pushy::delta::Status &status) {
495
- return [NSError errorWithDomain:PushyErrorDomain
496
- code:-1
497
- userInfo:@{
498
- NSLocalizedDescriptionKey: [NSString stringWithUTF8String:status.message.c_str()],
499
- PushyErrorCodeKey: PushyCode(pushy::error_codes::kDeltaFailed),
500
- }];
501
- }
502
-
503
- static NSUserDefaults *PushyDefaults(void) {
504
- return [NSUserDefaults standardUserDefaults];
505
- }
506
-
507
- static NSString *PushyFromStdString(const std::string &value) {
508
- if (value.empty()) {
509
- return nil;
510
- }
511
- return [NSString stringWithUTF8String:value.c_str()];
512
- }
513
-
514
- static void PushySetNullableString(NSUserDefaults *defaults, NSString *key, NSString *value) {
515
- if (value != nil) {
516
- [defaults setObject:value forKey:key];
517
- } else {
518
- [defaults removeObjectForKey:key];
519
- }
520
- }
521
-
522
- static NSString *PushyHashInfoKey(NSString *hash) {
523
- return [keyHashInfo stringByAppendingString:hash ?: @""];
524
- }
525
-
526
- static NSString *PushyOptionString(NSDictionary *options, NSString *key) {
527
- return [RCTConvert NSString:options[key]];
528
- }
529
-
530
- static BOOL PushyStringIsBlank(NSString *value) {
531
- if (value == nil || [value isKindOfClass:[NSNull class]]) {
532
- return YES;
533
- }
534
- return [[value stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] length] == 0;
535
- }
536
-
537
- // Server-provided identifiers (hash/originHash) are used as child names under
538
- // the download root; anything that could resolve outside of it (path
539
- // separators, "..", ".") must be rejected before touching the filesystem.
540
- static BOOL PushyIsSafePathComponent(NSString *value) {
541
- if (PushyStringIsBlank(value)) {
542
- return NO;
543
- }
544
- if ([value isEqualToString:@"."] || [value isEqualToString:@".."]) {
545
- return NO;
546
- }
547
- if ([value containsString:@"/"] || [value containsString:@"\\"]) {
548
- return NO;
549
- }
550
- if ([value rangeOfString:@"\0"].location != NSNotFound) {
551
- return NO;
552
- }
553
- return YES;
554
- }
555
-
556
- static void PushyRejectError(RCTPromiseRejectBlock reject, NSError *error) {
557
- // Prefer the stable cross-platform code (error_codes.h); fall back to the
558
- // numeric NSError code for system errors that were not classified.
559
- NSString *code = error.userInfo[PushyErrorCodeKey];
560
- if (code == nil) {
561
- code = [NSString stringWithFormat:@"%ld", (long)error.code];
562
- }
563
- reject(code, error.localizedDescription, error);
564
42
  }
565
43
 
566
- static NSError *PushyErrorWithCode(const char *code, NSString *message) {
567
- return [NSError errorWithDomain:PushyErrorDomain
568
- code:-1
569
- userInfo:@{
570
- NSLocalizedDescriptionKey: message ?: @"unknown error",
571
- PushyErrorCodeKey: PushyCode(code),
572
- }];
44
+ // A promise rejection for an exception caught (`@catch (...)`) around a
45
+ // state transition.
46
+ static void PushyRejectCaught(RCTPromiseRejectBlock reject, const char *code,
47
+ NSString *fallback) {
48
+ PushyRejectError(reject, PushyErrorWithCode(code, PushyCaughtReason(fallback)));
573
49
  }
574
50
 
575
- static BOOL PushyJsonIsAbsent(id value) {
576
- return value == nil || [value isKindOfClass:[NSNull class]];
577
- }
578
-
579
- // Builds the patch-core manifest from a parsed __diff.json. Every field is
580
- // type-checked: this runs inside a GCD block on _fileQueue, where the
581
- // unrecognized-selector exception a `copies` array or a numeric `from` would
582
- // raise takes the whole app down (and trips the crash rescue) instead of
583
- // failing the patch. NO with `reason` set for a malformed manifest.
584
- static BOOL PushyPatchManifestFromJson(NSDictionary *json,
585
- pushy::delta::PatchManifest *manifest,
586
- NSString **reason) {
587
- id copies = json[@"copies"];
588
- if (!PushyJsonIsAbsent(copies)) {
589
- if (![copies isKindOfClass:[NSDictionary class]]) {
590
- *reason = @"delta manifest: copies is not an object";
591
- return NO;
592
- }
593
- // Content checksum per copy target ("copiesCrc", pdiff manifests
594
- // from CLI >= 2.21.2). The patch core verifies the copy source
595
- // against it and fails the patch on mismatch, so the JS strategy
596
- // chain falls back to the full package instead of copying drifted
597
- // bytes from a rebuilt binary.
598
- NSDictionary *copiesCrc = json[@"copiesCrc"];
599
- if (![copiesCrc isKindOfClass:[NSDictionary class]]) {
600
- copiesCrc = nil;
601
- }
602
- for (id to in (NSDictionary *)copies) {
603
- id from = ((NSDictionary *)copies)[to];
604
- if (![to isKindOfClass:[NSString class]] || ![from isKindOfClass:[NSString class]]) {
605
- *reason = @"delta manifest: copies entry is not a string";
606
- return NO;
607
- }
608
- if ([from length] == 0) {
609
- from = to;
610
- }
611
- pushy::delta::CopyOperation operation;
612
- operation.from = PushyToStdString(from);
613
- operation.to = PushyToStdString(to);
614
- NSNumber *expectedCrc = copiesCrc[to];
615
- if ([expectedCrc isKindOfClass:[NSNumber class]]) {
616
- operation.has_expected_crc = true;
617
- operation.expected_crc = (uint32_t)[expectedCrc unsignedLongLongValue];
618
- }
619
- manifest->copies.push_back(operation);
620
- }
621
- }
622
-
623
- // The CLI emits `deletes` as an object keyed by path (Android reads its
624
- // keys); a plain array of paths is accepted as well, like Harmony.
625
- id deletes = json[@"deletes"];
626
- if (!PushyJsonIsAbsent(deletes)) {
627
- if (![deletes isKindOfClass:[NSDictionary class]] && ![deletes isKindOfClass:[NSArray class]]) {
628
- *reason = @"delta manifest: deletes is not an object or array";
629
- return NO;
630
- }
631
- for (id path in deletes) {
632
- if (![path isKindOfClass:[NSString class]]) {
633
- *reason = @"delta manifest: deletes entry is not a string";
634
- return NO;
635
- }
636
- manifest->deletes.push_back(PushyToStdString(path));
637
- }
638
- }
639
- return YES;
640
- }
641
-
642
- // Binary identity under the prefixed key, or — until the first state write
643
- // migrates it — the value an earlier SDK left under the unprefixed one, so an
644
- // installed update survives the SDK upgrade instead of reading as a rebuilt
645
- // binary.
646
- static NSString *PushyBinaryIdentityValue(NSUserDefaults *defaults, NSString *key, NSString *legacyKey) {
647
- return [defaults stringForKey:key] ?: [defaults stringForKey:legacyKey];
648
- }
649
-
650
- static pushy::state::State PushyStateFromDefaults(NSUserDefaults *defaults) {
651
- pushy::state::State state;
652
- state.package_version = PushyToStdString(
653
- PushyBinaryIdentityValue(defaults, paramPackageVersion, legacyParamPackageVersion));
654
- state.build_time = PushyToStdString(
655
- PushyBinaryIdentityValue(defaults, paramBuildTime, legacyParamBuildTime));
656
- NSDictionary *pushyInfo = [defaults dictionaryForKey:keyPushyInfo];
657
- if (pushyInfo != nil) {
658
- state.current_version = PushyToStdString(pushyInfo[paramCurrentVersion]);
659
- state.last_version = PushyToStdString(pushyInfo[paramLastVersion]);
660
- state.first_time = [pushyInfo[paramIsFirstTime] boolValue];
661
- id firstLoadOk = pushyInfo[paramIsFirstLoadOk];
662
- state.first_time_ok = firstLoadOk == nil ? true : [firstLoadOk boolValue];
663
- }
664
- state.rolled_back_version = PushyToStdString([defaults stringForKey:keyRolledBackMarked]);
665
- return state;
666
- }
667
-
668
- static void PushyApplyStateToDefaults(NSUserDefaults *defaults, const pushy::state::State &state) {
669
- PushySetNullableString(defaults, paramPackageVersion, PushyFromStdString(state.package_version));
670
- PushySetNullableString(defaults, paramBuildTime, PushyFromStdString(state.build_time));
671
- // One-time migration: once the prefixed keys hold the identity, the
672
- // legacy ones are retired so a host-app write there is never read again.
673
- [defaults removeObjectForKey:legacyParamPackageVersion];
674
- [defaults removeObjectForKey:legacyParamBuildTime];
675
-
676
- BOOL hasPushyInfo = !state.current_version.empty() || !state.last_version.empty() || state.first_time || !state.first_time_ok;
677
- if (hasPushyInfo) {
678
- NSMutableDictionary *newInfo = [[NSMutableDictionary alloc] init];
679
- if (!state.current_version.empty()) {
680
- newInfo[paramCurrentVersion] = PushyFromStdString(state.current_version);
681
- }
682
- if (!state.last_version.empty()) {
683
- newInfo[paramLastVersion] = PushyFromStdString(state.last_version);
684
- }
685
- newInfo[paramIsFirstTime] = @(state.first_time);
686
- newInfo[paramIsFirstLoadOk] = @(state.first_time_ok);
687
- [defaults setObject:newInfo forKey:keyPushyInfo];
688
- } else {
689
- [defaults removeObjectForKey:keyPushyInfo];
690
- }
691
-
692
- PushySetNullableString(
693
- defaults,
694
- keyRolledBackMarked,
695
- PushyFromStdString(state.rolled_back_version));
696
- }
697
-
698
- // Version switch without acquiring the state lock: the caller must already
699
- // hold it. Lets the cold-start check commit its whole result (version info,
700
- // switch, response cache) inside one lock acquisition, so resetToPackagedBundle
701
- // can never interleave between the generation check and the writes.
702
- static void PushySwitchVersionLocked(NSString *hash) {
703
- NSUserDefaults *defaults = PushyDefaults();
704
- pushy::state::State next = pushy::state::SwitchVersion(
705
- PushyStateFromDefaults(defaults),
706
- PushyToStdString(hash)
707
- );
708
- PushyApplyStateToDefaults(defaults, next);
709
- // Re-enable first-load consumption and rollback checks for the newly selected bundle.
710
- ignoreRollback = false;
711
- }
712
-
713
- @interface RCTPushy ()
714
- - (void)fetchPackage:(PushyType)type
715
- options:(NSDictionary *)options
716
- resolver:(RCTPromiseResolveBlock)resolve
717
- rejecter:(RCTPromiseRejectBlock)reject;
718
- - (void)performFetch:(PushyType)type
719
- options:(NSDictionary *)options
720
- callback:(void (^)(NSError *error))callback;
721
- - (NSError *)restartBridgeWithReason:(NSString *)reason;
722
- - (void)unzipDownloadedPackage:(NSString *)zipFilePath
723
- hash:(NSString *)hash
724
- type:(PushyType)type
725
- originHash:(NSString *)originHash
726
- callback:(void (^)(NSError *error))callback;
727
- - (void)finishDownloadedPackage:(NSString *)hash
728
- type:(PushyType)type
729
- originHash:(NSString *)originHash
730
- callback:(void (^)(NSError *error))callback;
731
- - (void)applyDeltaForHash:(NSString *)hash
732
- type:(PushyType)type
733
- fromBundle:(NSString *)bundleOrigin
734
- source:(NSString *)sourceOrigin
735
- callback:(void (^)(NSError *error))callback;
736
- - (BOOL)switchVersion:(NSString *)hash error:(NSError **)error;
737
- - (BOOL)ensureDirectoryExistsAtPath:(NSString *)path;
738
- + (void)excludeFromBackup:(NSString *)path;
739
- - (void)unzipFileAtPath:(NSString *)path
740
- toDestination:(NSString *)destination
741
- completionHandler:(void (^)(NSError *error))completionHandler;
742
- + (NSString *)downloadDir;
743
- + (NSURL *)binaryBundleURL;
744
- + (NSString *)packageVersion;
745
- + (NSString *)buildTime;
746
- @end
747
-
748
- // Native cold-start update check (NATIVE_CHECKUPDATE_DESIGN §10): runs once
749
- // per process, a few seconds after launch, entirely independent of the app
750
- // bundle — this is what lets a bricked hot update be replaced on the next
751
- // launch. Decisions come from cpp/update_flow_core; this class is IO glue.
752
- @interface RCTPushyOrchestrator : NSObject
753
- + (void)persistConfiguration:(NSString *)config;
754
- + (BOOL)hasRunnableConfig;
755
- + (NSDictionary *)prepareBundle;
756
- + (void)scheduleFromColdStart:(NSString *)launchRolledBackVersion;
757
- #if TARGET_OS_TV && !DEBUG
758
- + (BOOL)restorePurgedLaunch:(NSString *)purgedVersion
759
- rolledBack:(NSString *)launchRolledBackVersion;
760
- #endif
761
- + (void)recordJsRound:(NSString *)config;
762
- + (void)startRoundWithDeadline:(NSTimeInterval)deadlineUptime;
763
- + (void)runOnce:(NSString *)launchRolledBackVersion deadline:(NSTimeInterval)deadlineUptime;
764
- + (void)runHoldRoundWithDeadline:(NSTimeInterval)deadlineUptime;
765
- + (BOOL)commitRoundWithGeneration:(uint64_t)generation
766
- hashInfo:(NSDictionary *)hashInfoEntry
767
- activate:(NSString *)hashToActivate
768
- responseText:(NSString *)responseText
769
- request:(NSString *)requestBody
770
- config:(NSString *)configJson
771
- responseAt:(long long)responseAtSeconds
772
- activated:(BOOL *)activatedOut;
773
- @end
774
-
775
- // One round per process, whoever starts it first — the delayed cold-start
776
- // path or the crash-rescue thread (§11.3). The semaphore lets the rescue
777
- // wait out an in-flight round instead of racing it.
778
- static std::atomic<bool> pushyRoundStarted{false};
779
- static std::atomic<bool> pushyRoundCompleted{false};
780
- // Flipped the moment a crash is being held. JS is dead from that point on,
781
- // so there is no second decision maker: the round force-activates whatever
782
- // it downloads (§11.3).
783
- static std::atomic<bool> pushyCrashHoldActive{false};
784
- static std::atomic<bool> pushyHoldAttempted{false};
785
- // Set when this process's round is the tvOS purged-version restore.
786
- static std::atomic<bool> pushyPurgeRestoreActive{false};
787
- // True while +bundleURL waits for that restore. Only then may the round
788
- // activate what it downloads on its own (JS has not started to decide), since
789
- // the launch resolves the bundle again afterwards. Closed once the wait ends,
790
- // timeout or not; guarded by the state lock that every activation commits
791
- // under, so a commit lands either before the close (and launches) or after it
792
- // (and is left to JS) — never selected behind the packaged bundle's back.
793
- static bool pushyPurgeRestoreWindowOpen = false;
794
- static dispatch_semaphore_t pushyRoundDone;
795
- static NSString *pushyLaunchRolledBackForHold = nil;
796
- // A version this process downloaded but left for JS to activate. If the
797
- // process then crashes, JS will never activate it — the crash handler
798
- // activates it directly (bounded local work, no network). The generation is
799
- // the one the round committed under: a reset that lands afterwards bumps it,
800
- // and the late activation must lose to that reset exactly like the round
801
- // itself would. Guarded by @synchronized (RCTPushyOrchestrator class).
802
- static NSString *pushyUnactivatedHash = nil;
803
- static uint64_t pushyUnactivatedGeneration = 0;
804
- static NSUncaughtExceptionHandler *pushyPreviousExceptionHandler = NULL;
805
- // ≈ process start: scheduleFromColdStart runs during the first bundleURL.
806
- static NSTimeInterval pushyProcessAnchorUptime = 0;
807
- // Config JSON for which JS reported a completed check in this process
808
- // (markJsCheckCompleted). Process-scoped by design. Guarded by
809
- // @synchronized (RCTPushyOrchestrator class).
810
- static NSString *pushyJsCompletedConfig = nil;
811
- // The group supplements (rather than consumes) the crash-rescue semaphore.
812
- static dispatch_group_t pushyHostRoundGroup;
813
- static std::atomic<bool> pushySyncReady{false};
814
- static NSDictionary *pushyHostRoundResult = nil;
815
- static NSString *pushyHostRoundConfig = nil;
816
- static uint64_t pushyHostRoundGeneration = 0;
817
- static std::atomic<uint64_t> pushyNativeConfigGeneration{0};
818
- static uint64_t pushyHostRoundConfigGeneration = 0;
819
-
820
- static const NSTimeInterval kPushyHoldTriggerUptime = 60;
821
- static const NSTimeInterval kPushyHoldBudgetBackgroundThread = 10;
822
- // A held main thread freezes UI teardown; stay well under the watchdog.
823
- static const NSTimeInterval kPushyHoldBudgetMainThread = 3.5;
824
- // The purged-version restore blocks +bundleURL, usually inside
825
- // application:didFinishLaunching: — stay clear of the launch watchdog.
826
- static const NSTimeInterval kPushyPurgeRestoreBudget = 12;
827
-
828
- static void PushyMaybeHoldProcess(void) {
829
- // Once per process; a second crashing thread passes straight through
830
- // instead of waiting behind the first (§11.3: prefer under-rescuing over
831
- // wedging the teardown).
832
- bool expected = false;
833
- if (!pushyHoldAttempted.compare_exchange_strong(expected, true)) {
834
- return;
835
- }
836
- NSTimeInterval uptime = PushyMonotonicNow() - pushyProcessAnchorUptime;
837
- bool roundInFlight = pushyRoundStarted.load() && !pushyRoundCompleted.load();
838
- // Early crashes are the brick signature; a crash with an in-flight round
839
- // is worth finishing regardless of uptime. Everything else is an ordinary
840
- // crash whose UX must not be delayed.
841
- if (uptime >= kPushyHoldTriggerUptime && !roundInFlight) {
842
- return;
843
- }
844
- NSTimeInterval budget = [NSThread isMainThread]
845
- ? kPushyHoldBudgetMainThread
846
- : kPushyHoldBudgetBackgroundThread;
847
- NSTimeInterval deadline = PushyMonotonicNow() + budget;
848
- NSLog(@"RCTPushy -- crash hold: holding process for up to %.1fs "
849
- @"(uptime %.1fs)", budget, uptime);
850
-
851
- // The rescue runs on its own queue and the dying thread only waits with a
852
- // hard timeout: even a deadlocked rescue (a thread that died holding a
853
- // lock — uncaught ObjC exceptions skip @finally) can only delay the
854
- // process death, never prevent it.
855
- dispatch_semaphore_t done = dispatch_semaphore_create(0);
856
- dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0), ^{
51
+ // Host preparation runs off the caller's thread; the completion is delivered
52
+ // on the main queue. Calls run concurrently so each timeout counts from its
53
+ // own call: a serial queue would hold a later prepareBundle(timeout:) behind an
54
+ // earlier call's whole wait. Nothing here needs serializing — claimRound is an
55
+ // atomic compare-exchange and the round group (entered once at setup) releases
56
+ // every waiter when the round ends.
57
+ static void PushyRunHostPreparation(NSDictionary *(^work)(void),
58
+ RCTPushyBundlePreparationCompletion completion) {
59
+ static dispatch_queue_t hostQueue =
60
+ dispatch_queue_create("cn.reactnative.pushy.host-sync", DISPATCH_QUEUE_CONCURRENT);
61
+ dispatch_async(hostQueue, ^{
62
+ NSDictionary *result;
857
63
  @try {
858
- [RCTPushyOrchestrator runHoldRoundWithDeadline:deadline];
859
- } @catch (NSException *exception) {
860
- NSLog(@"RCTPushy -- crash hold failed: %@", exception.reason);
861
- }
862
- dispatch_semaphore_signal(done);
863
- });
864
- if (dispatch_semaphore_wait(done, dispatch_time(DISPATCH_TIME_NOW,
865
- (int64_t)(budget * NSEC_PER_SEC))) != 0) {
866
- NSLog(@"RCTPushy -- crash hold: budget exhausted, letting go");
867
- }
868
- }
869
-
870
- // Crash-moment brick rescue (§11): when the app is dying of an uncaught
871
- // exception during startup (RCTFatal raises NSException in release), the
872
- // process is still alive and JS will never run again — a natural,
873
- // false-positive-free window to finish the cold-start check. The previous
874
- // handler (crash reporters chain the same way) always runs afterwards.
875
- static void PushyCrashHoldExceptionHandler(NSException *exception) {
876
- @try {
877
- PushyMaybeHoldProcess();
878
- } @catch (NSException *inner) {
879
- // The dying process owes the previous handler its turn no matter
880
- // what the rescue did.
881
- }
882
- if (pushyPreviousExceptionHandler != NULL) {
883
- pushyPreviousExceptionHandler(exception);
884
- }
885
- }
886
-
887
- static void PushyInstallCrashHoldHandler(void) {
888
- static dispatch_once_t onceToken;
889
- dispatch_once(&onceToken, ^{
890
- pushyPreviousExceptionHandler = NSGetUncaughtExceptionHandler();
891
- NSSetUncaughtExceptionHandler(&PushyCrashHoldExceptionHandler);
892
- });
893
- }
894
-
895
- // Single-flight for the digest in PushyBundleHashSync: the delayed cold-start
896
- // round (utility queue) and JS getBundleHash (_fileQueue) can both miss the
897
- // cache right after an install; the second caller waits for the first and
898
- // then reads its cache entry instead of hashing the same multi-MB file twice.
899
- static NSLock *PushyBundleHashLock(void) {
900
- static NSLock *lock;
901
- static dispatch_once_t once;
902
- dispatch_once(&once, ^{
903
- lock = [NSLock new];
904
- });
905
- return lock;
906
- }
907
-
908
- static NSString *PushyCachedBundleHash(NSUserDefaults *defaults, NSString *cachedPrefix) {
909
- NSString *cached = [defaults stringForKey:keyBundleHashCache];
910
- if ([cached hasPrefix:cachedPrefix]) {
911
- return [cached substringFromIndex:cachedPrefix.length];
912
- }
913
- return nil;
914
- }
915
-
916
- // Shared by the getBundleHash RCT method and the native cold-start check.
917
- // Returns @"" when unknown. With computeIfMissing the sha256 of the embedded
918
- // bundle is taken on a cache miss (once per install; blocking — call off the
919
- // main thread). Without it only the cache is consulted: a crash rescue on
920
- // the main thread has a 3.5s budget in total, and hashing a large bundle on
921
- // a fresh install would spend all of it before the first request.
922
- static NSString *PushyBundleHashSync(BOOL computeIfMissing) {
923
- NSString *path = [[RCTPushy binaryBundleURL] path];
924
- if (path == nil) {
925
- return @"";
926
- }
927
- NSDictionary *attributes =
928
- [[NSFileManager defaultManager] attributesOfItemAtPath:path error:nil];
929
- if (attributes == nil) {
930
- return @"";
931
- }
932
- NSString *cacheKey = [NSString stringWithFormat:@"%@|%llu|%.0f",
933
- [RCTPushy packageVersion],
934
- attributes.fileSize,
935
- [attributes.fileModificationDate timeIntervalSince1970]];
936
-
937
- NSUserDefaults *defaults = PushyDefaults();
938
- NSString *cachedPrefix = [cacheKey stringByAppendingString:@"|"];
939
- NSString *cached = PushyCachedBundleHash(defaults, cachedPrefix);
940
- if (cached != nil) {
941
- return cached;
942
- }
943
- if (!computeIfMissing) {
944
- return @"";
945
- }
946
-
947
- NSLock *lock = PushyBundleHashLock();
948
- [lock lock];
949
- @try {
950
- // The previous holder may have filled the cache while we waited.
951
- cached = PushyCachedBundleHash(defaults, cachedPrefix);
952
- if (cached != nil) {
953
- return cached;
64
+ result = work();
65
+ } @catch (...) {
66
+ RCTLogWarn(@"RCTPushy -- native host sync failed: %@",
67
+ PushyCaughtReason(@"unknown exception"));
68
+ result = PushyHostResult(@"failed", @"internal_error");
954
69
  }
955
- NSString *hash = PushyFromStdString(
956
- pushy::digest::Sha256File(PushyToStdString(path))) ?: @"";
957
- if (hash.length > 0) {
958
- [defaults setObject:[cachedPrefix stringByAppendingString:hash]
959
- forKey:keyBundleHashCache];
70
+ if (completion != nil) {
71
+ NSDictionary *snapshot = [result copy];
72
+ dispatch_async(dispatch_get_main_queue(), ^{
73
+ completion(snapshot);
74
+ });
960
75
  }
961
- return hash;
962
- } @finally {
963
- [lock unlock];
964
- }
76
+ });
965
77
  }
966
78
 
967
79
  @implementation RCTPushy {
968
- dispatch_queue_t _fileQueue;
969
80
  bool hasListeners;
970
81
  }
971
82
 
@@ -992,129 +103,21 @@ RCT_EXPORT_MODULE(RCTPushy);
992
103
  }
993
104
  #endif
994
105
  return resolvedURL ?: [RCTPushy binaryBundleURL];
106
+ } @catch (...) {
107
+ // An exception here would end every launch the same way; the
108
+ // packaged bundle always loads, and the round scheduled below can
109
+ // still repair the state.
110
+ RCTLogError(@"RCTPushy -- cannot resolve the launch bundle: %@",
111
+ PushyCaughtReason(@"unknown exception"));
112
+ return [RCTPushy binaryBundleURL];
995
113
  } @finally {
996
114
  // State corruption is exactly when the rescue path matters most. If
997
115
  // resolution throws before a snapshot exists, nil safely omits only
998
116
  // this launch's rollback guard instead of disabling the check.
999
- [RCTPushyOrchestrator scheduleFromColdStart:launchRolledBackVersion];
1000
- }
1001
- }
1002
-
1003
- // Resolves the bundle to launch from the persisted state; nil means the
1004
- // packaged bundle. Consumes the launch's one-shot state (first_time), so a
1005
- // second call is only valid after something re-armed it (a switchVersion).
1006
- // purgedVersion is set on tvOS when the installed version's files are gone.
1007
- + (NSURL *)resolveLaunchBundleURL:(NSString **)launchRolledBackVersionOut
1008
- purgedVersion:(NSString **)purgedVersionOut
1009
- {
1010
- __block NSURL *resolvedURL = nil;
1011
- __block NSString *launchRolledBackVersion = nil;
1012
- __block NSString *purgedVersion = nil;
1013
- @try {
1014
- PushyWithStateLock(^{
1015
- NSUserDefaults *defaults = PushyDefaults();
1016
-
1017
- NSString *curPackageVersion = [RCTPushy packageVersion];
1018
- NSString *curBuildTime = [RCTPushy buildTime];
1019
-
1020
- pushy::state::State state = PushyStateFromDefaults(defaults);
1021
- pushy::state::BinaryVersionSyncResult sync = pushy::state::SyncBinaryVersion(
1022
- state,
1023
- PushyToStdString(curPackageVersion),
1024
- PushyToStdString(curBuildTime)
1025
- );
1026
- if (sync.changed) {
1027
- [defaults setObject:@(YES) forKey:KeyPackageUpdatedMarked];
1028
- state = sync.state;
1029
- PushyApplyStateToDefaults(defaults, state);
1030
- }
1031
-
1032
- if (!state.current_version.empty()) {
1033
- std::string const versionBeforeLaunch = state.current_version;
1034
- pushy::state::LaunchDecision decision = pushy::state::ResolveLaunchState(
1035
- state,
1036
- ignoreRollback.load(),
1037
- true
1038
- );
1039
- state = decision.state;
1040
-
1041
- if (decision.did_rollback) {
1042
- // The crash-protection rollback: the new version never called
1043
- // markSuccess. Keep this visible in release logs.
1044
- RCTLogWarn(@"RCTPushy -- version %@ was not marked as successful, rolled back to %@",
1045
- PushyFromStdString(versionBeforeLaunch),
1046
- PushyFromStdString(state.current_version));
1047
- }
1048
- if (decision.did_rollback || decision.consumed_first_time) {
1049
- PushyApplyStateToDefaults(defaults, state);
1050
- }
1051
- if (decision.consumed_first_time) {
1052
- // bundleURL may be called many times, ignore rollbacks before process restarted again.
1053
- ignoreRollback = true;
1054
- [defaults setObject:@(YES) forKey:keyFirstLoadMarked];
1055
- }
1056
-
1057
- NSString *loadVersion = PushyFromStdString(decision.load_version);
1058
- NSString *downloadDir = [RCTPushy downloadDir];
1059
- // Guard the rollback chain against cycles: a corrupted state
1060
- // returning an already-visited version would otherwise spin this
1061
- // loop forever during startup (Android has the same guard).
1062
- NSMutableSet<NSString *> *visitedVersions = [NSMutableSet set];
1063
- while (loadVersion.length && ![visitedVersions containsObject:loadVersion]) {
1064
- [visitedVersions addObject:loadVersion];
1065
- NSString *bundlePath = [[downloadDir stringByAppendingPathComponent:loadVersion] stringByAppendingPathComponent:BUNDLE_FILE_NAME];
1066
- if ([[NSFileManager defaultManager] fileExistsAtPath:bundlePath isDirectory:NULL]) {
1067
- pushyLaunchVersion = loadVersion;
1068
- resolvedURL = [NSURL fileURLWithPath:bundlePath];
1069
- break;
1070
- } else {
1071
- #if TARGET_OS_TV
1072
- // Caches is purgeable on tvOS: a missing bundle is the OS
1073
- // reclaiming space, not a bad release. Everything under
1074
- // rctpushy went with it, so walking back to last_version
1075
- // is pointless, and a rollback mark would make this
1076
- // launch's check refuse to reinstall the version.
1077
- RCTLogWarn(@"RCTPushy -- bundle version %@ was purged by the system", loadVersion);
1078
- purgedVersion = loadVersion;
1079
- state = pushy::state::ForgetPurgedVersions(state);
1080
- PushyApplyStateToDefaults(defaults, state);
1081
- if (decision.consumed_first_time) {
1082
- // The first load being consumed never happens: the
1083
- // packaged bundle must not report isFirstTime.
1084
- ignoreRollback = false;
1085
- [defaults removeObjectForKey:keyFirstLoadMarked];
1086
- }
1087
- break;
1088
- #else
1089
- RCTLogError(@"RCTPushy -- bundle version %@ not found, rolling back", loadVersion);
1090
- state = pushy::state::Rollback(state);
1091
- PushyApplyStateToDefaults(defaults, state);
1092
- loadVersion = PushyFromStdString(state.current_version);
1093
- #endif
1094
- }
1095
- }
1096
- }
1097
- // Capture before constantsToExport consumes this one-shot marker.
1098
- // The delayed native check must never forceBoot the version that this
1099
- // launch just rolled back.
1100
- launchRolledBackVersion = PushyFromStdString(state.rolled_back_version);
117
+ PushyGuard(@"native sync schedule", ^{
118
+ [RCTPushyOrchestrator scheduleFromColdStart:launchRolledBackVersion];
1101
119
  });
1102
- } @finally {
1103
- *launchRolledBackVersionOut = launchRolledBackVersion;
1104
- *purgedVersionOut = purgedVersion;
1105
120
  }
1106
- return resolvedURL;
1107
- }
1108
-
1109
- + (NSString *) rollback {
1110
- __block NSString *currentVersion = nil;
1111
- PushyWithStateLock(^{
1112
- NSUserDefaults *defaults = PushyDefaults();
1113
- pushy::state::State state = pushy::state::Rollback(PushyStateFromDefaults(defaults));
1114
- PushyApplyStateToDefaults(defaults, state);
1115
- currentVersion = PushyFromStdString(state.current_version);
1116
- });
1117
- return currentVersion;
1118
121
  }
1119
122
 
1120
123
  + (void)configure:(NSDictionary<NSString *, id> *)options
@@ -1123,19 +126,16 @@ RCT_EXPORT_MODULE(RCTPushy);
1123
126
  NSError *validationError = nil;
1124
127
  // Snapshot nested mutable caller values before crossing a queue boundary.
1125
128
  NSString *config = RCTPushyNormalizeConfiguration(options, &validationError);
1126
- static dispatch_queue_t configQueue;
1127
- static dispatch_once_t once;
1128
- dispatch_once(&once, ^{
1129
- configQueue = dispatch_queue_create("cn.reactnative.pushy.host-config", DISPATCH_QUEUE_SERIAL);
1130
- });
129
+ static dispatch_queue_t configQueue =
130
+ dispatch_queue_create("cn.reactnative.pushy.host-config", DISPATCH_QUEUE_SERIAL);
1131
131
  dispatch_async(configQueue, ^{
1132
132
  NSError *failure = validationError;
1133
133
  if (config != nil) {
1134
134
  @try {
1135
135
  [RCTPushyOrchestrator persistConfiguration:config];
1136
- } @catch (NSException *exception) {
136
+ } @catch (...) {
1137
137
  failure = PushyErrorWithCode(pushy::error_codes::kFileOperationFailed,
1138
- exception.reason ?: @"Native configuration failed");
138
+ PushyCaughtReason(@"Native configuration failed"));
1139
139
  }
1140
140
  }
1141
141
  if (completion != nil) {
@@ -1148,26 +148,17 @@ RCT_EXPORT_MODULE(RCTPushy);
1148
148
 
1149
149
  + (void)prepareBundleWithCompletion:(RCTPushyBundlePreparationCompletion)completion
1150
150
  {
1151
- static dispatch_queue_t hostQueue;
1152
- static dispatch_once_t once;
1153
- dispatch_once(&once, ^{
1154
- hostQueue = dispatch_queue_create("cn.reactnative.pushy.host-sync", DISPATCH_QUEUE_SERIAL);
1155
- });
1156
- dispatch_async(hostQueue, ^{
1157
- NSDictionary *result;
1158
- @try {
1159
- result = [RCTPushyOrchestrator prepareBundle];
1160
- } @catch (NSException *exception) {
1161
- RCTLogWarn(@"RCTPushy -- native host sync failed: %@", exception.reason);
1162
- result = PushyHostResult(@"failed", @"internal_error", nil, NO);
1163
- }
1164
- if (completion != nil) {
1165
- NSDictionary *snapshot = [result copy];
1166
- dispatch_async(dispatch_get_main_queue(), ^{
1167
- completion(snapshot);
1168
- });
1169
- }
1170
- });
151
+ PushyRunHostPreparation(^NSDictionary * {
152
+ return [RCTPushyOrchestrator prepareBundle];
153
+ }, completion);
154
+ }
155
+
156
+ + (void)prepareBundleWithTimeout:(NSTimeInterval)timeout
157
+ completion:(RCTPushyBundlePreparationCompletion)completion
158
+ {
159
+ PushyRunHostPreparation(^NSDictionary * {
160
+ return [RCTPushyOrchestrator prepareBundleWithin:timeout];
161
+ }, completion);
1171
162
  }
1172
163
 
1173
164
  + (BOOL)requiresMainQueueSetup
@@ -1178,7 +169,9 @@ RCT_EXPORT_MODULE(RCTPushy);
1178
169
  - (NSDictionary *)constantsToExport
1179
170
  {
1180
171
  NSMutableDictionary *ret = [NSMutableDictionary new];
1181
- PushyWithStateLock(^{
172
+ // Whatever was gathered before a failure is still returned: throwing out
173
+ // of getConstants takes the app down.
174
+ PushyGuard(@"constants", ^{ PushyWithStateLock(^{
1182
175
  NSUserDefaults *defaults = PushyDefaults();
1183
176
 
1184
177
  ret[@"downloadRootDir"] = [RCTPushy downloadDir];
@@ -1197,15 +190,8 @@ RCT_EXPORT_MODULE(RCTPushy);
1197
190
  if (currentVersion != nil) {
1198
191
  ret[@"currentVersionInfo"] = [defaults objectForKey:PushyHashInfoKey(currentVersion)];
1199
192
  // bundleSha256 from the install record, for crash-report attribution.
1200
- NSString *bundleSha256 = @"";
1201
- if (PushyIsSafePathComponent(currentVersion)) {
1202
- NSDictionary *record = PushyReadInstallRecord(
1203
- [[RCTPushy downloadDir] stringByAppendingPathComponent:currentVersion]);
1204
- if ([record[@"bundleSha256"] isKindOfClass:[NSString class]]) {
1205
- bundleSha256 = record[@"bundleSha256"];
1206
- }
1207
- }
1208
- ret[@"currentBundleSha256"] = bundleSha256;
193
+ ret[@"currentBundleSha256"] = PushyIsSafePathComponent(currentVersion)
194
+ ? PushyRecordedBundleSha256(PushyVersionDir(currentVersion)) : @"";
1209
195
  }
1210
196
 
1211
197
  if (ret[@"isFirstTime"]) {
@@ -1214,36 +200,18 @@ RCT_EXPORT_MODULE(RCTPushy);
1214
200
 
1215
201
  if (ret[@"rolledBackVersion"] != nil) {
1216
202
  [defaults removeObjectForKey:keyRolledBackMarked];
1217
- [self clearInvalidFiles];
203
+ [RCTPushyInstaller clearInvalidFiles];
1218
204
  }
1219
205
 
1220
- if ([[defaults objectForKey:KeyPackageUpdatedMarked] boolValue]) {
1221
- [defaults removeObjectForKey:KeyPackageUpdatedMarked];
1222
- [self clearInvalidFiles];
206
+ if ([[defaults objectForKey:KeyPackageUpdatedMarked()] boolValue]) {
207
+ [defaults removeObjectForKey:KeyPackageUpdatedMarked()];
208
+ [RCTPushyInstaller clearInvalidFiles];
1223
209
  }
1224
- });
210
+ }); });
1225
211
 
1226
212
  return ret;
1227
213
  }
1228
214
 
1229
- - (instancetype)init
1230
- {
1231
- self = [super init];
1232
- if (self) {
1233
- // One process-wide serial queue, not per-instance: a bridge reload can
1234
- // briefly keep two RCTPushy instances alive, and destructive file work
1235
- // (resetToPackagedBundle's full cleanup) must stay serialized with the
1236
- // other instance's unzip/patch jobs.
1237
- static dispatch_queue_t sharedFileQueue;
1238
- static dispatch_once_t onceToken;
1239
- dispatch_once(&onceToken, ^{
1240
- sharedFileQueue = dispatch_queue_create("cn.reactnative.pushy.file", DISPATCH_QUEUE_SERIAL);
1241
- });
1242
- _fileQueue = sharedFileQueue;
1243
- }
1244
- return self;
1245
- }
1246
-
1247
215
  RCT_EXPORT_METHOD(setUuid:(NSString *)uuid resolver:(RCTPromiseResolveBlock)resolve
1248
216
  rejecter:(RCTPromiseRejectBlock)reject)
1249
217
  {
@@ -1268,10 +236,8 @@ RCT_EXPORT_METHOD(syncNativeConfig:(NSString *)config
1268
236
  PushyRejectError(reject, PushyErrorWithCode(pushy::error_codes::kInvalidOptions, ERROR_OPTIONS));
1269
237
  return;
1270
238
  }
1271
- NSData *data = [config dataUsingEncoding:NSUTF8StringEncoding];
1272
239
  NSError *error = nil;
1273
- id object = data == nil ? nil : [NSJSONSerialization JSONObjectWithData:data options:0 error:&error];
1274
- if (![object isKindOfClass:[NSDictionary class]]) {
240
+ if (PushyJsonDictionary(config, &error) == nil) {
1275
241
  PushyRejectError(reject, PushyErrorWithCode(
1276
242
  pushy::error_codes::kInvalidOptions,
1277
243
  error != nil ? error.localizedDescription : ERROR_OPTIONS));
@@ -1280,9 +246,9 @@ RCT_EXPORT_METHOD(syncNativeConfig:(NSString *)config
1280
246
  @try {
1281
247
  [RCTPushyOrchestrator persistConfiguration:config];
1282
248
  resolve(@true);
1283
- } @catch (NSException *exception) {
1284
- PushyRejectError(reject, PushyErrorWithCode(pushy::error_codes::kFileOperationFailed,
1285
- exception.reason ?: @"Native configuration failed"));
249
+ } @catch (...) {
250
+ PushyRejectCaught(reject, pushy::error_codes::kFileOperationFailed,
251
+ @"Native configuration failed");
1286
252
  }
1287
253
  }
1288
254
 
@@ -1313,13 +279,9 @@ RCT_EXPORT_METHOD(setLocalHashInfo:(NSString *)hash
1313
279
  return;
1314
280
  }
1315
281
 
1316
- NSData *data = [value dataUsingEncoding:NSUTF8StringEncoding];
1317
282
  NSError *error = nil;
1318
- id object = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error];
1319
- if (object && [object isKindOfClass:[NSDictionary class]]) {
1320
- NSUserDefaults *defaults = PushyDefaults();
1321
- [defaults setObject:value forKey:PushyHashInfoKey(hash)];
1322
-
283
+ if (PushyJsonDictionary(value, &error) != nil) {
284
+ [PushyDefaults() setObject:value forKey:PushyHashInfoKey(hash)];
1323
285
  resolve(@true);
1324
286
  } else {
1325
287
  PushyRejectError(reject, PushyErrorWithCode(
@@ -1373,7 +335,7 @@ RCT_EXPORT_METHOD(setNeedUpdate:(NSDictionary *)options
1373
335
  rejecter:(RCTPromiseRejectBlock)reject)
1374
336
  {
1375
337
  NSError *error = nil;
1376
- if (![self switchVersion:PushyOptionString(options, @"hash") error:&error]) {
338
+ if (![RCTPushyInstaller switchVersion:PushyOptionString(options, @"hash") error:&error]) {
1377
339
  PushyRejectError(reject, error);
1378
340
  return;
1379
341
  }
@@ -1386,27 +348,19 @@ RCT_EXPORT_METHOD(reloadUpdate:(NSDictionary *)options
1386
348
  rejecter:(RCTPromiseRejectBlock)reject)
1387
349
  {
1388
350
  NSError *error = nil;
1389
- if (![self switchVersion:PushyOptionString(options, @"hash") error:&error]) {
351
+ if (![RCTPushyInstaller switchVersion:PushyOptionString(options, @"hash") error:&error]) {
1390
352
  PushyRejectError(reject, error);
1391
353
  return;
1392
354
  }
1393
355
 
1394
- NSError *reloadError = [self restartBridgeWithReason:@"pushy restart"];
1395
- if (reloadError != nil) {
1396
- PushyRejectError(reject, reloadError);
1397
- return;
1398
- }
356
+ PushyTriggerReload(@"pushy restart");
1399
357
  resolve(@true);
1400
358
  }
1401
359
 
1402
360
  RCT_EXPORT_METHOD(restartApp:(RCTPromiseResolveBlock)resolve
1403
361
  rejecter:(RCTPromiseRejectBlock)reject)
1404
362
  {
1405
- NSError *reloadError = [self restartBridgeWithReason:@"pushy restartApp"];
1406
- if (reloadError != nil) {
1407
- PushyRejectError(reject, reloadError);
1408
- return;
1409
- }
363
+ PushyTriggerReload(@"pushy restartApp");
1410
364
  resolve(@true);
1411
365
  }
1412
366
 
@@ -1424,8 +378,12 @@ RCT_EXPORT_METHOD(getBundleHash:(RCTPromiseResolveBlock)resolve
1424
378
  // is running. Mirrors the dev behaviour of buildTime.
1425
379
  resolve(@"");
1426
380
  #else
1427
- dispatch_async(_fileQueue, ^{
1428
- resolve(PushyBundleHashSync(YES));
381
+ dispatch_async(PushyFileQueue(), ^{
382
+ __block NSString *hash = @"";
383
+ PushyGuard(@"bundle hash", ^{
384
+ hash = PushyBundleHashSync(YES);
385
+ });
386
+ resolve(hash);
1429
387
  });
1430
388
  #endif
1431
389
  }
@@ -1449,13 +407,12 @@ RCT_EXPORT_METHOD(markSuccess:(RCTPromiseResolveBlock)resolve
1449
407
  }
1450
408
  PushyApplyStateToDefaults(defaults, result.state);
1451
409
  });
1452
- } @catch (NSException *exception) {
1453
- PushyRejectError(reject, PushyErrorWithCode(pushy::error_codes::kMarkSuccessFailed,
1454
- exception.reason ?: @"markSuccess failed"));
410
+ } @catch (...) {
411
+ PushyRejectCaught(reject, pushy::error_codes::kMarkSuccessFailed, @"markSuccess failed");
1455
412
  return;
1456
413
  }
1457
414
 
1458
- [self clearInvalidFiles];
415
+ [RCTPushyInstaller clearInvalidFiles];
1459
416
  resolve(@true);
1460
417
  #endif
1461
418
  }
@@ -1488,49 +445,47 @@ RCT_EXPORT_METHOD(resetToPackagedBundle:(RCTPromiseResolveBlock)resolve
1488
445
  state.build_time = PushyToStdString([RCTPushy buildTime]);
1489
446
  PushyApplyStateToDefaults(defaults, state);
1490
447
 
1491
- for (NSString *key in [defaults dictionaryRepresentation].allKeys) {
1492
- if ([key hasPrefix:keyHashInfo]) {
1493
- [defaults removeObjectForKey:key];
1494
- }
1495
- }
1496
- [defaults removeObjectForKey:keyFirstLoadMarked];
1497
- [defaults removeObjectForKey:KeyPackageUpdatedMarked];
1498
- // A cached response still advertises the version this reset just
1499
- // removed; dropping it stops the JS side from reusing that answer.
1500
- [defaults removeObjectForKey:keyNativeCheckCache];
448
+ // Version info, the first-load and package-updated markers and the
449
+ // response cache (it still advertises the versions this reset just
450
+ // removed), the same on every platform (scripts/contract.ts).
451
+ PushyRemoveKeys(defaults, PushyKeysClearedOnReset(),
452
+ PushyKeyPrefixesClearedOnReset());
1501
453
  ignoreRollback = false;
1502
454
  });
1503
- } @catch (NSException *exception) {
1504
- PushyRejectError(reject, PushyErrorWithCode(pushy::error_codes::kResetFailed,
1505
- exception.reason ?: @"reset failed"));
455
+ } @catch (...) {
456
+ PushyRejectCaught(reject, pushy::error_codes::kResetFailed, @"reset failed");
1506
457
  return;
1507
458
  }
1508
459
 
1509
- dispatch_async(_fileQueue, ^{
460
+ // The reset is done once the state is: the next launch resolves to the
461
+ // packaged bundle. Settle now, as Android and Harmony do; deleting the
462
+ // downloaded versions is best-effort below and never delays the caller
463
+ // (it queues behind any unzip/patch on the file queue). A directory a
464
+ // failed delete leaves behind is no longer referenced by any state, so
465
+ // the regular cleanup removes it later.
466
+ resolve(@true);
467
+
468
+ dispatch_async(PushyFileQueue(), ^{
1510
469
  // maxAgeDays=0: remove every downloaded entry except the running
1511
470
  // version's directory (cleaned up by the next regular cleanup). The
1512
- // state was just wiped, so that is the only name left to keep. The
1513
- // promise settles here, after the cleanup, so a failed wipe reaches
1514
- // JS as RESET_FAILED (Android parity) instead of a warning nobody
1515
- // aggregates.
1516
- pushy::delta::Status status = pushy::delta::CleanupOldEntries(
1517
- PushyToStdString([RCTPushy downloadDir]),
1518
- std::vector<std::string>{PushyToStdString(keepVersion)},
1519
- 0
1520
- );
471
+ // state was just wiped, so that is the only name left to keep.
472
+ pushy::delta::Status status = pushy::delta::Status::Ok();
473
+ try {
474
+ status = pushy::delta::CleanupOldEntries(
475
+ PushyToStdString([RCTPushy downloadDir]),
476
+ std::vector<std::string>{PushyToStdString(keepVersion)},
477
+ 0
478
+ );
479
+ } catch (const std::exception &error) {
480
+ status = pushy::delta::Status::Error(error.what());
481
+ }
1521
482
  if (!status.ok) {
1522
483
  RCTLogWarn(@"Pushy reset cleanup error: %s", status.message.c_str());
1523
- PushyRejectError(reject, PushyErrorWithCode(
1524
- pushy::error_codes::kResetFailed,
1525
- [NSString stringWithUTF8String:status.message.c_str()] ?: @"reset cleanup failed"));
1526
- return;
1527
484
  }
1528
- resolve(@true);
1529
485
  });
1530
486
  }
1531
487
 
1532
488
 
1533
-
1534
489
  #pragma mark - private
1535
490
  - (NSArray<NSString *> *)supportedEvents
1536
491
  {
@@ -1552,9 +507,19 @@ RCT_EXPORT_METHOD(resetToPackagedBundle:(RCTPromiseResolveBlock)resolve
1552
507
  resolver:(RCTPromiseResolveBlock)resolve
1553
508
  rejecter:(RCTPromiseRejectBlock)reject
1554
509
  {
1555
- [self performFetch:type options:options callback:^(NSError *error) {
510
+ NSString *hash = PushyOptionString(options, @"hash") ?: @"";
511
+ void (^progress)(long long, long long) = ^(long long receivedBytes, long long totalBytes) {
512
+ if (self->hasListeners) {
513
+ [self sendEventWithName:EVENT_PROGRESS_DOWNLOAD body:@{
514
+ PARAM_PROGRESS_HASH: hash,
515
+ PARAM_PROGRESS_RECEIVED: @(receivedBytes),
516
+ PARAM_PROGRESS_TOTAL: @(totalBytes),
517
+ }];
518
+ }
519
+ };
520
+ [RCTPushyInstaller fetch:type options:options progress:progress callback:^(NSError *error) {
1556
521
  if (error != nil) {
1557
- if (error.userInfo[PushyErrorCodeKey] == nil) {
522
+ if (error.userInfo[RCTPushyErrorCodeKey] == nil) {
1558
523
  // Unclassified (system/network) errors from the download
1559
524
  // pipeline; keep the original message.
1560
525
  error = PushyErrorWithCode(pushy::error_codes::kDownloadFailed,
@@ -1567,644 +532,6 @@ RCT_EXPORT_METHOD(resetToPackagedBundle:(RCTPromiseResolveBlock)resolve
1567
532
  }];
1568
533
  }
1569
534
 
1570
- // Triggers a JS reload; returns nil when one was scheduled, a RESTART_FAILED
1571
- // error when this host cannot restart. React Native re-resolves the bundle
1572
- // URL itself on reload (RCTHost asks its bundleURLProvider, RCTBridge.setUp
1573
- // re-asks sourceURLForBridge:), so +bundleURL is deliberately NOT called
1574
- // here: it consumes one-shot launch state (first_time, the first-load mark,
1575
- // ignoreRollback), and consuming it for a reload that never happens would
1576
- // make the next cold start roll the freshly switched version back.
1577
- - (NSError *)restartBridgeWithReason:(NSString *)reason
1578
- {
1579
- #if PUSHY_HAS_RELOAD_COMMAND
1580
- dispatch_async(dispatch_get_main_queue(), ^{
1581
- RCTTriggerReloadCommandListeners(reason);
1582
- });
1583
- return nil;
1584
- #else
1585
- // No RCTReloadCommand in this React Native: only the legacy bridge can
1586
- // reload. Under bridgeless the module's bridge is nil or an RCTBridgeProxy
1587
- // whose -reload is a logged no-op (object_getClass avoids messaging the
1588
- // proxy) — report that instead of resolving a restart that never happens.
1589
- RCTBridge *bridge = self.bridge;
1590
- if (bridge == nil || object_getClass(bridge) == NSClassFromString(@"RCTBridgeProxy")) {
1591
- return PushyErrorWithCode(pushy::error_codes::kRestartFailed,
1592
- @"no bridge available to restart (bridgeless host without the RN restart command)");
1593
- }
1594
- dispatch_async(dispatch_get_main_queue(), ^{
1595
- [bridge reload];
1596
- });
1597
- return nil;
1598
- #endif
1599
- }
1600
-
1601
- - (void)performFetch:(PushyType)type options:(NSDictionary *)options callback:(void (^)(NSError *error))callback
1602
- {
1603
- NSString *updateUrl = PushyOptionString(options, PushyOptionUrlKey);
1604
- NSString *hash = PushyOptionString(options, @"hash");
1605
-
1606
- if (PushyStringIsBlank(updateUrl) || !PushyIsSafePathComponent(hash)) {
1607
- callback(PushyErrorWithCode(pushy::error_codes::kInvalidOptions, ERROR_OPTIONS));
1608
- return;
1609
- }
1610
- NSString *originHash = PushyOptionString(options, @"originHash");
1611
- if (type == PushyTypePatchFromPpk && !PushyIsSafePathComponent(originHash)) {
1612
- callback(PushyErrorWithCode(pushy::error_codes::kInvalidOptions, ERROR_OPTIONS));
1613
- return;
1614
- }
1615
-
1616
- NSString *dir = [RCTPushy downloadDir];
1617
- BOOL success = [self ensureDirectoryExistsAtPath:dir];
1618
- if (!success) {
1619
- callback(PushyErrorWithCode(pushy::error_codes::kFileOperationFailed, ERROR_FILE_OPERATION));
1620
- return;
1621
- }
1622
-
1623
- NSString *unzipDir = [dir stringByAppendingPathComponent:hash];
1624
- if (PushyHasCompletedVersionAtPath(unzipDir, hash)) {
1625
- callback(nil);
1626
- return;
1627
- }
1628
-
1629
- NSTimeInterval deadlineUptime = PushyMonotonicNow() + 600;
1630
- NSNumber *configuredDeadline = options[@"deadlineUptime"];
1631
- if ([configuredDeadline isKindOfClass:[NSNumber class]]) {
1632
- deadlineUptime = configuredDeadline.doubleValue;
1633
- }
1634
- if (deadlineUptime <= PushyMonotonicNow()) {
1635
- callback(PushyDownloadDeadlineExpiredError());
1636
- return;
1637
- }
1638
-
1639
- void (^progress)(long long, long long) = ^(long long receivedBytes, long long totalBytes) {
1640
- if (self->hasListeners) {
1641
- [self sendEventWithName:EVENT_PROGRESS_DOWNLOAD body:@{
1642
- PARAM_PROGRESS_HASH:hash,
1643
- PARAM_PROGRESS_RECEIVED:@(receivedBytes),
1644
- PARAM_PROGRESS_TOTAL:@(totalBytes),
1645
- }];
1646
- }
1647
- };
1648
- void (^deferredStart)(void) = ^{
1649
- [self performFetch:type options:options callback:callback];
1650
- };
1651
- PushyDownloadRegistration registration = PushyRegisterDownload(
1652
- hash, type, deadlineUptime, callback, progress, deferredStart);
1653
- if (registration != PushyDownloadRegistrationOwner) {
1654
- RCTLogInfo(
1655
- @"RCTPushy -- %@ in-flight download for %@",
1656
- registration == PushyDownloadRegistrationJoined ? @"join" : @"defer",
1657
- hash);
1658
- return;
1659
- }
1660
-
1661
- NSString *zipFilePath = [dir stringByAppendingPathComponent:[NSString stringWithFormat:@"%@%@",hash, [self zipExtension:type]]];
1662
-
1663
- // On failure, remove the partial version directory like Android/Harmony
1664
- // do: a half-unzipped/half-patched dir leaks disk and could later be
1665
- // mistaken for a complete version. hash is validated non-blank above, so
1666
- // this can never resolve to the download root itself.
1667
- void (^completion)(NSError *) = ^(NSError *error) {
1668
- // Settle every JS/native waiter only after cleanup or the atomic
1669
- // completion marker write has run on the process-wide file queue.
1670
- dispatch_async(self->_fileQueue, ^{
1671
- NSError *finalError = error;
1672
- NSFileManager *fileManager = [NSFileManager defaultManager];
1673
- NSString *staging = PushyStagingDirForVersionDir(unzipDir);
1674
- NSString *artifactSha256 = nil;
1675
- @synchronized (PushyArtifactDigests()) {
1676
- artifactSha256 = PushyArtifactDigests()[hash];
1677
- [PushyArtifactDigests() removeObjectForKey:hash];
1678
- }
1679
- if (finalError == nil) {
1680
- // Two-phase install (cpp/patch_core/install_record.h): the
1681
- // completion record with the final bundle's digest goes into
1682
- // the staging directory, which is then renamed over the
1683
- // version directory in one atomic step.
1684
- NSString *bundlePath = [staging stringByAppendingPathComponent:BUNDLE_FILE_NAME];
1685
- if (![fileManager fileExistsAtPath:bundlePath]) {
1686
- finalError = PushyErrorWithCode(pushy::error_codes::kDeltaFailed,
1687
- @"bundle missing after install");
1688
- } else {
1689
- std::string bundleSha256 = pushy::digest::Sha256File(PushyToStdString(bundlePath));
1690
- NSMutableDictionary *record = [NSMutableDictionary dictionary];
1691
- record[@"schema"] = @(pushy::install_record::kSchema);
1692
- record[@"versionHash"] = hash;
1693
- if (!bundleSha256.empty()) {
1694
- record[@"bundleSha256"] = [NSString stringWithUTF8String:bundleSha256.c_str()];
1695
- }
1696
- if (artifactSha256.length > 0) {
1697
- record[@"artifactSha256"] = artifactSha256;
1698
- }
1699
- NSError *recordError = nil;
1700
- NSData *recordData = [NSJSONSerialization dataWithJSONObject:record options:0 error:&recordError];
1701
- NSString *marker = [staging stringByAppendingPathComponent:VERSION_COMPLETE_FILE_NAME];
1702
- if (recordData == nil || ![recordData writeToFile:marker
1703
- options:NSDataWritingAtomic
1704
- error:&recordError]) {
1705
- finalError = recordError ?: PushyErrorWithCode(
1706
- pushy::error_codes::kFileOperationFailed,
1707
- @"failed to write completion record");
1708
- } else {
1709
- if ([fileManager fileExistsAtPath:unzipDir]) {
1710
- [fileManager removeItemAtPath:unzipDir error:nil];
1711
- }
1712
- NSError *moveError = nil;
1713
- if (![fileManager moveItemAtPath:staging toPath:unzipDir error:&moveError]) {
1714
- finalError = moveError ?: PushyErrorWithCode(
1715
- pushy::error_codes::kFileOperationFailed,
1716
- @"failed to promote staging directory");
1717
- }
1718
- }
1719
- }
1720
- }
1721
- if (finalError != nil) {
1722
- // Only the staging directory is ours to drop: the final
1723
- // version directory is never touched by a failed install.
1724
- [fileManager removeItemAtPath:staging error:nil];
1725
- }
1726
- PushyFinishDownload(hash, finalError);
1727
- });
1728
- };
1729
-
1730
- RCTLogInfo(@"RCTPushy -- download file %@", updateUrl);
1731
- NSTimeInterval timeoutSeconds = deadlineUptime - PushyMonotonicNow();
1732
- if (timeoutSeconds <= 0) {
1733
- completion(PushyDownloadDeadlineExpiredError());
1734
- return;
1735
- }
1736
- [RCTPushyDownloader download:updateUrl
1737
- savePath:zipFilePath
1738
- timeoutInterval:timeoutSeconds
1739
- progressHandler:^(long long receivedBytes, long long totalBytes) {
1740
- PushyReportDownloadProgress(hash, receivedBytes, totalBytes);
1741
- } completionHandler:^(NSString *path, NSError *error) {
1742
- if (error != nil) {
1743
- completion(error);
1744
- return;
1745
- }
1746
- [self unzipDownloadedPackage:zipFilePath
1747
- hash:hash
1748
- type:type
1749
- originHash:originHash
1750
- callback:completion];
1751
- }];
1752
- }
1753
-
1754
- - (void)unzipDownloadedPackage:(NSString *)zipFilePath
1755
- hash:(NSString *)hash
1756
- type:(PushyType)type
1757
- originHash:(NSString *)originHash
1758
- callback:(void (^)(NSError *error))callback
1759
- {
1760
- RCTLogInfo(@"RCTPushy -- unzip file %@", zipFilePath);
1761
- // Everything lands in <hash>.staging; the completion block promotes it.
1762
- NSString *unzipFilePath = PushyStagingDirForVersionDir(
1763
- [[RCTPushy downloadDir] stringByAppendingPathComponent:hash]);
1764
- dispatch_async(_fileQueue, ^{
1765
- // Archive digest for the completion record, taken before the unzip
1766
- // consumes the file (same serial queue, so it runs first).
1767
- std::string digest = pushy::digest::Sha256File(PushyToStdString(zipFilePath));
1768
- @synchronized (PushyArtifactDigests()) {
1769
- PushyArtifactDigests()[hash] = [NSString stringWithUTF8String:digest.c_str()];
1770
- }
1771
- });
1772
- [self unzipFileAtPath:zipFilePath
1773
- toDestination:unzipFilePath
1774
- completionHandler:^(NSError *error) {
1775
- dispatch_async(self->_fileQueue, ^{
1776
- if (error != nil) {
1777
- callback(error);
1778
- return;
1779
- }
1780
- @try {
1781
- [self finishDownloadedPackage:hash type:type originHash:originHash callback:callback];
1782
- } @catch (NSException *exception) {
1783
- // An uncaught exception in a GCD block is fatal (and would
1784
- // trip the crash rescue); a bad package is a PATCH_FAILED.
1785
- callback(PushyErrorWithCode(pushy::error_codes::kDeltaFailed,
1786
- exception.reason ?: @"delta failed"));
1787
- }
1788
- });
1789
- }];
1790
- }
1791
-
1792
- - (void)finishDownloadedPackage:(NSString *)hash
1793
- type:(PushyType)type
1794
- originHash:(NSString *)originHash
1795
- callback:(void (^)(NSError *error))callback
1796
- {
1797
- switch (type) {
1798
- case PushyTypePatchFromPackage:
1799
- [self applyDeltaForHash:hash
1800
- type:type
1801
- fromBundle:[[RCTPushy binaryBundleURL] path]
1802
- source:[[NSBundle mainBundle] resourcePath]
1803
- callback:callback];
1804
- return;
1805
- case PushyTypePatchFromPpk: {
1806
- NSString *lastVersionDir = [[RCTPushy downloadDir] stringByAppendingPathComponent:originHash];
1807
- [self applyDeltaForHash:hash
1808
- type:type
1809
- fromBundle:[lastVersionDir stringByAppendingPathComponent:BUNDLE_FILE_NAME]
1810
- source:lastVersionDir
1811
- callback:callback];
1812
- return;
1813
- }
1814
- case PushyTypeFullDownload:
1815
- callback(nil);
1816
- return;
1817
- }
1818
- }
1819
-
1820
- - (void)applyDeltaForHash:(NSString *)hash
1821
- type:(PushyType)type
1822
- fromBundle:(NSString *)bundleOrigin
1823
- source:(NSString *)sourceOrigin
1824
- callback:(void (^)(NSError *error))callback
1825
- {
1826
- // Patch work happens in the staging directory (two-phase install).
1827
- NSString *unzipDir = PushyStagingDirForVersionDir(
1828
- [[RCTPushy downloadDir] stringByAppendingPathComponent:hash]);
1829
- NSString *sourcePatch = [unzipDir stringByAppendingPathComponent:SOURCE_PATCH_NAME];
1830
- NSString *bundlePatch = [unzipDir stringByAppendingPathComponent:BUNDLE_PATCH_NAME];
1831
-
1832
- NSString *destination = [unzipDir stringByAppendingPathComponent:BUNDLE_FILE_NAME];
1833
- long long manifestBytes = RCTPushyFileSize(sourcePatch);
1834
- if (manifestBytes > pushy::archive_limits::kMaxManifestBytes) {
1835
- callback(PushyErrorWithCode(pushy::error_codes::kDeltaFailed,
1836
- [NSString stringWithFormat:@"delta manifest too large: %lld bytes", manifestBytes]));
1837
- return;
1838
- }
1839
- NSData *data = [NSData dataWithContentsOfFile:sourcePatch];
1840
- if (data == nil) {
1841
- callback(PushyErrorWithCode(pushy::error_codes::kDeltaFailed, @"missing delta manifest"));
1842
- return;
1843
- }
1844
-
1845
- NSError *error = nil;
1846
- id jsonObject = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:&error];
1847
- if (error != nil) {
1848
- // Classify as a patch failure like the sibling manifest branches;
1849
- // unclassified errors would otherwise be tagged DOWNLOAD_FAILED by the
1850
- // fetchPackage fallback even though the download itself succeeded.
1851
- callback(PushyErrorWithCode(pushy::error_codes::kDeltaFailed, error.localizedDescription));
1852
- return;
1853
- }
1854
- if (![jsonObject isKindOfClass:[NSDictionary class]]) {
1855
- callback(PushyErrorWithCode(pushy::error_codes::kDeltaFailed, @"invalid delta manifest"));
1856
- return;
1857
- }
1858
- NSDictionary *json = (NSDictionary *)jsonObject;
1859
- pushy::delta::PatchManifest manifest;
1860
- NSString *manifestReason = nil;
1861
- if (!PushyPatchManifestFromJson(json, &manifest, &manifestReason)) {
1862
- callback(PushyErrorWithCode(pushy::error_codes::kDeltaFailed, manifestReason));
1863
- return;
1864
- }
1865
-
1866
- std::vector<std::string> entryNames;
1867
- if ([[NSFileManager defaultManager] fileExistsAtPath:sourcePatch isDirectory:NULL]) {
1868
- entryNames.push_back(PushyToStdString(SOURCE_PATCH_NAME));
1869
- }
1870
- if ([[NSFileManager defaultManager] fileExistsAtPath:bundlePatch isDirectory:NULL]) {
1871
- entryNames.push_back(PushyToStdString(BUNDLE_PATCH_NAME));
1872
- }
1873
-
1874
- pushy::archive_patch::ArchivePatchPlan plan;
1875
- pushy::delta::Status planStatus = pushy::archive_patch::BuildArchivePatchPlan(
1876
- type == PushyTypePatchFromPackage
1877
- ? pushy::archive_patch::ArchivePatchType::kPatchFromPackage
1878
- : pushy::archive_patch::ArchivePatchType::kPatchFromPpk,
1879
- manifest,
1880
- entryNames,
1881
- &plan
1882
- );
1883
- if (!planStatus.ok) {
1884
- callback(PushyNSErrorFromStatus(planStatus));
1885
- return;
1886
- }
1887
-
1888
- pushy::delta::FileSourcePatchOptions options;
1889
- pushy::delta::Status optionStatus = pushy::archive_patch::BuildFileSourcePatchOptions(
1890
- plan,
1891
- PushyToStdString(sourceOrigin),
1892
- PushyToStdString(unzipDir),
1893
- PushyToStdString(bundleOrigin),
1894
- PushyToStdString(bundlePatch),
1895
- PushyToStdString(destination),
1896
- &options
1897
- );
1898
- if (!optionStatus.ok) {
1899
- callback(PushyNSErrorFromStatus(optionStatus));
1900
- return;
1901
- }
1902
-
1903
- // __diff.json 的 hbcTransform 元数据(HBC 变换域 patch,hdiffv2 轨道):
1904
- // 存在时透传给 patch 内核执行 T(origin) → hpatch → T⁻¹;缺失走现状路径。
1905
- NSDictionary *hbcTransform = json[@"hbcTransform"];
1906
- if ([hbcTransform isKindOfClass:[NSDictionary class]]) {
1907
- NSDictionary *meta = hbcTransform[BUNDLE_PATCH_NAME];
1908
- if ([meta isKindOfClass:[NSDictionary class]]) {
1909
- NSError *metaError = nil;
1910
- NSData *metaData = [NSJSONSerialization dataWithJSONObject:meta options:0 error:&metaError];
1911
- if (metaData != nil && metaError == nil) {
1912
- NSString *metaString = [[NSString alloc] initWithData:metaData encoding:NSUTF8StringEncoding];
1913
- options.bundle_hbc_transform_meta = PushyToStdString(metaString);
1914
- }
1915
- }
1916
- }
1917
-
1918
- pushy::delta::Status status = pushy::delta::ApplyPatchFromFileSource(options);
1919
- if (!status.ok) {
1920
- callback(PushyNSErrorFromStatus(status));
1921
- return;
1922
- }
1923
-
1924
- callback(nil);
1925
- }
1926
-
1927
- - (BOOL)switchVersion:(NSString *)hash error:(NSError **)error
1928
- {
1929
- if (!PushyIsSafePathComponent(hash)) {
1930
- if (error != NULL) {
1931
- *error = PushyErrorWithCode(pushy::error_codes::kInvalidOptions, ERROR_OPTIONS);
1932
- }
1933
- return NO;
1934
- }
1935
-
1936
- __block NSError *switchError = nil;
1937
- PushyWithStateLock(^{
1938
- // Same rule as Android: only a version this SDK recorded as completely
1939
- // installed (bundle + marker) may be activated. Versions activated
1940
- // before markers existed are grandfathered through current/last
1941
- // state; any other markerless directory may be a crash-left partial
1942
- // install or a directory something else put there.
1943
- NSString *versionDir = [[RCTPushy downloadDir] stringByAppendingPathComponent:hash];
1944
- pushy::state::State state = PushyStateFromDefaults(PushyDefaults());
1945
- std::string hashStd = PushyToStdString(hash);
1946
- BOOL legacyActivated = state.current_version == hashStd || state.last_version == hashStd;
1947
- if (!PushyHasCompletedVersionAtPath(versionDir, hash) && !legacyActivated) {
1948
- BOOL hasBundle = [[NSFileManager defaultManager] fileExistsAtPath:
1949
- [versionDir stringByAppendingPathComponent:BUNDLE_FILE_NAME]];
1950
- switchError = PushyErrorWithCode(
1951
- pushy::error_codes::kSwitchVersionFailed,
1952
- [NSString stringWithFormat:@"Bundle version %@ %@", hash,
1953
- hasBundle ? @"is incomplete." : @"not found."]);
1954
- return;
1955
- }
1956
- if (!legacyActivated) {
1957
- // The record's bundle digest must match the bytes on disk before
1958
- // the next launch is pointed at them.
1959
- NSString *reason = PushyVerifyInstallForActivation(versionDir, hash);
1960
- if (reason != nil) {
1961
- switchError = PushyErrorWithCode(pushy::error_codes::kSwitchVersionFailed, reason);
1962
- return;
1963
- }
1964
- }
1965
- PushySwitchVersionLocked(hash);
1966
- });
1967
- if (switchError != nil) {
1968
- if (error != NULL) {
1969
- *error = switchError;
1970
- }
1971
- return NO;
1972
- }
1973
- return YES;
1974
- }
1975
-
1976
- - (BOOL)ensureDirectoryExistsAtPath:(NSString *)path
1977
- {
1978
- // No _fileQueue hop here: that queue also runs multi-second unzip/patch
1979
- // work, and a dispatch_sync onto it would block the whole module method
1980
- // queue for the duration. createDirectoryAtPath is idempotent and
1981
- // thread-safe, so checking inline is fine.
1982
- NSFileManager *fileManager = [NSFileManager defaultManager];
1983
- BOOL isDirectory = NO;
1984
- if ([fileManager fileExistsAtPath:path isDirectory:&isDirectory]) {
1985
- if (isDirectory) {
1986
- // Directories created by older versions never got the flag.
1987
- [RCTPushy excludeFromBackup:path];
1988
- }
1989
- return isDirectory;
1990
- }
1991
-
1992
- NSError *error = nil;
1993
- BOOL success = [fileManager createDirectoryAtPath:path
1994
- withIntermediateDirectories:YES
1995
- attributes:nil
1996
- error:&error];
1997
- if (!success && error != nil) {
1998
- RCTLogWarn(@"Pushy create directory error: %@", error.localizedDescription);
1999
- }
2000
- if (success) {
2001
- [RCTPushy excludeFromBackup:path];
2002
- }
2003
-
2004
- return success;
2005
- }
2006
-
2007
- // Everything under rctpushy is re-downloadable, and Application Support is
2008
- // backed up to iCloud by default — Apple requires such content to be
2009
- // excluded from backups.
2010
- + (void)excludeFromBackup:(NSString *)path
2011
- {
2012
- #if !TARGET_OS_TV // tvOS stores under Caches, which is never backed up.
2013
- NSURL *url = [NSURL fileURLWithPath:path isDirectory:YES];
2014
- NSError *error = nil;
2015
- if (![url setResourceValue:@YES
2016
- forKey:NSURLIsExcludedFromBackupKey
2017
- error:&error]) {
2018
- RCTLogWarn(@"Pushy exclude from backup error: %@", error.localizedDescription);
2019
- }
2020
- #endif
2021
- }
2022
-
2023
- - (void)unzipFileAtPath:(NSString *)path
2024
- toDestination:(NSString *)destination
2025
- completionHandler:(void (^)(NSError *error))completionHandler
2026
- {
2027
- dispatch_async(_fileQueue, ^{
2028
- NSFileManager *fileManager = [NSFileManager defaultManager];
2029
- if ([fileManager fileExistsAtPath:destination]) {
2030
- [fileManager removeItemAtPath:destination error:nil];
2031
- }
2032
-
2033
- // Resource caps before the first byte is extracted
2034
- // (cpp/patch_core/archive_limits.h). SSZipArchive only exposes the
2035
- // entry count up front (zipArchiveWillUnzipArchiveAtPath:zipInfo:),
2036
- // not the uncompressed total, so the disk check uses a 2x-archive
2037
- // heuristic; the delegate below enforces the exact caps entry by
2038
- // entry.
2039
- long long archiveBytes = RCTPushyFileSize(path);
2040
- NSError *preflight = nil;
2041
- if (archiveBytes > pushy::archive_limits::kMaxArchiveBytes) {
2042
- preflight = PushyErrorWithCode(pushy::error_codes::kDeltaFailed,
2043
- [NSString stringWithFormat:@"archive too large: %lld bytes", archiveBytes]);
2044
- } else {
2045
- preflight = PushyEnsureFreeSpace(destination, MAX(0LL, archiveBytes) * 2);
2046
- }
2047
- if (preflight != nil) {
2048
- [fileManager removeItemAtPath:path error:nil];
2049
- [fileManager removeItemAtPath:RCTPushyResumeSidecarPath(path) error:nil];
2050
- if (completionHandler != nil) {
2051
- completionHandler(preflight);
2052
- }
2053
- return;
2054
- }
2055
-
2056
- PushyUnzipGuard *guard = [PushyUnzipGuard new];
2057
- // preserveAttributes:NO — the archive's mode/mtime bits have no use
2058
- // here and are one less thing a hostile package controls.
2059
- [SSZipArchive unzipFileAtPath:path
2060
- toDestination:destination
2061
- preserveAttributes:NO
2062
- overwrite:YES
2063
- nestedZipLevel:0
2064
- password:nil
2065
- error:nil
2066
- delegate:guard
2067
- progressHandler:nil
2068
- completionHandler:^(NSString *archivePath, BOOL succeeded, NSError *error) {
2069
- [fileManager removeItemAtPath:archivePath error:nil];
2070
- // The resume sidecar dies with its archive (§11.4): whether the
2071
- // unzip consumed it or classified it as poisoned, nothing must
2072
- // survive to vouch for bytes that are gone.
2073
- [fileManager removeItemAtPath:RCTPushyResumeSidecarPath(archivePath) error:nil];
2074
- if (completionHandler == nil) {
2075
- return;
2076
- }
2077
-
2078
- NSError *unzipError = error;
2079
- if (guard.violation != nil) {
2080
- unzipError = PushyErrorWithCode(pushy::error_codes::kDeltaFailed, guard.violation);
2081
- } else if (!succeeded && unzipError == nil) {
2082
- unzipError = PushyErrorWithCode(pushy::error_codes::kDeltaFailed, @"unzip failed");
2083
- } else if (unzipError != nil && unzipError.userInfo[PushyErrorCodeKey] == nil) {
2084
- // SSZipArchive's own NSError (corrupt zip, bad magic, ...) has
2085
- // no stable code; without one, fetchPackage's fallback would
2086
- // classify it as DOWNLOAD_FAILED even though the download
2087
- // succeeded — keep the classification deterministic.
2088
- unzipError = PushyErrorWithCode(pushy::error_codes::kDeltaFailed,
2089
- unzipError.localizedDescription ?: @"unzip failed");
2090
- }
2091
- completionHandler(unzipError);
2092
- }];
2093
- });
2094
- }
2095
-
2096
- - (void)clearInvalidFiles
2097
- {
2098
- dispatch_async(_fileQueue, ^{
2099
- // Snapshot the state under the lock, but run the (slow) filesystem
2100
- // cleanup outside of it so state operations are not blocked.
2101
- __block pushy::state::State state;
2102
- __block NSString *launchVersion = nil;
2103
- PushyWithStateLock(^{
2104
- state = PushyStateFromDefaults(PushyDefaults());
2105
- launchVersion = pushyLaunchVersion;
2106
- });
2107
- NSString *downloadDir = [RCTPushy downloadDir];
2108
- // Keep the persisted current/last versions AND the version this
2109
- // process booted from: two switches without a restart would otherwise
2110
- // evict the running bundle while its on-demand assets are still
2111
- // being served.
2112
- pushy::delta::Status status = pushy::delta::CleanupOldEntries(
2113
- PushyToStdString(downloadDir),
2114
- std::vector<std::string>{
2115
- state.current_version,
2116
- state.last_version,
2117
- PushyToStdString(launchVersion),
2118
- },
2119
- 3
2120
- );
2121
- if (!status.ok) {
2122
- RCTLogWarn(@"Pushy cleanup error: %s", status.message.c_str());
2123
- }
2124
- });
2125
- }
2126
-
2127
- - (NSString *)zipExtension:(PushyType)type
2128
- {
2129
- switch (type) {
2130
- case PushyTypePatchFromPackage:
2131
- return PushyIpaDeltaSuffix;
2132
- case PushyTypePatchFromPpk:
2133
- return PushyPpkDeltaSuffix;
2134
- case PushyTypeFullDownload:
2135
- break;
2136
- }
2137
- return @".ppk";
2138
- }
2139
-
2140
- + (NSString *)downloadDir
2141
- {
2142
- #if TARGET_OS_TV
2143
- // tvOS apps cannot write Application Support; Caches is the only
2144
- // writable, non-temporary location, and the system may purge it while
2145
- // the app is not running (see the purged-version path in +bundleURL).
2146
- NSSearchPathDirectory base = NSCachesDirectory;
2147
- #else
2148
- NSSearchPathDirectory base = NSApplicationSupportDirectory;
2149
- #endif
2150
- NSString *directory = [NSSearchPathForDirectoriesInDomains(base, NSUserDomainMask, YES) firstObject];
2151
- return [directory stringByAppendingPathComponent:@"rctpushy"];
2152
- }
2153
-
2154
- + (NSURL *)binaryBundleURL
2155
- {
2156
- return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
2157
- }
2158
-
2159
- + (NSString *)packageVersion
2160
- {
2161
- static NSString *version = nil;
2162
-
2163
- static dispatch_once_t onceToken;
2164
- dispatch_once(&onceToken, ^{
2165
- NSDictionary *infoDictionary = [[NSBundle mainBundle] infoDictionary];
2166
- version = [infoDictionary objectForKey:@"CFBundleShortVersionString"];
2167
- });
2168
- return version;
2169
- }
2170
-
2171
- + (NSString *)buildTime
2172
- {
2173
- #if DEBUG
2174
- return @"0";
2175
- #else
2176
- static NSString *buildTime;
2177
- static dispatch_once_t onceToken;
2178
- dispatch_once(&onceToken, ^{
2179
- // The podspec's s.resource lands in the app bundle for a static
2180
- // library but inside this pod's own framework bundle under
2181
- // use_frameworks!; look in both.
2182
- NSString *stampPath =
2183
- [[NSBundle mainBundle] pathForResource:@"pushy_build_time" ofType:@"txt"]
2184
- ?: [[NSBundle bundleForClass:[RCTPushy class]] pathForResource:@"pushy_build_time"
2185
- ofType:@"txt"];
2186
- NSString *stamp = stampPath == nil ? nil
2187
- : [[NSString stringWithContentsOfFile:stampPath encoding:NSUTF8StringEncoding error:nil]
2188
- stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
2189
- if (stamp.length == 0) {
2190
- // Never empty: with "" SyncBinaryVersion would miss a rebuilt
2191
- // binary that kept its CFBundleShortVersionString, and a stale
2192
- // hot update would outlive an App Store update. Every build
2193
- // rewrites Info.plist, so its mtime (integer seconds) stands in.
2194
- RCTLogWarn(@"RCTPushy -- pushy_build_time.txt %@; using the Info.plist mtime as buildTime",
2195
- stampPath == nil ? @"not found in the main or framework bundle" : @"is empty");
2196
- NSString *infoPlist =
2197
- [[NSBundle mainBundle].bundlePath stringByAppendingPathComponent:@"Info.plist"];
2198
- NSDate *modified = [[NSFileManager defaultManager] attributesOfItemAtPath:infoPlist
2199
- error:nil].fileModificationDate;
2200
- stamp = [NSString stringWithFormat:@"%lld", (long long)[modified timeIntervalSince1970]];
2201
- }
2202
- buildTime = stamp;
2203
- });
2204
- return buildTime;
2205
- #endif
2206
- }
2207
-
2208
535
  #ifdef RCT_NEW_ARCH_ENABLED
2209
536
  - (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:
2210
537
  (const facebook::react::ObjCTurboModule::InitParams &)params
@@ -2214,861 +541,3 @@ RCT_EXPORT_METHOD(resetToPackagedBundle:(RCTPromiseResolveBlock)resolve
2214
541
  #endif
2215
542
 
2216
543
  @end
2217
-
2218
- #pragma mark - native cold-start check orchestration
2219
-
2220
- // Refuses an https→http downgrade before the redirected request — and with
2221
- // it the POST body (appKey, uuid, versions) — leaves the device. The shared
2222
- // session would follow first and only the response could be rejected; ATS
2223
- // blocks plaintext by default, this covers apps that opted out with
2224
- // NSAllowsArbitraryLoads. Same rule as RCTPushyDownloader. Returning nil
2225
- // delivers the 3xx itself as the final response, which the status check in
2226
- // PushyHttpRequest then treats as a failed endpoint.
2227
- @interface PushyRequestRedirectGuard : NSObject <NSURLSessionTaskDelegate>
2228
- @end
2229
-
2230
- @implementation PushyRequestRedirectGuard
2231
-
2232
- - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task
2233
- willPerformHTTPRedirection:(NSHTTPURLResponse *)response
2234
- newRequest:(NSURLRequest *)request
2235
- completionHandler:(void (^)(NSURLRequest *))completionHandler
2236
- {
2237
- NSString *originalScheme = [task.originalRequest.URL.scheme lowercaseString];
2238
- NSString *nextScheme = [request.URL.scheme lowercaseString];
2239
- if ([originalScheme isEqualToString:@"https"] && [nextScheme isEqualToString:@"http"]) {
2240
- completionHandler(nil);
2241
- return;
2242
- }
2243
- completionHandler(request);
2244
- }
2245
-
2246
- @end
2247
-
2248
- // Blocking JSON HTTP round-trip on the orchestrator's utility thread. Returns
2249
- // the response body on 2xx, nil on any failure. The semaphore timeout is a
2250
- // backstop over the request's own timeoutInterval.
2251
- static NSString *PushyHttpRequest(NSString *urlString, NSString *method,
2252
- NSString *body, NSTimeInterval timeout) {
2253
- NSURL *url = [NSURL URLWithString:urlString];
2254
- if (url == nil) {
2255
- return nil;
2256
- }
2257
- NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
2258
- request.HTTPMethod = method;
2259
- request.timeoutInterval = timeout;
2260
- [request setValue:@"application/json" forHTTPHeaderField:@"Accept"];
2261
- if (body != nil) {
2262
- [request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
2263
- request.HTTPBody = [body dataUsingEncoding:NSUTF8StringEncoding];
2264
- }
2265
- dispatch_semaphore_t sem = dispatch_semaphore_create(0);
2266
- __block NSString *result = nil;
2267
- // A delegate session (not the shared one) so the redirect guard sees the
2268
- // 3xx before it is followed; the completion handler still receives the
2269
- // final response.
2270
- NSURLSession *session = [NSURLSession
2271
- sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]
2272
- delegate:[PushyRequestRedirectGuard new]
2273
- delegateQueue:nil];
2274
- NSURLSessionDataTask *task = [session dataTaskWithRequest:request
2275
- completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
2276
- NSHTTPURLResponse *httpResponse =
2277
- [response isKindOfClass:[NSHTTPURLResponse class]]
2278
- ? (NSHTTPURLResponse *)response
2279
- : nil;
2280
- NSInteger status = httpResponse.statusCode;
2281
- if (error == nil && httpResponse != nil && status >= 200 && status < 300
2282
- && data != nil) {
2283
- result = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
2284
- }
2285
- dispatch_semaphore_signal(sem);
2286
- }];
2287
- [task resume];
2288
- // The session retains its delegate until invalidated: let this one task
2289
- // settle, then release everything.
2290
- [session finishTasksAndInvalidate];
2291
- if (dispatch_semaphore_wait(
2292
- sem, dispatch_time(DISPATCH_TIME_NOW,
2293
- (int64_t)((timeout + 5) * NSEC_PER_SEC))) != 0) {
2294
- [task cancel];
2295
- return nil;
2296
- }
2297
- return result;
2298
- }
2299
-
2300
- static NSString *PushyNormalizeEndpointBase(NSString *base) {
2301
- while ([base hasSuffix:@"/"]) {
2302
- base = [base substringToIndex:base.length - 1];
2303
- }
2304
- return base;
2305
- }
2306
-
2307
- static BOOL PushyIsValidResponse(NSString *responseText) {
2308
- if (responseText == nil) {
2309
- return NO;
2310
- }
2311
- // Shared schema rule (update_flow_core::IsValidResponse): a 200 with
2312
- // `{"error": ...}` is a failed endpoint, not a verdict.
2313
- return flowcore::IsValidResponse(PushyToStdString(responseText)) ? YES : NO;
2314
- }
2315
-
2316
- @implementation RCTPushyOrchestrator
2317
-
2318
- + (void)persistConfiguration:(NSString *)config {
2319
- PushyWithStateLock(^{
2320
- NSUserDefaults *defaults = PushyDefaults();
2321
- if ([defaults stringForKey:keyUuid].length == 0) {
2322
- [defaults setObject:[NSUUID UUID].UUIDString forKey:keyUuid];
2323
- }
2324
- if ([[defaults stringForKey:keyNativeConfig] isEqualToString:config]) {
2325
- return;
2326
- }
2327
- // The same generation protects reset and replacement of the request
2328
- // identity/policy, including a late crash-rescue activation.
2329
- pushyResetGeneration.fetch_add(1);
2330
- pushyNativeConfigGeneration.fetch_add(1);
2331
- [defaults setObject:config forKey:keyNativeConfig];
2332
- [defaults removeObjectForKey:keyNativeCheckCache];
2333
- [self recordJsRound:nil];
2334
- });
2335
- if (pushySyncReady.load() && [self hasRunnableConfig]) {
2336
- PushyInstallCrashHoldHandler();
2337
- }
2338
- }
2339
-
2340
- + (BOOL)hasRunnableConfig {
2341
- NSString *json = [PushyDefaults() stringForKey:keyNativeConfig];
2342
- if (json.length == 0) {
2343
- return NO;
2344
- }
2345
- bool ok = false;
2346
- flowjson::Value config = flowjson::Parse(PushyToStdString(json), &ok);
2347
- return ok && config.IsObject() && !config.Get("disabled").Truthy()
2348
- && !config.Get("appKey").AsString().empty();
2349
- }
2350
-
2351
- // Process-wide round state, set up by whichever launch path needs a round
2352
- // first: the delayed cold-start check or the tvOS purged-version restore.
2353
- + (void)prepareProcess:(NSString *)launchRolledBackVersion {
2354
- static dispatch_once_t onceToken;
2355
- dispatch_once(&onceToken, ^{
2356
- pushyRoundDone = dispatch_semaphore_create(0);
2357
- pushyHostRoundGroup = dispatch_group_create();
2358
- dispatch_group_enter(pushyHostRoundGroup);
2359
- pushyProcessAnchorUptime = PushyMonotonicNow();
2360
- pushyLaunchRolledBackForHold = [launchRolledBackVersion copy];
2361
- pushySyncReady.store(true);
2362
- // The crash-hold rescue shares the orchestrator's rollout gate: no
2363
- // persisted config, no handler (§11.3).
2364
- if ([PushyDefaults() stringForKey:keyNativeConfig].length > 0) {
2365
- PushyInstallCrashHoldHandler();
2366
- }
2367
- });
2368
- }
2369
-
2370
- + (void)scheduleFromColdStart:(NSString *)launchRolledBackVersion {
2371
- #if !DEBUG
2372
- // Once per process; a few seconds of delay keeps the check away from the
2373
- // cold-start critical path (§7 R5) — its result targets the NEXT launch.
2374
- // Unless the previous process died mid-round (residual incomplete
2375
- // marker), in which case every launch second counts (§11.4).
2376
- static dispatch_once_t onceToken;
2377
- dispatch_once(&onceToken, ^{
2378
- [self prepareProcess:launchRolledBackVersion];
2379
- NSUserDefaults *defaults = PushyDefaults();
2380
- int64_t delaySeconds =
2381
- [defaults objectForKey:keyNativeCheckIncomplete] != nil ? 0 : 5;
2382
- dispatch_after(dispatch_time(DISPATCH_TIME_NOW, delaySeconds * NSEC_PER_SEC),
2383
- dispatch_get_global_queue(QOS_CLASS_UTILITY, 0), ^{
2384
- if ([self hasJsRound]) {
2385
- // Not consuming the round: a later crash rescue may still
2386
- // need it.
2387
- NSLog(@"RCTPushy -- native sync skipped: JS already queried in this process");
2388
- return;
2389
- }
2390
- [self startRoundWithDeadline:0];
2391
- });
2392
- });
2393
- #endif
2394
- }
2395
-
2396
- #if TARGET_OS_TV && !DEBUG
2397
- // Runs this process's round before the first bundle loads, blocking the
2398
- // caller for at most kPushyPurgeRestoreBudget. The state no longer has a
2399
- // current version, so the server answers with the latest version for the
2400
- // packaged bundle, and the round activates it while the restore window is
2401
- // open. Returns NO when no round can run; otherwise the caller must resolve
2402
- // the launch bundle again: it launches whatever was activated before the
2403
- // window closed, or the packaged bundle. The round stops at the same
2404
- // deadline, keeping a partial download for the next check to resume.
2405
- + (BOOL)restorePurgedLaunch:(NSString *)purgedVersion
2406
- rolledBack:(NSString *)launchRolledBackVersion {
2407
- [self prepareProcess:launchRolledBackVersion];
2408
- if (![self hasRunnableConfig]) {
2409
- RCTLogWarn(@"RCTPushy -- version %@ was purged and no native sync is "
2410
- @"configured; launching the packaged bundle", purgedVersion);
2411
- return NO;
2412
- }
2413
- PushyWithStateLock(^{
2414
- pushyPurgeRestoreWindowOpen = true;
2415
- });
2416
- pushyPurgeRestoreActive.store(true);
2417
- NSTimeInterval deadline = PushyMonotonicNow() + kPushyPurgeRestoreBudget;
2418
- dispatch_semaphore_t done = dispatch_semaphore_create(0);
2419
- dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0), ^{
2420
- [self startRoundWithDeadline:deadline];
2421
- dispatch_semaphore_signal(done);
2422
- });
2423
- BOOL timedOut = dispatch_semaphore_wait(done, dispatch_time(DISPATCH_TIME_NOW,
2424
- (int64_t)(kPushyPurgeRestoreBudget * NSEC_PER_SEC))) != 0;
2425
- // The round's deadline does not bound a slow response (the request
2426
- // timeout is an idle timeout) nor a version already on disk, so the close
2427
- // is what stops a late activation — not the deadline.
2428
- PushyWithStateLock(^{
2429
- pushyPurgeRestoreWindowOpen = false;
2430
- });
2431
- if (timedOut) {
2432
- RCTLogWarn(@"RCTPushy -- purged version %@ restore timed out", purgedVersion);
2433
- } else {
2434
- NSDictionary *result = pushyHostRoundResult;
2435
- RCTLogInfo(@"RCTPushy -- purged version %@ restore: %@ %@ %@", purgedVersion,
2436
- result[@"status"], result[@"reason"], result[@"hash"]);
2437
- }
2438
- return YES;
2439
- }
2440
- #endif
2441
-
2442
- + (NSDictionary *)prepareBundle {
2443
- #if DEBUG
2444
- return PushyHostResult(@"skipped", @"debug", nil, NO);
2445
- #else
2446
- if (!pushySyncReady.load()) {
2447
- return PushyHostResult(@"skipped", @"not_initialized", nil, NO);
2448
- }
2449
- NSString *configJson = [PushyDefaults() stringForKey:keyNativeConfig];
2450
- if (configJson.length == 0) {
2451
- return PushyHostResult(@"skipped", @"not_configured", nil, NO);
2452
- }
2453
- id config = [NSJSONSerialization JSONObjectWithData:
2454
- [configJson dataUsingEncoding:NSUTF8StringEncoding] options:0 error:nil];
2455
- if (![config isKindOfClass:NSDictionary.class]) {
2456
- return PushyHostResult(@"failed", @"invalid_config", nil, NO);
2457
- }
2458
- id disabled = config[@"disabled"];
2459
- if ([disabled respondsToSelector:@selector(boolValue)] && [disabled boolValue]) {
2460
- return PushyHostResult(@"skipped", @"disabled", nil, NO);
2461
- }
2462
- id appKey = config[@"appKey"];
2463
- if (![appKey isKindOfClass:NSString.class] || [appKey length] == 0) {
2464
- return PushyHostResult(@"failed", @"invalid_config", nil, NO);
2465
- }
2466
- [self startRoundWithDeadline:0];
2467
- // A group is broadcast-style. Sharing the rescue semaphore would let one
2468
- // waiter consume the only signal and leave the other waiting forever.
2469
- if (!pushyRoundStarted.load()) {
2470
- return PushyHostResult(@"skipped", @"config_changed", nil, NO);
2471
- }
2472
- dispatch_group_wait(pushyHostRoundGroup, DISPATCH_TIME_FOREVER);
2473
- if (pushyHostRoundConfigGeneration != pushyNativeConfigGeneration.load()
2474
- || ![configJson isEqualToString:pushyHostRoundConfig]
2475
- || ![configJson isEqualToString:[PushyDefaults() stringForKey:keyNativeConfig]]) {
2476
- return PushyHostResult(@"cancelled", @"config_changed", nil, NO);
2477
- }
2478
- if (pushyHostRoundGeneration != pushyResetGeneration.load()) {
2479
- return PushyHostResult(@"cancelled", @"reset", nil, NO);
2480
- }
2481
- return pushyHostRoundResult ?: PushyHostResult(@"failed", @"internal_error", nil, NO);
2482
- #endif
2483
- }
2484
-
2485
- + (void)recordJsRound:(NSString *)config {
2486
- @synchronized (RCTPushyOrchestrator.class) {
2487
- pushyJsCompletedConfig = [config copy];
2488
- }
2489
- }
2490
-
2491
- // True when JS already obtained a valid response in this process for the
2492
- // exact config the native round would use (§10.3): the delayed round is
2493
- // then a duplicate request. Only the scheduled round asks — the crash-rescue
2494
- // path still runs, JS is dead by then.
2495
- + (BOOL)hasJsRound {
2496
- NSString *jsConfig;
2497
- @synchronized (RCTPushyOrchestrator.class) {
2498
- jsConfig = pushyJsCompletedConfig;
2499
- }
2500
- if (jsConfig == nil) {
2501
- return NO;
2502
- }
2503
- NSString *persisted = [PushyDefaults() stringForKey:keyNativeConfig];
2504
- return persisted != nil && [persisted isEqualToString:jsConfig];
2505
- }
2506
-
2507
- // Runs the process's single round on the calling thread if nobody has
2508
- // started it yet. deadlineUptime > 0 (crash rescue) caps every HTTP call and
2509
- // download phase to the remaining budget.
2510
- + (void)startRoundWithDeadline:(NSTimeInterval)deadlineUptime {
2511
- // Missing/disabled configuration is a preflight skip, not a used round.
2512
- if (![self hasRunnableConfig]) {
2513
- return;
2514
- }
2515
- bool expected = false;
2516
- if (!pushyRoundStarted.compare_exchange_strong(expected, true)) {
2517
- return;
2518
- }
2519
- @try {
2520
- [self runOnce:pushyLaunchRolledBackForHold deadline:deadlineUptime];
2521
- } @catch (NSException *exception) {
2522
- // The rescue path must never take the app down with it.
2523
- RCTLogWarn(@"RCTPushy -- native sync crashed: %@", exception.reason);
2524
- pushyHostRoundResult = PushyHostResult(@"failed", @"internal_error", nil, NO);
2525
- } @finally {
2526
- pushyRoundCompleted.store(true);
2527
- dispatch_semaphore_signal(pushyRoundDone);
2528
- dispatch_group_leave(pushyHostRoundGroup);
2529
- }
2530
- }
2531
-
2532
- // Crash-rescue entry (§11.3), called from the handler's worker queue while
2533
- // the uncaught-exception handler holds the dying process. Ensures this
2534
- // process's round runs to completion within the budget, then activates a
2535
- // downloaded-but-unactivated version if one exists — the last chance before
2536
- // the process is gone.
2537
- + (void)runHoldRoundWithDeadline:(NSTimeInterval)deadlineUptime {
2538
- pushyCrashHoldActive.store(true);
2539
- [self startRoundWithDeadline:deadlineUptime];
2540
- if (pushyRoundStarted.load() && !pushyRoundCompleted.load()) {
2541
- NSTimeInterval remaining = deadlineUptime - PushyMonotonicNow();
2542
- if (remaining > 0) {
2543
- dispatch_semaphore_wait(pushyRoundDone, dispatch_time(DISPATCH_TIME_NOW,
2544
- (int64_t)(remaining * NSEC_PER_SEC)));
2545
- }
2546
- }
2547
- [self activatePendingVersion];
2548
- }
2549
-
2550
- // The alert-strategy variant of the §10.7 hole: the round downloaded a fix
2551
- // but deferred activation to JS, and JS is now dead. Activation is local and
2552
- // bounded (a state switch under the commit lock).
2553
- + (void)activatePendingVersion {
2554
- NSString *hash = nil;
2555
- uint64_t generation = 0;
2556
- @synchronized (self) {
2557
- hash = pushyUnactivatedHash;
2558
- generation = pushyUnactivatedGeneration;
2559
- }
2560
- if (hash == nil) {
2561
- return;
2562
- }
2563
- // Unlike Android's switchVersion, PushySwitchVersionLocked does not
2564
- // validate the version directory; never point the next launch at bytes
2565
- // that are gone (e.g. wiped by a reset whose generation bump we would
2566
- // catch below anyway, or by cleanup).
2567
- NSString *versionDir = [[RCTPushy downloadDir] stringByAppendingPathComponent:hash];
2568
- if (!PushyHasCompletedVersionAtPath(versionDir, hash)) {
2569
- NSLog(@"RCTPushy -- crash hold: version %@ no longer on disk, dropping activation", hash);
2570
- return;
2571
- }
2572
- NSDictionary *hashInfoEntry = nil;
2573
- NSString *existingInfo = [PushyDefaults() stringForKey:PushyHashInfoKey(hash)];
2574
- NSData *existingData = [existingInfo dataUsingEncoding:NSUTF8StringEncoding];
2575
- id parsed = existingData == nil ? nil :
2576
- [NSJSONSerialization JSONObjectWithData:existingData options:0 error:nil];
2577
- if ([parsed isKindOfClass:[NSDictionary class]]) {
2578
- NSMutableDictionary *merged = [parsed mutableCopy];
2579
- merged[PushyInfoCrashHold] = @YES;
2580
- hashInfoEntry = @{@"hash": hash, @"info": merged};
2581
- }
2582
- BOOL committed = [self commitRoundWithGeneration:generation
2583
- hashInfo:hashInfoEntry
2584
- activate:hash
2585
- responseText:nil
2586
- request:nil
2587
- config:nil
2588
- responseAt:0
2589
- activated:NULL];
2590
- if (committed) {
2591
- @synchronized (self) {
2592
- pushyUnactivatedHash = nil;
2593
- }
2594
- NSLog(@"RCTPushy -- crash hold: activated downloaded version %@", hash);
2595
- } else {
2596
- NSLog(@"RCTPushy -- crash hold: reset since download, dropping activation");
2597
- }
2598
- }
2599
-
2600
- // A bare module instance drives the existing download/patch pipeline without
2601
- // a bridge: the file queue is process-global and progress events are gated on
2602
- // hasListeners (never set without a bridge).
2603
- + (RCTPushy *)engine {
2604
- static RCTPushy *engine;
2605
- static dispatch_once_t onceToken;
2606
- dispatch_once(&onceToken, ^{
2607
- engine = [[RCTPushy alloc] init];
2608
- });
2609
- return engine;
2610
- }
2611
-
2612
- + (void)runOnce:(NSString *)launchRolledBackVersion deadline:(NSTimeInterval)deadlineUptime {
2613
- const uint64_t resetGeneration = pushyResetGeneration.load();
2614
- pushyHostRoundGeneration = resetGeneration;
2615
- pushyHostRoundConfigGeneration = pushyNativeConfigGeneration.load();
2616
- pushyHostRoundResult = PushyHostResult(@"failed", @"check_failed", nil, NO);
2617
- NSUserDefaults *defaults = PushyDefaults();
2618
- NSString *configJson = [defaults stringForKey:keyNativeConfig];
2619
- pushyHostRoundConfig = [configJson copy];
2620
- if (configJson.length == 0) {
2621
- pushyHostRoundResult = PushyHostResult(@"skipped", @"not_configured", nil, NO);
2622
- return;
2623
- }
2624
- bool ok = false;
2625
- flowjson::Value config = flowjson::Parse(PushyToStdString(configJson), &ok);
2626
- if (!ok || !config.IsObject()) {
2627
- pushyHostRoundResult = PushyHostResult(@"failed", @"invalid_config", nil, NO);
2628
- return;
2629
- }
2630
- if (config.Get("disabled").Truthy()) {
2631
- pushyHostRoundResult = PushyHostResult(@"skipped", @"disabled", nil, NO);
2632
- return;
2633
- }
2634
- NSString *appKey = PushyFromStdString(config.Get("appKey").AsString());
2635
- if (appKey.length == 0) {
2636
- pushyHostRoundResult = PushyHostResult(@"failed", @"invalid_config", nil, NO);
2637
- return;
2638
- }
2639
- // Preserve the interrupted-round breadcrumb and reset-safe atomic commit.
2640
- [defaults setObject:@YES forKey:keyNativeCheckIncomplete];
2641
- @try {
2642
- [self runConfiguredRound:config
2643
- configJson:configJson
2644
- appKey:appKey
2645
- launchRolledBackVersion:launchRolledBackVersion
2646
- resetGeneration:resetGeneration
2647
- deadline:deadlineUptime];
2648
- } @finally {
2649
- [defaults removeObjectForKey:keyNativeCheckIncomplete];
2650
- }
2651
- }
2652
-
2653
- + (void)runConfiguredRound:(const flowjson::Value &)config
2654
- configJson:(NSString *)configJson
2655
- appKey:(NSString *)appKey
2656
- launchRolledBackVersion:(NSString *)launchRolledBackVersion
2657
- resetGeneration:(uint64_t)resetGeneration
2658
- deadline:(NSTimeInterval)deadlineUptime {
2659
- NSUserDefaults *defaults = PushyDefaults();
2660
- NSString *packageVersion =
2661
- PushyFromStdString(config.Get("packageVersion").AsString());
2662
- if (packageVersion.length == 0) {
2663
- packageVersion = [RCTPushy packageVersion];
2664
- }
2665
-
2666
- __block NSString *currentVersion = nil;
2667
- PushyWithStateLock(^{
2668
- pushy::state::State state = PushyStateFromDefaults(PushyDefaults());
2669
- currentVersion = PushyFromStdString(state.current_version);
2670
- });
2671
- NSString *rolledBackVersion = launchRolledBackVersion;
2672
- NSString *uuid = [defaults stringForKey:keyUuid] ?: @"";
2673
-
2674
- flowjson::Value identity = flowjson::Value::Object();
2675
- identity.Set("packageVersion",
2676
- flowjson::Value::String(PushyToStdString(packageVersion)));
2677
- if (currentVersion != nil) {
2678
- identity.Set("currentVersion",
2679
- flowjson::Value::String(PushyToStdString(currentVersion)));
2680
- }
2681
- identity.Set("uuid", flowjson::Value::String(PushyToStdString(uuid)));
2682
- if (rolledBackVersion != nil) {
2683
- identity.Set("rolledBackVersion",
2684
- flowjson::Value::String(PushyToStdString(rolledBackVersion)));
2685
- }
2686
-
2687
- flowjson::Value cInfo = flowjson::Value::Object();
2688
- cInfo.Set("rnu", config.Get("rnu"));
2689
- cInfo.Set("rn", config.Get("rn"));
2690
- cInfo.Set("os", flowjson::Value::String(PushyToStdString([NSString
2691
- stringWithFormat:@"%@ %@", PushyOsName, [[UIDevice currentDevice] systemVersion]])));
2692
- cInfo.Set("uuid", flowjson::Value::String(PushyToStdString(uuid)));
2693
-
2694
- flowjson::Value input = flowjson::Value::Object();
2695
- input.Set("packageVersion", identity.Get("packageVersion"));
2696
- if (currentVersion != nil) {
2697
- input.Set("currentVersion", identity.Get("currentVersion"));
2698
- }
2699
- input.Set("buildTime",
2700
- flowjson::Value::String(PushyToStdString([RCTPushy buildTime])));
2701
- input.Set("cInfo", cInfo);
2702
- input.Set("supportedDiffVersion",
2703
- flowjson::Value::Number(pushy::hbc::kSupportedDiffVersion));
2704
- // Cache-only while a crash is being held (§11.3 budget); computed (and
2705
- // shared with JS getBundleHash) on the ordinary delayed round.
2706
- input.Set("bundleHash",
2707
- flowjson::Value::String(PushyToStdString(
2708
- PushyBundleHashSync(!pushyCrashHoldActive.load()))));
2709
-
2710
- std::string bodyJson =
2711
- flowjson::Stringify(flowcore::BuildRequestBody(input));
2712
- NSString *body = [NSString stringWithUTF8String:bodyJson.c_str()];
2713
- if (body == nil) {
2714
- RCTLogWarn(@"RCTPushy -- native sync: request body is not valid UTF-8");
2715
- pushyHostRoundResult = PushyHostResult(@"failed", @"invalid_request", nil, NO);
2716
- return;
2717
- }
2718
-
2719
- NSString *responseText = [self runQueryRequest:config
2720
- appKey:appKey
2721
- body:body
2722
- deadline:deadlineUptime];
2723
- if (responseText == nil) {
2724
- RCTLogInfo(@"RCTPushy -- native sync: no endpoint reachable, giving up until next launch");
2725
- return;
2726
- }
2727
- long long responseAtSeconds = (long long)[[NSDate date] timeIntervalSince1970];
2728
-
2729
- flowjson::Value decision = flowcore::HandleResponse(
2730
- PushyToStdString(responseText), identity, false,
2731
- config.Get("afterDownload").AsString());
2732
- if (decision.Get("action").AsString() != "download") {
2733
- BOOL committed = [self commitRoundWithGeneration:resetGeneration
2734
- hashInfo:nil
2735
- activate:nil
2736
- responseText:responseText
2737
- request:body
2738
- config:configJson
2739
- responseAt:responseAtSeconds
2740
- activated:NULL];
2741
- pushyHostRoundResult = committed
2742
- ? PushyHostResult(PushyStatusNone, PushyFromStdString(decision.Get("reason").AsString()), nil, NO)
2743
- : PushyHostResult(@"cancelled", @"reset", nil, NO);
2744
- RCTLogInfo(@"RCTPushy -- native sync: nothing to do (%s)",
2745
- decision.Get("reason").AsString().c_str());
2746
- return;
2747
- }
2748
- NSString *hash = PushyFromStdString(decision.Get("hash").AsString());
2749
- if (!PushyIsSafePathComponent(hash)) {
2750
- pushyHostRoundResult = PushyHostResult(@"failed", @"invalid_response", nil, NO);
2751
- return;
2752
- }
2753
-
2754
- NSString *versionDir = [[RCTPushy downloadDir] stringByAppendingPathComponent:hash];
2755
- BOOL downloaded = PushyHasCompletedVersionAtPath(versionDir, hash);
2756
- if (!downloaded) {
2757
- downloaded = [self performAttempts:decision.Get("attempts")
2758
- hash:hash
2759
- originHash:currentVersion
2760
- deadline:deadlineUptime];
2761
- }
2762
- if (!downloaded) {
2763
- BOOL committed = [self commitRoundWithGeneration:resetGeneration
2764
- hashInfo:nil
2765
- activate:nil
2766
- responseText:responseText
2767
- request:body
2768
- config:configJson
2769
- responseAt:responseAtSeconds
2770
- activated:NULL];
2771
- pushyHostRoundResult = committed
2772
- ? PushyHostResult(@"failed", @"download_failed", nil, NO)
2773
- : PushyHostResult(@"cancelled", @"reset", nil, NO);
2774
- return;
2775
- }
2776
-
2777
- // Persist name/description/metaInfo alongside the version, mirroring the
2778
- // JS side's setLocalHashInfo after a successful download.
2779
- const flowjson::Value &info = decision.Get("info");
2780
- NSMutableDictionary *versionInfo = [NSMutableDictionary dictionary];
2781
- for (const char *key : {"name", "description", "metaInfo"}) {
2782
- if (info.Get(key).IsString()) {
2783
- versionInfo[@(key)] = PushyFromStdString(info.Get(key).AsString()) ?: @"";
2784
- }
2785
- }
2786
- // A forceBoot activation is the brick-rescue path: mark it in the
2787
- // persisted info so JS can report force_boot_rescue when this version
2788
- // survives to markSuccess. Only the server-sent directive counts — a
2789
- // silent-strategy activation is ordinary delivery.
2790
- if (info.Get("config").Get("forceBoot").Truthy()) {
2791
- versionInfo[PushyInfoForceBoot] = @YES;
2792
- }
2793
- if (pushyCrashHoldActive.load()) {
2794
- versionInfo[PushyInfoCrashHold] = @YES;
2795
- }
2796
- // Silent strategies or a server-marked forceBoot version (per-version
2797
- // remote override — the brick rescue) activate for the next launch;
2798
- // otherwise activation stays with the JS side (§6/§10.1). Unless a crash
2799
- // is being held: JS is dead, deferring to it would leave the fix on disk
2800
- // forever (§11.3). Or the launch is blocked on reinstalling a version
2801
- // tvOS purged: JS has not started, and the whole point is to boot it —
2802
- // commitRoundWithGeneration drops that one once the restore window closed.
2803
- BOOL activate = decision.Get("activate").Truthy() || pushyCrashHoldActive.load()
2804
- || pushyPurgeRestoreActive.load();
2805
- BOOL activated = NO;
2806
- BOOL committed = [self commitRoundWithGeneration:resetGeneration
2807
- hashInfo:@{@"hash": hash, @"info": versionInfo}
2808
- activate:activate ? hash : nil
2809
- responseText:responseText
2810
- request:body
2811
- config:configJson
2812
- responseAt:responseAtSeconds
2813
- activated:&activated];
2814
- activate = activated;
2815
- if (!committed) {
2816
- RCTLogInfo(@"RCTPushy -- native sync: reset during round, dropping result");
2817
- } else if (activate) {
2818
- @synchronized (self) {
2819
- pushyUnactivatedHash = nil;
2820
- }
2821
- RCTLogInfo(@"RCTPushy -- native sync: downloaded %@ and set for next launch", hash);
2822
- } else {
2823
- // Remembered so a crash later in this process can still activate it
2824
- // (activatePendingVersion) — JS never will.
2825
- @synchronized (self) {
2826
- pushyUnactivatedHash = [hash copy];
2827
- pushyUnactivatedGeneration = resetGeneration;
2828
- }
2829
- RCTLogInfo(@"RCTPushy -- native sync: downloaded %@, activation left to JS", hash);
2830
- }
2831
- pushyHostRoundResult = committed
2832
- ? PushyHostResult(@"downloaded", @"", hash, activate)
2833
- : PushyHostResult(@"cancelled", @"reset", nil, NO);
2834
- }
2835
-
2836
- // Everything a round persists — version info, the activation, the response
2837
- // cache — is written inside ONE state-lock acquisition that first re-checks the
2838
- // reset generation. resetToPackagedBundle bumps that generation under the same
2839
- // lock, so there is no compare-and-act window: either the whole round commits,
2840
- // or the reset wins and none of it does.
2841
- + (BOOL)commitRoundWithGeneration:(uint64_t)generation
2842
- hashInfo:(NSDictionary *)hashInfoEntry
2843
- activate:(NSString *)hashToActivate
2844
- responseText:(NSString *)responseText
2845
- request:(NSString *)requestBody
2846
- config:(NSString *)configJson
2847
- responseAt:(long long)responseAtSeconds
2848
- activated:(BOOL *)activatedOut {
2849
- // responseText is nil for the crash handler's activation-only commit
2850
- // (activatePendingVersion): no round ran, so there is no cache to write.
2851
- NSData *cacheData = nil;
2852
- if (responseText != nil) {
2853
- NSDictionary *cacheEntry = @{
2854
- @"ts": @(responseAtSeconds),
2855
- @"body": responseText,
2856
- @"request": requestBody,
2857
- @"config": configJson,
2858
- };
2859
- cacheData = [NSJSONSerialization dataWithJSONObject:cacheEntry options:0 error:nil];
2860
- }
2861
- __block BOOL committed = NO;
2862
- __block BOOL activated = NO;
2863
- PushyWithStateLock(^{
2864
- if (pushyResetGeneration.load() != generation) {
2865
- return;
2866
- }
2867
- NSString *activation = hashToActivate;
2868
- NSDictionary *info = hashInfoEntry[@"info"];
2869
- // The tvOS purged-version restore round (a round commit, not the
2870
- // crash handler's activation-only one): it may take over this launch
2871
- // only while +bundleURL still waits for it; after that the version is
2872
- // left to JS unless a crash is being held (JS is dead then).
2873
- if (responseText != nil && pushyPurgeRestoreActive.load()) {
2874
- if (pushyPurgeRestoreWindowOpen) {
2875
- if (activation != nil && info != nil) {
2876
- NSMutableDictionary *marked = [info mutableCopy];
2877
- marked[@"purgeRestore"] = @YES;
2878
- info = marked;
2879
- }
2880
- } else if (!pushyCrashHoldActive.load()) {
2881
- activation = nil;
2882
- }
2883
- }
2884
- NSUserDefaults *defaults = PushyDefaults();
2885
- if (hashInfoEntry != nil) {
2886
- NSData *infoData = [NSJSONSerialization dataWithJSONObject:info
2887
- options:0
2888
- error:nil];
2889
- if (infoData != nil) {
2890
- [defaults setObject:[[NSString alloc] initWithData:infoData encoding:NSUTF8StringEncoding]
2891
- forKey:PushyHashInfoKey(hashInfoEntry[@"hash"])];
2892
- }
2893
- }
2894
- if (activation != nil) {
2895
- PushySwitchVersionLocked(activation);
2896
- activated = YES;
2897
- }
2898
- if (cacheData != nil) {
2899
- [defaults setObject:[[NSString alloc] initWithData:cacheData encoding:NSUTF8StringEncoding]
2900
- forKey:keyNativeCheckCache];
2901
- }
2902
- committed = YES;
2903
- });
2904
- if (activatedOut != NULL) {
2905
- *activatedOut = activated;
2906
- }
2907
- return committed;
2908
- }
2909
-
2910
- // Sequential fallback over the ordered candidates (§5.1): one request at a
2911
- // time with its own timeout; after the configured round fails, queryUrls
2912
- // discovery merges remote candidates (excluding the already-tried) for one
2913
- // more round. No hedged race on purpose — this path is latency-insensitive.
2914
- // Per-request timeout, capped to the crash-rescue budget when one is active.
2915
- // <= 0 means the budget is gone and the round must stop issuing requests.
2916
- static NSTimeInterval PushyQueryRequestTimeout(NSTimeInterval deadlineUptime) {
2917
- if (deadlineUptime <= 0) {
2918
- return 10;
2919
- }
2920
- return MIN(10, deadlineUptime - PushyMonotonicNow());
2921
- }
2922
-
2923
- + (NSString *)runQueryRequest:(const flowjson::Value &)config
2924
- appKey:(NSString *)appKey
2925
- body:(NSString *)body
2926
- deadline:(NSTimeInterval)deadlineUptime {
2927
- double sample = arc4random() / 4294967296.0;
2928
- flowjson::Value ordered =
2929
- flowcore::OrderEndpointCandidates(config.Get("endpoints"), sample);
2930
- NSMutableSet<NSString *> *tried = [NSMutableSet set];
2931
- const NSUInteger maxHttpAttempts = 8;
2932
- NSUInteger httpAttempts = 0;
2933
- for (const auto &endpoint : ordered.elements()) {
2934
- NSString *base = PushyNormalizeEndpointBase(
2935
- PushyFromStdString(endpoint.AsString()));
2936
- if (base.length == 0 || [tried containsObject:base]) {
2937
- continue;
2938
- }
2939
- if (httpAttempts++ >= maxHttpAttempts) {
2940
- return nil;
2941
- }
2942
- [tried addObject:base];
2943
- NSTimeInterval timeout = PushyQueryRequestTimeout(deadlineUptime);
2944
- if (timeout <= 0) {
2945
- return nil;
2946
- }
2947
- NSString *response = PushyHttpRequest(
2948
- [NSString stringWithFormat:@"%@%@%@", base, RCTPushyQueryPath(), appKey],
2949
- @"POST", body, timeout);
2950
- if (PushyIsValidResponse(response)) {
2951
- return response;
2952
- }
2953
- }
2954
- for (const auto &queryUrl : config.Get("queryUrls").elements()) {
2955
- NSString *listUrl = PushyFromStdString(queryUrl.AsString());
2956
- if (listUrl == nil) {
2957
- continue;
2958
- }
2959
- if (httpAttempts++ >= maxHttpAttempts) {
2960
- return nil;
2961
- }
2962
- NSTimeInterval listTimeout = PushyQueryRequestTimeout(deadlineUptime);
2963
- if (listTimeout <= 0) {
2964
- return nil;
2965
- }
2966
- NSString *listText = PushyHttpRequest(listUrl, @"GET", nil, listTimeout);
2967
- if (listText == nil) {
2968
- continue;
2969
- }
2970
- bool ok = false;
2971
- flowjson::Value remote = flowjson::Parse(PushyToStdString(listText), &ok);
2972
- if (!ok || !remote.IsArray()) {
2973
- continue;
2974
- }
2975
- for (const auto &endpoint : remote.elements()) {
2976
- NSString *base = PushyNormalizeEndpointBase(
2977
- PushyFromStdString(endpoint.AsString()));
2978
- if (base.length == 0 || [tried containsObject:base]) {
2979
- continue;
2980
- }
2981
- if (httpAttempts++ >= maxHttpAttempts) {
2982
- return nil;
2983
- }
2984
- [tried addObject:base];
2985
- NSTimeInterval timeout = PushyQueryRequestTimeout(deadlineUptime);
2986
- if (timeout <= 0) {
2987
- return nil;
2988
- }
2989
- NSString *response = PushyHttpRequest(
2990
- [NSString stringWithFormat:@"%@%@%@", base, RCTPushyQueryPath(), appKey],
2991
- @"POST", body, timeout);
2992
- if (PushyIsValidResponse(response)) {
2993
- return response;
2994
- }
2995
- }
2996
- break; // one successfully fetched remote list is enough
2997
- }
2998
- return nil;
2999
- }
3000
-
3001
- + (BOOL)performAttempts:(const flowjson::Value &)attempts
3002
- hash:(NSString *)hash
3003
- originHash:(NSString *)originHash
3004
- deadline:(NSTimeInterval)holdDeadline {
3005
- RCTPushy *engine = [self engine];
3006
- // Crash-rescue budget caps every phase; 0 keeps the normal 10min windows.
3007
- NSTimeInterval incrementalDeadline = PushyMonotonicNow() + 600;
3008
- if (holdDeadline > 0) {
3009
- incrementalDeadline = MIN(incrementalDeadline, holdDeadline);
3010
- }
3011
- NSTimeInterval fullDeadline = 0;
3012
- for (const auto &attempt : attempts.elements()) {
3013
- const std::string &type = attempt.Get("type").AsString();
3014
- PushyType pushyType = type == "diff" ? PushyTypePatchFromPpk
3015
- : type == "pdiff" ? PushyTypePatchFromPackage
3016
- : PushyTypeFullDownload;
3017
- if (pushyType == PushyTypePatchFromPpk && originHash.length == 0) {
3018
- continue; // diff patches from the running version; none running
3019
- }
3020
- BOOL isFullAttempt = pushyType == PushyTypeFullDownload;
3021
- if (isFullAttempt && fullDeadline == 0) {
3022
- // diff/pdiff cannot starve the last-resort full download.
3023
- fullDeadline = PushyMonotonicNow() + 600;
3024
- if (holdDeadline > 0) {
3025
- fullDeadline = MIN(fullDeadline, holdDeadline);
3026
- }
3027
- }
3028
- NSTimeInterval deadline = isFullAttempt ? fullDeadline : incrementalDeadline;
3029
- for (const auto &urlValue : attempt.Get("urls").elements()) {
3030
- NSString *url = PushyFromStdString(urlValue.AsString());
3031
- if (url == nil) {
3032
- continue;
3033
- }
3034
- NSTimeInterval remaining = deadline - PushyMonotonicNow();
3035
- if (remaining <= 0) {
3036
- if (isFullAttempt) {
3037
- return NO;
3038
- }
3039
- break;
3040
- }
3041
- NSMutableDictionary *options =
3042
- [@{
3043
- PushyOptionUrlKey: url,
3044
- @"hash": hash,
3045
- @"deadlineUptime": @(deadline),
3046
- } mutableCopy];
3047
- if (pushyType == PushyTypePatchFromPpk) {
3048
- options[@"originHash"] = originHash;
3049
- }
3050
- dispatch_semaphore_t sem = dispatch_semaphore_create(0);
3051
- __block NSError *resultError = nil;
3052
- [engine performFetch:pushyType options:options callback:^(NSError *error) {
3053
- resultError = error;
3054
- dispatch_semaphore_signal(sem);
3055
- }];
3056
- if (dispatch_semaphore_wait(sem, dispatch_time(DISPATCH_TIME_NOW,
3057
- (int64_t)(remaining * NSEC_PER_SEC))) != 0) {
3058
- RCTLogWarn(@"RCTPushy -- native sync: %s attempt timed out", type.c_str());
3059
- if (isFullAttempt) {
3060
- return NO;
3061
- }
3062
- break;
3063
- }
3064
- if (resultError == nil) {
3065
- return YES;
3066
- }
3067
- RCTLogInfo(@"RCTPushy -- native sync: %s attempt failed: %@",
3068
- type.c_str(), resultError.localizedDescription);
3069
- }
3070
- }
3071
- return NO;
3072
- }
3073
-
3074
- @end