react-native-update 10.37.19 → 10.38.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (132) 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 +136 -136
  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/android/src/main/java/cn/reactnative/modules/update/UpdateModuleImpl.java +88 -40
  18. package/cpp/patch_core/archive_patch_core.cpp +125 -0
  19. package/cpp/patch_core/archive_patch_core.h +59 -0
  20. package/cpp/patch_core/patch_core.cpp +533 -0
  21. package/cpp/patch_core/patch_core.h +68 -0
  22. package/cpp/patch_core/patch_core_android.cpp +112 -0
  23. package/cpp/patch_core/state_core.cpp +110 -0
  24. package/cpp/patch_core/state_core.h +58 -0
  25. package/cpp/patch_core/tests/patch_core_test.cpp +473 -0
  26. package/cpp/patch_core/update_core_android.cpp +469 -0
  27. package/harmony/pushy.har +0 -0
  28. package/ios/RCTPushy/RCTPushy.mm +233 -143
  29. package/package.json +17 -15
  30. package/react-native-update.podspec +3 -0
  31. package/scripts/build-harmony-har.js +12 -0
  32. package/scripts/prepublish.ts +49 -3
  33. package/scripts/prune-host-stl.sh +6 -0
  34. package/scripts/test-patch-core.sh +39 -0
  35. package/src/client.ts +129 -76
  36. package/src/core.ts +2 -1
  37. package/src/endpoint.ts +171 -0
  38. package/src/utils.ts +40 -27
  39. package/android/jni/lzma/DOC/7zC.txt +0 -187
  40. package/android/jni/lzma/DOC/7zFormat.txt +0 -469
  41. package/android/jni/lzma/DOC/Methods.txt +0 -173
  42. package/android/jni/lzma/DOC/installer.txt +0 -166
  43. package/android/jni/lzma/DOC/lzma-history.txt +0 -446
  44. package/android/jni/lzma/DOC/lzma-sdk.txt +0 -357
  45. package/android/jni/lzma/DOC/lzma-specification.txt +0 -1176
  46. package/android/jni/lzma/DOC/lzma.txt +0 -328
  47. package/android/jni/lzma/bin/7zS2.sfx +0 -0
  48. package/android/jni/lzma/bin/7zS2con.sfx +0 -0
  49. package/android/jni/lzma/bin/7zSD.sfx +0 -0
  50. package/android/jni/lzma/bin/7zdec.exe +0 -0
  51. package/android/jni/lzma/bin/7zr.exe +0 -0
  52. package/android/jni/lzma/bin/installer/config.txt +0 -5
  53. package/android/jni/lzma/bin/installer/cr.bat +0 -5
  54. package/android/jni/lzma/bin/lzma.exe +0 -0
  55. package/android/jni/lzma/bin/x64/7zr.exe +0 -0
  56. package/error.js +0 -1609
  57. package/harmony/har-wrapper/AppScope/app.json5 +0 -8
  58. package/harmony/har-wrapper/build-profile.json5 +0 -35
  59. package/harmony/har-wrapper/hvigor/hvigor-config.json5 +0 -5
  60. package/harmony/har-wrapper/hvigorfile.ts +0 -6
  61. package/harmony/har-wrapper/oh-package.json5 +0 -4
  62. package/harmony/pushy/.cxx/default/default/release/arm64-v8a/.cmake/api/v1/reply/cache-v2-77b153ce45aba0ed28ef.json +0 -1415
  63. package/harmony/pushy/.cxx/default/default/release/arm64-v8a/.cmake/api/v1/reply/cmakeFiles-v1-b65a07793384e0ce3e08.json +0 -809
  64. package/harmony/pushy/.cxx/default/default/release/arm64-v8a/.cmake/api/v1/reply/codemodel-v2-ce0e89410afd8bf3a057.json +0 -60
  65. package/harmony/pushy/.cxx/default/default/release/arm64-v8a/.cmake/api/v1/reply/directory-.-Release-f5ebdc15457944623624.json +0 -14
  66. package/harmony/pushy/.cxx/default/default/release/arm64-v8a/.cmake/api/v1/reply/index-2026-03-18T12-02-38-0668.json +0 -89
  67. package/harmony/pushy/.cxx/default/default/release/arm64-v8a/.cmake/api/v1/reply/target-rnupdate-Release-267153624504c9c3ffdd.json +0 -222
  68. package/harmony/pushy/.cxx/default/default/release/arm64-v8a/.ninja_deps +0 -0
  69. package/harmony/pushy/.cxx/default/default/release/arm64-v8a/.ninja_log +0 -8
  70. package/harmony/pushy/.cxx/default/default/release/arm64-v8a/CMakeCache.txt +0 -415
  71. package/harmony/pushy/.cxx/default/default/release/arm64-v8a/CMakeFiles/3.28.2/CMakeCCompiler.cmake +0 -74
  72. package/harmony/pushy/.cxx/default/default/release/arm64-v8a/CMakeFiles/3.28.2/CMakeCXXCompiler.cmake +0 -85
  73. package/harmony/pushy/.cxx/default/default/release/arm64-v8a/CMakeFiles/3.28.2/CMakeDetermineCompilerABI_C.bin +0 -0
  74. package/harmony/pushy/.cxx/default/default/release/arm64-v8a/CMakeFiles/3.28.2/CMakeDetermineCompilerABI_CXX.bin +0 -0
  75. package/harmony/pushy/.cxx/default/default/release/arm64-v8a/CMakeFiles/3.28.2/CMakeSystem.cmake +0 -15
  76. package/harmony/pushy/.cxx/default/default/release/arm64-v8a/CMakeFiles/3.28.2/CompilerIdC/CMakeCCompilerId.c +0 -880
  77. package/harmony/pushy/.cxx/default/default/release/arm64-v8a/CMakeFiles/3.28.2/CompilerIdC/CMakeCCompilerId.o +0 -0
  78. package/harmony/pushy/.cxx/default/default/release/arm64-v8a/CMakeFiles/3.28.2/CompilerIdCXX/CMakeCXXCompilerId.cpp +0 -869
  79. package/harmony/pushy/.cxx/default/default/release/arm64-v8a/CMakeFiles/3.28.2/CompilerIdCXX/CMakeCXXCompilerId.o +0 -0
  80. package/harmony/pushy/.cxx/default/default/release/arm64-v8a/CMakeFiles/CMakeConfigureLog.yaml +0 -388
  81. package/harmony/pushy/.cxx/default/default/release/arm64-v8a/CMakeFiles/TargetDirectories.txt +0 -3
  82. package/harmony/pushy/.cxx/default/default/release/arm64-v8a/CMakeFiles/cmake.check_cache +0 -1
  83. 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
  84. 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
  85. 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
  86. 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
  87. 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
  88. package/harmony/pushy/.cxx/default/default/release/arm64-v8a/CMakeFiles/rnupdate.dir/pushy.c.o +0 -0
  89. package/harmony/pushy/.cxx/default/default/release/arm64-v8a/CMakeFiles/rules.ninja +0 -64
  90. package/harmony/pushy/.cxx/default/default/release/arm64-v8a/additional_project_files.txt +0 -0
  91. package/harmony/pushy/.cxx/default/default/release/arm64-v8a/build.ninja +0 -206
  92. package/harmony/pushy/.cxx/default/default/release/arm64-v8a/build_file_index.txt +0 -1
  93. package/harmony/pushy/.cxx/default/default/release/arm64-v8a/cmake_install.cmake +0 -54
  94. package/harmony/pushy/.cxx/default/default/release/arm64-v8a/compile_commands.json +0 -38
  95. package/harmony/pushy/.cxx/default/default/release/arm64-v8a/configure_fingerprint.json +0 -1
  96. package/harmony/pushy/.cxx/default/default/release/arm64-v8a/hvigor_native_config.json +0 -1
  97. package/harmony/pushy/.cxx/default/default/release/arm64-v8a/metadata_generation_command.txt +0 -17
  98. package/harmony/pushy/.cxx/default/default/release/arm64-v8a/native_work_dir.txt +0 -1
  99. package/harmony/pushy/.cxx/default/default/release/arm64-v8a/output.log +0 -14
  100. package/harmony/pushy/.cxx/default/default/release/hvigor/arm64-v8a/summary.cmake +0 -0
  101. package/harmony/pushy/BuildProfile.ets +0 -17
  102. package/harmony/pushy/OAT.xml +0 -38
  103. package/harmony/pushy/README.md +0 -0
  104. package/harmony/pushy/build-profile.json5 +0 -15
  105. package/harmony/pushy/hvigor-plugin.ts +0 -34
  106. package/harmony/pushy/hvigorfile.ts +0 -1
  107. package/harmony/pushy/index.ets +0 -2
  108. package/harmony/pushy/oh-package-lock.json5 +0 -20
  109. package/harmony/pushy/oh-package.json5 +0 -13
  110. package/harmony/pushy/src/main/cpp/CMakeLists.txt +0 -51
  111. package/harmony/pushy/src/main/cpp/PushyPackage.h +0 -55
  112. package/harmony/pushy/src/main/cpp/PushyTurboModule.cpp +0 -142
  113. package/harmony/pushy/src/main/cpp/PushyTurboModule.h +0 -38
  114. package/harmony/pushy/src/main/cpp/pushy.c +0 -117
  115. package/harmony/pushy/src/main/cpp/pushy.h +0 -8
  116. package/harmony/pushy/src/main/ets/DownloadTask.ts +0 -570
  117. package/harmony/pushy/src/main/ets/DownloadTaskParams.ts +0 -19
  118. package/harmony/pushy/src/main/ets/EventHub.ts +0 -39
  119. package/harmony/pushy/src/main/ets/Logger.ts +0 -52
  120. package/harmony/pushy/src/main/ets/PushyFileJSBundleProvider.ets +0 -50
  121. package/harmony/pushy/src/main/ets/PushyPackage.ts +0 -22
  122. package/harmony/pushy/src/main/ets/PushyTurboModule.ts +0 -171
  123. package/harmony/pushy/src/main/ets/SaveFile.ts +0 -34
  124. package/harmony/pushy/src/main/ets/UpdateContext.ts +0 -262
  125. package/harmony/pushy/src/main/ets/UpdateModuleImpl.ts +0 -123
  126. package/harmony/pushy/src/main/module.json5 +0 -7
  127. package/harmony/pushy/src/main/resources/base/element/string.json +0 -8
  128. package/harmony/pushy/src/main/resources/en_US/element/string.json +0 -8
  129. package/harmony/pushy/src/main/resources/zh_CN/element/string.json +0 -8
  130. package/harmony/pushy/ts.ts +0 -3
  131. package/src/__tests__/core.test.ts +0 -103
  132. 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,43 @@ 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];
172
- }
194
+ private void copyBundledAssetToFile(String assetName, File destination) throws IOException {
195
+ InputStream in = context.getAssets().open(assetName);
196
+ FileOutputStream fout = new FileOutputStream(destination);
173
197
  int count;
174
-
175
- ByteArrayOutputStream fout = new ByteArrayOutputStream();
176
- while ((count = in.read(buffer)) != -1)
177
- {
198
+ while ((count = in.read(buffer)) != -1) {
178
199
  fout.write(buffer, 0, count);
179
200
  }
180
-
181
201
  fout.close();
182
202
  in.close();
183
- return fout.toByteArray();
184
203
  }
185
204
 
186
- private byte[] readFile(File file) throws IOException {
187
- InputStream in = new FileInputStream(file);
188
- int count;
189
-
190
- ByteArrayOutputStream fout = new ByteArrayOutputStream();
191
- while ((count = in.read(buffer)) != -1)
192
- {
193
- fout.write(buffer, 0, count);
205
+ private HashMap<String, ArrayList<File>> buildCopyList(
206
+ File unzipDirectory,
207
+ CopyGroupResult[] groups
208
+ ) throws IOException {
209
+ HashMap<String, ArrayList<File>> copyList = new HashMap<String, ArrayList<File>>();
210
+ if (groups == null) {
211
+ return copyList;
194
212
  }
195
213
 
196
- fout.close();
197
- in.close();
198
- return fout.toByteArray();
199
- }
200
-
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);
214
+ String rootPath = unzipDirectory.getCanonicalPath() + File.separator;
215
+ for (CopyGroupResult group : groups) {
216
+ ArrayList<File> targets = new ArrayList<File>();
217
+ if (group.toPaths != null) {
218
+ for (String to : group.toPaths) {
219
+ File toFile = new File(unzipDirectory, to);
220
+ String canonicalPath = toFile.getCanonicalPath();
221
+ if (!canonicalPath.startsWith(rootPath)) {
222
+ throw new SecurityException("Illegal name: " + to);
223
+ }
224
+ targets.add(toFile);
219
225
  }
220
226
  }
227
+ copyList.put(group.from, targets);
221
228
  }
222
- }
223
229
 
224
- private void copyFilesWithBlacklist(File from, File to, JSONObject blackList) throws IOException {
225
- copyFilesWithBlacklist("", from, to, blackList);
230
+ return copyList;
226
231
  }
227
232
 
228
233
  private void doFullPatch(DownloadTaskParams param) throws IOException {
@@ -457,20 +462,20 @@ class DownloadTask extends AsyncTask<DownloadTaskParams, long[], Void> {
457
462
 
458
463
  removeDirectory(param.unzipDirectory);
459
464
  param.unzipDirectory.mkdirs();
460
- HashMap<String, ArrayList<File>> copyList = new HashMap<String, ArrayList<File>>();
461
465
  HashMap<String, String> copiesMap = new HashMap<String, String>(); // to -> from 映射
462
-
463
- boolean foundDiff = false;
464
- boolean foundBundlePatch = false;
466
+ ArrayList<String> entryNames = new ArrayList<String>();
467
+ ArrayList<String> copyFroms = new ArrayList<String>();
468
+ ArrayList<String> copyTos = new ArrayList<String>();
469
+ ArrayList<String> deletes = new ArrayList<String>();
465
470
 
466
471
  SafeZipFile zipFile = new SafeZipFile(param.targetFile);
467
472
  Enumeration<? extends ZipEntry> entries = zipFile.entries();
468
473
  while (entries.hasMoreElements()) {
469
474
  ZipEntry ze = entries.nextElement();
470
475
  String fn = ze.getName();
476
+ entryNames.add(fn);
471
477
 
472
478
  if (fn.equals("__diff.json")) {
473
- foundDiff = true;
474
479
  // copy files from assets
475
480
  byte[] bytes = readBytes(zipFile.getInputStream(ze));
476
481
  String json = new String(bytes, "UTF-8");
@@ -484,51 +489,55 @@ class DownloadTask extends AsyncTask<DownloadTaskParams, long[], Void> {
484
489
  if (from.isEmpty()) {
485
490
  from = to;
486
491
  }
492
+ copyFroms.add(from);
493
+ copyTos.add(to);
487
494
  // 保存 copies 映射关系(to -> from)
488
495
  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);
496
+ }
497
+ JSONObject blackList = obj.getJSONObject("deletes");
498
+ Iterator<?> deleteKeys = blackList.keys();
499
+ while (deleteKeys.hasNext()) {
500
+ deletes.add((String)deleteKeys.next());
506
501
  }
507
502
  continue;
508
503
  }
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();
517
- continue;
518
- }
519
-
520
-
521
504
  zipFile.unzipToPath(ze, param.unzipDirectory);
522
505
  }
523
506
 
524
507
  zipFile.close();
525
508
 
526
-
527
- if (!foundDiff) {
528
- throw new Error("diff.json not found");
529
- }
530
- if (!foundBundlePatch) {
531
- throw new Error("bundle patch not found");
509
+ buildArchivePatchPlan(
510
+ DownloadTaskParams.TASK_TYPE_PATCH_FROM_APK,
511
+ entryNames.toArray(new String[0]),
512
+ copyFroms.toArray(new String[0]),
513
+ copyTos.toArray(new String[0]),
514
+ deletes.toArray(new String[0])
515
+ );
516
+ HashMap<String, ArrayList<File>> copyList = buildCopyList(
517
+ param.unzipDirectory,
518
+ buildCopyGroups(
519
+ copyFroms.toArray(new String[0]),
520
+ copyTos.toArray(new String[0])
521
+ )
522
+ );
523
+
524
+ File originBundleFile = new File(param.unzipDirectory, ".origin.bundle");
525
+ copyBundledAssetToFile("index.android.bundle", originBundleFile);
526
+ try {
527
+ applyPatchFromFileSource(
528
+ param.unzipDirectory.getAbsolutePath(),
529
+ param.unzipDirectory.getAbsolutePath(),
530
+ originBundleFile.getAbsolutePath(),
531
+ new File(param.unzipDirectory, "index.bundlejs.patch").getAbsolutePath(),
532
+ new File(param.unzipDirectory, "index.bundlejs").getAbsolutePath(),
533
+ "",
534
+ false,
535
+ new String[0],
536
+ new String[0],
537
+ new String[0]
538
+ );
539
+ } finally {
540
+ originBundleFile.delete();
532
541
  }
533
542
 
534
543
  if (UpdateContext.DEBUG) {
@@ -552,10 +561,10 @@ class DownloadTask extends AsyncTask<DownloadTaskParams, long[], Void> {
552
561
  removeDirectory(param.unzipDirectory);
553
562
  param.unzipDirectory.mkdirs();
554
563
 
555
- int count;
556
- String filename;
557
- boolean foundDiff = false;
558
- boolean foundBundlePatch = false;
564
+ ArrayList<String> entryNames = new ArrayList<String>();
565
+ ArrayList<String> copyFroms = new ArrayList<String>();
566
+ ArrayList<String> copyTos = new ArrayList<String>();
567
+ ArrayList<String> deletes = new ArrayList<String>();
559
568
 
560
569
 
561
570
  SafeZipFile zipFile = new SafeZipFile(param.targetFile);
@@ -563,9 +572,9 @@ class DownloadTask extends AsyncTask<DownloadTaskParams, long[], Void> {
563
572
  while (entries.hasMoreElements()) {
564
573
  ZipEntry ze = entries.nextElement();
565
574
  String fn = ze.getName();
575
+ entryNames.add(fn);
566
576
 
567
577
  if (fn.equals("__diff.json")) {
568
- foundDiff = true;
569
578
  // copy files from assets
570
579
  byte[] bytes = readBytes(zipFile.getInputStream(ze));
571
580
  String json = new String(bytes, "UTF-8");
@@ -579,33 +588,42 @@ class DownloadTask extends AsyncTask<DownloadTaskParams, long[], Void> {
579
588
  if (from.isEmpty()) {
580
589
  from = to;
581
590
  }
582
- copyFile(new File(param.originDirectory, from), new File(param.unzipDirectory, to));
591
+ copyFroms.add(from);
592
+ copyTos.add(to);
583
593
  }
584
594
  JSONObject blackList = obj.getJSONObject("deletes");
585
- copyFilesWithBlacklist(param.originDirectory, param.unzipDirectory, blackList);
586
- continue;
587
- }
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
+ Iterator<?> deleteKeys = blackList.keys();
596
+ while (deleteKeys.hasNext()) {
597
+ deletes.add((String)deleteKeys.next());
598
+ }
595
599
  continue;
596
600
  }
597
-
598
601
  zipFile.unzipToPath(ze, param.unzipDirectory);
599
602
  }
600
603
 
601
604
  zipFile.close();
602
605
 
603
- if (!foundDiff) {
604
- throw new Error("diff.json not found");
605
- }
606
- if (!foundBundlePatch) {
607
- throw new Error("bundle patch not found");
608
- }
606
+ ArchivePatchPlanResult plan = buildArchivePatchPlan(
607
+ DownloadTaskParams.TASK_TYPE_PATCH_FROM_PPK,
608
+ entryNames.toArray(new String[0]),
609
+ copyFroms.toArray(new String[0]),
610
+ copyTos.toArray(new String[0]),
611
+ deletes.toArray(new String[0])
612
+ );
613
+
614
+ applyPatchFromFileSource(
615
+ param.originDirectory.getAbsolutePath(),
616
+ param.unzipDirectory.getAbsolutePath(),
617
+ new File(param.originDirectory, "index.bundlejs").getAbsolutePath(),
618
+ new File(param.unzipDirectory, "index.bundlejs.patch").getAbsolutePath(),
619
+ new File(param.unzipDirectory, "index.bundlejs").getAbsolutePath(),
620
+ plan.mergeSourceSubdir,
621
+ plan.enableMerge,
622
+ copyFroms.toArray(new String[0]),
623
+ copyTos.toArray(new String[0]),
624
+ deletes.toArray(new String[0])
625
+ );
626
+
609
627
  if (UpdateContext.DEBUG) {
610
628
  Log.d("react-native-update", "Unzip finished");
611
629
  }
@@ -614,30 +632,12 @@ class DownloadTask extends AsyncTask<DownloadTaskParams, long[], Void> {
614
632
  if (UpdateContext.DEBUG) {
615
633
  Log.d("react-native-update", "Start cleaning up");
616
634
  }
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;
635
+ cleanupOldEntries(
636
+ param.unzipDirectory.getAbsolutePath(),
637
+ param.hash,
638
+ param.originHash,
639
+ 7
640
+ );
641
641
  }
642
642
 
643
643
  @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
+ }