react-native-update 10.59.1 → 10.60.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 (125) hide show
  1. package/README-CN.md +7 -7
  2. package/README.md +7 -6
  3. package/android/build.gradle +25 -33
  4. package/android/jni/Android.mk +16 -24
  5. package/android/lib/arm64-v8a/librnpushy.so +0 -0
  6. package/android/lib/armeabi-v7a/librnpushy.so +0 -0
  7. package/android/lib/x86/librnpushy.so +0 -0
  8. package/android/lib/x86_64/librnpushy.so +0 -0
  9. package/android/proguard.pro +9 -7
  10. package/android/src/main/java/cn/reactnative/modules/update/ApkInstaller.java +11 -12
  11. package/android/src/main/java/cn/reactnative/modules/update/ArchiveLimits.java +23 -6
  12. package/android/src/main/java/cn/reactnative/modules/update/BundledResourceCopier.java +50 -51
  13. package/android/src/main/java/cn/reactnative/modules/update/CleanupKeepNames.java +12 -13
  14. package/android/src/main/java/cn/reactnative/modules/update/CopyGroupResult.java +3 -0
  15. package/android/src/main/java/cn/reactnative/modules/update/CrashHold.java +22 -30
  16. package/android/src/main/java/cn/reactnative/modules/update/DownloadTask.java +181 -332
  17. package/android/src/main/java/cn/reactnative/modules/update/DownloadTaskParams.java +3 -1
  18. package/android/src/main/java/cn/reactnative/modules/update/ErrorCodes.java +27 -0
  19. package/android/src/main/java/cn/reactnative/modules/update/FileOperationException.java +1 -1
  20. package/android/src/main/java/cn/reactnative/modules/update/FlowBridge.java +32 -8
  21. package/android/src/main/java/cn/reactnative/modules/update/HttpClients.java +63 -0
  22. package/android/src/main/java/cn/reactnative/modules/update/HttpUtils.java +0 -11
  23. package/android/src/main/java/cn/reactnative/modules/update/InstallRecord.java +66 -116
  24. package/android/src/main/java/cn/reactnative/modules/update/NativeCore.java +9 -1
  25. package/android/src/main/java/cn/reactnative/modules/update/PatchFailedException.java +1 -1
  26. package/android/src/main/java/cn/reactnative/modules/update/PushyRuntime.java +32 -60
  27. package/android/src/main/java/cn/reactnative/modules/update/ReactReloadManager.java +8 -23
  28. package/android/src/main/java/cn/reactnative/modules/update/SafeZipFile.java +42 -80
  29. package/android/src/main/java/cn/reactnative/modules/update/StateSerialRunner.java +22 -39
  30. package/android/src/main/java/cn/reactnative/modules/update/SyncCoordinator.java +176 -299
  31. package/android/src/main/java/cn/reactnative/modules/update/Threads.java +35 -0
  32. package/android/src/main/java/cn/reactnative/modules/update/UpdateContext.java +242 -174
  33. package/android/src/main/java/cn/reactnative/modules/update/UpdateEventEmitter.java +1 -1
  34. package/android/src/main/java/cn/reactnative/modules/update/UpdateFileUtils.java +75 -17
  35. package/android/src/main/java/cn/reactnative/modules/update/{UpdateModuleImpl.java → UpdateModule.java} +113 -95
  36. package/android/src/main/java/cn/reactnative/modules/update/UpdatePackage.java +16 -19
  37. package/android/src/oldarch/cn/reactnative/modules/update/NativePushySpec.java +69 -0
  38. package/cpp/android_jni/archive_limits_jni.cpp +83 -0
  39. package/cpp/android_jni/install_record_jni.cpp +89 -0
  40. package/cpp/android_jni/jni_log.h +31 -0
  41. package/cpp/android_jni/jni_natives.h +40 -0
  42. package/cpp/android_jni/jni_registration.cpp +133 -0
  43. package/cpp/android_jni/jni_util.h +160 -0
  44. package/cpp/android_jni/update_core_android.cpp +450 -0
  45. package/cpp/android_jni/update_flow_jni.cpp +133 -0
  46. package/{android/jni → cpp/hpatch}/hpatch.c +5 -5
  47. package/{android/jni → cpp/hpatch}/hpatch.h +4 -1
  48. package/cpp/patch_core/archive_limits.cpp +194 -0
  49. package/cpp/patch_core/archive_limits.h +99 -10
  50. package/cpp/patch_core/archive_patch_core.cpp +96 -12
  51. package/cpp/patch_core/archive_patch_core.h +49 -0
  52. package/cpp/patch_core/digest.h +5 -4
  53. package/cpp/patch_core/error_codes.h +5 -3
  54. package/cpp/patch_core/install_record.cpp +430 -0
  55. package/cpp/patch_core/install_record.h +163 -9
  56. package/cpp/patch_core/patch_core.cpp +62 -16
  57. package/cpp/patch_core/patch_core.h +27 -7
  58. package/cpp/patch_core/patch_manifest.cpp +201 -0
  59. package/cpp/patch_core/patch_manifest.h +74 -0
  60. package/cpp/patch_core/state_ops.h +3 -3
  61. package/cpp/update_flow_core/flow_json.cpp +82 -8
  62. package/cpp/update_flow_core/flow_json.h +21 -0
  63. package/cpp/update_flow_core/native_config.cpp +456 -0
  64. package/cpp/update_flow_core/native_config.h +84 -0
  65. package/cpp/update_flow_core/native_round.cpp +326 -0
  66. package/cpp/update_flow_core/native_round.h +90 -0
  67. package/cpp/update_flow_core/update_flow_core.h +2 -2
  68. package/harmony/hvigor-plugin.ts +11 -19
  69. package/harmony/pushy/src/main/cpp/CMakeLists.txt +19 -26
  70. package/harmony/pushy/src/main/cpp/PushyPackage.h +2 -2
  71. package/harmony/pushy/src/main/cpp/PushyTurboModule.cpp +55 -94
  72. package/harmony/pushy/src/main/cpp/pushy.cpp +694 -517
  73. package/harmony/pushy/src/main/ets/ArchiveLimits.ts +48 -72
  74. package/harmony/pushy/src/main/ets/DownloadTask.ts +164 -404
  75. package/harmony/pushy/src/main/ets/DownloadTaskParams.ts +1 -3
  76. package/harmony/pushy/src/main/ets/ErrorCodes.ts +1 -14
  77. package/harmony/pushy/src/main/ets/EventHub.ts +1 -1
  78. package/harmony/pushy/src/main/ets/InstallRecord.ts +52 -151
  79. package/harmony/pushy/src/main/ets/Logger.ts +20 -34
  80. package/harmony/pushy/src/main/ets/NativePatchCore.ts +116 -42
  81. package/harmony/pushy/src/main/ets/PushyConfiguration.ts +8 -85
  82. package/harmony/pushy/src/main/ets/PushyTurboModule.ts +11 -25
  83. package/harmony/pushy/src/main/ets/SaveFile.ts +99 -18
  84. package/harmony/pushy/src/main/ets/SyncCoordinator.ts +136 -335
  85. package/harmony/pushy/src/main/ets/Texts.ts +0 -4
  86. package/harmony/pushy/src/main/ets/UpdateContext.ts +106 -119
  87. package/harmony/pushy.har +0 -0
  88. package/ios/RCTPushy/RCTPushy+Launch.mm +185 -0
  89. package/ios/RCTPushy/RCTPushy.h +12 -0
  90. package/ios/RCTPushy/RCTPushy.mm +87 -2628
  91. package/ios/RCTPushy/RCTPushyCommon.h +34 -0
  92. package/ios/RCTPushy/RCTPushyConfiguration.h +0 -8
  93. package/ios/RCTPushy/RCTPushyConfiguration.mm +40 -121
  94. package/ios/RCTPushy/RCTPushyDownloader.mm +40 -37
  95. package/ios/RCTPushy/RCTPushyInstallRecord.mm +61 -0
  96. package/ios/RCTPushy/RCTPushyInstaller.mm +689 -0
  97. package/ios/RCTPushy/RCTPushyInternal.h +144 -0
  98. package/ios/RCTPushy/RCTPushyOrchestrator.mm +1001 -0
  99. package/ios/RCTPushy/RCTPushyState.mm +314 -0
  100. package/package.json +25 -23
  101. package/react-native-update.podspec +74 -83
  102. package/src/client.ts +361 -451
  103. package/src/context.ts +1 -1
  104. package/src/core.ts +35 -8
  105. package/src/endpoint.ts +0 -2
  106. package/src/error.ts +13 -32
  107. package/src/i18n.ts +18 -83
  108. package/src/locales/en.ts +0 -4
  109. package/src/locales/zh.ts +0 -4
  110. package/src/provider.tsx +60 -53
  111. package/src/telemetry.ts +0 -13
  112. package/src/type.ts +0 -15
  113. package/src/updateFlowCore.ts +116 -10
  114. package/src/utils.ts +49 -42
  115. package/android/src/main/java/cn/reactnative/modules/update/ArchivePatchPlanResult.java +0 -6
  116. package/android/src/main/java/cn/reactnative/modules/update/PushyConfiguration.java +0 -104
  117. package/android/src/main/java/cn/reactnative/modules/update/UiThreadRunner.java +0 -37
  118. package/android/src/newarch/cn/reactnative/modules/update/UpdateModule.java +0 -121
  119. package/android/src/oldarch/cn/reactnative/modules/update/UpdateModule.java +0 -125
  120. package/cpp/patch_core/jni_natives.h +0 -27
  121. package/cpp/patch_core/jni_registration.cpp +0 -103
  122. package/cpp/patch_core/jni_util.h +0 -60
  123. package/cpp/patch_core/patch_core_android.cpp +0 -97
  124. package/cpp/patch_core/update_core_android.cpp +0 -485
  125. package/cpp/update_flow_core/update_flow_jni.cpp +0 -150
@@ -0,0 +1,450 @@
1
+ #include <jni.h>
2
+
3
+ #include "jni_log.h"
4
+ #include "jni_natives.h"
5
+ #include "../patch_core/obscured_text.h"
6
+
7
+ #include <string>
8
+ #include <vector>
9
+
10
+ #include "../patch_core/archive_patch_core.h"
11
+ #include "../patch_core/hbc_transform_wire.h"
12
+ #include "../patch_core/patch_core.h"
13
+ #include "jni_util.h"
14
+ #include "../patch_core/state_core.h"
15
+ #include "../patch_core/state_ops.h"
16
+
17
+ namespace {
18
+
19
+ using pushy::jni_util::GuardJni;
20
+ using pushy::jni_util::JArrayToVector;
21
+ using pushy::jni_util::JByteArrayToString;
22
+ using pushy::jni_util::JStringToString;
23
+ using pushy::jni_util::ThrowRuntimeException;
24
+ using pushy::jni_util::ToJString;
25
+ using pushy::state_ops::StateOperation;
26
+
27
+ constexpr const char* kStringSig = "Ljava/lang/String;";
28
+
29
+ // JNI result classes, resolved once in JNI_OnLoad (InitResultClassCache)
30
+ // instead of FindClass + a GetFieldID per field on every state operation.
31
+ // Written only before any native method can run, read-only afterwards.
32
+ struct StateResultIds {
33
+ bool ready = false;
34
+ jclass clazz = nullptr;
35
+ jmethodID constructor = nullptr;
36
+ jfieldID package_version = nullptr;
37
+ jfieldID build_time = nullptr;
38
+ jfieldID current_version = nullptr;
39
+ jfieldID last_version = nullptr;
40
+ jfieldID first_time = nullptr;
41
+ jfieldID first_time_ok = nullptr;
42
+ jfieldID rolled_back_version = nullptr;
43
+ jfieldID changed = nullptr;
44
+ jfieldID stale_version_to_delete = nullptr;
45
+ jfieldID load_version = nullptr;
46
+ jfieldID did_rollback = nullptr;
47
+ jfieldID consumed_first_time = nullptr;
48
+ };
49
+
50
+ struct CopyGroupIds {
51
+ bool ready = false;
52
+ jclass clazz = nullptr;
53
+ jclass string_class = nullptr;
54
+ jmethodID constructor = nullptr;
55
+ jfieldID from = nullptr;
56
+ jfieldID to_paths = nullptr;
57
+ jfieldID expected_crc = nullptr;
58
+ };
59
+
60
+ StateResultIds g_state;
61
+ CopyGroupIds g_copy_group;
62
+
63
+ // Resolves a class to a global ref. Logs and clears the pending exception on
64
+ // failure.
65
+ jclass FindGlobalClass(JNIEnv* env, const std::string& name) {
66
+ jclass local = env->FindClass(name.c_str());
67
+ if (local == nullptr) {
68
+ env->ExceptionClear();
69
+ PUSHY_JNI_LOGE("JNI result class %s not found", name.c_str());
70
+ return nullptr;
71
+ }
72
+ auto* global = static_cast<jclass>(env->NewGlobalRef(local));
73
+ env->DeleteLocalRef(local);
74
+ return global;
75
+ }
76
+
77
+ // A missing field must never silently default: a lost firstTimeOk would read
78
+ // as false and roll back every launch. Logs the exact member.
79
+ bool LookupField(
80
+ JNIEnv* env,
81
+ jclass clazz,
82
+ const std::string& class_name,
83
+ const char* name,
84
+ const char* signature,
85
+ jfieldID* out) {
86
+ *out = env->GetFieldID(clazz, name, signature);
87
+ if (*out == nullptr) {
88
+ env->ExceptionClear();
89
+ PUSHY_JNI_LOGE("JNI field %s.%s (%s) not found", class_name.c_str(), name,
90
+ signature);
91
+ return false;
92
+ }
93
+ return true;
94
+ }
95
+
96
+ bool LookupConstructor(
97
+ JNIEnv* env, jclass clazz, const std::string& class_name, jmethodID* out) {
98
+ *out = env->GetMethodID(clazz, "<init>", "()V");
99
+ if (*out == nullptr) {
100
+ env->ExceptionClear();
101
+ PUSHY_JNI_LOGE("JNI no-arg constructor of %s not found", class_name.c_str());
102
+ return false;
103
+ }
104
+ return true;
105
+ }
106
+
107
+ bool InitStateResultIds(JNIEnv* env) {
108
+ const std::string name = pushy::text::Reveal(
109
+ "3919bbc3ab8a6b512c3e08f0c0b6df60452311edfbc8f780624b2d1de38c93a99b63511201f9cd97878c695522");
110
+ StateResultIds ids;
111
+ ids.clazz = FindGlobalClass(env, name);
112
+ if (ids.clazz == nullptr) {
113
+ return false;
114
+ }
115
+ bool ok = LookupConstructor(env, ids.clazz, name, &ids.constructor);
116
+ ok &= LookupField(env, ids.clazz, name, "packageVersion", kStringSig, &ids.package_version);
117
+ ok &= LookupField(env, ids.clazz, name, "buildTime", kStringSig, &ids.build_time);
118
+ ok &= LookupField(env, ids.clazz, name, "currentVersion", kStringSig, &ids.current_version);
119
+ ok &= LookupField(env, ids.clazz, name, "lastVersion", kStringSig, &ids.last_version);
120
+ ok &= LookupField(env, ids.clazz, name, "firstTime", "Z", &ids.first_time);
121
+ ok &= LookupField(env, ids.clazz, name, "firstTimeOk", "Z", &ids.first_time_ok);
122
+ ok &= LookupField(env, ids.clazz, name, "rolledBackVersion", kStringSig, &ids.rolled_back_version);
123
+ ok &= LookupField(env, ids.clazz, name, "changed", "Z", &ids.changed);
124
+ ok &= LookupField(env, ids.clazz, name, "staleVersionToDelete", kStringSig, &ids.stale_version_to_delete);
125
+ ok &= LookupField(env, ids.clazz, name, "loadVersion", kStringSig, &ids.load_version);
126
+ ok &= LookupField(env, ids.clazz, name, "didRollback", "Z", &ids.did_rollback);
127
+ ok &= LookupField(env, ids.clazz, name, "consumedFirstTime", "Z", &ids.consumed_first_time);
128
+ ids.ready = ok;
129
+ g_state = ids;
130
+ return ok;
131
+ }
132
+
133
+ bool InitCopyGroupIds(JNIEnv* env) {
134
+ const std::string name = pushy::text::Reveal(
135
+ "3919bbc3ab8a6b512c3e08f0c0b6df60452311edfbc8f780624b2d1de38c83b28a6e732301fed897878c695522");
136
+ CopyGroupIds ids;
137
+ ids.clazz = FindGlobalClass(env, name);
138
+ ids.string_class = FindGlobalClass(env, "java/lang/String");
139
+ if (ids.clazz == nullptr || ids.string_class == nullptr) {
140
+ return false;
141
+ }
142
+ bool ok = LookupConstructor(env, ids.clazz, name, &ids.constructor);
143
+ ok &= LookupField(env, ids.clazz, name, "from", kStringSig, &ids.from);
144
+ ok &= LookupField(env, ids.clazz, name, "toPaths", "[Ljava/lang/String;", &ids.to_paths);
145
+ ok &= LookupField(env, ids.clazz, name, "expectedCrc", "J", &ids.expected_crc);
146
+ ids.ready = ok;
147
+ g_copy_group = ids;
148
+ return ok;
149
+ }
150
+
151
+ // Throws (and returns false) when the class needed by a native is unusable;
152
+ // the reason was logged once at load time.
153
+ bool RequireReady(JNIEnv* env, bool ready, const char* what) {
154
+ if (!ready) {
155
+ ThrowRuntimeException(
156
+ env, std::string("JNI result class unavailable: ") + what +
157
+ " (see the load-time log for the missing member)");
158
+ }
159
+ return ready;
160
+ }
161
+
162
+ void SetStringField(
163
+ JNIEnv* env,
164
+ jobject target,
165
+ jfieldID field,
166
+ const std::string& value) {
167
+ if (value.empty()) {
168
+ env->SetObjectField(target, field, nullptr);
169
+ return;
170
+ }
171
+
172
+ jstring java_value = ToJString(env, value);
173
+ env->SetObjectField(target, field, java_value);
174
+ env->DeleteLocalRef(java_value);
175
+ }
176
+
177
+ void SetBooleanField(JNIEnv* env, jobject target, jfieldID field, bool value) {
178
+ env->SetBooleanField(target, field, value ? JNI_TRUE : JNI_FALSE);
179
+ }
180
+
181
+ std::string GetStringField(JNIEnv* env, jobject target, jfieldID field) {
182
+ auto* value = static_cast<jstring>(env->GetObjectField(target, field));
183
+ std::string result = JStringToString(env, value);
184
+ if (value != nullptr) {
185
+ env->DeleteLocalRef(value);
186
+ }
187
+ return result;
188
+ }
189
+
190
+ bool GetBooleanField(JNIEnv* env, jobject target, jfieldID field) {
191
+ return env->GetBooleanField(target, field) == JNI_TRUE;
192
+ }
193
+
194
+ // Caller has checked g_state.ready.
195
+ pushy::state::State ReadStateFromResult(JNIEnv* env, jobject state_result) {
196
+ pushy::state::State state;
197
+ if (state_result == nullptr) {
198
+ return state;
199
+ }
200
+ state.package_version = GetStringField(env, state_result, g_state.package_version);
201
+ state.build_time = GetStringField(env, state_result, g_state.build_time);
202
+ state.current_version = GetStringField(env, state_result, g_state.current_version);
203
+ state.last_version = GetStringField(env, state_result, g_state.last_version);
204
+ state.first_time = GetBooleanField(env, state_result, g_state.first_time);
205
+ state.first_time_ok = GetBooleanField(env, state_result, g_state.first_time_ok);
206
+ state.rolled_back_version =
207
+ GetStringField(env, state_result, g_state.rolled_back_version);
208
+ return state;
209
+ }
210
+
211
+ // Caller has checked g_state.ready.
212
+ jobject NewStateCoreResult(
213
+ JNIEnv* env,
214
+ const pushy::state::State& state,
215
+ bool changed = false,
216
+ const std::string& stale_version_to_delete = std::string(),
217
+ const std::string& load_version = std::string(),
218
+ bool did_rollback = false,
219
+ bool consumed_first_time = false) {
220
+ jobject result = env->NewObject(g_state.clazz, g_state.constructor);
221
+ if (result == nullptr) {
222
+ return nullptr;
223
+ }
224
+
225
+ SetStringField(env, result, g_state.package_version, state.package_version);
226
+ SetStringField(env, result, g_state.build_time, state.build_time);
227
+ SetStringField(env, result, g_state.current_version, state.current_version);
228
+ SetStringField(env, result, g_state.last_version, state.last_version);
229
+ SetBooleanField(env, result, g_state.first_time, state.first_time);
230
+ SetBooleanField(env, result, g_state.first_time_ok, state.first_time_ok);
231
+ SetStringField(
232
+ env, result, g_state.rolled_back_version, state.rolled_back_version);
233
+ SetBooleanField(env, result, g_state.changed, changed);
234
+ SetStringField(
235
+ env, result, g_state.stale_version_to_delete, stale_version_to_delete);
236
+ SetStringField(env, result, g_state.load_version, load_version);
237
+ SetBooleanField(env, result, g_state.did_rollback, did_rollback);
238
+ SetBooleanField(
239
+ env, result, g_state.consumed_first_time, consumed_first_time);
240
+ return result;
241
+ }
242
+
243
+ // Caller has checked g_copy_group.ready.
244
+ jobject NewCopyGroupResult(
245
+ JNIEnv* env,
246
+ const pushy::archive_patch::CopyGroup& group) {
247
+ jobject result = env->NewObject(g_copy_group.clazz, g_copy_group.constructor);
248
+ if (result == nullptr) {
249
+ return nullptr;
250
+ }
251
+
252
+ SetStringField(env, result, g_copy_group.from, group.from);
253
+ jobjectArray to_paths = env->NewObjectArray(
254
+ static_cast<jsize>(group.to_paths.size()), g_copy_group.string_class,
255
+ nullptr);
256
+ if (to_paths != nullptr) {
257
+ for (jsize index = 0; index < static_cast<jsize>(group.to_paths.size());
258
+ ++index) {
259
+ jstring value = ToJString(env, group.to_paths[index]);
260
+ env->SetObjectArrayElement(to_paths, index, value);
261
+ env->DeleteLocalRef(value);
262
+ }
263
+ env->SetObjectField(result, g_copy_group.to_paths, to_paths);
264
+ env->DeleteLocalRef(to_paths);
265
+ }
266
+ env->SetLongField(
267
+ result, g_copy_group.expected_crc,
268
+ group.has_expected_crc ? static_cast<jlong>(group.expected_crc) : -1);
269
+
270
+ return result;
271
+ }
272
+
273
+ } // namespace
274
+
275
+ bool pushy::jni_natives::InitResultClassCache(JNIEnv* env) {
276
+ // Every class is attempted so the log names all missing members at once.
277
+ bool ok = InitStateResultIds(env);
278
+ ok &= InitCopyGroupIds(env);
279
+ return ok;
280
+ }
281
+
282
+ // 客户端可消费的 diff 轨道版本(能力上报,而非 SDK 版本映射);JS 层经
283
+ // getConstants 暴露,随 checkUpdate 以 diffV 上报,服务端据此下发 v2 轨道。
284
+ jint pushy::jni_natives::SupportedDiffVersion(
285
+ JNIEnv*,
286
+ jclass) {
287
+ return static_cast<jint>(pushy::hbc::kSupportedDiffVersion);
288
+ }
289
+
290
+ jobject pushy::jni_natives::SyncStateWithBinaryVersion(
291
+ JNIEnv* env,
292
+ jclass,
293
+ jstring package_version,
294
+ jstring build_time,
295
+ jobject state_result) {
296
+ return GuardJni(
297
+ env, "Unexpected native exception in syncStateWithBinaryVersion",
298
+ [&]() -> jobject {
299
+ if (!RequireReady(env, g_state.ready, "state result")) {
300
+ return nullptr;
301
+ }
302
+ pushy::state::State state = ReadStateFromResult(env, state_result);
303
+ pushy::state::BinaryVersionSyncResult result =
304
+ pushy::state::SyncBinaryVersion(
305
+ state,
306
+ JStringToString(env, package_version),
307
+ JStringToString(env, build_time));
308
+ return NewStateCoreResult(env, result.state, result.changed);
309
+ });
310
+ }
311
+
312
+ jobject pushy::jni_natives::RunStateCore(
313
+ JNIEnv* env,
314
+ jclass,
315
+ jint operation,
316
+ jobject state_result,
317
+ jstring string_arg,
318
+ jboolean flag_a,
319
+ jboolean flag_b) {
320
+ return GuardJni(
321
+ env, "Unexpected native exception in runStateCore", [&]() -> jobject {
322
+ if (!RequireReady(env, g_state.ready, "state result")) {
323
+ return nullptr;
324
+ }
325
+ const pushy::state::State state = ReadStateFromResult(env, state_result);
326
+ switch (static_cast<StateOperation>(operation)) {
327
+ case StateOperation::kSwitchVersion:
328
+ return NewStateCoreResult(
329
+ env,
330
+ pushy::state::SwitchVersion(
331
+ state, JStringToString(env, string_arg)));
332
+ case StateOperation::kMarkSuccess: {
333
+ const pushy::state::MarkSuccessResult result =
334
+ pushy::state::MarkSuccess(state);
335
+ return NewStateCoreResult(
336
+ env, result.state, false, result.stale_version_to_delete);
337
+ }
338
+ case StateOperation::kRollback: {
339
+ const pushy::state::State next = pushy::state::Rollback(state);
340
+ return NewStateCoreResult(
341
+ env, next, false, std::string(), next.current_version, true);
342
+ }
343
+ case StateOperation::kClearFirstTime:
344
+ return NewStateCoreResult(env, pushy::state::ClearFirstTime(state));
345
+ case StateOperation::kClearRollbackMark:
346
+ return NewStateCoreResult(
347
+ env, pushy::state::ClearRollbackMark(state));
348
+ case StateOperation::kResolveLaunch: {
349
+ const pushy::state::LaunchDecision decision =
350
+ pushy::state::ResolveLaunchState(
351
+ state, flag_a == JNI_TRUE, flag_b == JNI_TRUE);
352
+ return NewStateCoreResult(
353
+ env,
354
+ decision.state,
355
+ false,
356
+ std::string(),
357
+ decision.load_version,
358
+ decision.did_rollback,
359
+ decision.consumed_first_time);
360
+ }
361
+ }
362
+
363
+ ThrowRuntimeException(env, "Unknown state operation");
364
+ return nullptr;
365
+ });
366
+ }
367
+
368
+ jobjectArray pushy::jni_natives::ApplyArchiveDelta(
369
+ JNIEnv* env,
370
+ jclass,
371
+ jint patch_type,
372
+ jobjectArray entry_names,
373
+ jbyteArray manifest_json,
374
+ jstring source_root,
375
+ jstring target_root,
376
+ jstring origin_bundle_path,
377
+ jstring bundle_patch_path,
378
+ jstring bundle_output_path,
379
+ jboolean host_copies_resources) {
380
+ return GuardJni(
381
+ env, "Unexpected native exception while applying archive delta",
382
+ [&]() -> jobjectArray {
383
+ if (!RequireReady(env, g_copy_group.ready, "copy group result")) {
384
+ return nullptr;
385
+ }
386
+ pushy::archive_patch::ArchivePatchRequest request;
387
+ // Unknown values never fall back to kFull (which would skip the
388
+ // manifest and entry checks).
389
+ if (!pushy::archive_patch::TryParseArchivePatchType(
390
+ static_cast<int>(patch_type), &request.archive.type)) {
391
+ ThrowRuntimeException(env, "Unknown archive delta type");
392
+ return nullptr;
393
+ }
394
+ request.archive.entry_names = JArrayToVector(env, entry_names);
395
+ // The manifest's own UTF-8 bytes: a jstring would come through as
396
+ // modified UTF-8 and mangle supplementary characters in paths.
397
+ request.archive.manifest_json = JByteArrayToString(env, manifest_json);
398
+ request.source_root = JStringToString(env, source_root);
399
+ request.target_root = JStringToString(env, target_root);
400
+ request.origin_bundle_path = JStringToString(env, origin_bundle_path);
401
+ request.bundle_patch_path = JStringToString(env, bundle_patch_path);
402
+ request.bundle_output_path = JStringToString(env, bundle_output_path);
403
+ request.host_copies_resources = host_copies_resources == JNI_TRUE;
404
+
405
+ std::vector<pushy::archive_patch::CopyGroup> groups;
406
+ const pushy::delta::Status status =
407
+ pushy::archive_patch::ApplyArchivePatch(request, &groups);
408
+ if (!status.ok) {
409
+ ThrowRuntimeException(env, status.message);
410
+ return nullptr;
411
+ }
412
+
413
+ jobjectArray result = env->NewObjectArray(
414
+ static_cast<jsize>(groups.size()), g_copy_group.clazz, nullptr);
415
+ if (result == nullptr) {
416
+ return nullptr;
417
+ }
418
+ for (jsize index = 0; index < static_cast<jsize>(groups.size());
419
+ ++index) {
420
+ jobject group = NewCopyGroupResult(env, groups[index]);
421
+ env->SetObjectArrayElement(result, index, group);
422
+ env->DeleteLocalRef(group);
423
+ }
424
+ return result;
425
+ });
426
+ }
427
+
428
+ void pushy::jni_natives::CleanupOldEntries(
429
+ JNIEnv* env,
430
+ jclass,
431
+ jstring root_dir,
432
+ jobjectArray keep_names,
433
+ jint max_age_days,
434
+ jstring trash_dir) {
435
+ GuardJni(env, "Unexpected native exception in cleanupOldEntries", [&] {
436
+ const std::string root = JStringToString(env, root_dir);
437
+ const std::vector<std::string> keep = JArrayToVector(env, keep_names);
438
+ const std::string trash = JStringToString(env, trash_dir);
439
+ // A trash directory turns the delete into renames (cheap enough to run
440
+ // under the caller's version-files lock); the caller purges it later.
441
+ const pushy::delta::Status status = trash.empty()
442
+ ? pushy::delta::CleanupOldEntries(
443
+ root, keep, static_cast<int>(max_age_days))
444
+ : pushy::delta::MoveOldEntriesToTrash(
445
+ root, keep, static_cast<int>(max_age_days), trash);
446
+ if (!status.ok) {
447
+ ThrowRuntimeException(env, status.message);
448
+ }
449
+ });
450
+ }
@@ -0,0 +1,133 @@
1
+ // JNI surface of the update-flow decision layer for the Android orchestrator
2
+ // (SyncCoordinator.java). Pure string-in/string-out: every payload is
3
+ // JSON, matching the decision layer's own boundary. A null return means
4
+ // "input did not parse" and the caller skips the check round; a C++ exception
5
+ // (bad_alloc/length_error from an oversized body) maps onto that same path —
6
+ // it must never unwind through the JNI boundary.
7
+ #include <jni.h>
8
+
9
+ #include "jni_natives.h"
10
+
11
+ #include <string>
12
+
13
+ #include "jni_util.h"
14
+ #include "../update_flow_core/flow_json.h"
15
+ #include "../update_flow_core/native_config.h"
16
+ #include "../update_flow_core/native_round.h"
17
+
18
+ using pushy::jni_util::ToJString;
19
+
20
+ namespace {
21
+
22
+ // Parses a JSON object argument; Undefined (-> null result) otherwise.
23
+ flowjson::Value ParseObject(JNIEnv* env, jstring json) {
24
+ bool ok = false;
25
+ flowjson::Value value =
26
+ flowjson::Parse(pushy::jni_util::JStringToString(env, json), &ok);
27
+ return ok && value.IsObject() ? value : flowjson::Value::Undefined();
28
+ }
29
+
30
+ // Stringify, or null for an Undefined result (input the core rejected).
31
+ jstring ToJsonOrNull(JNIEnv* env, const flowjson::Value& value) {
32
+ return value.IsUndefined() ? nullptr
33
+ : ToJString(env, flowjson::Stringify(value));
34
+ }
35
+
36
+ } // namespace
37
+
38
+ // Native round (update_flow_core/native_round.h).
39
+ jstring pushy::jni_natives::FlowPrepareRound(JNIEnv* env, jclass,
40
+ jstring configJson,
41
+ jstring envJson,
42
+ jdouble randomSample) {
43
+ try {
44
+ return ToJsonOrNull(
45
+ env, flowcore::PrepareRound(
46
+ pushy::jni_util::JStringToString(env, configJson),
47
+ ParseObject(env, envJson), randomSample));
48
+ } catch (...) {
49
+ return nullptr;
50
+ }
51
+ }
52
+
53
+ // A null responseText is a failed request (or none made yet).
54
+ jstring pushy::jni_natives::FlowNextQueryStep(JNIEnv* env, jclass,
55
+ jstring stateJson,
56
+ jstring responseText) {
57
+ try {
58
+ return ToJsonOrNull(
59
+ env, flowcore::NextQueryStep(
60
+ ParseObject(env, stateJson),
61
+ responseText == nullptr
62
+ ? flowjson::Value::Undefined()
63
+ : flowjson::Value::String(pushy::jni_util::JStringToString(
64
+ env, responseText))));
65
+ } catch (...) {
66
+ return nullptr;
67
+ }
68
+ }
69
+
70
+ jstring pushy::jni_natives::FlowHandleResponse(
71
+ JNIEnv* env, jclass, jstring responseText, jstring configJson,
72
+ jstring envJson, jstring requestBody, jlong responseAtSeconds) {
73
+ try {
74
+ return ToJsonOrNull(
75
+ env, flowcore::HandleRoundResponse(
76
+ pushy::jni_util::JStringToString(env, responseText),
77
+ pushy::jni_util::JStringToString(env, configJson),
78
+ ParseObject(env, envJson),
79
+ pushy::jni_util::JStringToString(env, requestBody),
80
+ static_cast<double>(responseAtSeconds)));
81
+ } catch (...) {
82
+ return nullptr;
83
+ }
84
+ }
85
+
86
+ // Persisted-config layer (update_flow_core/native_config.h). A null Java
87
+ // string is an absent config: never equivalent, not_configured at the gate.
88
+ jboolean pushy::jni_natives::FlowConfigsEquivalent(
89
+ JNIEnv* env, jclass, jstring a, jstring b) {
90
+ if (a == nullptr || b == nullptr) {
91
+ return JNI_FALSE;
92
+ }
93
+ try {
94
+ return flowcore::ConfigsEquivalent(pushy::jni_util::JStringToString(env, a),
95
+ pushy::jni_util::JStringToString(env, b))
96
+ ? JNI_TRUE
97
+ : JNI_FALSE;
98
+ } catch (...) {
99
+ return JNI_FALSE;
100
+ }
101
+ }
102
+
103
+ jstring pushy::jni_natives::FlowRunnableConfig(JNIEnv* env, jclass,
104
+ jstring configJson) {
105
+ try {
106
+ return ToJString(env, flowcore::RunnableConfigJson(
107
+ pushy::jni_util::JStringToString(env, configJson)));
108
+ } catch (...) {
109
+ return nullptr;
110
+ }
111
+ }
112
+
113
+ // Throws IllegalArgumentException("Invalid native configuration: ...") for
114
+ // options the host must fix; null only when memory ran out.
115
+ jstring pushy::jni_natives::FlowNormalizeConfiguration(JNIEnv* env, jclass,
116
+ jstring optionsJson) {
117
+ try {
118
+ std::string config;
119
+ std::string error;
120
+ if (!flowcore::NormalizeConfiguration(
121
+ pushy::jni_util::JStringToString(env, optionsJson), &config,
122
+ &error)) {
123
+ jclass exception = env->FindClass("java/lang/IllegalArgumentException");
124
+ if (exception != nullptr) {
125
+ env->ThrowNew(exception, error.c_str());
126
+ }
127
+ return nullptr;
128
+ }
129
+ return ToJString(env, config);
130
+ } catch (...) {
131
+ return nullptr;
132
+ }
133
+ }
@@ -1,17 +1,17 @@
1
1
  // hpatch.c
2
2
  // Copyright 2021 housisong, All rights reserved
3
3
  #include "hpatch.h"
4
- #include "HDiffPatch/libHDiffPatch/HPatch/patch.h"
5
- #include "HDiffPatch/file_for_patch.h"
4
+ #include "../../android/jni/HDiffPatch/libHDiffPatch/HPatch/patch.h"
5
+ #include "../../android/jni/HDiffPatch/file_for_patch.h"
6
6
 
7
7
  //#define _CompressPlugin_zlib
8
8
  //#define _CompressPlugin_bz2
9
9
  #define _CompressPlugin_lzma
10
10
  #define _CompressPlugin_lzma2
11
11
  #define _IsNeedIncludeDefaultCompressHead 0
12
- #include "lzma/C/LzmaDec.h"
13
- #include "lzma/C/Lzma2Dec.h"
14
- #include "HDiffPatch/decompress_plugin_demo.h"
12
+ #include "../../android/jni/lzma/C/LzmaDec.h"
13
+ #include "../../android/jni/lzma/C/Lzma2Dec.h"
14
+ #include "../../android/jni/HDiffPatch/decompress_plugin_demo.h"
15
15
 
16
16
  #define kMaxLoadMemOldSize ((1<<20)*8)
17
17
 
@@ -5,7 +5,10 @@
5
5
  #ifndef HDIFFPATCH_PATCH_H
6
6
  #define HDIFFPATCH_PATCH_H
7
7
  # include <stdint.h> //for uint8_t
8
- #include "HDiffPatch/libHDiffPatch/HPatch/patch_types.h" //for hpatch_singleCompressedDiffInfo
8
+ // The HDiffPatch and lzma submodules stay under android/jni; every build
9
+ // (iOS without header search paths into the pod) resolves them relative to
10
+ // this file.
11
+ #include "../../android/jni/HDiffPatch/libHDiffPatch/HPatch/patch_types.h" //for hpatch_singleCompressedDiffInfo
9
12
  #ifdef __cplusplus
10
13
  extern "C" {
11
14
  #endif