react-native-update 10.37.20 → 10.38.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (131) hide show
  1. package/README-CN.md +4 -4
  2. package/README.md +2 -12
  3. package/android/bin/.settings/org.eclipse.buildship.core.prefs +13 -0
  4. package/android/build.gradle +4 -0
  5. package/android/jni/Android.mk +14 -1
  6. package/android/jni/Application.mk +5 -2
  7. package/android/lib/arm64-v8a/librnupdate.so +0 -0
  8. package/android/lib/armeabi-v7a/librnupdate.so +0 -0
  9. package/android/lib/x86/librnupdate.so +0 -0
  10. package/android/lib/x86_64/librnupdate.so +0 -0
  11. package/android/src/main/java/cn/reactnative/modules/update/ArchivePatchPlanResult.java +6 -0
  12. package/android/src/main/java/cn/reactnative/modules/update/CopyGroupResult.java +6 -0
  13. package/android/src/main/java/cn/reactnative/modules/update/DownloadTask.java +155 -156
  14. package/android/src/main/java/cn/reactnative/modules/update/NativeUpdateCore.java +34 -0
  15. package/android/src/main/java/cn/reactnative/modules/update/StateCoreResult.java +16 -0
  16. package/android/src/main/java/cn/reactnative/modules/update/UpdateContext.java +131 -48
  17. package/cpp/patch_core/archive_patch_core.cpp +125 -0
  18. package/cpp/patch_core/archive_patch_core.h +59 -0
  19. package/cpp/patch_core/patch_core.cpp +533 -0
  20. package/cpp/patch_core/patch_core.h +68 -0
  21. package/cpp/patch_core/patch_core_android.cpp +112 -0
  22. package/cpp/patch_core/state_core.cpp +110 -0
  23. package/cpp/patch_core/state_core.h +58 -0
  24. package/cpp/patch_core/tests/patch_core_test.cpp +473 -0
  25. package/cpp/patch_core/update_core_android.cpp +469 -0
  26. package/harmony/pushy.har +0 -0
  27. package/ios/RCTPushy/RCTPushy.mm +233 -143
  28. package/package.json +17 -15
  29. package/react-native-update.podspec +3 -0
  30. package/scripts/build-harmony-har.js +12 -0
  31. package/scripts/prepublish.ts +49 -3
  32. package/scripts/prune-host-stl.sh +6 -0
  33. package/scripts/test-patch-core.sh +39 -0
  34. package/src/client.ts +129 -76
  35. package/src/core.ts +2 -1
  36. package/src/endpoint.ts +171 -0
  37. package/src/utils.ts +40 -27
  38. package/android/jni/lzma/DOC/7zC.txt +0 -187
  39. package/android/jni/lzma/DOC/7zFormat.txt +0 -469
  40. package/android/jni/lzma/DOC/Methods.txt +0 -173
  41. package/android/jni/lzma/DOC/installer.txt +0 -166
  42. package/android/jni/lzma/DOC/lzma-history.txt +0 -446
  43. package/android/jni/lzma/DOC/lzma-sdk.txt +0 -357
  44. package/android/jni/lzma/DOC/lzma-specification.txt +0 -1176
  45. package/android/jni/lzma/DOC/lzma.txt +0 -328
  46. package/android/jni/lzma/bin/7zS2.sfx +0 -0
  47. package/android/jni/lzma/bin/7zS2con.sfx +0 -0
  48. package/android/jni/lzma/bin/7zSD.sfx +0 -0
  49. package/android/jni/lzma/bin/7zdec.exe +0 -0
  50. package/android/jni/lzma/bin/7zr.exe +0 -0
  51. package/android/jni/lzma/bin/installer/config.txt +0 -5
  52. package/android/jni/lzma/bin/installer/cr.bat +0 -5
  53. package/android/jni/lzma/bin/lzma.exe +0 -0
  54. package/android/jni/lzma/bin/x64/7zr.exe +0 -0
  55. package/error.js +0 -1609
  56. package/harmony/har-wrapper/AppScope/app.json5 +0 -8
  57. package/harmony/har-wrapper/build-profile.json5 +0 -35
  58. package/harmony/har-wrapper/hvigor/hvigor-config.json5 +0 -5
  59. package/harmony/har-wrapper/hvigorfile.ts +0 -6
  60. package/harmony/har-wrapper/oh-package.json5 +0 -4
  61. package/harmony/pushy/.cxx/default/default/release/arm64-v8a/.cmake/api/v1/reply/cache-v2-77b153ce45aba0ed28ef.json +0 -1415
  62. package/harmony/pushy/.cxx/default/default/release/arm64-v8a/.cmake/api/v1/reply/cmakeFiles-v1-b65a07793384e0ce3e08.json +0 -809
  63. package/harmony/pushy/.cxx/default/default/release/arm64-v8a/.cmake/api/v1/reply/codemodel-v2-ce0e89410afd8bf3a057.json +0 -60
  64. package/harmony/pushy/.cxx/default/default/release/arm64-v8a/.cmake/api/v1/reply/directory-.-Release-f5ebdc15457944623624.json +0 -14
  65. package/harmony/pushy/.cxx/default/default/release/arm64-v8a/.cmake/api/v1/reply/index-2026-03-18T12-50-36-0050.json +0 -89
  66. package/harmony/pushy/.cxx/default/default/release/arm64-v8a/.cmake/api/v1/reply/target-rnupdate-Release-267153624504c9c3ffdd.json +0 -222
  67. package/harmony/pushy/.cxx/default/default/release/arm64-v8a/.ninja_deps +0 -0
  68. package/harmony/pushy/.cxx/default/default/release/arm64-v8a/.ninja_log +0 -8
  69. package/harmony/pushy/.cxx/default/default/release/arm64-v8a/CMakeCache.txt +0 -415
  70. package/harmony/pushy/.cxx/default/default/release/arm64-v8a/CMakeFiles/3.28.2/CMakeCCompiler.cmake +0 -74
  71. package/harmony/pushy/.cxx/default/default/release/arm64-v8a/CMakeFiles/3.28.2/CMakeCXXCompiler.cmake +0 -85
  72. package/harmony/pushy/.cxx/default/default/release/arm64-v8a/CMakeFiles/3.28.2/CMakeDetermineCompilerABI_C.bin +0 -0
  73. package/harmony/pushy/.cxx/default/default/release/arm64-v8a/CMakeFiles/3.28.2/CMakeDetermineCompilerABI_CXX.bin +0 -0
  74. package/harmony/pushy/.cxx/default/default/release/arm64-v8a/CMakeFiles/3.28.2/CMakeSystem.cmake +0 -15
  75. package/harmony/pushy/.cxx/default/default/release/arm64-v8a/CMakeFiles/3.28.2/CompilerIdC/CMakeCCompilerId.c +0 -880
  76. package/harmony/pushy/.cxx/default/default/release/arm64-v8a/CMakeFiles/3.28.2/CompilerIdC/CMakeCCompilerId.o +0 -0
  77. package/harmony/pushy/.cxx/default/default/release/arm64-v8a/CMakeFiles/3.28.2/CompilerIdCXX/CMakeCXXCompilerId.cpp +0 -869
  78. package/harmony/pushy/.cxx/default/default/release/arm64-v8a/CMakeFiles/3.28.2/CompilerIdCXX/CMakeCXXCompilerId.o +0 -0
  79. package/harmony/pushy/.cxx/default/default/release/arm64-v8a/CMakeFiles/CMakeConfigureLog.yaml +0 -388
  80. package/harmony/pushy/.cxx/default/default/release/arm64-v8a/CMakeFiles/TargetDirectories.txt +0 -3
  81. package/harmony/pushy/.cxx/default/default/release/arm64-v8a/CMakeFiles/cmake.check_cache +0 -1
  82. package/harmony/pushy/.cxx/default/default/release/arm64-v8a/CMakeFiles/rnupdate.dir/__w/react-native-update/react-native-update/android/jni/HDiffPatch/file_for_patch.c.o +0 -0
  83. package/harmony/pushy/.cxx/default/default/release/arm64-v8a/CMakeFiles/rnupdate.dir/__w/react-native-update/react-native-update/android/jni/HDiffPatch/libHDiffPatch/HPatch/patch.c.o +0 -0
  84. package/harmony/pushy/.cxx/default/default/release/arm64-v8a/CMakeFiles/rnupdate.dir/__w/react-native-update/react-native-update/android/jni/hpatch.c.o +0 -0
  85. package/harmony/pushy/.cxx/default/default/release/arm64-v8a/CMakeFiles/rnupdate.dir/__w/react-native-update/react-native-update/android/jni/lzma/C/Lzma2Dec.c.o +0 -0
  86. package/harmony/pushy/.cxx/default/default/release/arm64-v8a/CMakeFiles/rnupdate.dir/__w/react-native-update/react-native-update/android/jni/lzma/C/LzmaDec.c.o +0 -0
  87. package/harmony/pushy/.cxx/default/default/release/arm64-v8a/CMakeFiles/rnupdate.dir/pushy.c.o +0 -0
  88. package/harmony/pushy/.cxx/default/default/release/arm64-v8a/CMakeFiles/rules.ninja +0 -64
  89. package/harmony/pushy/.cxx/default/default/release/arm64-v8a/additional_project_files.txt +0 -0
  90. package/harmony/pushy/.cxx/default/default/release/arm64-v8a/build.ninja +0 -206
  91. package/harmony/pushy/.cxx/default/default/release/arm64-v8a/build_file_index.txt +0 -1
  92. package/harmony/pushy/.cxx/default/default/release/arm64-v8a/cmake_install.cmake +0 -54
  93. package/harmony/pushy/.cxx/default/default/release/arm64-v8a/compile_commands.json +0 -38
  94. package/harmony/pushy/.cxx/default/default/release/arm64-v8a/configure_fingerprint.json +0 -1
  95. package/harmony/pushy/.cxx/default/default/release/arm64-v8a/hvigor_native_config.json +0 -1
  96. package/harmony/pushy/.cxx/default/default/release/arm64-v8a/metadata_generation_command.txt +0 -17
  97. package/harmony/pushy/.cxx/default/default/release/arm64-v8a/native_work_dir.txt +0 -1
  98. package/harmony/pushy/.cxx/default/default/release/arm64-v8a/output.log +0 -14
  99. package/harmony/pushy/.cxx/default/default/release/hvigor/arm64-v8a/summary.cmake +0 -0
  100. package/harmony/pushy/BuildProfile.ets +0 -17
  101. package/harmony/pushy/OAT.xml +0 -38
  102. package/harmony/pushy/README.md +0 -0
  103. package/harmony/pushy/build-profile.json5 +0 -15
  104. package/harmony/pushy/hvigor-plugin.ts +0 -34
  105. package/harmony/pushy/hvigorfile.ts +0 -1
  106. package/harmony/pushy/index.ets +0 -2
  107. package/harmony/pushy/oh-package-lock.json5 +0 -20
  108. package/harmony/pushy/oh-package.json5 +0 -13
  109. package/harmony/pushy/src/main/cpp/CMakeLists.txt +0 -51
  110. package/harmony/pushy/src/main/cpp/PushyPackage.h +0 -55
  111. package/harmony/pushy/src/main/cpp/PushyTurboModule.cpp +0 -142
  112. package/harmony/pushy/src/main/cpp/PushyTurboModule.h +0 -38
  113. package/harmony/pushy/src/main/cpp/pushy.c +0 -117
  114. package/harmony/pushy/src/main/cpp/pushy.h +0 -8
  115. package/harmony/pushy/src/main/ets/DownloadTask.ts +0 -570
  116. package/harmony/pushy/src/main/ets/DownloadTaskParams.ts +0 -19
  117. package/harmony/pushy/src/main/ets/EventHub.ts +0 -39
  118. package/harmony/pushy/src/main/ets/Logger.ts +0 -52
  119. package/harmony/pushy/src/main/ets/PushyFileJSBundleProvider.ets +0 -50
  120. package/harmony/pushy/src/main/ets/PushyPackage.ts +0 -22
  121. package/harmony/pushy/src/main/ets/PushyTurboModule.ts +0 -171
  122. package/harmony/pushy/src/main/ets/SaveFile.ts +0 -34
  123. package/harmony/pushy/src/main/ets/UpdateContext.ts +0 -262
  124. package/harmony/pushy/src/main/ets/UpdateModuleImpl.ts +0 -123
  125. package/harmony/pushy/src/main/module.json5 +0 -7
  126. package/harmony/pushy/src/main/resources/base/element/string.json +0 -8
  127. package/harmony/pushy/src/main/resources/en_US/element/string.json +0 -8
  128. package/harmony/pushy/src/main/resources/zh_CN/element/string.json +0 -8
  129. package/harmony/pushy/ts.ts +0 -3
  130. package/src/__tests__/core.test.ts +0 -103
  131. package/src/__tests__/utils.test.ts +0 -36
package/README-CN.md CHANGED
@@ -6,9 +6,9 @@
6
6
 
7
7
  ### 区域服务说明
8
8
 
9
- - 中国区服务使用 **Pushy**(<https://pushy.reactnative.cn>),由**武汉青罗网络科技有限公司**运营,服务器与用户数据存放于中国境内。
10
- - 全球区服务使用 **Cresc**(<https://cresc.dev>),由 **CHARMLOT PTE. LTD.** 运营,服务器与用户数据存放于新加坡。
11
- - 中国区与全球区服务由不同公司实体独立运营,服务器、数据存放位置及控制台系统彼此隔离。中国大陆以外开发者建议直接使用 Cresc。
9
+ - 中国区服务使用 **Pushy**(<https://pushy.reactnative.cn>),由**武汉青罗网络科技有限公司**运营,服务器与用户数据存放于中国境内,也通过 cloudflare 智能分流,完全支持海外用户高速访问。**使用人民币支付订阅**。
10
+ - 全球区服务使用 **Cresc**(<https://cresc.dev>),由 **CHARMLOT PTE. LTD.** 运营,服务器与用户数据存放于新加坡。**使用美元支付订阅**。
11
+ - 中国区与全球区服务由不同公司实体独立运营,服务器、数据存放位置及控制台系统彼此隔离。如果可以使用网银和支付宝结算,建议使用 Pushy,否则建议使用 Cresc。
12
12
 
13
13
  **现已支持鸿蒙以及新架构**
14
14
 
@@ -18,7 +18,7 @@
18
18
 
19
19
  ### 优势
20
20
 
21
- 1. 对中国用户使用阿里云高速 CDN 分发,对比其他服务器在国外的热更新服务,分发更稳定,更新成功率极高。中国大陆以外开发者建议使用 Cresc 全球服务(<https://cresc.dev>),其全球节点同样提供稳定高速的分发体验。
21
+ 1. 对中国用户使用阿里云高速 CDN 分发,对比其他服务器在国外的热更新服务,分发更稳定,更新成功率极高。海外用户智能分流至 cloudflare,同样提供稳定高速的分发体验。
22
22
  2. 基于 bsdiff/hdiff 算法创建的**超小更新包**,通常版本迭代后在几十至几百 KB 级别(其他全量热更新服务所需流量通常在几十 MB 级别)。
23
23
  3. 始终跟进 RN 最新正式版本,第一时间提供支持。支持 hermes 字节码格式。支持新架构(注:安卓 0.73.0 ~ 0.76.0 的新架构因官方 bug 不支持,0.73 以下或 0.76.1 以上的新架构可用)。
24
24
  4. 跨越多个版本进行更新时,只需要下载**一个更新包**,不需要逐版本依次更新。
package/README.md CHANGED
@@ -4,27 +4,19 @@
4
4
 
5
5
  `react-native-update` provides over-the-air update capabilities for React Native apps. For full documentation, visit:
6
6
 
7
- - China service: <https://pushy.reactnative.cn>
8
7
  - Global service: <https://cresc.dev>
9
8
 
10
- ## Regional Service Notice
11
-
12
- - **Pushy** (<https://pushy.reactnative.cn>) is the China service, operated by **Wuhan Qingluo Network Technology Co., Ltd.**, with servers and user data hosted in mainland China.
13
- - **Cresc** (<https://cresc.dev>) is the global service, operated by **CHARMLOT PTE. LTD.**, with servers and user data hosted in Singapore.
14
- - Pushy and Cresc are operated by different legal entities with separate infrastructure, data storage, and admin systems. Developers outside mainland China should use Cresc directly.
15
-
16
- **HarmonyOS and React Native New Architecture are supported.**
9
+ **React Native New Architecture is supported.**
17
10
 
18
11
  ## Quick Start
19
12
 
20
13
  See the docs:
21
14
 
22
- - Chinese docs: <https://pushy.reactnative.cn/docs/getting-started.html>
23
15
  - English docs: <https://cresc.dev/docs/getting-started>
24
16
 
25
17
  ## Advantages
26
18
 
27
- 1. For users in China, updates are distributed through Alibaba Cloud CDN for high stability and excellent delivery success. For developers outside mainland China, Cresc provides a dedicated global service with fast and reliable worldwide delivery.
19
+ 1. react-native-update provides a dedicated global service with fast and reliable worldwide delivery.
28
20
  2. **Tiny update packages** generated with bsdiff/hdiff are typically only tens to hundreds of KB, instead of the tens of MB usually required by full-bundle update systems.
29
21
  3. The library tracks new React Native stable releases closely, supports Hermes bytecode, and supports the new architecture. Note: Android RN 0.73.0 to 0.76.0 new architecture is unavailable because of upstream issues; versions below 0.73 or above 0.76.1 are supported.
30
22
  4. When updating across multiple versions, clients only need to download **one update package** instead of applying every intermediate version in sequence.
@@ -39,7 +31,6 @@ See the docs:
39
31
  |---------|---------------------|-------------|------------------------|
40
32
  | **Price / Cost** | Free tier with multiple paid plans (starting at about CNY 66/month), bandwidth included | Free tier with multiple paid plans (starting at about CNY 136/month), extra bandwidth charges apply | ❌ **Discontinued** (Microsoft App Center shut down on March 31, 2025) |
41
33
  | **Package Size** | ⭐⭐⭐⭐⭐ Tens to hundreds of KB (incremental) | ⭐⭐⭐ Full bundle updates (usually tens of MB) | ❌ **Discontinued** |
42
- | **China Access Speed** | ⭐⭐⭐⭐⭐ Alibaba Cloud CDN, very fast | ⭐⭐ Overseas servers, may be slower | ❌ **Discontinued** |
43
34
  | **iOS Support** | ✅ Supported | ✅ Supported | ❌ **Discontinued** |
44
35
  | **Android Support** | ✅ Supported | ✅ Supported | ❌ **Discontinued** |
45
36
  | **HarmonyOS Support** | ✅ Supported | ❌ Not supported | ❌ **Discontinued** |
@@ -56,7 +47,6 @@ See the docs:
56
47
  | **Server Deployment** | ✅ Hosted service or paid private deployment | ✅ Hosted by Expo (EAS Update) | ❌ **Discontinued** |
57
48
  | **Update Strategy** | Flexible configuration (silent / prompted / immediate / delayed) | More fixed workflow | ❌ **Discontinued** |
58
49
  | **Bandwidth Usage** | ⭐⭐⭐⭐⭐ Very low (incremental) | ⭐⭐⭐ Higher (full bundle) | ❌ **Discontinued** |
59
- | **Update Success Rate** | ⭐⭐⭐⭐⭐ Excellent, especially in China | ⭐⭐⭐ Moderate | ❌ **Discontinued** |
60
50
 
61
51
  ## Local Development
62
52
 
@@ -0,0 +1,13 @@
1
+ arguments=--init-script /var/folders/l6/0fn3x28s5s585ld3p04gsy1h0000gn/T/db3b08fc4a9ef609cb16b96b200fa13e563f396e9bb1ed0905fdab7bc3bc513b.gradle --init-script /var/folders/l6/0fn3x28s5s585ld3p04gsy1h0000gn/T/52cde0cfcf3e28b8b7510e992210d9614505e0911af0c190bd590d7158574963.gradle
2
+ auto.sync=false
3
+ build.scans.enabled=false
4
+ connection.gradle.distribution=GRADLE_DISTRIBUTION(VERSION(8.9))
5
+ connection.project.dir=
6
+ eclipse.preferences.version=1
7
+ gradle.user.home=
8
+ java.home=/Users/sunny/.sdkman/candidates/java/17.0.9-zulu/zulu-17.jdk/Contents/Home
9
+ jvm.arguments=
10
+ offline.mode=false
11
+ override.workspace.settings=true
12
+ show.console.view=true
13
+ show.executions.view=true
@@ -162,6 +162,10 @@ android {
162
162
  resValue("string", "pushy_build_time", "0")
163
163
  }
164
164
  }
165
+
166
+ packagingOptions {
167
+ exclude "**/libc++_shared.so"
168
+ }
165
169
 
166
170
  lintOptions {
167
171
  abortOnError false
@@ -3,6 +3,14 @@ LOCAL_PATH := $(call my-dir)
3
3
  include $(CLEAR_VARS)
4
4
 
5
5
  LOCAL_MODULE := rnupdate
6
+ LOCAL_CPPFLAGS += -std=c++17
7
+ LOCAL_LDFLAGS += -Wl,--exclude-libs,ALL
8
+ LOCAL_C_INCLUDES := \
9
+ $(LOCAL_PATH) \
10
+ $(LOCAL_PATH)/HDiffPatch \
11
+ $(LOCAL_PATH)/HDiffPatch/libHDiffPatch/HPatch \
12
+ $(LOCAL_PATH)/lzma/C \
13
+ $(LOCAL_PATH)/../../cpp/patch_core
6
14
 
7
15
  Hdp_Files := \
8
16
  hpatch.c \
@@ -13,6 +21,11 @@ Hdp_Files := \
13
21
 
14
22
  LOCAL_SRC_FILES := \
15
23
  DownloadTask.c \
24
+ ../../cpp/patch_core/archive_patch_core.cpp \
25
+ ../../cpp/patch_core/patch_core.cpp \
26
+ ../../cpp/patch_core/patch_core_android.cpp \
27
+ ../../cpp/patch_core/state_core.cpp \
28
+ ../../cpp/patch_core/update_core_android.cpp \
16
29
  $(Hdp_Files)
17
30
 
18
- include $(BUILD_SHARED_LIBRARY)
31
+ include $(BUILD_SHARED_LIBRARY)
@@ -1,7 +1,10 @@
1
- APP_PLATFORM := android-16
1
+ APP_PLATFORM := android-21
2
2
  APP_CFLAGS += -Wno-error=format-security
3
3
  APP_CFLAGS += -fvisibility=hidden -fvisibility-inlines-hidden
4
4
  APP_CFLAGS += -ffunction-sections -fdata-sections
5
- APP_LDFLAGS += -Wl,--gc-sections
5
+ APP_CFLAGS += -Oz -fno-unwind-tables -fno-asynchronous-unwind-tables
6
+ APP_CPPFLAGS += -std=c++17 -Oz -fno-exceptions -fno-rtti -fno-unwind-tables -fno-asynchronous-unwind-tables
7
+ APP_LDFLAGS += -Wl,--gc-sections -Wl,--exclude-libs,ALL
6
8
  APP_BUILD_SCRIPT := Android.mk
7
9
  APP_ABI := armeabi-v7a arm64-v8a x86 x86_64
10
+ APP_STL := c++_shared
Binary file
Binary file
@@ -0,0 +1,6 @@
1
+ package cn.reactnative.modules.update;
2
+
3
+ class ArchivePatchPlanResult {
4
+ String mergeSourceSubdir;
5
+ boolean enableMerge;
6
+ }
@@ -0,0 +1,6 @@
1
+ package cn.reactnative.modules.update;
2
+
3
+ class CopyGroupResult {
4
+ String from;
5
+ String[] toPaths;
6
+ }
@@ -47,7 +47,7 @@ class DownloadTask extends AsyncTask<DownloadTaskParams, long[], Void> {
47
47
  }
48
48
 
49
49
  static {
50
- System.loadLibrary("rnupdate");
50
+ NativeUpdateCore.ensureLoaded();
51
51
  }
52
52
 
53
53
  private void removeDirectory(File file) throws IOException {
@@ -131,7 +131,35 @@ class DownloadTask extends AsyncTask<DownloadTaskParams, long[], Void> {
131
131
 
132
132
  byte[] buffer = new byte[1024*4];
133
133
 
134
- private static native byte[] hdiffPatch(byte[] origin, byte[] patch);
134
+ private static native void applyPatchFromFileSource(
135
+ String sourceRoot,
136
+ String targetRoot,
137
+ String originBundlePath,
138
+ String bundlePatchPath,
139
+ String bundleOutputPath,
140
+ String mergeSourceSubdir,
141
+ boolean enableMerge,
142
+ String[] copyFroms,
143
+ String[] copyTos,
144
+ String[] deletes
145
+ );
146
+ private static native void cleanupOldEntries(
147
+ String rootDir,
148
+ String keepCurrent,
149
+ String keepPrevious,
150
+ int maxAgeDays
151
+ );
152
+ private static native ArchivePatchPlanResult buildArchivePatchPlan(
153
+ int patchType,
154
+ String[] entryNames,
155
+ String[] copyFroms,
156
+ String[] copyTos,
157
+ String[] deletes
158
+ );
159
+ private static native CopyGroupResult[] buildCopyGroups(
160
+ String[] copyFroms,
161
+ String[] copyTos
162
+ );
135
163
 
136
164
 
137
165
  private void copyFile(File from, File fmd) throws IOException {
@@ -163,66 +191,76 @@ class DownloadTask extends AsyncTask<DownloadTaskParams, long[], Void> {
163
191
  return fout.toByteArray();
164
192
  }
165
193
 
166
- private byte[] readOriginBundle() throws IOException {
167
- InputStream in;
168
- try {
169
- in = context.getAssets().open("index.android.bundle");
170
- } catch (Exception e) {
171
- return new byte[0];
194
+ private void appendManifestEntries(
195
+ JSONObject manifest,
196
+ ArrayList<String> copyFroms,
197
+ ArrayList<String> copyTos,
198
+ ArrayList<String> deletes,
199
+ HashMap<String, String> copiesMap
200
+ ) throws JSONException {
201
+ JSONObject copies = manifest.optJSONObject("copies");
202
+ if (copies != null) {
203
+ Iterator<?> keys = copies.keys();
204
+ while (keys.hasNext()) {
205
+ String to = (String) keys.next();
206
+ String from = copies.getString(to);
207
+ if (from.isEmpty()) {
208
+ from = to;
209
+ }
210
+ copyFroms.add(from);
211
+ copyTos.add(to);
212
+ if (copiesMap != null) {
213
+ copiesMap.put(to, from);
214
+ }
215
+ }
172
216
  }
173
- int count;
174
217
 
175
- ByteArrayOutputStream fout = new ByteArrayOutputStream();
176
- while ((count = in.read(buffer)) != -1)
177
- {
178
- fout.write(buffer, 0, count);
218
+ JSONObject deleteMap = manifest.optJSONObject("deletes");
219
+ if (deleteMap != null) {
220
+ Iterator<?> deleteKeys = deleteMap.keys();
221
+ while (deleteKeys.hasNext()) {
222
+ deletes.add((String) deleteKeys.next());
223
+ }
179
224
  }
180
-
181
- fout.close();
182
- in.close();
183
- return fout.toByteArray();
184
225
  }
185
226
 
186
- private byte[] readFile(File file) throws IOException {
187
- InputStream in = new FileInputStream(file);
227
+ private void copyBundledAssetToFile(String assetName, File destination) throws IOException {
228
+ InputStream in = context.getAssets().open(assetName);
229
+ FileOutputStream fout = new FileOutputStream(destination);
188
230
  int count;
189
-
190
- ByteArrayOutputStream fout = new ByteArrayOutputStream();
191
- while ((count = in.read(buffer)) != -1)
192
- {
231
+ while ((count = in.read(buffer)) != -1) {
193
232
  fout.write(buffer, 0, count);
194
233
  }
195
-
196
234
  fout.close();
197
235
  in.close();
198
- return fout.toByteArray();
199
236
  }
200
237
 
201
- private void copyFilesWithBlacklist(String current, File from, File to, JSONObject blackList) throws IOException {
202
- File[] files = from.listFiles();
203
- for (File file : files) {
204
- if (file.isDirectory()) {
205
- String subName = current + file.getName() + '/';
206
- if (blackList.has(subName)) {
207
- continue;
208
- }
209
- File toFile = new File(to, file.getName());
210
- if (!toFile.exists()) {
211
- toFile.mkdir();
212
- }
213
- copyFilesWithBlacklist(subName, file, toFile, blackList);
214
- } else if (!blackList.has(current + file.getName())) {
215
- // Copy file.
216
- File toFile = new File(to, file.getName());
217
- if (!toFile.exists()) {
218
- copyFile(file, toFile);
238
+ private HashMap<String, ArrayList<File>> buildCopyList(
239
+ File unzipDirectory,
240
+ CopyGroupResult[] groups
241
+ ) throws IOException {
242
+ HashMap<String, ArrayList<File>> copyList = new HashMap<String, ArrayList<File>>();
243
+ if (groups == null) {
244
+ return copyList;
245
+ }
246
+
247
+ String rootPath = unzipDirectory.getCanonicalPath() + File.separator;
248
+ for (CopyGroupResult group : groups) {
249
+ ArrayList<File> targets = new ArrayList<File>();
250
+ if (group.toPaths != null) {
251
+ for (String to : group.toPaths) {
252
+ File toFile = new File(unzipDirectory, to);
253
+ String canonicalPath = toFile.getCanonicalPath();
254
+ if (!canonicalPath.startsWith(rootPath)) {
255
+ throw new SecurityException("Illegal name: " + to);
256
+ }
257
+ targets.add(toFile);
219
258
  }
220
259
  }
260
+ copyList.put(group.from, targets);
221
261
  }
222
- }
223
262
 
224
- private void copyFilesWithBlacklist(File from, File to, JSONObject blackList) throws IOException {
225
- copyFilesWithBlacklist("", from, to, blackList);
263
+ return copyList;
226
264
  }
227
265
 
228
266
  private void doFullPatch(DownloadTaskParams param) throws IOException {
@@ -457,78 +495,64 @@ class DownloadTask extends AsyncTask<DownloadTaskParams, long[], Void> {
457
495
 
458
496
  removeDirectory(param.unzipDirectory);
459
497
  param.unzipDirectory.mkdirs();
460
- HashMap<String, ArrayList<File>> copyList = new HashMap<String, ArrayList<File>>();
461
498
  HashMap<String, String> copiesMap = new HashMap<String, String>(); // to -> from 映射
462
-
463
- boolean foundDiff = false;
464
- boolean foundBundlePatch = false;
499
+ ArrayList<String> entryNames = new ArrayList<String>();
500
+ ArrayList<String> copyFroms = new ArrayList<String>();
501
+ ArrayList<String> copyTos = new ArrayList<String>();
502
+ ArrayList<String> deletes = new ArrayList<String>();
465
503
 
466
504
  SafeZipFile zipFile = new SafeZipFile(param.targetFile);
467
505
  Enumeration<? extends ZipEntry> entries = zipFile.entries();
468
506
  while (entries.hasMoreElements()) {
469
507
  ZipEntry ze = entries.nextElement();
470
508
  String fn = ze.getName();
509
+ entryNames.add(fn);
471
510
 
472
511
  if (fn.equals("__diff.json")) {
473
- foundDiff = true;
474
512
  // copy files from assets
475
513
  byte[] bytes = readBytes(zipFile.getInputStream(ze));
476
514
  String json = new String(bytes, "UTF-8");
477
515
  JSONObject obj = (JSONObject)new JSONTokener(json).nextValue();
478
-
479
- JSONObject copies = obj.getJSONObject("copies");
480
- Iterator<?> keys = copies.keys();
481
- while( keys.hasNext() ) {
482
- String to = (String)keys.next();
483
- String from = copies.getString(to);
484
- if (from.isEmpty()) {
485
- from = to;
486
- }
487
- // 保存 copies 映射关系(to -> from)
488
- copiesMap.put(to, from);
489
-
490
- ArrayList<File> target = null;
491
- if (!copyList.containsKey(from)) {
492
- target = new ArrayList<File>();
493
- copyList.put(from, target);
494
- } else {
495
- target = copyList.get((from));
496
- }
497
- File toFile = new File(param.unzipDirectory, to);
498
-
499
- // Fixing a Zip Path Traversal Vulnerability
500
- // https://support.google.com/faqs/answer/9294009
501
- String canonicalPath = toFile.getCanonicalPath();
502
- if (!canonicalPath.startsWith(param.unzipDirectory.getCanonicalPath() + File.separator)) {
503
- throw new SecurityException("Illegal name: " + to);
504
- }
505
- target.add(toFile);
506
- }
507
- continue;
508
- }
509
- if (fn.equals("index.bundlejs.patch")) {
510
- foundBundlePatch = true;
511
-
512
- byte[] patched = hdiffPatch(readOriginBundle(), readBytes(zipFile.getInputStream(ze)));
513
-
514
- FileOutputStream fout = new FileOutputStream(new File(param.unzipDirectory, "index.bundlejs"));
515
- fout.write(patched);
516
- fout.close();
516
+ appendManifestEntries(obj, copyFroms, copyTos, deletes, copiesMap);
517
517
  continue;
518
518
  }
519
-
520
-
521
519
  zipFile.unzipToPath(ze, param.unzipDirectory);
522
520
  }
523
521
 
524
522
  zipFile.close();
525
523
 
526
-
527
- if (!foundDiff) {
528
- throw new Error("diff.json not found");
529
- }
530
- if (!foundBundlePatch) {
531
- throw new Error("bundle patch not found");
524
+ buildArchivePatchPlan(
525
+ DownloadTaskParams.TASK_TYPE_PATCH_FROM_APK,
526
+ entryNames.toArray(new String[0]),
527
+ copyFroms.toArray(new String[0]),
528
+ copyTos.toArray(new String[0]),
529
+ deletes.toArray(new String[0])
530
+ );
531
+ HashMap<String, ArrayList<File>> copyList = buildCopyList(
532
+ param.unzipDirectory,
533
+ buildCopyGroups(
534
+ copyFroms.toArray(new String[0]),
535
+ copyTos.toArray(new String[0])
536
+ )
537
+ );
538
+
539
+ File originBundleFile = new File(param.unzipDirectory, ".origin.bundle");
540
+ copyBundledAssetToFile("index.android.bundle", originBundleFile);
541
+ try {
542
+ applyPatchFromFileSource(
543
+ param.unzipDirectory.getAbsolutePath(),
544
+ param.unzipDirectory.getAbsolutePath(),
545
+ originBundleFile.getAbsolutePath(),
546
+ new File(param.unzipDirectory, "index.bundlejs.patch").getAbsolutePath(),
547
+ new File(param.unzipDirectory, "index.bundlejs").getAbsolutePath(),
548
+ "",
549
+ false,
550
+ new String[0],
551
+ new String[0],
552
+ new String[0]
553
+ );
554
+ } finally {
555
+ originBundleFile.delete();
532
556
  }
533
557
 
534
558
  if (UpdateContext.DEBUG) {
@@ -552,10 +576,10 @@ class DownloadTask extends AsyncTask<DownloadTaskParams, long[], Void> {
552
576
  removeDirectory(param.unzipDirectory);
553
577
  param.unzipDirectory.mkdirs();
554
578
 
555
- int count;
556
- String filename;
557
- boolean foundDiff = false;
558
- boolean foundBundlePatch = false;
579
+ ArrayList<String> entryNames = new ArrayList<String>();
580
+ ArrayList<String> copyFroms = new ArrayList<String>();
581
+ ArrayList<String> copyTos = new ArrayList<String>();
582
+ ArrayList<String> deletes = new ArrayList<String>();
559
583
 
560
584
 
561
585
  SafeZipFile zipFile = new SafeZipFile(param.targetFile);
@@ -563,49 +587,42 @@ class DownloadTask extends AsyncTask<DownloadTaskParams, long[], Void> {
563
587
  while (entries.hasMoreElements()) {
564
588
  ZipEntry ze = entries.nextElement();
565
589
  String fn = ze.getName();
590
+ entryNames.add(fn);
566
591
 
567
592
  if (fn.equals("__diff.json")) {
568
- foundDiff = true;
569
593
  // copy files from assets
570
594
  byte[] bytes = readBytes(zipFile.getInputStream(ze));
571
595
  String json = new String(bytes, "UTF-8");
572
596
  JSONObject obj = (JSONObject)new JSONTokener(json).nextValue();
573
-
574
- JSONObject copies = obj.getJSONObject("copies");
575
- Iterator<?> keys = copies.keys();
576
- while( keys.hasNext() ) {
577
- String to = (String)keys.next();
578
- String from = copies.getString(to);
579
- if (from.isEmpty()) {
580
- from = to;
581
- }
582
- copyFile(new File(param.originDirectory, from), new File(param.unzipDirectory, to));
583
- }
584
- JSONObject blackList = obj.getJSONObject("deletes");
585
- copyFilesWithBlacklist(param.originDirectory, param.unzipDirectory, blackList);
597
+ appendManifestEntries(obj, copyFroms, copyTos, deletes, null);
586
598
  continue;
587
599
  }
588
- if (fn.equals("index.bundlejs.patch")) {
589
- foundBundlePatch = true;
590
- byte[] patched = hdiffPatch(readFile(new File(param.originDirectory, "index.bundlejs")), readBytes(zipFile.getInputStream(ze)));
591
-
592
- FileOutputStream fout = new FileOutputStream(new File(param.unzipDirectory, "index.bundlejs"));
593
- fout.write(patched);
594
- fout.close();
595
- continue;
596
- }
597
-
598
600
  zipFile.unzipToPath(ze, param.unzipDirectory);
599
601
  }
600
602
 
601
603
  zipFile.close();
602
604
 
603
- if (!foundDiff) {
604
- throw new Error("diff.json not found");
605
- }
606
- if (!foundBundlePatch) {
607
- throw new Error("bundle patch not found");
608
- }
605
+ ArchivePatchPlanResult plan = buildArchivePatchPlan(
606
+ DownloadTaskParams.TASK_TYPE_PATCH_FROM_PPK,
607
+ entryNames.toArray(new String[0]),
608
+ copyFroms.toArray(new String[0]),
609
+ copyTos.toArray(new String[0]),
610
+ deletes.toArray(new String[0])
611
+ );
612
+
613
+ applyPatchFromFileSource(
614
+ param.originDirectory.getAbsolutePath(),
615
+ param.unzipDirectory.getAbsolutePath(),
616
+ new File(param.originDirectory, "index.bundlejs").getAbsolutePath(),
617
+ new File(param.unzipDirectory, "index.bundlejs.patch").getAbsolutePath(),
618
+ new File(param.unzipDirectory, "index.bundlejs").getAbsolutePath(),
619
+ plan.mergeSourceSubdir,
620
+ plan.enableMerge,
621
+ copyFroms.toArray(new String[0]),
622
+ copyTos.toArray(new String[0]),
623
+ deletes.toArray(new String[0])
624
+ );
625
+
609
626
  if (UpdateContext.DEBUG) {
610
627
  Log.d("react-native-update", "Unzip finished");
611
628
  }
@@ -614,30 +631,12 @@ class DownloadTask extends AsyncTask<DownloadTaskParams, long[], Void> {
614
631
  if (UpdateContext.DEBUG) {
615
632
  Log.d("react-native-update", "Start cleaning up");
616
633
  }
617
- File root = param.unzipDirectory;
618
- for (File sub : root.listFiles()) {
619
- if (sub.getName().charAt(0) == '.') {
620
- continue;
621
- }
622
- if (isFileUpdatedWithinDays(sub, 7)) {
623
- continue;
624
- }
625
- if (sub.isFile()) {
626
- sub.delete();
627
- } else {
628
- if (sub.getName().equals(param.hash) || sub.getName().equals(param.originHash)) {
629
- continue;
630
- }
631
- removeDirectory(sub);
632
- }
633
- }
634
- }
635
-
636
- private boolean isFileUpdatedWithinDays(File file, int days) {
637
- long currentTime = System.currentTimeMillis();
638
- long lastModified = file.lastModified();
639
- long daysInMillis = days * 24 * 60 * 60 * 1000L;
640
- return (currentTime - lastModified) < daysInMillis;
634
+ cleanupOldEntries(
635
+ param.unzipDirectory.getAbsolutePath(),
636
+ param.hash,
637
+ param.originHash,
638
+ 7
639
+ );
641
640
  }
642
641
 
643
642
  @Override
@@ -0,0 +1,34 @@
1
+ package cn.reactnative.modules.update;
2
+
3
+ final class NativeUpdateCore {
4
+ private static boolean loaded = false;
5
+
6
+ private NativeUpdateCore() {
7
+ }
8
+
9
+ static synchronized void ensureLoaded() {
10
+ if (loaded) {
11
+ return;
12
+ }
13
+
14
+ try {
15
+ System.loadLibrary("c++_shared");
16
+ } catch (UnsatisfiedLinkError ignored) {
17
+ // Fall back to the transitive dependency load path when the host app already
18
+ // packages libc++_shared.so but the linker has not loaded it yet.
19
+ }
20
+
21
+ try {
22
+ System.loadLibrary("rnupdate");
23
+ } catch (UnsatisfiedLinkError error) {
24
+ UnsatisfiedLinkError wrapped = new UnsatisfiedLinkError(
25
+ "Failed to load rnupdate. Ensure the host app packages libc++_shared.so "
26
+ + "when using the shared C++ runtime. Original error: "
27
+ + error.getMessage());
28
+ wrapped.initCause(error);
29
+ throw wrapped;
30
+ }
31
+
32
+ loaded = true;
33
+ }
34
+ }
@@ -0,0 +1,16 @@
1
+ package cn.reactnative.modules.update;
2
+
3
+ class StateCoreResult {
4
+ String packageVersion;
5
+ String buildTime;
6
+ String currentVersion;
7
+ String lastVersion;
8
+ boolean firstTime;
9
+ boolean firstTimeOk;
10
+ String rolledBackVersion;
11
+ boolean changed;
12
+ String staleVersionToDelete;
13
+ String loadVersion;
14
+ boolean didRollback;
15
+ boolean consumedFirstTime;
16
+ }