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.
- package/README-CN.md +13 -7
- package/README.md +15 -6
- package/android/build.gradle +25 -33
- package/android/jni/Android.mk +18 -24
- package/android/jni/Application.mk +3 -1
- package/android/lib/arm64-v8a/librnpushy.so +0 -0
- package/android/lib/armeabi-v7a/librnpushy.so +0 -0
- package/android/lib/x86/librnpushy.so +0 -0
- package/android/lib/x86_64/librnpushy.so +0 -0
- package/android/proguard.pro +9 -7
- package/android/src/main/java/cn/reactnative/modules/update/ApkInstaller.java +3 -8
- package/android/src/main/java/cn/reactnative/modules/update/ArchiveLimits.java +24 -6
- package/android/src/main/java/cn/reactnative/modules/update/BundlePreparationResult.java +2 -0
- package/android/src/main/java/cn/reactnative/modules/update/BundledResourceCopier.java +50 -51
- package/android/src/main/java/cn/reactnative/modules/update/CleanupKeepNames.java +12 -13
- package/android/src/main/java/cn/reactnative/modules/update/CopyGroupResult.java +3 -0
- package/android/src/main/java/cn/reactnative/modules/update/CrashHold.java +22 -30
- package/android/src/main/java/cn/reactnative/modules/update/DownloadTask.java +284 -452
- package/android/src/main/java/cn/reactnative/modules/update/DownloadTaskParams.java +8 -4
- package/android/src/main/java/cn/reactnative/modules/update/ErrorCodes.java +32 -3
- package/android/src/main/java/cn/reactnative/modules/update/FileOperationException.java +1 -1
- package/android/src/main/java/cn/reactnative/modules/update/FlowBridge.java +39 -8
- package/android/src/main/java/cn/reactnative/modules/update/HttpClients.java +63 -0
- package/android/src/main/java/cn/reactnative/modules/update/HttpUtils.java +0 -45
- package/android/src/main/java/cn/reactnative/modules/update/InstallRecord.java +68 -116
- package/android/src/main/java/cn/reactnative/modules/update/NativeCore.java +10 -2
- package/android/src/main/java/cn/reactnative/modules/update/PatchFailedException.java +1 -1
- package/android/src/main/java/cn/reactnative/modules/update/PushyRuntime.java +53 -62
- package/android/src/main/java/cn/reactnative/modules/update/ReactReloadManager.java +9 -24
- package/android/src/main/java/cn/reactnative/modules/update/ResumePlan.java +31 -0
- package/android/src/main/java/cn/reactnative/modules/update/SafeZipFile.java +42 -80
- package/android/src/main/java/cn/reactnative/modules/update/StateSerialRunner.java +22 -39
- package/android/src/main/java/cn/reactnative/modules/update/SyncCoordinator.java +474 -500
- package/android/src/main/java/cn/reactnative/modules/update/Texts.java +15 -1
- package/android/src/main/java/cn/reactnative/modules/update/Threads.java +44 -0
- package/android/src/main/java/cn/reactnative/modules/update/UpdateContext.java +265 -188
- package/android/src/main/java/cn/reactnative/modules/update/UpdateEventEmitter.java +1 -1
- package/android/src/main/java/cn/reactnative/modules/update/UpdateFileUtils.java +75 -17
- package/android/src/main/java/cn/reactnative/modules/update/{UpdateModuleImpl.java → UpdateModule.java} +114 -96
- package/android/src/main/java/cn/reactnative/modules/update/UpdatePackage.java +16 -19
- package/android/src/main/java/cn/reactnative/modules/update/VersionScopedKeys.java +64 -0
- package/android/src/oldarch/cn/reactnative/modules/update/NativePushySpec.java +71 -0
- package/cpp/android_jni/archive_limits_jni.cpp +83 -0
- package/cpp/android_jni/install_record_jni.cpp +89 -0
- package/cpp/android_jni/jni_log.h +30 -0
- package/cpp/android_jni/jni_natives.h +46 -0
- package/cpp/android_jni/jni_registration.cpp +146 -0
- package/cpp/android_jni/jni_util.h +160 -0
- package/cpp/android_jni/resume_plan_jni.cpp +58 -0
- package/cpp/android_jni/update_core_android.cpp +448 -0
- package/cpp/android_jni/update_flow_jni.cpp +150 -0
- package/{android/jni → cpp/hpatch}/hpatch.c +5 -5
- package/{android/jni → cpp/hpatch}/hpatch.h +4 -1
- package/cpp/patch_core/archive_limits.cpp +194 -0
- package/cpp/patch_core/archive_limits.h +101 -10
- package/cpp/patch_core/archive_patch_core.cpp +98 -15
- package/cpp/patch_core/archive_patch_core.h +51 -0
- package/cpp/patch_core/digest.h +5 -4
- package/cpp/patch_core/error_codes.h +15 -11
- package/cpp/patch_core/install_record.cpp +430 -0
- package/cpp/patch_core/install_record.h +165 -9
- package/cpp/patch_core/native_texts.h +102 -0
- package/cpp/patch_core/patch_core.cpp +62 -16
- package/cpp/patch_core/patch_core.h +27 -7
- package/cpp/patch_core/patch_manifest.cpp +201 -0
- package/cpp/patch_core/patch_manifest.h +74 -0
- package/cpp/patch_core/resume_plan.cpp +226 -0
- package/cpp/patch_core/resume_plan.h +88 -0
- package/cpp/patch_core/state_ops.h +5 -3
- package/cpp/update_flow_core/flow_json.cpp +82 -8
- package/cpp/update_flow_core/flow_json.h +21 -0
- package/cpp/update_flow_core/native_config.cpp +436 -0
- package/cpp/update_flow_core/native_config.h +84 -0
- package/cpp/update_flow_core/native_round.cpp +321 -0
- package/cpp/update_flow_core/native_round.h +94 -0
- package/cpp/update_flow_core/round_engine.cpp +456 -0
- package/cpp/update_flow_core/round_engine.h +106 -0
- package/cpp/update_flow_core/update_flow_core.cpp +5 -14
- package/cpp/update_flow_core/update_flow_core.h +2 -2
- package/harmony/hvigor-plugin.ts +11 -19
- package/harmony/pushy/src/main/cpp/CMakeLists.txt +19 -26
- package/harmony/pushy/src/main/cpp/PushyPackage.h +2 -2
- package/harmony/pushy/src/main/cpp/PushyTurboModule.cpp +57 -94
- package/harmony/pushy/src/main/cpp/pushy.cpp +817 -518
- package/harmony/pushy/src/main/ets/ArchiveLimits.ts +55 -75
- package/harmony/pushy/src/main/ets/DownloadTask.ts +279 -539
- package/harmony/pushy/src/main/ets/DownloadTaskParams.ts +6 -6
- package/harmony/pushy/src/main/ets/ErrorCodes.ts +3 -15
- package/harmony/pushy/src/main/ets/EventHub.ts +1 -1
- package/harmony/pushy/src/main/ets/InstallRecord.ts +54 -151
- package/harmony/pushy/src/main/ets/Logger.ts +20 -34
- package/harmony/pushy/src/main/ets/NativePatchCore.ts +150 -41
- package/harmony/pushy/src/main/ets/PushyConfiguration.ts +8 -85
- package/harmony/pushy/src/main/ets/PushyFileJSBundleProvider.ets +8 -3
- package/harmony/pushy/src/main/ets/PushyTurboModule.ts +11 -25
- package/harmony/pushy/src/main/ets/SaveFile.ts +99 -18
- package/harmony/pushy/src/main/ets/SyncCoordinator.ts +291 -543
- package/harmony/pushy/src/main/ets/Texts.ts +8 -7
- package/harmony/pushy/src/main/ets/UpdateContext.ts +245 -152
- package/harmony/pushy.har +0 -0
- package/ios/RCTPushy/RCTPushy+Launch.mm +190 -0
- package/ios/RCTPushy/RCTPushy.h +12 -0
- package/ios/RCTPushy/RCTPushy.mm +135 -2666
- package/ios/RCTPushy/RCTPushyCommon.h +28 -0
- package/ios/RCTPushy/RCTPushyConfiguration.h +0 -8
- package/ios/RCTPushy/RCTPushyConfiguration.mm +40 -123
- package/ios/RCTPushy/RCTPushyDownloader.mm +152 -198
- package/ios/RCTPushy/RCTPushyInstallRecord.mm +101 -0
- package/ios/RCTPushy/RCTPushyInstaller.mm +732 -0
- package/ios/RCTPushy/RCTPushyInternal.h +185 -0
- package/ios/RCTPushy/RCTPushyOrchestrator.mm +969 -0
- package/ios/RCTPushy/RCTPushyState.mm +382 -0
- package/package.json +46 -25
- package/react-native-update.podspec +73 -82
- package/src/client.ts +126 -1633
- package/src/clientActivation.ts +324 -0
- package/src/clientCheck.ts +405 -0
- package/src/clientDownload.ts +448 -0
- package/src/clientNativeConfig.ts +171 -0
- package/src/clientReporter.ts +398 -0
- package/src/clientState.ts +51 -0
- package/src/context.ts +1 -1
- package/src/core.ts +39 -10
- package/src/endpoint.ts +22 -8
- package/src/error.ts +18 -35
- package/src/i18n.ts +18 -83
- package/src/locales/en.ts +1 -5
- package/src/locales/zh.ts +1 -5
- package/src/provider.tsx +86 -67
- package/src/telemetry.ts +0 -13
- package/src/type.ts +0 -15
- package/src/updateFlowCore.ts +116 -10
- package/src/utils.ts +49 -42
- package/android/jni/HDiffPatch/.travis.yml +0 -33
- package/android/jni/HDiffPatch/CHANGELOG.md +0 -153
- package/android/jni/HDiffPatch/Makefile +0 -157
- package/android/jni/HDiffPatch/_atosize.h +0 -83
- package/android/jni/HDiffPatch/_clock_for_demo.h +0 -47
- package/android/jni/HDiffPatch/_dir_ignore.h +0 -191
- package/android/jni/HDiffPatch/builds/android_ndk_jni_mk/Android.mk +0 -37
- package/android/jni/HDiffPatch/builds/android_ndk_jni_mk/Application.mk +0 -7
- package/android/jni/HDiffPatch/builds/android_ndk_jni_mk/build_libs.bat +0 -1
- package/android/jni/HDiffPatch/builds/android_ndk_jni_mk/build_libs.sh +0 -1
- package/android/jni/HDiffPatch/builds/android_ndk_jni_mk/hpatch.c +0 -18
- package/android/jni/HDiffPatch/builds/android_ndk_jni_mk/hpatch.h +0 -20
- package/android/jni/HDiffPatch/builds/android_ndk_jni_mk/hpatch_jni.c +0 -28
- package/android/jni/HDiffPatch/builds/android_ndk_jni_mk/java/com/github/sisong/HPatch.java +0 -8
- package/android/jni/HDiffPatch/builds/codeblocks/HDiffPatch.workspace +0 -8
- package/android/jni/HDiffPatch/builds/codeblocks/HDiffZ.cbp +0 -219
- package/android/jni/HDiffPatch/builds/codeblocks/HPatchZ.cbp +0 -156
- package/android/jni/HDiffPatch/builds/codeblocks/unitTest.cbp +0 -65
- package/android/jni/HDiffPatch/builds/vc/HDiffPatch.sln +0 -105
- package/android/jni/HDiffPatch/builds/vc/HDiffZ.vcxproj +0 -237
- package/android/jni/HDiffPatch/builds/vc/HDiffZ.vcxproj.user +0 -3
- package/android/jni/HDiffPatch/builds/vc/HPatchZ.vcxproj +0 -318
- package/android/jni/HDiffPatch/builds/vc/HPatchZ.vcxproj.user +0 -3
- package/android/jni/HDiffPatch/builds/vc/unitTest.vcxproj +0 -182
- package/android/jni/HDiffPatch/builds/vc/unitTest.vcxproj.user +0 -3
- package/android/jni/HDiffPatch/builds/xcode/HDiffPatch.xcworkspace/contents.xcworkspacedata +0 -30
- package/android/jni/HDiffPatch/builds/xcode/HDiffPatch.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +0 -8
- package/android/jni/HDiffPatch/builds/xcode/bestParams.xcodeproj/project.pbxproj +0 -408
- package/android/jni/HDiffPatch/builds/xcode/bestParams.xcodeproj/project.xcworkspace/contents.xcworkspacedata +0 -7
- package/android/jni/HDiffPatch/builds/xcode/hdiffz.xcodeproj/project.pbxproj +0 -573
- package/android/jni/HDiffPatch/builds/xcode/hdiffz.xcodeproj/project.xcworkspace/contents.xcworkspacedata +0 -7
- package/android/jni/HDiffPatch/builds/xcode/hdiffz.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +0 -8
- package/android/jni/HDiffPatch/builds/xcode/hpatchz.xcodeproj/project.pbxproj +0 -396
- package/android/jni/HDiffPatch/builds/xcode/hpatchz.xcodeproj/project.xcworkspace/contents.xcworkspacedata +0 -7
- package/android/jni/HDiffPatch/builds/xcode/lz4.xcodeproj/project.pbxproj +0 -268
- package/android/jni/HDiffPatch/builds/xcode/lz4.xcodeproj/project.xcworkspace/contents.xcworkspacedata +0 -7
- package/android/jni/HDiffPatch/builds/xcode/lzma.xcodeproj/project.pbxproj +0 -361
- package/android/jni/HDiffPatch/builds/xcode/lzma.xcodeproj/project.xcworkspace/contents.xcworkspacedata +0 -7
- package/android/jni/HDiffPatch/builds/xcode/testHashClash.xcodeproj/project.pbxproj +0 -301
- package/android/jni/HDiffPatch/builds/xcode/testHashClash.xcodeproj/project.xcworkspace/contents.xcworkspacedata +0 -7
- package/android/jni/HDiffPatch/builds/xcode/testHashClash.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +0 -8
- package/android/jni/HDiffPatch/builds/xcode/unitTest.xcodeproj/project.pbxproj +0 -411
- package/android/jni/HDiffPatch/builds/xcode/unitTest.xcodeproj/project.xcworkspace/contents.xcworkspacedata +0 -7
- package/android/jni/HDiffPatch/builds/xcode/zstd.xcodeproj/project.pbxproj +0 -504
- package/android/jni/HDiffPatch/builds/xcode/zstd.xcodeproj/project.xcworkspace/contents.xcworkspacedata +0 -7
- package/android/jni/HDiffPatch/checksum_plugin_demo.h +0 -344
- package/android/jni/HDiffPatch/compress_parallel.cpp +0 -166
- package/android/jni/HDiffPatch/compress_parallel.h +0 -59
- package/android/jni/HDiffPatch/compress_plugin_demo.h +0 -1152
- package/android/jni/HDiffPatch/dirDiffPatch/dir_diff/dir_diff.cpp +0 -797
- package/android/jni/HDiffPatch/dirDiffPatch/dir_diff/dir_diff.h +0 -65
- package/android/jni/HDiffPatch/dirDiffPatch/dir_diff/dir_diff_tools.cpp +0 -200
- package/android/jni/HDiffPatch/dirDiffPatch/dir_diff/dir_diff_tools.h +0 -178
- package/android/jni/HDiffPatch/dirDiffPatch/dir_diff/dir_manifest.cpp +0 -357
- package/android/jni/HDiffPatch/dirDiffPatch/dir_diff/dir_manifest.h +0 -87
- package/android/jni/HDiffPatch/dirDiffPatch/dir_diff/file_for_dirDiff.h +0 -151
- package/android/jni/HDiffPatch/dirDiffPatch/dir_patch/dir_patch.c +0 -770
- package/android/jni/HDiffPatch/dirDiffPatch/dir_patch/dir_patch.h +0 -236
- package/android/jni/HDiffPatch/dirDiffPatch/dir_patch/dir_patch_private.h +0 -47
- package/android/jni/HDiffPatch/dirDiffPatch/dir_patch/dir_patch_tools.c +0 -112
- package/android/jni/HDiffPatch/dirDiffPatch/dir_patch/dir_patch_tools.h +0 -53
- package/android/jni/HDiffPatch/dirDiffPatch/dir_patch/new_dir_output.c +0 -331
- package/android/jni/HDiffPatch/dirDiffPatch/dir_patch/new_dir_output.h +0 -144
- package/android/jni/HDiffPatch/dirDiffPatch/dir_patch/new_stream.c +0 -176
- package/android/jni/HDiffPatch/dirDiffPatch/dir_patch/new_stream.h +0 -84
- package/android/jni/HDiffPatch/dirDiffPatch/dir_patch/ref_stream.c +0 -146
- package/android/jni/HDiffPatch/dirDiffPatch/dir_patch/ref_stream.h +0 -61
- package/android/jni/HDiffPatch/dirDiffPatch/dir_patch/res_handle_limit.c +0 -152
- package/android/jni/HDiffPatch/dirDiffPatch/dir_patch/res_handle_limit.h +0 -76
- package/android/jni/HDiffPatch/hdiffz.cpp +0 -1570
- package/android/jni/HDiffPatch/hpatch_dir_listener.h +0 -290
- package/android/jni/HDiffPatch/hpatchz.c +0 -1221
- package/android/jni/HDiffPatch/libHDiffPatch/HDiff/diff.cpp +0 -1025
- package/android/jni/HDiffPatch/libHDiffPatch/HDiff/diff.h +0 -116
- package/android/jni/HDiffPatch/libHDiffPatch/HDiff/diff_types.h +0 -72
- package/android/jni/HDiffPatch/libHDiffPatch/HDiff/private_diff/bytes_rle.cpp +0 -213
- package/android/jni/HDiffPatch/libHDiffPatch/HDiff/private_diff/bytes_rle.h +0 -63
- package/android/jni/HDiffPatch/libHDiffPatch/HDiff/private_diff/compress_detect.cpp +0 -170
- package/android/jni/HDiffPatch/libHDiffPatch/HDiff/private_diff/compress_detect.h +0 -86
- package/android/jni/HDiffPatch/libHDiffPatch/HDiff/private_diff/libdivsufsort/config.h +0 -89
- package/android/jni/HDiffPatch/libHDiffPatch/HDiff/private_diff/libdivsufsort/divsufsort.c +0 -13
- package/android/jni/HDiffPatch/libHDiffPatch/HDiff/private_diff/libdivsufsort/divsufsort.c.inc.h +0 -398
- package/android/jni/HDiffPatch/libHDiffPatch/HDiff/private_diff/libdivsufsort/divsufsort.h +0 -194
- package/android/jni/HDiffPatch/libHDiffPatch/HDiff/private_diff/libdivsufsort/divsufsort64.c +0 -13
- package/android/jni/HDiffPatch/libHDiffPatch/HDiff/private_diff/libdivsufsort/divsufsort64.h +0 -204
- package/android/jni/HDiffPatch/libHDiffPatch/HDiff/private_diff/libdivsufsort/divsufsort_private.h +0 -207
- package/android/jni/HDiffPatch/libHDiffPatch/HDiff/private_diff/libdivsufsort/sssort.c.inc.h +0 -815
- package/android/jni/HDiffPatch/libHDiffPatch/HDiff/private_diff/libdivsufsort/trsort.c.inc.h +0 -586
- package/android/jni/HDiffPatch/libHDiffPatch/HDiff/private_diff/libdivsufsort/utils.c.inc.h +0 -381
- package/android/jni/HDiffPatch/libHDiffPatch/HDiff/private_diff/limit_mem_diff/adler_roll.c +0 -352
- package/android/jni/HDiffPatch/libHDiffPatch/HDiff/private_diff/limit_mem_diff/adler_roll.h +0 -137
- package/android/jni/HDiffPatch/libHDiffPatch/HDiff/private_diff/limit_mem_diff/bloom_filter.h +0 -137
- package/android/jni/HDiffPatch/libHDiffPatch/HDiff/private_diff/limit_mem_diff/covers.h +0 -70
- package/android/jni/HDiffPatch/libHDiffPatch/HDiff/private_diff/limit_mem_diff/digest_matcher.cpp +0 -628
- package/android/jni/HDiffPatch/libHDiffPatch/HDiff/private_diff/limit_mem_diff/digest_matcher.h +0 -87
- package/android/jni/HDiffPatch/libHDiffPatch/HDiff/private_diff/limit_mem_diff/stream_serialize.cpp +0 -363
- package/android/jni/HDiffPatch/libHDiffPatch/HDiff/private_diff/limit_mem_diff/stream_serialize.h +0 -139
- package/android/jni/HDiffPatch/libHDiffPatch/HDiff/private_diff/mem_buf.h +0 -68
- package/android/jni/HDiffPatch/libHDiffPatch/HDiff/private_diff/pack_uint.h +0 -115
- package/android/jni/HDiffPatch/libHDiffPatch/HDiff/private_diff/suffix_string.cpp +0 -342
- package/android/jni/HDiffPatch/libHDiffPatch/HDiff/private_diff/suffix_string.h +0 -94
- package/android/jni/HDiffPatch/libHDiffPatch/HPatch/checksum_plugin.h +0 -52
- package/android/jni/HDiffPatch/libParallel/parallel_channel.cpp +0 -174
- package/android/jni/HDiffPatch/libParallel/parallel_channel.h +0 -73
- package/android/jni/HDiffPatch/libParallel/parallel_import.cpp +0 -367
- package/android/jni/HDiffPatch/libParallel/parallel_import.h +0 -92
- package/android/jni/lzma/Asm/arm/7zCrcOpt.asm +0 -100
- package/android/jni/lzma/Asm/x86/7zAsm.asm +0 -147
- package/android/jni/lzma/Asm/x86/7zCrcOpt.asm +0 -147
- package/android/jni/lzma/Asm/x86/AesOpt.asm +0 -237
- package/android/jni/lzma/Asm/x86/LzmaDecOpt.asm +0 -1258
- package/android/jni/lzma/Asm/x86/XzCrc64Opt.asm +0 -205
- package/android/jni/lzma/C/7z.h +0 -202
- package/android/jni/lzma/C/7zAlloc.c +0 -80
- package/android/jni/lzma/C/7zAlloc.h +0 -19
- package/android/jni/lzma/C/7zArcIn.c +0 -1771
- package/android/jni/lzma/C/7zBuf.c +0 -36
- package/android/jni/lzma/C/7zBuf.h +0 -35
- package/android/jni/lzma/C/7zBuf2.c +0 -52
- package/android/jni/lzma/C/7zCrc.c +0 -128
- package/android/jni/lzma/C/7zCrc.h +0 -25
- package/android/jni/lzma/C/7zCrcOpt.c +0 -115
- package/android/jni/lzma/C/7zDec.c +0 -591
- package/android/jni/lzma/C/7zFile.c +0 -286
- package/android/jni/lzma/C/7zFile.h +0 -83
- package/android/jni/lzma/C/7zStream.c +0 -176
- package/android/jni/lzma/C/7zVersion.h +0 -27
- package/android/jni/lzma/C/7zVersion.rc +0 -55
- package/android/jni/lzma/C/Aes.c +0 -306
- package/android/jni/lzma/C/Aes.h +0 -38
- package/android/jni/lzma/C/AesOpt.c +0 -184
- package/android/jni/lzma/C/Alloc.c +0 -455
- package/android/jni/lzma/C/Alloc.h +0 -51
- package/android/jni/lzma/C/Bcj2.c +0 -257
- package/android/jni/lzma/C/Bcj2.h +0 -146
- package/android/jni/lzma/C/Bcj2Enc.c +0 -311
- package/android/jni/lzma/C/Bra.c +0 -230
- package/android/jni/lzma/C/Bra.h +0 -64
- package/android/jni/lzma/C/Bra86.c +0 -82
- package/android/jni/lzma/C/BraIA64.c +0 -53
- package/android/jni/lzma/C/CpuArch.c +0 -218
- package/android/jni/lzma/C/CpuArch.h +0 -336
- package/android/jni/lzma/C/Delta.c +0 -64
- package/android/jni/lzma/C/Delta.h +0 -19
- package/android/jni/lzma/C/DllSecur.c +0 -108
- package/android/jni/lzma/C/DllSecur.h +0 -20
- package/android/jni/lzma/C/LzFind.c +0 -1127
- package/android/jni/lzma/C/LzFind.h +0 -121
- package/android/jni/lzma/C/LzFindMt.c +0 -853
- package/android/jni/lzma/C/LzFindMt.h +0 -101
- package/android/jni/lzma/C/LzHash.h +0 -57
- package/android/jni/lzma/C/Lzma2DecMt.c +0 -1082
- package/android/jni/lzma/C/Lzma2DecMt.h +0 -79
- package/android/jni/lzma/C/Lzma2Enc.c +0 -803
- package/android/jni/lzma/C/Lzma2Enc.h +0 -55
- package/android/jni/lzma/C/Lzma86.h +0 -111
- package/android/jni/lzma/C/Lzma86Dec.c +0 -54
- package/android/jni/lzma/C/Lzma86Enc.c +0 -106
- package/android/jni/lzma/C/LzmaEnc.c +0 -2976
- package/android/jni/lzma/C/LzmaEnc.h +0 -76
- package/android/jni/lzma/C/LzmaLib.c +0 -40
- package/android/jni/lzma/C/LzmaLib.h +0 -131
- package/android/jni/lzma/C/MtCoder.c +0 -601
- package/android/jni/lzma/C/MtCoder.h +0 -141
- package/android/jni/lzma/C/MtDec.c +0 -1138
- package/android/jni/lzma/C/MtDec.h +0 -201
- package/android/jni/lzma/C/Ppmd.h +0 -85
- package/android/jni/lzma/C/Ppmd7.c +0 -712
- package/android/jni/lzma/C/Ppmd7.h +0 -142
- package/android/jni/lzma/C/Ppmd7Dec.c +0 -191
- package/android/jni/lzma/C/Ppmd7Enc.c +0 -187
- package/android/jni/lzma/C/RotateDefs.h +0 -30
- package/android/jni/lzma/C/Sha256.c +0 -248
- package/android/jni/lzma/C/Sha256.h +0 -26
- package/android/jni/lzma/C/Sort.c +0 -141
- package/android/jni/lzma/C/Sort.h +0 -18
- package/android/jni/lzma/C/Threads.c +0 -95
- package/android/jni/lzma/C/Threads.h +0 -70
- package/android/jni/lzma/C/ThreadsP.c +0 -396
- package/android/jni/lzma/C/ThreadsP.h +0 -31
- package/android/jni/lzma/C/Util/7z/7z.dsp +0 -241
- package/android/jni/lzma/C/Util/7z/7z.dsw +0 -29
- package/android/jni/lzma/C/Util/7z/7zMain.c +0 -686
- package/android/jni/lzma/C/Util/7z/Precomp.c +0 -4
- package/android/jni/lzma/C/Util/7z/Precomp.h +0 -10
- package/android/jni/lzma/C/Util/7z/makefile +0 -40
- package/android/jni/lzma/C/Util/7z/makefile.gcc +0 -75
- package/android/jni/lzma/C/Util/Lzma/LzmaUtil.c +0 -258
- package/android/jni/lzma/C/Util/Lzma/LzmaUtil.dsp +0 -168
- package/android/jni/lzma/C/Util/Lzma/LzmaUtil.dsw +0 -29
- package/android/jni/lzma/C/Util/Lzma/makefile +0 -28
- package/android/jni/lzma/C/Util/Lzma/makefile.gcc +0 -44
- package/android/jni/lzma/C/Util/LzmaLib/LzmaLib.def +0 -4
- package/android/jni/lzma/C/Util/LzmaLib/LzmaLib.dsp +0 -178
- package/android/jni/lzma/C/Util/LzmaLib/LzmaLib.dsw +0 -29
- package/android/jni/lzma/C/Util/LzmaLib/LzmaLibExports.c +0 -14
- package/android/jni/lzma/C/Util/LzmaLib/makefile +0 -34
- package/android/jni/lzma/C/Util/LzmaLib/resource.rc +0 -3
- package/android/jni/lzma/C/Util/SfxSetup/Precomp.c +0 -4
- package/android/jni/lzma/C/Util/SfxSetup/Precomp.h +0 -10
- package/android/jni/lzma/C/Util/SfxSetup/SfxSetup.c +0 -640
- package/android/jni/lzma/C/Util/SfxSetup/SfxSetup.dsp +0 -231
- package/android/jni/lzma/C/Util/SfxSetup/SfxSetup.dsw +0 -29
- package/android/jni/lzma/C/Util/SfxSetup/makefile +0 -37
- package/android/jni/lzma/C/Util/SfxSetup/makefile_con +0 -38
- package/android/jni/lzma/C/Util/SfxSetup/resource.rc +0 -5
- package/android/jni/lzma/C/Util/SfxSetup/setup.ico +0 -0
- package/android/jni/lzma/C/Xz.c +0 -90
- package/android/jni/lzma/C/Xz.h +0 -460
- package/android/jni/lzma/C/XzCrc64.c +0 -86
- package/android/jni/lzma/C/XzCrc64.h +0 -26
- package/android/jni/lzma/C/XzCrc64Opt.c +0 -69
- package/android/jni/lzma/C/XzDec.c +0 -2766
- package/android/jni/lzma/C/XzEnc.c +0 -1329
- package/android/jni/lzma/C/XzEnc.h +0 -60
- package/android/jni/lzma/C/XzIn.c +0 -319
- package/android/jni/lzma/CPP/7zip/7zip.mak +0 -240
- package/android/jni/lzma/CPP/7zip/Aes.mak +0 -7
- package/android/jni/lzma/CPP/7zip/Archive/7z/7zCompressionMode.cpp +0 -3
- package/android/jni/lzma/CPP/7zip/Archive/7z/7zCompressionMode.h +0 -76
- package/android/jni/lzma/CPP/7zip/Archive/7z/7zDecode.cpp +0 -569
- package/android/jni/lzma/CPP/7zip/Archive/7z/7zDecode.h +0 -70
- package/android/jni/lzma/CPP/7zip/Archive/7z/7zEncode.cpp +0 -678
- package/android/jni/lzma/CPP/7zip/Archive/7z/7zEncode.h +0 -92
- package/android/jni/lzma/CPP/7zip/Archive/7z/7zExtract.cpp +0 -423
- package/android/jni/lzma/CPP/7zip/Archive/7z/7zFolderInStream.cpp +0 -139
- package/android/jni/lzma/CPP/7zip/Archive/7z/7zFolderInStream.h +0 -61
- package/android/jni/lzma/CPP/7zip/Archive/7z/7zHandler.cpp +0 -763
- package/android/jni/lzma/CPP/7zip/Archive/7z/7zHandler.h +0 -181
- package/android/jni/lzma/CPP/7zip/Archive/7z/7zHandlerOut.cpp +0 -942
- package/android/jni/lzma/CPP/7zip/Archive/7z/7zHeader.cpp +0 -19
- package/android/jni/lzma/CPP/7zip/Archive/7z/7zHeader.h +0 -148
- package/android/jni/lzma/CPP/7zip/Archive/7z/7zIn.cpp +0 -1666
- package/android/jni/lzma/CPP/7zip/Archive/7z/7zIn.h +0 -445
- package/android/jni/lzma/CPP/7zip/Archive/7z/7zItem.h +0 -202
- package/android/jni/lzma/CPP/7zip/Archive/7z/7zOut.cpp +0 -901
- package/android/jni/lzma/CPP/7zip/Archive/7z/7zOut.h +0 -335
- package/android/jni/lzma/CPP/7zip/Archive/7z/7zProperties.cpp +0 -174
- package/android/jni/lzma/CPP/7zip/Archive/7z/7zProperties.h +0 -22
- package/android/jni/lzma/CPP/7zip/Archive/7z/7zRegister.cpp +0 -21
- package/android/jni/lzma/CPP/7zip/Archive/7z/7zSpecStream.cpp +0 -22
- package/android/jni/lzma/CPP/7zip/Archive/7z/7zSpecStream.h +0 -35
- package/android/jni/lzma/CPP/7zip/Archive/7z/7zUpdate.cpp +0 -2500
- package/android/jni/lzma/CPP/7zip/Archive/7z/7zUpdate.h +0 -139
- package/android/jni/lzma/CPP/7zip/Archive/7z/StdAfx.cpp +0 -3
- package/android/jni/lzma/CPP/7zip/Archive/7z/StdAfx.h +0 -8
- package/android/jni/lzma/CPP/7zip/Archive/Archive.def +0 -12
- package/android/jni/lzma/CPP/7zip/Archive/Archive2.def +0 -19
- package/android/jni/lzma/CPP/7zip/Archive/ArchiveExports.cpp +0 -151
- package/android/jni/lzma/CPP/7zip/Archive/Common/CoderMixer2.cpp +0 -1125
- package/android/jni/lzma/CPP/7zip/Archive/Common/CoderMixer2.h +0 -447
- package/android/jni/lzma/CPP/7zip/Archive/Common/DummyOutStream.cpp +0 -17
- package/android/jni/lzma/CPP/7zip/Archive/Common/DummyOutStream.h +0 -25
- package/android/jni/lzma/CPP/7zip/Archive/Common/HandlerOut.cpp +0 -232
- package/android/jni/lzma/CPP/7zip/Archive/Common/HandlerOut.h +0 -110
- package/android/jni/lzma/CPP/7zip/Archive/Common/InStreamWithCRC.cpp +0 -46
- package/android/jni/lzma/CPP/7zip/Archive/Common/InStreamWithCRC.h +0 -67
- package/android/jni/lzma/CPP/7zip/Archive/Common/ItemNameUtils.cpp +0 -88
- package/android/jni/lzma/CPP/7zip/Archive/Common/ItemNameUtils.h +0 -28
- package/android/jni/lzma/CPP/7zip/Archive/Common/MultiStream.cpp +0 -191
- package/android/jni/lzma/CPP/7zip/Archive/Common/MultiStream.h +0 -89
- package/android/jni/lzma/CPP/7zip/Archive/Common/OutStreamWithCRC.cpp +0 -18
- package/android/jni/lzma/CPP/7zip/Archive/Common/OutStreamWithCRC.h +0 -37
- package/android/jni/lzma/CPP/7zip/Archive/Common/ParseProperties.cpp +0 -3
- package/android/jni/lzma/CPP/7zip/Archive/Common/ParseProperties.h +0 -6
- package/android/jni/lzma/CPP/7zip/Archive/Common/StdAfx.h +0 -8
- package/android/jni/lzma/CPP/7zip/Archive/DllExports2.cpp +0 -122
- package/android/jni/lzma/CPP/7zip/Archive/IArchive.h +0 -608
- package/android/jni/lzma/CPP/7zip/Archive/Icons/7z.ico +0 -0
- package/android/jni/lzma/CPP/7zip/Archive/LzmaHandler.cpp +0 -629
- package/android/jni/lzma/CPP/7zip/Archive/SplitHandler.cpp +0 -359
- package/android/jni/lzma/CPP/7zip/Archive/StdAfx.h +0 -8
- package/android/jni/lzma/CPP/7zip/Archive/XzHandler.cpp +0 -1308
- package/android/jni/lzma/CPP/7zip/Archive/XzHandler.h +0 -11
- package/android/jni/lzma/CPP/7zip/Asm.mak +0 -9
- package/android/jni/lzma/CPP/7zip/Bundles/Alone7z/Alone.dsp +0 -1910
- package/android/jni/lzma/CPP/7zip/Bundles/Alone7z/Alone.dsw +0 -29
- package/android/jni/lzma/CPP/7zip/Bundles/Alone7z/StdAfx.cpp +0 -3
- package/android/jni/lzma/CPP/7zip/Bundles/Alone7z/StdAfx.h +0 -8
- package/android/jni/lzma/CPP/7zip/Bundles/Alone7z/makefile +0 -154
- package/android/jni/lzma/CPP/7zip/Bundles/Alone7z/resource.rc +0 -7
- package/android/jni/lzma/CPP/7zip/Bundles/Format7zExtractR/StdAfx.cpp +0 -3
- package/android/jni/lzma/CPP/7zip/Bundles/Format7zExtractR/StdAfx.h +0 -8
- package/android/jni/lzma/CPP/7zip/Bundles/Format7zExtractR/makefile +0 -96
- package/android/jni/lzma/CPP/7zip/Bundles/Format7zExtractR/resource.rc +0 -5
- package/android/jni/lzma/CPP/7zip/Bundles/Format7zR/StdAfx.cpp +0 -3
- package/android/jni/lzma/CPP/7zip/Bundles/Format7zR/StdAfx.h +0 -8
- package/android/jni/lzma/CPP/7zip/Bundles/Format7zR/makefile +0 -116
- package/android/jni/lzma/CPP/7zip/Bundles/Format7zR/resource.rc +0 -5
- package/android/jni/lzma/CPP/7zip/Bundles/LzmaCon/LzmaAlone.cpp +0 -799
- package/android/jni/lzma/CPP/7zip/Bundles/LzmaCon/LzmaCon.dsp +0 -477
- package/android/jni/lzma/CPP/7zip/Bundles/LzmaCon/LzmaCon.dsw +0 -29
- package/android/jni/lzma/CPP/7zip/Bundles/LzmaCon/StdAfx.cpp +0 -3
- package/android/jni/lzma/CPP/7zip/Bundles/LzmaCon/StdAfx.h +0 -8
- package/android/jni/lzma/CPP/7zip/Bundles/LzmaCon/makefile +0 -59
- package/android/jni/lzma/CPP/7zip/Bundles/LzmaCon/makefile.gcc +0 -195
- package/android/jni/lzma/CPP/7zip/Bundles/LzmaCon/resource.rc +0 -3
- package/android/jni/lzma/CPP/7zip/Bundles/LzmaSpec/LzmaSpec.cpp +0 -715
- package/android/jni/lzma/CPP/7zip/Bundles/SFXCon/7z.ico +0 -0
- package/android/jni/lzma/CPP/7zip/Bundles/SFXCon/SFXCon.dsp +0 -912
- package/android/jni/lzma/CPP/7zip/Bundles/SFXCon/SFXCon.dsw +0 -29
- package/android/jni/lzma/CPP/7zip/Bundles/SFXCon/SfxCon.cpp +0 -482
- package/android/jni/lzma/CPP/7zip/Bundles/SFXCon/StdAfx.cpp +0 -3
- package/android/jni/lzma/CPP/7zip/Bundles/SFXCon/StdAfx.h +0 -8
- package/android/jni/lzma/CPP/7zip/Bundles/SFXCon/makefile +0 -134
- package/android/jni/lzma/CPP/7zip/Bundles/SFXCon/resource.rc +0 -5
- package/android/jni/lzma/CPP/7zip/Bundles/SFXSetup/ExtractCallbackSfx.cpp +0 -246
- package/android/jni/lzma/CPP/7zip/Bundles/SFXSetup/ExtractCallbackSfx.h +0 -86
- package/android/jni/lzma/CPP/7zip/Bundles/SFXSetup/ExtractEngine.cpp +0 -137
- package/android/jni/lzma/CPP/7zip/Bundles/SFXSetup/ExtractEngine.h +0 -11
- package/android/jni/lzma/CPP/7zip/Bundles/SFXSetup/SFXSetup.dsp +0 -803
- package/android/jni/lzma/CPP/7zip/Bundles/SFXSetup/SFXSetup.dsw +0 -29
- package/android/jni/lzma/CPP/7zip/Bundles/SFXSetup/SfxSetup.cpp +0 -364
- package/android/jni/lzma/CPP/7zip/Bundles/SFXSetup/StdAfx.cpp +0 -3
- package/android/jni/lzma/CPP/7zip/Bundles/SFXSetup/StdAfx.h +0 -13
- package/android/jni/lzma/CPP/7zip/Bundles/SFXSetup/makefile +0 -117
- package/android/jni/lzma/CPP/7zip/Bundles/SFXSetup/resource.h +0 -6
- package/android/jni/lzma/CPP/7zip/Bundles/SFXSetup/resource.rc +0 -16
- package/android/jni/lzma/CPP/7zip/Bundles/SFXSetup/setup.ico +0 -0
- package/android/jni/lzma/CPP/7zip/Bundles/SFXWin/7z.ico +0 -0
- package/android/jni/lzma/CPP/7zip/Bundles/SFXWin/SFXWin.dsp +0 -988
- package/android/jni/lzma/CPP/7zip/Bundles/SFXWin/SFXWin.dsw +0 -29
- package/android/jni/lzma/CPP/7zip/Bundles/SFXWin/SfxWin.cpp +0 -241
- package/android/jni/lzma/CPP/7zip/Bundles/SFXWin/StdAfx.cpp +0 -3
- package/android/jni/lzma/CPP/7zip/Bundles/SFXWin/StdAfx.h +0 -14
- package/android/jni/lzma/CPP/7zip/Bundles/SFXWin/makefile +0 -153
- package/android/jni/lzma/CPP/7zip/Bundles/SFXWin/resource.h +0 -1
- package/android/jni/lzma/CPP/7zip/Bundles/SFXWin/resource.rc +0 -50
- package/android/jni/lzma/CPP/7zip/Common/CWrappers.cpp +0 -250
- package/android/jni/lzma/CPP/7zip/Common/CWrappers.h +0 -120
- package/android/jni/lzma/CPP/7zip/Common/CreateCoder.cpp +0 -536
- package/android/jni/lzma/CPP/7zip/Common/CreateCoder.h +0 -192
- package/android/jni/lzma/CPP/7zip/Common/FilePathAutoRename.cpp +0 -46
- package/android/jni/lzma/CPP/7zip/Common/FilePathAutoRename.h +0 -10
- package/android/jni/lzma/CPP/7zip/Common/FileStreams.cpp +0 -475
- package/android/jni/lzma/CPP/7zip/Common/FileStreams.h +0 -166
- package/android/jni/lzma/CPP/7zip/Common/FilterCoder.cpp +0 -435
- package/android/jni/lzma/CPP/7zip/Common/FilterCoder.h +0 -205
- package/android/jni/lzma/CPP/7zip/Common/InBuffer.cpp +0 -163
- package/android/jni/lzma/CPP/7zip/Common/InBuffer.h +0 -92
- package/android/jni/lzma/CPP/7zip/Common/InOutTempBuffer.cpp +0 -127
- package/android/jni/lzma/CPP/7zip/Common/InOutTempBuffer.h +0 -48
- package/android/jni/lzma/CPP/7zip/Common/LimitedStreams.cpp +0 -367
- package/android/jni/lzma/CPP/7zip/Common/LimitedStreams.h +0 -252
- package/android/jni/lzma/CPP/7zip/Common/LockedStream.cpp +0 -3
- package/android/jni/lzma/CPP/7zip/Common/LockedStream.h +0 -6
- package/android/jni/lzma/CPP/7zip/Common/MethodId.cpp +0 -3
- package/android/jni/lzma/CPP/7zip/Common/MethodId.h +0 -10
- package/android/jni/lzma/CPP/7zip/Common/MethodProps.cpp +0 -509
- package/android/jni/lzma/CPP/7zip/Common/MethodProps.h +0 -264
- package/android/jni/lzma/CPP/7zip/Common/OffsetStream.cpp +0 -39
- package/android/jni/lzma/CPP/7zip/Common/OffsetStream.h +0 -26
- package/android/jni/lzma/CPP/7zip/Common/OutBuffer.cpp +0 -111
- package/android/jni/lzma/CPP/7zip/Common/OutBuffer.h +0 -66
- package/android/jni/lzma/CPP/7zip/Common/ProgressUtils.cpp +0 -51
- package/android/jni/lzma/CPP/7zip/Common/ProgressUtils.h +0 -35
- package/android/jni/lzma/CPP/7zip/Common/PropId.cpp +0 -108
- package/android/jni/lzma/CPP/7zip/Common/RegisterArc.h +0 -78
- package/android/jni/lzma/CPP/7zip/Common/RegisterCodec.h +0 -106
- package/android/jni/lzma/CPP/7zip/Common/StdAfx.h +0 -8
- package/android/jni/lzma/CPP/7zip/Common/StreamBinder.cpp +0 -156
- package/android/jni/lzma/CPP/7zip/Common/StreamBinder.h +0 -60
- package/android/jni/lzma/CPP/7zip/Common/StreamObjects.cpp +0 -285
- package/android/jni/lzma/CPP/7zip/Common/StreamObjects.h +0 -157
- package/android/jni/lzma/CPP/7zip/Common/StreamUtils.cpp +0 -56
- package/android/jni/lzma/CPP/7zip/Common/StreamUtils.h +0 -13
- package/android/jni/lzma/CPP/7zip/Common/UniqBlocks.cpp +0 -57
- package/android/jni/lzma/CPP/7zip/Common/UniqBlocks.h +0 -26
- package/android/jni/lzma/CPP/7zip/Common/VirtThread.cpp +0 -48
- package/android/jni/lzma/CPP/7zip/Common/VirtThread.h +0 -24
- package/android/jni/lzma/CPP/7zip/Compress/Bcj2Coder.cpp +0 -666
- package/android/jni/lzma/CPP/7zip/Compress/Bcj2Coder.h +0 -120
- package/android/jni/lzma/CPP/7zip/Compress/Bcj2Register.cpp +0 -24
- package/android/jni/lzma/CPP/7zip/Compress/BcjCoder.cpp +0 -24
- package/android/jni/lzma/CPP/7zip/Compress/BcjCoder.h +0 -31
- package/android/jni/lzma/CPP/7zip/Compress/BcjRegister.cpp +0 -17
- package/android/jni/lzma/CPP/7zip/Compress/BranchMisc.cpp +0 -23
- package/android/jni/lzma/CPP/7zip/Compress/BranchMisc.h +0 -35
- package/android/jni/lzma/CPP/7zip/Compress/BranchRegister.cpp +0 -41
- package/android/jni/lzma/CPP/7zip/Compress/ByteSwap.cpp +0 -92
- package/android/jni/lzma/CPP/7zip/Compress/CodecExports.cpp +0 -344
- package/android/jni/lzma/CPP/7zip/Compress/CopyCoder.cpp +0 -120
- package/android/jni/lzma/CPP/7zip/Compress/CopyCoder.h +0 -49
- package/android/jni/lzma/CPP/7zip/Compress/CopyRegister.cpp +0 -15
- package/android/jni/lzma/CPP/7zip/Compress/DeltaFilter.cpp +0 -128
- package/android/jni/lzma/CPP/7zip/Compress/Lzma2Decoder.cpp +0 -265
- package/android/jni/lzma/CPP/7zip/Compress/Lzma2Decoder.h +0 -96
- package/android/jni/lzma/CPP/7zip/Compress/Lzma2Encoder.cpp +0 -122
- package/android/jni/lzma/CPP/7zip/Compress/Lzma2Encoder.h +0 -42
- package/android/jni/lzma/CPP/7zip/Compress/Lzma2Register.cpp +0 -22
- package/android/jni/lzma/CPP/7zip/Compress/LzmaDecoder.cpp +0 -343
- package/android/jni/lzma/CPP/7zip/Compress/LzmaDecoder.h +0 -113
- package/android/jni/lzma/CPP/7zip/Compress/LzmaEncoder.cpp +0 -182
- package/android/jni/lzma/CPP/7zip/Compress/LzmaEncoder.h +0 -46
- package/android/jni/lzma/CPP/7zip/Compress/LzmaRegister.cpp +0 -22
- package/android/jni/lzma/CPP/7zip/Compress/PpmdDecoder.cpp +0 -170
- package/android/jni/lzma/CPP/7zip/Compress/PpmdDecoder.h +0 -86
- package/android/jni/lzma/CPP/7zip/Compress/PpmdEncoder.cpp +0 -152
- package/android/jni/lzma/CPP/7zip/Compress/PpmdEncoder.h +0 -58
- package/android/jni/lzma/CPP/7zip/Compress/PpmdRegister.cpp +0 -22
- package/android/jni/lzma/CPP/7zip/Compress/StdAfx.h +0 -8
- package/android/jni/lzma/CPP/7zip/Compress/XzDecoder.cpp +0 -150
- package/android/jni/lzma/CPP/7zip/Compress/XzDecoder.h +0 -92
- package/android/jni/lzma/CPP/7zip/Compress/XzEncoder.cpp +0 -245
- package/android/jni/lzma/CPP/7zip/Compress/XzEncoder.h +0 -46
- package/android/jni/lzma/CPP/7zip/Crc.mak +0 -8
- package/android/jni/lzma/CPP/7zip/Crc64.mak +0 -8
- package/android/jni/lzma/CPP/7zip/Crypto/7zAes.cpp +0 -280
- package/android/jni/lzma/CPP/7zip/Crypto/7zAes.h +0 -118
- package/android/jni/lzma/CPP/7zip/Crypto/7zAesRegister.cpp +0 -17
- package/android/jni/lzma/CPP/7zip/Crypto/MyAes.cpp +0 -112
- package/android/jni/lzma/CPP/7zip/Crypto/MyAes.h +0 -57
- package/android/jni/lzma/CPP/7zip/Crypto/MyAesReg.cpp +0 -16
- package/android/jni/lzma/CPP/7zip/Crypto/RandGen.cpp +0 -233
- package/android/jni/lzma/CPP/7zip/Crypto/RandGen.h +0 -40
- package/android/jni/lzma/CPP/7zip/Crypto/StdAfx.h +0 -8
- package/android/jni/lzma/CPP/7zip/GuiCommon.rc +0 -84
- package/android/jni/lzma/CPP/7zip/Guid.txt +0 -220
- package/android/jni/lzma/CPP/7zip/ICoder.h +0 -399
- package/android/jni/lzma/CPP/7zip/IDecl.h +0 -28
- package/android/jni/lzma/CPP/7zip/IPassword.h +0 -23
- package/android/jni/lzma/CPP/7zip/IProgress.h +0 -19
- package/android/jni/lzma/CPP/7zip/IStream.h +0 -127
- package/android/jni/lzma/CPP/7zip/LzmaDec.mak +0 -5
- package/android/jni/lzma/CPP/7zip/MyVersion.h +0 -2
- package/android/jni/lzma/CPP/7zip/MyVersionInfo.rc +0 -2
- package/android/jni/lzma/CPP/7zip/PropID.h +0 -127
- package/android/jni/lzma/CPP/7zip/SubBuild.mak +0 -3
- package/android/jni/lzma/CPP/7zip/UI/Client7z/Client7z.cpp +0 -993
- package/android/jni/lzma/CPP/7zip/UI/Client7z/Client7z.dsp +0 -235
- package/android/jni/lzma/CPP/7zip/UI/Client7z/Client7z.dsw +0 -29
- package/android/jni/lzma/CPP/7zip/UI/Client7z/StdAfx.cpp +0 -3
- package/android/jni/lzma/CPP/7zip/UI/Client7z/StdAfx.h +0 -8
- package/android/jni/lzma/CPP/7zip/UI/Client7z/makefile +0 -28
- package/android/jni/lzma/CPP/7zip/UI/Client7z/resource.rc +0 -3
- package/android/jni/lzma/CPP/7zip/UI/Common/ArchiveCommandLine.cpp +0 -1295
- package/android/jni/lzma/CPP/7zip/UI/Common/ArchiveCommandLine.h +0 -136
- package/android/jni/lzma/CPP/7zip/UI/Common/ArchiveExtractCallback.cpp +0 -1715
- package/android/jni/lzma/CPP/7zip/UI/Common/ArchiveExtractCallback.h +0 -403
- package/android/jni/lzma/CPP/7zip/UI/Common/ArchiveName.cpp +0 -155
- package/android/jni/lzma/CPP/7zip/UI/Common/ArchiveName.h +0 -10
- package/android/jni/lzma/CPP/7zip/UI/Common/ArchiveOpenCallback.cpp +0 -161
- package/android/jni/lzma/CPP/7zip/UI/Common/ArchiveOpenCallback.h +0 -112
- package/android/jni/lzma/CPP/7zip/UI/Common/Bench.cpp +0 -3618
- package/android/jni/lzma/CPP/7zip/UI/Common/Bench.h +0 -77
- package/android/jni/lzma/CPP/7zip/UI/Common/DefaultName.cpp +0 -37
- package/android/jni/lzma/CPP/7zip/UI/Common/DefaultName.h +0 -11
- package/android/jni/lzma/CPP/7zip/UI/Common/DirItem.h +0 -190
- package/android/jni/lzma/CPP/7zip/UI/Common/EnumDirItems.cpp +0 -1096
- package/android/jni/lzma/CPP/7zip/UI/Common/EnumDirItems.h +0 -42
- package/android/jni/lzma/CPP/7zip/UI/Common/ExitCode.h +0 -27
- package/android/jni/lzma/CPP/7zip/UI/Common/Extract.cpp +0 -482
- package/android/jni/lzma/CPP/7zip/UI/Common/Extract.h +0 -94
- package/android/jni/lzma/CPP/7zip/UI/Common/ExtractMode.h +0 -34
- package/android/jni/lzma/CPP/7zip/UI/Common/ExtractingFilePath.cpp +0 -280
- package/android/jni/lzma/CPP/7zip/UI/Common/ExtractingFilePath.h +0 -31
- package/android/jni/lzma/CPP/7zip/UI/Common/HashCalc.cpp +0 -347
- package/android/jni/lzma/CPP/7zip/UI/Common/HashCalc.h +0 -110
- package/android/jni/lzma/CPP/7zip/UI/Common/IFileExtractCallback.h +0 -114
- package/android/jni/lzma/CPP/7zip/UI/Common/LoadCodecs.cpp +0 -1074
- package/android/jni/lzma/CPP/7zip/UI/Common/LoadCodecs.h +0 -424
- package/android/jni/lzma/CPP/7zip/UI/Common/OpenArchive.cpp +0 -3553
- package/android/jni/lzma/CPP/7zip/UI/Common/OpenArchive.h +0 -436
- package/android/jni/lzma/CPP/7zip/UI/Common/PropIDUtils.cpp +0 -668
- package/android/jni/lzma/CPP/7zip/UI/Common/PropIDUtils.h +0 -18
- package/android/jni/lzma/CPP/7zip/UI/Common/Property.h +0 -14
- package/android/jni/lzma/CPP/7zip/UI/Common/SetProperties.cpp +0 -80
- package/android/jni/lzma/CPP/7zip/UI/Common/SetProperties.h +0 -10
- package/android/jni/lzma/CPP/7zip/UI/Common/SortUtils.cpp +0 -25
- package/android/jni/lzma/CPP/7zip/UI/Common/SortUtils.h +0 -10
- package/android/jni/lzma/CPP/7zip/UI/Common/StdAfx.h +0 -8
- package/android/jni/lzma/CPP/7zip/UI/Common/TempFiles.cpp +0 -19
- package/android/jni/lzma/CPP/7zip/UI/Common/TempFiles.h +0 -16
- package/android/jni/lzma/CPP/7zip/UI/Common/Update.cpp +0 -1704
- package/android/jni/lzma/CPP/7zip/UI/Common/Update.h +0 -200
- package/android/jni/lzma/CPP/7zip/UI/Common/UpdateAction.cpp +0 -64
- package/android/jni/lzma/CPP/7zip/UI/Common/UpdateAction.h +0 -66
- package/android/jni/lzma/CPP/7zip/UI/Common/UpdateCallback.cpp +0 -771
- package/android/jni/lzma/CPP/7zip/UI/Common/UpdateCallback.h +0 -162
- package/android/jni/lzma/CPP/7zip/UI/Common/UpdatePair.cpp +0 -233
- package/android/jni/lzma/CPP/7zip/UI/Common/UpdatePair.h +0 -27
- package/android/jni/lzma/CPP/7zip/UI/Common/UpdateProduce.cpp +0 -70
- package/android/jni/lzma/CPP/7zip/UI/Common/UpdateProduce.h +0 -55
- package/android/jni/lzma/CPP/7zip/UI/Common/WorkDir.cpp +0 -94
- package/android/jni/lzma/CPP/7zip/UI/Common/WorkDir.h +0 -26
- package/android/jni/lzma/CPP/7zip/UI/Common/ZipRegistry.h +0 -130
- package/android/jni/lzma/CPP/7zip/UI/Console/BenchCon.cpp +0 -41
- package/android/jni/lzma/CPP/7zip/UI/Console/BenchCon.h +0 -14
- package/android/jni/lzma/CPP/7zip/UI/Console/Console.mak +0 -43
- package/android/jni/lzma/CPP/7zip/UI/Console/Console.manifest +0 -13
- package/android/jni/lzma/CPP/7zip/UI/Console/ConsoleClose.cpp +0 -69
- package/android/jni/lzma/CPP/7zip/UI/Console/ConsoleClose.h +0 -33
- package/android/jni/lzma/CPP/7zip/UI/Console/ExtractCallbackConsole.cpp +0 -825
- package/android/jni/lzma/CPP/7zip/UI/Console/ExtractCallbackConsole.h +0 -164
- package/android/jni/lzma/CPP/7zip/UI/Console/HashCon.cpp +0 -367
- package/android/jni/lzma/CPP/7zip/UI/Console/HashCon.h +0 -48
- package/android/jni/lzma/CPP/7zip/UI/Console/List.cpp +0 -1359
- package/android/jni/lzma/CPP/7zip/UI/Console/List.h +0 -27
- package/android/jni/lzma/CPP/7zip/UI/Console/Main.cpp +0 -1154
- package/android/jni/lzma/CPP/7zip/UI/Console/MainAr.cpp +0 -175
- package/android/jni/lzma/CPP/7zip/UI/Console/OpenCallbackConsole.cpp +0 -115
- package/android/jni/lzma/CPP/7zip/UI/Console/OpenCallbackConsole.h +0 -66
- package/android/jni/lzma/CPP/7zip/UI/Console/PercentPrinter.cpp +0 -183
- package/android/jni/lzma/CPP/7zip/UI/Console/PercentPrinter.h +0 -62
- package/android/jni/lzma/CPP/7zip/UI/Console/StdAfx.cpp +0 -3
- package/android/jni/lzma/CPP/7zip/UI/Console/StdAfx.h +0 -8
- package/android/jni/lzma/CPP/7zip/UI/Console/UpdateCallbackConsole.cpp +0 -702
- package/android/jni/lzma/CPP/7zip/UI/Console/UpdateCallbackConsole.h +0 -124
- package/android/jni/lzma/CPP/7zip/UI/Console/UserInputUtils.cpp +0 -110
- package/android/jni/lzma/CPP/7zip/UI/Console/UserInputUtils.h +0 -27
- package/android/jni/lzma/CPP/7zip/UI/Console/makefile +0 -64
- package/android/jni/lzma/CPP/7zip/UI/Console/resource.rc +0 -7
- package/android/jni/lzma/CPP/7zip/UI/Explorer/MyMessages.cpp +0 -37
- package/android/jni/lzma/CPP/7zip/UI/Explorer/MyMessages.h +0 -16
- package/android/jni/lzma/CPP/7zip/UI/FileManager/BrowseDialog.cpp +0 -1025
- package/android/jni/lzma/CPP/7zip/UI/FileManager/BrowseDialog.h +0 -21
- package/android/jni/lzma/CPP/7zip/UI/FileManager/BrowseDialogRes.h +0 -9
- package/android/jni/lzma/CPP/7zip/UI/FileManager/ComboDialog.cpp +0 -64
- package/android/jni/lzma/CPP/7zip/UI/FileManager/ComboDialog.h +0 -28
- package/android/jni/lzma/CPP/7zip/UI/FileManager/ComboDialogRes.h +0 -4
- package/android/jni/lzma/CPP/7zip/UI/FileManager/DialogSize.h +0 -16
- package/android/jni/lzma/CPP/7zip/UI/FileManager/ExtractCallback.cpp +0 -1037
- package/android/jni/lzma/CPP/7zip/UI/FileManager/ExtractCallback.h +0 -328
- package/android/jni/lzma/CPP/7zip/UI/FileManager/FormatUtils.cpp +0 -28
- package/android/jni/lzma/CPP/7zip/UI/FileManager/FormatUtils.h +0 -14
- package/android/jni/lzma/CPP/7zip/UI/FileManager/LangUtils.h +0 -40
- package/android/jni/lzma/CPP/7zip/UI/FileManager/MyWindowsNew.h +0 -76
- package/android/jni/lzma/CPP/7zip/UI/FileManager/OverwriteDialog.cpp +0 -122
- package/android/jni/lzma/CPP/7zip/UI/FileManager/OverwriteDialog.h +0 -69
- package/android/jni/lzma/CPP/7zip/UI/FileManager/OverwriteDialog.rc +0 -91
- package/android/jni/lzma/CPP/7zip/UI/FileManager/OverwriteDialogRes.h +0 -17
- package/android/jni/lzma/CPP/7zip/UI/FileManager/PasswordDialog.cpp +0 -58
- package/android/jni/lzma/CPP/7zip/UI/FileManager/PasswordDialog.h +0 -28
- package/android/jni/lzma/CPP/7zip/UI/FileManager/PasswordDialog.rc +0 -14
- package/android/jni/lzma/CPP/7zip/UI/FileManager/PasswordDialogRes.h +0 -5
- package/android/jni/lzma/CPP/7zip/UI/FileManager/ProgressDialog.cpp +0 -196
- package/android/jni/lzma/CPP/7zip/UI/FileManager/ProgressDialog.h +0 -170
- package/android/jni/lzma/CPP/7zip/UI/FileManager/ProgressDialog.rc +0 -12
- package/android/jni/lzma/CPP/7zip/UI/FileManager/ProgressDialog2.cpp +0 -1337
- package/android/jni/lzma/CPP/7zip/UI/FileManager/ProgressDialog2.h +0 -351
- package/android/jni/lzma/CPP/7zip/UI/FileManager/ProgressDialog2.rc +0 -40
- package/android/jni/lzma/CPP/7zip/UI/FileManager/ProgressDialog2Res.h +0 -48
- package/android/jni/lzma/CPP/7zip/UI/FileManager/ProgressDialog2a.rc +0 -80
- package/android/jni/lzma/CPP/7zip/UI/FileManager/ProgressDialogRes.h +0 -3
- package/android/jni/lzma/CPP/7zip/UI/FileManager/PropertyName.cpp +0 -23
- package/android/jni/lzma/CPP/7zip/UI/FileManager/PropertyName.h +0 -10
- package/android/jni/lzma/CPP/7zip/UI/FileManager/PropertyNameRes.h +0 -95
- package/android/jni/lzma/CPP/7zip/UI/FileManager/SysIconUtils.cpp +0 -255
- package/android/jni/lzma/CPP/7zip/UI/FileManager/SysIconUtils.h +0 -62
- package/android/jni/lzma/CPP/7zip/UI/FileManager/resource.h +0 -177
- package/android/jni/lzma/CPP/7zip/UI/FileManager/resourceGui.h +0 -15
- package/android/jni/lzma/CPP/7zip/UI/GUI/Extract.rc +0 -59
- package/android/jni/lzma/CPP/7zip/UI/GUI/ExtractDialog.cpp +0 -418
- package/android/jni/lzma/CPP/7zip/UI/GUI/ExtractDialog.h +0 -113
- package/android/jni/lzma/CPP/7zip/UI/GUI/ExtractDialog.rc +0 -98
- package/android/jni/lzma/CPP/7zip/UI/GUI/ExtractDialogRes.h +0 -24
- package/android/jni/lzma/CPP/7zip/UI/GUI/ExtractGUI.cpp +0 -280
- package/android/jni/lzma/CPP/7zip/UI/GUI/ExtractGUI.h +0 -38
- package/android/jni/lzma/CPP/7zip/UI/GUI/ExtractRes.h +0 -51
- package/android/jni/lzma/CPP/7zip/UI/GUI/HashGUI.h +0 -27
- package/android/jni/lzma/CPP/7zip/UI/GUI/resource2.h +0 -2
- package/android/jni/lzma/CPP/Build.mak +0 -156
- package/android/jni/lzma/CPP/Common/AutoPtr.h +0 -35
- package/android/jni/lzma/CPP/Common/CRC.cpp +0 -7
- package/android/jni/lzma/CPP/Common/C_FileIO.cpp +0 -92
- package/android/jni/lzma/CPP/Common/C_FileIO.h +0 -53
- package/android/jni/lzma/CPP/Common/ComTry.h +0 -21
- package/android/jni/lzma/CPP/Common/CommandLineParser.cpp +0 -197
- package/android/jni/lzma/CPP/Common/CommandLineParser.h +0 -63
- package/android/jni/lzma/CPP/Common/Common.h +0 -43
- package/android/jni/lzma/CPP/Common/CrcReg.cpp +0 -98
- package/android/jni/lzma/CPP/Common/Defs.h +0 -15
- package/android/jni/lzma/CPP/Common/DynamicBuffer.h +0 -64
- package/android/jni/lzma/CPP/Common/IntToString.cpp +0 -193
- package/android/jni/lzma/CPP/Common/IntToString.h +0 -28
- package/android/jni/lzma/CPP/Common/Lang.h +0 -23
- package/android/jni/lzma/CPP/Common/ListFileUtils.cpp +0 -132
- package/android/jni/lzma/CPP/Common/ListFileUtils.h +0 -18
- package/android/jni/lzma/CPP/Common/MyBuffer.h +0 -259
- package/android/jni/lzma/CPP/Common/MyBuffer2.h +0 -100
- package/android/jni/lzma/CPP/Common/MyCom.h +0 -277
- package/android/jni/lzma/CPP/Common/MyException.h +0 -14
- package/android/jni/lzma/CPP/Common/MyGuidDef.h +0 -54
- package/android/jni/lzma/CPP/Common/MyInitGuid.h +0 -45
- package/android/jni/lzma/CPP/Common/MyLinux.h +0 -42
- package/android/jni/lzma/CPP/Common/MyString.cpp +0 -1659
- package/android/jni/lzma/CPP/Common/MyString.h +0 -868
- package/android/jni/lzma/CPP/Common/MyTypes.h +0 -35
- package/android/jni/lzma/CPP/Common/MyUnknown.h +0 -17
- package/android/jni/lzma/CPP/Common/MyVector.cpp +0 -3
- package/android/jni/lzma/CPP/Common/MyVector.h +0 -634
- package/android/jni/lzma/CPP/Common/MyWindows.cpp +0 -145
- package/android/jni/lzma/CPP/Common/MyWindows.h +0 -231
- package/android/jni/lzma/CPP/Common/NewHandler.cpp +0 -163
- package/android/jni/lzma/CPP/Common/NewHandler.h +0 -88
- package/android/jni/lzma/CPP/Common/Sha256Reg.cpp +0 -40
- package/android/jni/lzma/CPP/Common/StdAfx.h +0 -8
- package/android/jni/lzma/CPP/Common/StdInStream.cpp +0 -89
- package/android/jni/lzma/CPP/Common/StdInStream.h +0 -38
- package/android/jni/lzma/CPP/Common/StdOutStream.cpp +0 -163
- package/android/jni/lzma/CPP/Common/StdOutStream.h +0 -71
- package/android/jni/lzma/CPP/Common/StringConvert.cpp +0 -319
- package/android/jni/lzma/CPP/Common/StringConvert.h +0 -88
- package/android/jni/lzma/CPP/Common/StringToInt.cpp +0 -144
- package/android/jni/lzma/CPP/Common/StringToInt.h +0 -21
- package/android/jni/lzma/CPP/Common/TextConfig.cpp +0 -124
- package/android/jni/lzma/CPP/Common/TextConfig.h +0 -19
- package/android/jni/lzma/CPP/Common/UTFConvert.cpp +0 -288
- package/android/jni/lzma/CPP/Common/UTFConvert.h +0 -12
- package/android/jni/lzma/CPP/Common/Wildcard.cpp +0 -676
- package/android/jni/lzma/CPP/Common/Wildcard.h +0 -149
- package/android/jni/lzma/CPP/Common/XzCrc64Init.cpp +0 -7
- package/android/jni/lzma/CPP/Common/XzCrc64Reg.cpp +0 -42
- package/android/jni/lzma/CPP/Windows/COM.h +0 -70
- package/android/jni/lzma/CPP/Windows/CommonDialog.cpp +0 -185
- package/android/jni/lzma/CPP/Windows/CommonDialog.h +0 -23
- package/android/jni/lzma/CPP/Windows/Control/ComboBox.cpp +0 -66
- package/android/jni/lzma/CPP/Windows/Control/ComboBox.h +0 -65
- package/android/jni/lzma/CPP/Windows/Control/CommandBar.h +0 -52
- package/android/jni/lzma/CPP/Windows/Control/Dialog.cpp +0 -251
- package/android/jni/lzma/CPP/Windows/Control/Dialog.h +0 -170
- package/android/jni/lzma/CPP/Windows/Control/Edit.h +0 -19
- package/android/jni/lzma/CPP/Windows/Control/ImageList.cpp +0 -10
- package/android/jni/lzma/CPP/Windows/Control/ImageList.h +0 -87
- package/android/jni/lzma/CPP/Windows/Control/ListView.cpp +0 -155
- package/android/jni/lzma/CPP/Windows/Control/ListView.h +0 -146
- package/android/jni/lzma/CPP/Windows/Control/ProgressBar.h +0 -35
- package/android/jni/lzma/CPP/Windows/Control/PropertyPage.cpp +0 -143
- package/android/jni/lzma/CPP/Windows/Control/PropertyPage.h +0 -50
- package/android/jni/lzma/CPP/Windows/Control/ReBar.h +0 -34
- package/android/jni/lzma/CPP/Windows/Control/Static.h +0 -28
- package/android/jni/lzma/CPP/Windows/Control/StatusBar.h +0 -42
- package/android/jni/lzma/CPP/Windows/Control/StdAfx.h +0 -8
- package/android/jni/lzma/CPP/Windows/Control/ToolBar.h +0 -43
- package/android/jni/lzma/CPP/Windows/Control/Trackbar.h +0 -27
- package/android/jni/lzma/CPP/Windows/Control/Window2.cpp +0 -200
- package/android/jni/lzma/CPP/Windows/Control/Window2.h +0 -51
- package/android/jni/lzma/CPP/Windows/DLL.cpp +0 -109
- package/android/jni/lzma/CPP/Windows/DLL.h +0 -58
- package/android/jni/lzma/CPP/Windows/Defs.h +0 -17
- package/android/jni/lzma/CPP/Windows/ErrorMsg.cpp +0 -66
- package/android/jni/lzma/CPP/Windows/ErrorMsg.h +0 -15
- package/android/jni/lzma/CPP/Windows/FileDir.cpp +0 -714
- package/android/jni/lzma/CPP/Windows/FileDir.h +0 -117
- package/android/jni/lzma/CPP/Windows/FileFind.cpp +0 -749
- package/android/jni/lzma/CPP/Windows/FileFind.h +0 -161
- package/android/jni/lzma/CPP/Windows/FileIO.cpp +0 -432
- package/android/jni/lzma/CPP/Windows/FileIO.h +0 -212
- package/android/jni/lzma/CPP/Windows/FileLink.cpp +0 -440
- package/android/jni/lzma/CPP/Windows/FileMapping.cpp +0 -12
- package/android/jni/lzma/CPP/Windows/FileMapping.h +0 -66
- package/android/jni/lzma/CPP/Windows/FileName.cpp +0 -839
- package/android/jni/lzma/CPP/Windows/FileName.h +0 -115
- package/android/jni/lzma/CPP/Windows/FileSystem.cpp +0 -131
- package/android/jni/lzma/CPP/Windows/FileSystem.h +0 -27
- package/android/jni/lzma/CPP/Windows/Handle.h +0 -37
- package/android/jni/lzma/CPP/Windows/MemoryLock.cpp +0 -112
- package/android/jni/lzma/CPP/Windows/MemoryLock.h +0 -40
- package/android/jni/lzma/CPP/Windows/NtCheck.h +0 -46
- package/android/jni/lzma/CPP/Windows/PropVariant.cpp +0 -347
- package/android/jni/lzma/CPP/Windows/PropVariant.h +0 -114
- package/android/jni/lzma/CPP/Windows/PropVariantConv.cpp +0 -138
- package/android/jni/lzma/CPP/Windows/PropVariantConv.h +0 -37
- package/android/jni/lzma/CPP/Windows/Registry.cpp +0 -390
- package/android/jni/lzma/CPP/Windows/Registry.h +0 -84
- package/android/jni/lzma/CPP/Windows/ResourceString.cpp +0 -103
- package/android/jni/lzma/CPP/Windows/ResourceString.h +0 -16
- package/android/jni/lzma/CPP/Windows/SecurityUtils.cpp +0 -181
- package/android/jni/lzma/CPP/Windows/SecurityUtils.h +0 -167
- package/android/jni/lzma/CPP/Windows/Shell.cpp +0 -358
- package/android/jni/lzma/CPP/Windows/Shell.h +0 -94
- package/android/jni/lzma/CPP/Windows/StdAfx.h +0 -8
- package/android/jni/lzma/CPP/Windows/Synchronization.cpp +0 -10
- package/android/jni/lzma/CPP/Windows/Synchronization.h +0 -164
- package/android/jni/lzma/CPP/Windows/System.cpp +0 -142
- package/android/jni/lzma/CPP/Windows/System.h +0 -40
- package/android/jni/lzma/CPP/Windows/Thread.h +0 -38
- package/android/jni/lzma/CPP/Windows/TimeUtils.cpp +0 -213
- package/android/jni/lzma/CPP/Windows/TimeUtils.h +0 -32
- package/android/jni/lzma/CPP/Windows/Window.cpp +0 -179
- package/android/jni/lzma/CPP/Windows/Window.h +0 -284
- package/android/jni/lzma/CS/7zip/Common/CRC.cs +0 -55
- package/android/jni/lzma/CS/7zip/Common/CommandLineParser.cs +0 -274
- package/android/jni/lzma/CS/7zip/Common/InBuffer.cs +0 -72
- package/android/jni/lzma/CS/7zip/Common/OutBuffer.cs +0 -47
- package/android/jni/lzma/CS/7zip/Compress/LZ/IMatchFinder.cs +0 -24
- package/android/jni/lzma/CS/7zip/Compress/LZ/LzBinTree.cs +0 -367
- package/android/jni/lzma/CS/7zip/Compress/LZ/LzInWindow.cs +0 -132
- package/android/jni/lzma/CS/7zip/Compress/LZ/LzOutWindow.cs +0 -110
- package/android/jni/lzma/CS/7zip/Compress/LZMA/LzmaBase.cs +0 -76
- package/android/jni/lzma/CS/7zip/Compress/LZMA/LzmaDecoder.cs +0 -398
- package/android/jni/lzma/CS/7zip/Compress/LZMA/LzmaEncoder.cs +0 -1480
- package/android/jni/lzma/CS/7zip/Compress/LzmaAlone/LzmaAlone.cs +0 -364
- package/android/jni/lzma/CS/7zip/Compress/LzmaAlone/LzmaAlone.csproj +0 -90
- package/android/jni/lzma/CS/7zip/Compress/LzmaAlone/LzmaAlone.sln +0 -20
- package/android/jni/lzma/CS/7zip/Compress/LzmaAlone/LzmaBench.cs +0 -340
- package/android/jni/lzma/CS/7zip/Compress/LzmaAlone/Properties/AssemblyInfo.cs +0 -29
- package/android/jni/lzma/CS/7zip/Compress/LzmaAlone/Properties/Resources.cs +0 -70
- package/android/jni/lzma/CS/7zip/Compress/LzmaAlone/Properties/Settings.cs +0 -42
- package/android/jni/lzma/CS/7zip/Compress/RangeCoder/RangeCoder.cs +0 -234
- package/android/jni/lzma/CS/7zip/Compress/RangeCoder/RangeCoderBit.cs +0 -117
- package/android/jni/lzma/CS/7zip/Compress/RangeCoder/RangeCoderBitTree.cs +0 -157
- package/android/jni/lzma/CS/7zip/ICoder.cs +0 -157
- package/android/jni/lzma/DOC/7zC.txt +0 -187
- package/android/jni/lzma/DOC/7zFormat.txt +0 -469
- package/android/jni/lzma/DOC/Methods.txt +0 -173
- package/android/jni/lzma/DOC/installer.txt +0 -166
- package/android/jni/lzma/DOC/lzma-history.txt +0 -446
- package/android/jni/lzma/DOC/lzma-specification.txt +0 -1176
- package/android/jni/lzma/DOC/lzma.txt +0 -328
- package/android/jni/lzma/Java/SevenZip/CRC.java +0 -52
- package/android/jni/lzma/Java/SevenZip/Compression/LZ/BinTree.java +0 -382
- package/android/jni/lzma/Java/SevenZip/Compression/LZ/InWindow.java +0 -131
- package/android/jni/lzma/Java/SevenZip/Compression/LZ/OutWindow.java +0 -85
- package/android/jni/lzma/Java/SevenZip/Compression/LZMA/Base.java +0 -88
- package/android/jni/lzma/Java/SevenZip/Compression/LZMA/Decoder.java +0 -329
- package/android/jni/lzma/Java/SevenZip/Compression/LZMA/Encoder.java +0 -1416
- package/android/jni/lzma/Java/SevenZip/Compression/RangeCoder/BitTreeDecoder.java +0 -55
- package/android/jni/lzma/Java/SevenZip/Compression/RangeCoder/BitTreeEncoder.java +0 -99
- package/android/jni/lzma/Java/SevenZip/Compression/RangeCoder/Decoder.java +0 -88
- package/android/jni/lzma/Java/SevenZip/Compression/RangeCoder/Encoder.java +0 -151
- package/android/jni/lzma/Java/SevenZip/ICodeProgress.java +0 -6
- package/android/jni/lzma/Java/SevenZip/LzmaAlone.java +0 -253
- package/android/jni/lzma/Java/SevenZip/LzmaBench.java +0 -392
- package/android/jni/lzma/bin/7zS2.sfx +0 -0
- package/android/jni/lzma/bin/7zS2con.sfx +0 -0
- package/android/jni/lzma/bin/7zSD.sfx +0 -0
- package/android/jni/lzma/bin/7zdec.exe +0 -0
- package/android/jni/lzma/bin/7zr.exe +0 -0
- package/android/jni/lzma/bin/installer/config.txt +0 -5
- package/android/jni/lzma/bin/installer/cr.bat +0 -5
- package/android/jni/lzma/bin/lzma.exe +0 -0
- package/android/jni/lzma/bin/x64/7zr.exe +0 -0
- package/android/src/main/java/cn/reactnative/modules/update/ArchivePatchPlanResult.java +0 -6
- package/android/src/main/java/cn/reactnative/modules/update/PushyConfiguration.java +0 -104
- package/android/src/main/java/cn/reactnative/modules/update/UiThreadRunner.java +0 -37
- package/android/src/newarch/cn/reactnative/modules/update/UpdateModule.java +0 -121
- package/android/src/oldarch/cn/reactnative/modules/update/UpdateModule.java +0 -125
- package/cpp/patch_core/jni_natives.h +0 -27
- package/cpp/patch_core/jni_registration.cpp +0 -103
- package/cpp/patch_core/jni_util.h +0 -60
- package/cpp/patch_core/patch_core_android.cpp +0 -97
- package/cpp/patch_core/update_core_android.cpp +0 -485
- package/cpp/update_flow_core/update_flow_jni.cpp +0 -150
package/ios/RCTPushy/RCTPushy.mm
CHANGED
|
@@ -1,971 +1,82 @@
|
|
|
1
|
-
#import "
|
|
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
|
|
20
|
-
//
|
|
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
|
-
#
|
|
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
|
|
347
|
-
//
|
|
348
|
-
//
|
|
349
|
-
//
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
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
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
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
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
//
|
|
580
|
-
//
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
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
|
-
|
|
859
|
-
} @catch (
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
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
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
70
|
+
if (completion != nil) {
|
|
71
|
+
NSDictionary *snapshot = [result copy];
|
|
72
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
73
|
+
completion(snapshot);
|
|
74
|
+
});
|
|
960
75
|
}
|
|
961
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 (
|
|
136
|
+
} @catch (...) {
|
|
1137
137
|
failure = PushyErrorWithCode(pushy::error_codes::kFileOperationFailed,
|
|
1138
|
-
|
|
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
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1201
|
-
|
|
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
|
-
[
|
|
203
|
+
[RCTPushyInstaller clearInvalidFiles];
|
|
1218
204
|
}
|
|
1219
205
|
|
|
1220
|
-
if ([[defaults objectForKey:KeyPackageUpdatedMarked] boolValue]) {
|
|
1221
|
-
[defaults removeObjectForKey:KeyPackageUpdatedMarked];
|
|
1222
|
-
[
|
|
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
|
-
|
|
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 (
|
|
1284
|
-
|
|
1285
|
-
|
|
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
|
-
|
|
1319
|
-
|
|
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 (![
|
|
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 (![
|
|
351
|
+
if (![RCTPushyInstaller switchVersion:PushyOptionString(options, @"hash") error:&error]) {
|
|
1390
352
|
PushyRejectError(reject, error);
|
|
1391
353
|
return;
|
|
1392
354
|
}
|
|
1393
355
|
|
|
1394
|
-
|
|
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
|
-
|
|
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(
|
|
1428
|
-
|
|
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 (
|
|
1453
|
-
|
|
1454
|
-
exception.reason ?: @"markSuccess failed"));
|
|
410
|
+
} @catch (...) {
|
|
411
|
+
PushyRejectCaught(reject, pushy::error_codes::kMarkSuccessFailed, @"markSuccess failed");
|
|
1455
412
|
return;
|
|
1456
413
|
}
|
|
1457
414
|
|
|
1458
|
-
[
|
|
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
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
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 (
|
|
1504
|
-
|
|
1505
|
-
exception.reason ?: @"reset failed"));
|
|
455
|
+
} @catch (...) {
|
|
456
|
+
PushyRejectCaught(reject, pushy::error_codes::kResetFailed, @"reset failed");
|
|
1506
457
|
return;
|
|
1507
458
|
}
|
|
1508
459
|
|
|
1509
|
-
|
|
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.
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
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
|
-
|
|
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[
|
|
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
|