react-native-update 10.59.0 → 10.59.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.
- package/android/build.gradle +46 -14
- package/android/jni/Android.mk +2 -1
- package/android/lib/arm64-v8a/librnpushy.so +0 -0
- package/android/lib/armeabi-v7a/librnpushy.so +0 -0
- package/android/lib/x86/librnpushy.so +0 -0
- package/android/lib/x86_64/librnpushy.so +0 -0
- package/android/src/main/java/cn/reactnative/modules/update/ApkInstaller.java +6 -6
- package/android/src/main/java/cn/reactnative/modules/update/BundledResourceCopier.java +4 -4
- package/android/src/main/java/cn/reactnative/modules/update/{CrashRescue.java → CrashHold.java} +12 -12
- package/android/src/main/java/cn/reactnative/modules/update/DownloadTask.java +28 -24
- package/android/src/main/java/cn/reactnative/modules/update/ErrorCodes.java +2 -1
- package/android/src/main/java/cn/reactnative/modules/update/{NativeUpdateFlow.java → FlowBridge.java} +6 -6
- package/android/src/main/java/cn/reactnative/modules/update/HttpUtils.java +6 -3
- package/android/src/main/java/cn/reactnative/modules/update/{NativeUpdateCore.java → NativeCore.java} +4 -4
- package/android/src/main/java/cn/reactnative/modules/update/PushyConfiguration.java +10 -5
- package/android/src/main/java/cn/reactnative/modules/update/PushyRuntime.java +4 -4
- package/android/src/main/java/cn/reactnative/modules/update/ReactReloadManager.java +7 -7
- package/android/src/main/java/cn/reactnative/modules/update/StateSerialRunner.java +1 -1
- package/android/src/main/java/cn/reactnative/modules/update/{NativeCheckOrchestrator.java → SyncCoordinator.java} +79 -75
- package/android/src/main/java/cn/reactnative/modules/update/Texts.java +28 -0
- package/android/src/main/java/cn/reactnative/modules/update/UiThreadRunner.java +1 -1
- package/android/src/main/java/cn/reactnative/modules/update/UpdateContext.java +28 -28
- package/android/src/main/java/cn/reactnative/modules/update/UpdateModuleImpl.java +12 -9
- package/cpp/patch_core/archive_limits.h +5 -0
- package/cpp/patch_core/archive_patch_core.cpp +26 -19
- package/cpp/patch_core/archive_patch_core.h +17 -8
- package/cpp/patch_core/digest.h +6 -1
- package/cpp/patch_core/error_codes.h +8 -1
- package/cpp/patch_core/hbc_transform.h +5 -0
- package/cpp/patch_core/hbc_transform_wire.h +5 -0
- package/cpp/patch_core/install_record.h +5 -0
- package/cpp/patch_core/jni_natives.h +27 -0
- package/cpp/patch_core/jni_registration.cpp +103 -0
- package/cpp/patch_core/obscured_text.h +44 -0
- package/cpp/patch_core/patch_core.cpp +13 -13
- package/cpp/patch_core/patch_core.h +11 -6
- package/cpp/patch_core/patch_core_android.cpp +10 -10
- package/cpp/patch_core/state_core.h +5 -0
- package/cpp/patch_core/state_ops.h +5 -0
- package/cpp/patch_core/update_core_android.cpp +23 -22
- package/cpp/update_flow_core/flow_json.h +6 -1
- package/cpp/update_flow_core/update_flow_core.cpp +35 -18
- package/cpp/update_flow_core/update_flow_core.h +13 -8
- package/cpp/update_flow_core/update_flow_jni.cpp +11 -13
- package/harmony/pushy/src/main/cpp/CMakeLists.txt +8 -8
- package/harmony/pushy/src/main/cpp/pushy.cpp +33 -33
- package/harmony/pushy/src/main/ets/ArchiveLimits.ts +15 -15
- package/harmony/pushy/src/main/ets/DownloadTask.ts +60 -60
- package/harmony/pushy/src/main/ets/ErrorCodes.ts +13 -11
- package/harmony/pushy/src/main/ets/InstallRecord.ts +4 -4
- package/harmony/pushy/src/main/ets/NativePatchCore.ts +9 -9
- package/harmony/pushy/src/main/ets/PathUtils.ts +2 -2
- package/harmony/pushy/src/main/ets/PushyConfiguration.ts +8 -6
- package/harmony/pushy/src/main/ets/PushyFileJSBundleProvider.ets +1 -1
- package/harmony/pushy/src/main/ets/PushyTurboModule.ts +42 -41
- package/harmony/pushy/src/main/ets/{NativeCheckOrchestrator.ts → SyncCoordinator.ts} +33 -32
- package/harmony/pushy/src/main/ets/Texts.ts +28 -0
- package/harmony/pushy/src/main/ets/UpdateContext.ts +31 -30
- package/harmony/pushy.har +0 -0
- package/ios/RCTPushy/RCTPushy.mm +148 -140
- package/ios/RCTPushy/RCTPushyConfiguration.h +8 -0
- package/ios/RCTPushy/RCTPushyConfiguration.mm +32 -5
- package/package.json +1 -1
- package/react-native-update.podspec +15 -3
- package/android/lib/arm64-v8a/librnupdate.so +0 -0
- package/android/lib/armeabi-v7a/librnupdate.so +0 -0
- package/android/lib/x86/librnupdate.so +0 -0
- package/android/lib/x86_64/librnupdate.so +0 -0
|
@@ -2,12 +2,29 @@
|
|
|
2
2
|
|
|
3
3
|
#include <cmath>
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
#include "../patch_core/error_codes.h"
|
|
6
|
+
#include "../patch_core/obscured_text.h"
|
|
7
|
+
|
|
8
|
+
namespace flowcore {
|
|
6
9
|
|
|
7
10
|
using flowjson::Value;
|
|
8
11
|
|
|
9
12
|
namespace {
|
|
10
13
|
|
|
14
|
+
// Protocol and policy strings, stored encoded (see obscured_text.h).
|
|
15
|
+
const std::string& FieldAvailable() {
|
|
16
|
+
static const std::string value = pushy::text::Reveal("2f07f0d0ba8e");
|
|
17
|
+
return value;
|
|
18
|
+
}
|
|
19
|
+
const std::string& StatusNone() {
|
|
20
|
+
static const std::string value = pushy::text::Reveal("3418c1c1aa8a7c40");
|
|
21
|
+
return value;
|
|
22
|
+
}
|
|
23
|
+
const std::string& PolicyNextLaunch() {
|
|
24
|
+
static const std::string value = pushy::text::Reveal("2912e0ffab8e6c70323b1dedd3");
|
|
25
|
+
return value;
|
|
26
|
+
}
|
|
27
|
+
|
|
11
28
|
// The low byte of every UTF-16 code unit of `utf8`, which is exactly what the
|
|
12
29
|
// TS reference hashes (`key.charCodeAt(i) & 0xff` over `key.length` units).
|
|
13
30
|
// Identity for ASCII; an astral code point contributes its two surrogate
|
|
@@ -127,7 +144,7 @@ bool IsInRollout(double rollout, const std::string& uuid) {
|
|
|
127
144
|
}
|
|
128
145
|
|
|
129
146
|
bool IsMirrorRetryableCode(const std::string& code) {
|
|
130
|
-
return code !=
|
|
147
|
+
return code != pushy::error_codes::kDeltaFailed;
|
|
131
148
|
}
|
|
132
149
|
|
|
133
150
|
namespace {
|
|
@@ -216,7 +233,7 @@ Value OrderEndpointCandidates(const Value& endpoints, double randomSample) {
|
|
|
216
233
|
return ordered;
|
|
217
234
|
}
|
|
218
235
|
|
|
219
|
-
Value
|
|
236
|
+
Value BuildRequestBody(const Value& input) {
|
|
220
237
|
Value body = Value::Object();
|
|
221
238
|
// Caller extras go in FIRST: they may add fields but never override the
|
|
222
239
|
// identity fields the server keys its decision on (mirrors the TS
|
|
@@ -258,7 +275,7 @@ Value BuildCheckRequestBody(const Value& input) {
|
|
|
258
275
|
return body;
|
|
259
276
|
}
|
|
260
277
|
|
|
261
|
-
Value
|
|
278
|
+
Value ResolveResult(const Value& rootInfo, const Value& identity) {
|
|
262
279
|
Value rootResult = Value::Object();
|
|
263
280
|
for (const auto& member : rootInfo.members()) {
|
|
264
281
|
if (member.first != "expVersion") {
|
|
@@ -271,7 +288,7 @@ Value ResolveCheckResult(const Value& rootInfo, const Value& identity) {
|
|
|
271
288
|
// non-object returns Undefined, mirroring optional chaining.
|
|
272
289
|
const Value& rollout = expVersion.Get("config").Get("rollout").Get(
|
|
273
290
|
identity.Get("packageVersion").AsString());
|
|
274
|
-
if (rootResult.Get(
|
|
291
|
+
if (rootResult.Get(FieldAvailable()).Truthy() && expVersion.Truthy() &&
|
|
275
292
|
rollout.IsNumber()) {
|
|
276
293
|
if (IsInRollout(rollout.AsNumber(), identity.Get("uuid").AsString())) {
|
|
277
294
|
const Value& expHash = expVersion.Get("hash");
|
|
@@ -282,7 +299,7 @@ Value ResolveCheckResult(const Value& rootInfo, const Value& identity) {
|
|
|
282
299
|
return upToDate;
|
|
283
300
|
}
|
|
284
301
|
Value info = Value::Object();
|
|
285
|
-
info.Set(
|
|
302
|
+
info.Set(FieldAvailable(), Value::Bool(true));
|
|
286
303
|
for (const auto& member : expVersion.members()) {
|
|
287
304
|
info.Set(member.first, member.second);
|
|
288
305
|
}
|
|
@@ -293,7 +310,7 @@ Value ResolveCheckResult(const Value& rootInfo, const Value& identity) {
|
|
|
293
310
|
}
|
|
294
311
|
}
|
|
295
312
|
const Value& rootHash = rootResult.Get("hash");
|
|
296
|
-
if (rootResult.Get(
|
|
313
|
+
if (rootResult.Get(FieldAvailable()).Truthy() && rootHash.IsString() &&
|
|
297
314
|
!rootHash.AsString().empty() &&
|
|
298
315
|
Value::StrictEquals(rootHash, currentVersion)) {
|
|
299
316
|
Value upToDate = Value::Object();
|
|
@@ -320,8 +337,8 @@ Value DecideDownload(const Value& info, const Value& identity, bool isDev) {
|
|
|
320
337
|
if (paths.IsUndefined() || paths.kind() == Value::Kind::Null) {
|
|
321
338
|
paths = Value::Array(); // info.paths ?? [] — a server `null` too
|
|
322
339
|
}
|
|
323
|
-
if (!info.Get(
|
|
324
|
-
return DeclineDownload(
|
|
340
|
+
if (!info.Get(FieldAvailable()).Truthy() || !hash.Truthy()) {
|
|
341
|
+
return DeclineDownload(StatusNone().c_str());
|
|
325
342
|
}
|
|
326
343
|
if (Value::StrictEquals(hash, identity.Get("currentVersion"))) {
|
|
327
344
|
return DeclineDownload("alreadyCurrent");
|
|
@@ -362,37 +379,37 @@ Value DecideDownload(const Value& info, const Value& identity, bool isDev) {
|
|
|
362
379
|
|
|
363
380
|
bool ShouldActivateAfterDownload(const Value& info,
|
|
364
381
|
const std::string& afterDownload) {
|
|
365
|
-
return afterDownload ==
|
|
382
|
+
return afterDownload == PolicyNextLaunch() ||
|
|
366
383
|
info.Get("config").Get("forceBoot").Truthy();
|
|
367
384
|
}
|
|
368
385
|
|
|
369
|
-
bool
|
|
386
|
+
bool IsValidResult(const Value& root) {
|
|
370
387
|
if (!root.IsObject()) {
|
|
371
388
|
return false;
|
|
372
389
|
}
|
|
373
390
|
const Value& upToDate = root.Get("upToDate");
|
|
374
|
-
const Value& update = root.Get(
|
|
391
|
+
const Value& update = root.Get(FieldAvailable());
|
|
375
392
|
const Value& expired = root.Get("expired");
|
|
376
393
|
const Value& paused = root.Get("paused");
|
|
377
394
|
return upToDate.IsBool() || update.IsBool() || expired.IsBool() ||
|
|
378
395
|
paused.IsString();
|
|
379
396
|
}
|
|
380
397
|
|
|
381
|
-
bool
|
|
398
|
+
bool IsValidResponse(const std::string& responseText) {
|
|
382
399
|
bool ok = false;
|
|
383
400
|
Value root = flowjson::Parse(responseText, &ok);
|
|
384
|
-
return ok &&
|
|
401
|
+
return ok && IsValidResult(root);
|
|
385
402
|
}
|
|
386
403
|
|
|
387
|
-
Value
|
|
404
|
+
Value HandleResponse(const std::string& responseText,
|
|
388
405
|
const Value& identity, bool isDev,
|
|
389
406
|
const std::string& afterDownload) {
|
|
390
407
|
bool ok = false;
|
|
391
408
|
Value root = flowjson::Parse(responseText, &ok);
|
|
392
|
-
if (!ok || !
|
|
409
|
+
if (!ok || !IsValidResult(root)) {
|
|
393
410
|
return DeclineDownload("invalidResponse");
|
|
394
411
|
}
|
|
395
|
-
Value resolved =
|
|
412
|
+
Value resolved = ResolveResult(root, identity);
|
|
396
413
|
Value decision = DecideDownload(resolved, identity, isDev);
|
|
397
414
|
if (decision.Get("action").AsString() == "download") {
|
|
398
415
|
decision.Set("activate", Value::Bool(ShouldActivateAfterDownload(
|
|
@@ -402,4 +419,4 @@ Value HandleCheckResponse(const std::string& responseText,
|
|
|
402
419
|
return decision;
|
|
403
420
|
}
|
|
404
421
|
|
|
405
|
-
} // namespace
|
|
422
|
+
} // namespace flowcore
|
|
@@ -18,7 +18,10 @@
|
|
|
18
18
|
// Like the TS side this layer is pure: no IO, no time, no randomness — the
|
|
19
19
|
// random sample, identity and parsed JSON all arrive as parameters. The
|
|
20
20
|
// orchestrators (per-platform HTTP/download/state glue) own all effects.
|
|
21
|
-
|
|
21
|
+
// Keep the core internal to whichever binary links it (see podspec Core).
|
|
22
|
+
#pragma GCC visibility push(hidden)
|
|
23
|
+
|
|
24
|
+
namespace flowcore {
|
|
22
25
|
|
|
23
26
|
uint32_t Murmur3_32(const std::string& key, uint32_t seed = 0);
|
|
24
27
|
|
|
@@ -45,10 +48,10 @@ flowjson::Value OrderEndpointCandidates(const flowjson::Value& endpoints,
|
|
|
45
48
|
|
|
46
49
|
// input: { packageVersion, currentVersion, buildTime, cInfo,
|
|
47
50
|
// supportedDiffVersion?, bundleHash?, isDev?, extra? }
|
|
48
|
-
flowjson::Value
|
|
51
|
+
flowjson::Value BuildRequestBody(const flowjson::Value& input);
|
|
49
52
|
|
|
50
53
|
// identity: { packageVersion, currentVersion?, uuid }
|
|
51
|
-
flowjson::Value
|
|
54
|
+
flowjson::Value ResolveResult(const flowjson::Value& rootInfo,
|
|
52
55
|
const flowjson::Value& identity);
|
|
53
56
|
|
|
54
57
|
// identity: { currentVersion?, rolledBackVersion? }
|
|
@@ -71,13 +74,13 @@ flowjson::Value DecideDownload(const flowjson::Value& info,
|
|
|
71
74
|
// array, `{"error": "..."}`, an HTML page that happened to parse — is a
|
|
72
75
|
// failed endpoint and must not stop the endpoint fallback. Mirrors
|
|
73
76
|
// src/updateFlowCore.ts isValidCheckResult.
|
|
74
|
-
bool
|
|
75
|
-
bool
|
|
77
|
+
bool IsValidResult(const flowjson::Value& root);
|
|
78
|
+
bool IsValidResponse(const std::string& responseText);
|
|
76
79
|
|
|
77
80
|
bool ShouldActivateAfterDownload(const flowjson::Value& info,
|
|
78
81
|
const std::string& afterDownload);
|
|
79
82
|
|
|
80
|
-
// Composes Parse →
|
|
83
|
+
// Composes Parse → ResolveResult → DecideDownload: one call from the
|
|
81
84
|
// raw checkUpdate response text to a download decision, so the platform
|
|
82
85
|
// orchestrators contain no decision logic at all. identity is the union of
|
|
83
86
|
// both composed functions' needs: { packageVersion, currentVersion?, uuid,
|
|
@@ -87,9 +90,11 @@ bool ShouldActivateAfterDownload(const flowjson::Value& info,
|
|
|
87
90
|
// check result — so orchestrators can persist name/description/metaInfo
|
|
88
91
|
// alongside a downloaded version (the JS side's setLocalHashInfo).
|
|
89
92
|
// Malformed JSON yields { action: 'none', reason: 'invalidResponse' }.
|
|
90
|
-
flowjson::Value
|
|
93
|
+
flowjson::Value HandleResponse(const std::string& responseText,
|
|
91
94
|
const flowjson::Value& identity,
|
|
92
95
|
bool isDev,
|
|
93
96
|
const std::string& afterDownload);
|
|
94
97
|
|
|
95
|
-
} // namespace
|
|
98
|
+
} // namespace flowcore
|
|
99
|
+
|
|
100
|
+
#pragma GCC visibility pop
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
// JNI surface of the update-flow decision layer for the Android orchestrator
|
|
2
|
-
// (
|
|
2
|
+
// (SyncCoordinator.java). Pure string-in/string-out: every payload is
|
|
3
3
|
// JSON, matching the decision layer's own boundary. A null return means
|
|
4
4
|
// "input did not parse" and the caller skips the check round; a C++ exception
|
|
5
5
|
// (bad_alloc/length_error from an oversized body) maps onto that same path —
|
|
6
6
|
// it must never unwind through the JNI boundary.
|
|
7
7
|
#include <jni.h>
|
|
8
8
|
|
|
9
|
+
#include "jni_natives.h"
|
|
10
|
+
|
|
9
11
|
#include <cstdint>
|
|
10
12
|
#include <exception>
|
|
11
13
|
#include <limits>
|
|
@@ -81,8 +83,7 @@ jstring ToJString(JNIEnv* env, const std::string& value) {
|
|
|
81
83
|
|
|
82
84
|
} // namespace
|
|
83
85
|
|
|
84
|
-
|
|
85
|
-
Java_cn_reactnative_modules_update_NativeUpdateFlow_buildCheckRequestBody(
|
|
86
|
+
jstring pushy::jni_natives::FlowBuildRequestBody(
|
|
86
87
|
JNIEnv* env, jclass, jstring inputJson) {
|
|
87
88
|
try {
|
|
88
89
|
bool ok = false;
|
|
@@ -92,14 +93,13 @@ Java_cn_reactnative_modules_update_NativeUpdateFlow_buildCheckRequestBody(
|
|
|
92
93
|
return nullptr;
|
|
93
94
|
}
|
|
94
95
|
return ToJString(
|
|
95
|
-
env, flowjson::Stringify(
|
|
96
|
+
env, flowjson::Stringify(flowcore::BuildRequestBody(input)));
|
|
96
97
|
} catch (...) {
|
|
97
98
|
return nullptr;
|
|
98
99
|
}
|
|
99
100
|
}
|
|
100
101
|
|
|
101
|
-
|
|
102
|
-
Java_cn_reactnative_modules_update_NativeUpdateFlow_orderEndpointCandidates(
|
|
102
|
+
jstring pushy::jni_natives::FlowOrderEndpointCandidates(
|
|
103
103
|
JNIEnv* env, jclass, jstring endpointsJson, jdouble randomSample) {
|
|
104
104
|
try {
|
|
105
105
|
bool ok = false;
|
|
@@ -108,21 +108,20 @@ Java_cn_reactnative_modules_update_NativeUpdateFlow_orderEndpointCandidates(
|
|
|
108
108
|
if (!ok || !endpoints.IsArray()) {
|
|
109
109
|
return nullptr;
|
|
110
110
|
}
|
|
111
|
-
return ToJString(env, flowjson::Stringify(
|
|
111
|
+
return ToJString(env, flowjson::Stringify(flowcore::OrderEndpointCandidates(
|
|
112
112
|
endpoints, randomSample)));
|
|
113
113
|
} catch (...) {
|
|
114
114
|
return nullptr;
|
|
115
115
|
}
|
|
116
116
|
}
|
|
117
117
|
|
|
118
|
-
|
|
119
|
-
Java_cn_reactnative_modules_update_NativeUpdateFlow_isValidCheckResponse(
|
|
118
|
+
jboolean pushy::jni_natives::FlowIsValidResponse(
|
|
120
119
|
JNIEnv* env, jclass, jstring responseText) {
|
|
121
120
|
if (responseText == nullptr) {
|
|
122
121
|
return JNI_FALSE;
|
|
123
122
|
}
|
|
124
123
|
try {
|
|
125
|
-
return
|
|
124
|
+
return flowcore::IsValidResponse(
|
|
126
125
|
pushy::jni_util::JStringToString(env, responseText))
|
|
127
126
|
? JNI_TRUE
|
|
128
127
|
: JNI_FALSE;
|
|
@@ -131,8 +130,7 @@ Java_cn_reactnative_modules_update_NativeUpdateFlow_isValidCheckResponse(
|
|
|
131
130
|
}
|
|
132
131
|
}
|
|
133
132
|
|
|
134
|
-
|
|
135
|
-
Java_cn_reactnative_modules_update_NativeUpdateFlow_handleCheckResponse(
|
|
133
|
+
jstring pushy::jni_natives::FlowHandleResponse(
|
|
136
134
|
JNIEnv* env, jclass, jstring responseText, jstring identityJson,
|
|
137
135
|
jstring afterDownload) {
|
|
138
136
|
try {
|
|
@@ -142,7 +140,7 @@ Java_cn_reactnative_modules_update_NativeUpdateFlow_handleCheckResponse(
|
|
|
142
140
|
if (!ok || !identity.IsObject()) {
|
|
143
141
|
return nullptr;
|
|
144
142
|
}
|
|
145
|
-
return ToJString(env, flowjson::Stringify(
|
|
143
|
+
return ToJString(env, flowjson::Stringify(flowcore::HandleResponse(
|
|
146
144
|
pushy::jni_util::JStringToString(env, responseText),
|
|
147
145
|
identity, false,
|
|
148
146
|
pushy::jni_util::JStringToString(env, afterDownload))));
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
cmake_minimum_required(VERSION 3.13)
|
|
2
|
-
project(
|
|
2
|
+
project(rnpushy LANGUAGES C CXX)
|
|
3
3
|
|
|
4
4
|
set(CMAKE_C_STANDARD 11)
|
|
5
5
|
set(CMAKE_CXX_STANDARD 17)
|
|
@@ -23,7 +23,7 @@ if(NOT EXISTS ${UPDATE_FLOW_CORE_DIR}/update_flow_core.cpp)
|
|
|
23
23
|
endif()
|
|
24
24
|
|
|
25
25
|
# Always compile from source. A prebuilt-import branch used to live here, but
|
|
26
|
-
# it had no symbol verification: a stale libs/<abi>/
|
|
26
|
+
# it had no symbol verification: a stale libs/<abi>/librnpushy.so missing a
|
|
27
27
|
# newly added export (e.g. getSupportedDiffVersion) would silently ship and
|
|
28
28
|
# crash getConstants at runtime.
|
|
29
29
|
set(HDIFFPATCH_DIR ${ANDROID_JNI_DIR}/HDiffPatch)
|
|
@@ -45,11 +45,11 @@ set(HDP_SOURCES
|
|
|
45
45
|
${LZMA_DIR}/C/Lzma2Dec.c
|
|
46
46
|
)
|
|
47
47
|
|
|
48
|
-
add_library(
|
|
48
|
+
add_library(rnpushy SHARED
|
|
49
49
|
${HDP_SOURCES}
|
|
50
50
|
)
|
|
51
51
|
|
|
52
|
-
target_include_directories(
|
|
52
|
+
target_include_directories(rnpushy PRIVATE
|
|
53
53
|
${CMAKE_CURRENT_SOURCE_DIR}
|
|
54
54
|
${PATCH_CORE_DIR}
|
|
55
55
|
${UPDATE_FLOW_CORE_DIR}
|
|
@@ -59,7 +59,7 @@ target_include_directories(rnupdate PRIVATE
|
|
|
59
59
|
${LZMA_DIR}/C
|
|
60
60
|
)
|
|
61
61
|
|
|
62
|
-
target_link_libraries(
|
|
62
|
+
target_link_libraries(rnpushy PUBLIC
|
|
63
63
|
libace_napi.z.so
|
|
64
64
|
)
|
|
65
65
|
|
|
@@ -74,15 +74,15 @@ endif()
|
|
|
74
74
|
# Optimize binary size for Release builds of the NAPI library. --gc-sections
|
|
75
75
|
# only drops anything when every function/data item sits in its own section,
|
|
76
76
|
# and the module registers itself through NAPI_MODULE's static constructor,
|
|
77
|
-
# so no symbol needs default visibility. Scoped to
|
|
77
|
+
# so no symbol needs default visibility. Scoped to rnpushy: rnoh_pushy must
|
|
78
78
|
# keep exporting PushyTurboModule (JSI_EXPORT) for the host's PackageProvider.
|
|
79
79
|
if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
|
|
80
|
-
target_compile_options(
|
|
80
|
+
target_compile_options(rnpushy PRIVATE
|
|
81
81
|
-ffunction-sections
|
|
82
82
|
-fdata-sections
|
|
83
83
|
-fvisibility=hidden
|
|
84
84
|
-fvisibility-inlines-hidden
|
|
85
85
|
)
|
|
86
86
|
# Strip debug symbols and garbage-collect the unused sections.
|
|
87
|
-
target_link_options(
|
|
87
|
+
target_link_options(rnpushy PRIVATE -Wl,--gc-sections -s)
|
|
88
88
|
endif()
|
|
@@ -22,7 +22,6 @@ namespace {
|
|
|
22
22
|
|
|
23
23
|
using pushy::state_ops::StateOperation;
|
|
24
24
|
|
|
25
|
-
constexpr const char* kDefaultBundlePatchEntryName = "index.bundlejs.patch";
|
|
26
25
|
|
|
27
26
|
void ThrowError(napi_env env, const std::string& message) {
|
|
28
27
|
napi_throw_error(env, nullptr, message.c_str());
|
|
@@ -400,14 +399,14 @@ napi_value NewCopyGroupArray(
|
|
|
400
399
|
return result;
|
|
401
400
|
}
|
|
402
401
|
|
|
403
|
-
pushy::
|
|
402
|
+
pushy::delta::PatchManifest BuildManifest(
|
|
404
403
|
const std::vector<std::string>& copy_froms,
|
|
405
404
|
const std::vector<std::string>& copy_tos,
|
|
406
405
|
const std::vector<std::string>& deletes) {
|
|
407
|
-
pushy::
|
|
406
|
+
pushy::delta::PatchManifest manifest;
|
|
408
407
|
for (size_t index = 0; index < copy_froms.size(); ++index) {
|
|
409
408
|
manifest.copies.push_back(
|
|
410
|
-
pushy::
|
|
409
|
+
pushy::delta::CopyOperation{copy_froms[index], copy_tos[index]});
|
|
411
410
|
}
|
|
412
411
|
manifest.deletes = deletes;
|
|
413
412
|
return manifest;
|
|
@@ -566,7 +565,8 @@ napi_value BuildArchivePatchPlan(napi_env env, napi_callback_info info) {
|
|
|
566
565
|
return nullptr;
|
|
567
566
|
}
|
|
568
567
|
|
|
569
|
-
std::string bundle_patch_entry_name =
|
|
568
|
+
std::string bundle_patch_entry_name =
|
|
569
|
+
pushy::archive_patch::DefaultBundleDeltaEntryName();
|
|
570
570
|
if (argc >= 6) {
|
|
571
571
|
bool ok = false;
|
|
572
572
|
const std::string candidate = GetString(env, args[5], &ok);
|
|
@@ -578,15 +578,15 @@ napi_value BuildArchivePatchPlan(napi_env env, napi_callback_info info) {
|
|
|
578
578
|
}
|
|
579
579
|
}
|
|
580
580
|
|
|
581
|
-
const pushy::
|
|
581
|
+
const pushy::delta::PatchManifest manifest =
|
|
582
582
|
BuildManifest(copy_froms, copy_tos, deletes);
|
|
583
583
|
pushy::archive_patch::ArchivePatchType archive_type;
|
|
584
584
|
if (!pushy::archive_patch::TryParseArchivePatchType(patch_type, &archive_type)) {
|
|
585
|
-
ThrowError(env, "Unknown archive
|
|
585
|
+
ThrowError(env, "Unknown archive delta type");
|
|
586
586
|
return nullptr;
|
|
587
587
|
}
|
|
588
588
|
pushy::archive_patch::ArchivePatchPlan plan;
|
|
589
|
-
const pushy::
|
|
589
|
+
const pushy::delta::Status status = pushy::archive_patch::BuildArchivePatchPlan(
|
|
590
590
|
archive_type,
|
|
591
591
|
manifest,
|
|
592
592
|
entry_names,
|
|
@@ -619,10 +619,10 @@ napi_value BuildCopyGroups(napi_env env, napi_callback_info info) {
|
|
|
619
619
|
return nullptr;
|
|
620
620
|
}
|
|
621
621
|
|
|
622
|
-
const pushy::
|
|
622
|
+
const pushy::delta::PatchManifest manifest =
|
|
623
623
|
BuildManifest(copy_froms, copy_tos, std::vector<std::string>());
|
|
624
624
|
std::vector<pushy::archive_patch::CopyGroup> groups;
|
|
625
|
-
const pushy::
|
|
625
|
+
const pushy::delta::Status status =
|
|
626
626
|
pushy::archive_patch::BuildCopyGroups(manifest, &groups);
|
|
627
627
|
if (!status.ok) {
|
|
628
628
|
ThrowError(env, status.message);
|
|
@@ -635,7 +635,7 @@ napi_value BuildCopyGroups(napi_env env, napi_callback_info info) {
|
|
|
635
635
|
// ---------------------------------------------------------------------------
|
|
636
636
|
// Async work plumbing for the heavy patch operations.
|
|
637
637
|
//
|
|
638
|
-
//
|
|
638
|
+
// applyDeltaFromSource and cleanupOldEntries run hdiff / recursive file IO
|
|
639
639
|
// that can take hundreds of ms to seconds. The Pushy TurboModule executes on
|
|
640
640
|
// the UI thread, so running these synchronously froze the UI. These are now
|
|
641
641
|
// wrapped in napi_create_async_work: arguments are parsed on the JS thread, the
|
|
@@ -659,8 +659,8 @@ void RejectDeferredWithMessage(
|
|
|
659
659
|
struct ApplyPatchWork {
|
|
660
660
|
napi_async_work work = nullptr;
|
|
661
661
|
napi_deferred deferred = nullptr;
|
|
662
|
-
pushy::
|
|
663
|
-
pushy::
|
|
662
|
+
pushy::delta::FileSourcePatchOptions options;
|
|
663
|
+
pushy::delta::Status status{false, ""};
|
|
664
664
|
};
|
|
665
665
|
|
|
666
666
|
struct CleanupWork {
|
|
@@ -672,7 +672,7 @@ struct CleanupWork {
|
|
|
672
672
|
// evict the running bundle while its on-demand assets are still served).
|
|
673
673
|
std::vector<std::string> keep_names;
|
|
674
674
|
int32_t max_age_days = 0;
|
|
675
|
-
pushy::
|
|
675
|
+
pushy::delta::Status status{false, ""};
|
|
676
676
|
};
|
|
677
677
|
|
|
678
678
|
// Streaming file digest on a worker thread. A full package or bundle is tens
|
|
@@ -714,7 +714,7 @@ napi_value ApplyPatchFromFileSource(napi_env env, napi_callback_info info) {
|
|
|
714
714
|
!GetOptionalStringProperty(env, args[0], "sourceRoot", &source_root) ||
|
|
715
715
|
!GetOptionalStringProperty(env, args[0], "targetRoot", &target_root) ||
|
|
716
716
|
!GetOptionalStringProperty(env, args[0], "originBundlePath", &origin_bundle_path) ||
|
|
717
|
-
!GetOptionalStringProperty(env, args[0], "
|
|
717
|
+
!GetOptionalStringProperty(env, args[0], "bundleDeltaPath", &bundle_patch_path) ||
|
|
718
718
|
!GetOptionalStringProperty(env, args[0], "bundleOutputPath", &bundle_output_path) ||
|
|
719
719
|
!GetOptionalStringProperty(env, args[0], "mergeSourceSubdir", &merge_source_subdir) ||
|
|
720
720
|
!GetOptionalStringProperty(
|
|
@@ -747,14 +747,14 @@ napi_value ApplyPatchFromFileSource(napi_env env, napi_callback_info info) {
|
|
|
747
747
|
|
|
748
748
|
napi_value resource_name = nullptr;
|
|
749
749
|
napi_create_string_utf8(
|
|
750
|
-
env, "
|
|
750
|
+
env, "applyDeltaFromSource", NAPI_AUTO_LENGTH, &resource_name);
|
|
751
751
|
if (napi_create_async_work(
|
|
752
752
|
env,
|
|
753
753
|
nullptr,
|
|
754
754
|
resource_name,
|
|
755
755
|
[](napi_env, void* data) {
|
|
756
756
|
auto* w = static_cast<ApplyPatchWork*>(data);
|
|
757
|
-
w->status = pushy::
|
|
757
|
+
w->status = pushy::delta::ApplyPatchFromFileSource(w->options);
|
|
758
758
|
},
|
|
759
759
|
[](napi_env cb_env, napi_status status, void* data) {
|
|
760
760
|
auto* w = static_cast<ApplyPatchWork*>(data);
|
|
@@ -841,7 +841,7 @@ napi_value CleanupOldEntries(napi_env env, napi_callback_info info) {
|
|
|
841
841
|
resource_name,
|
|
842
842
|
[](napi_env, void* data) {
|
|
843
843
|
auto* w = static_cast<CleanupWork*>(data);
|
|
844
|
-
w->status = pushy::
|
|
844
|
+
w->status = pushy::delta::CleanupOldEntries(
|
|
845
845
|
w->root_dir, w->keep_names, w->max_age_days);
|
|
846
846
|
},
|
|
847
847
|
[](napi_env cb_env, napi_status status, void* data) {
|
|
@@ -1108,12 +1108,12 @@ static napi_value MakeUtf8String(napi_env env, const std::string& value) {
|
|
|
1108
1108
|
return result;
|
|
1109
1109
|
}
|
|
1110
1110
|
|
|
1111
|
-
static napi_value
|
|
1111
|
+
static napi_value FlowBuildRequestBody(napi_env env,
|
|
1112
1112
|
napi_callback_info info) {
|
|
1113
1113
|
size_t argc = 1;
|
|
1114
1114
|
napi_value args[1] = {nullptr};
|
|
1115
1115
|
if (!GetArgCount(env, info, &argc, args) || argc < 1) {
|
|
1116
|
-
ThrowError(env, "
|
|
1116
|
+
ThrowError(env, "buildRequestBody: missing input argument");
|
|
1117
1117
|
return nullptr;
|
|
1118
1118
|
}
|
|
1119
1119
|
bool ok = false;
|
|
@@ -1127,7 +1127,7 @@ static napi_value FlowBuildCheckRequestBody(napi_env env,
|
|
|
1127
1127
|
return nullptr;
|
|
1128
1128
|
}
|
|
1129
1129
|
return MakeUtf8String(
|
|
1130
|
-
env, flowjson::Stringify(
|
|
1130
|
+
env, flowjson::Stringify(flowcore::BuildRequestBody(input)));
|
|
1131
1131
|
}
|
|
1132
1132
|
|
|
1133
1133
|
static napi_value FlowOrderEndpointCandidates(napi_env env,
|
|
@@ -1154,16 +1154,16 @@ static napi_value FlowOrderEndpointCandidates(napi_env env,
|
|
|
1154
1154
|
return nullptr;
|
|
1155
1155
|
}
|
|
1156
1156
|
return MakeUtf8String(env,
|
|
1157
|
-
flowjson::Stringify(
|
|
1157
|
+
flowjson::Stringify(flowcore::OrderEndpointCandidates(
|
|
1158
1158
|
endpoints, sample)));
|
|
1159
1159
|
}
|
|
1160
1160
|
|
|
1161
|
-
static napi_value
|
|
1161
|
+
static napi_value FlowIsValidResponse(napi_env env,
|
|
1162
1162
|
napi_callback_info info) {
|
|
1163
1163
|
size_t argc = 1;
|
|
1164
1164
|
napi_value args[1] = {nullptr};
|
|
1165
1165
|
if (!GetArgCount(env, info, &argc, args) || argc < 1) {
|
|
1166
|
-
ThrowError(env, "
|
|
1166
|
+
ThrowError(env, "isValidResponse: missing arguments");
|
|
1167
1167
|
return nullptr;
|
|
1168
1168
|
}
|
|
1169
1169
|
bool ok = false;
|
|
@@ -1174,17 +1174,17 @@ static napi_value FlowIsValidCheckResponse(napi_env env,
|
|
|
1174
1174
|
return nullptr;
|
|
1175
1175
|
}
|
|
1176
1176
|
napi_value result = nullptr;
|
|
1177
|
-
napi_get_boolean(env,
|
|
1177
|
+
napi_get_boolean(env, flowcore::IsValidResponse(response_text),
|
|
1178
1178
|
&result);
|
|
1179
1179
|
return result;
|
|
1180
1180
|
}
|
|
1181
1181
|
|
|
1182
|
-
static napi_value
|
|
1182
|
+
static napi_value FlowHandleResponse(napi_env env,
|
|
1183
1183
|
napi_callback_info info) {
|
|
1184
1184
|
size_t argc = 3;
|
|
1185
1185
|
napi_value args[3] = {nullptr, nullptr, nullptr};
|
|
1186
1186
|
if (!GetArgCount(env, info, &argc, args) || argc < 3) {
|
|
1187
|
-
ThrowError(env, "
|
|
1187
|
+
ThrowError(env, "handleResponse: missing arguments");
|
|
1188
1188
|
return nullptr;
|
|
1189
1189
|
}
|
|
1190
1190
|
bool ok = false;
|
|
@@ -1206,26 +1206,26 @@ static napi_value FlowHandleCheckResponse(napi_env env,
|
|
|
1206
1206
|
return nullptr;
|
|
1207
1207
|
}
|
|
1208
1208
|
return MakeUtf8String(env,
|
|
1209
|
-
flowjson::Stringify(
|
|
1209
|
+
flowjson::Stringify(flowcore::HandleResponse(
|
|
1210
1210
|
response_text, identity, false, after_download)));
|
|
1211
1211
|
}
|
|
1212
1212
|
|
|
1213
1213
|
napi_value Init(napi_env env, napi_value exports) {
|
|
1214
1214
|
if (!ExportFunction(env, exports, "syncStateWithBinaryVersion", SyncStateWithBinaryVersion) ||
|
|
1215
1215
|
!ExportFunction(env, exports, "runStateCore", RunStateCore) ||
|
|
1216
|
-
!ExportFunction(env, exports, "
|
|
1216
|
+
!ExportFunction(env, exports, "buildArchivePlan", BuildArchivePatchPlan) ||
|
|
1217
1217
|
!ExportFunction(env, exports, "buildCopyGroups", BuildCopyGroups) ||
|
|
1218
|
-
!ExportFunction(env, exports, "
|
|
1218
|
+
!ExportFunction(env, exports, "applyDeltaFromSource", ApplyPatchFromFileSource) ||
|
|
1219
1219
|
!ExportFunction(env, exports, "cleanupOldEntries", CleanupOldEntries) ||
|
|
1220
1220
|
!ExportFunction(env, exports, "sha256Hex", Sha256Hex) ||
|
|
1221
1221
|
!ExportFunction(env, exports, "sha256HexFile", Sha256HexFile) ||
|
|
1222
1222
|
!ExportFunction(env, exports, "sha256HexFileAsync", Sha256HexFileAsync) ||
|
|
1223
1223
|
!ExportFunction(env, exports, "crc32", Crc32) ||
|
|
1224
1224
|
!ExportFunction(env, exports, "getSupportedDiffVersion", GetSupportedDiffVersion) ||
|
|
1225
|
-
!ExportFunction(env, exports, "
|
|
1225
|
+
!ExportFunction(env, exports, "buildRequestBody", FlowBuildRequestBody) ||
|
|
1226
1226
|
!ExportFunction(env, exports, "orderEndpointCandidates", FlowOrderEndpointCandidates) ||
|
|
1227
|
-
!ExportFunction(env, exports, "
|
|
1228
|
-
!ExportFunction(env, exports, "
|
|
1227
|
+
!ExportFunction(env, exports, "isValidResponse", FlowIsValidResponse) ||
|
|
1228
|
+
!ExportFunction(env, exports, "handleResponse", FlowHandleResponse)) {
|
|
1229
1229
|
return nullptr;
|
|
1230
1230
|
}
|
|
1231
1231
|
return exports;
|
|
@@ -2,8 +2,8 @@ import fileIo from '@ohos.file.fs';
|
|
|
2
2
|
import statvfs from '@ohos.file.statvfs';
|
|
3
3
|
import {
|
|
4
4
|
ERROR_FILE_OPERATION_FAILED,
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
ERROR_DELTA_FAILED,
|
|
6
|
+
createPushyError,
|
|
7
7
|
} from './ErrorCodes';
|
|
8
8
|
import { isReservedEntryName } from './InstallRecord';
|
|
9
9
|
|
|
@@ -32,8 +32,8 @@ export function checkUncompressedSize(
|
|
|
32
32
|
uncompressedBytes: number,
|
|
33
33
|
): void {
|
|
34
34
|
if (uncompressedBytes > MAX_TOTAL_UNCOMPRESSED_BYTES) {
|
|
35
|
-
throw
|
|
36
|
-
|
|
35
|
+
throw createPushyError(
|
|
36
|
+
ERROR_DELTA_FAILED,
|
|
37
37
|
`archive expands beyond ${MAX_TOTAL_UNCOMPRESSED_BYTES} bytes ` +
|
|
38
38
|
`(${uncompressedBytes})`,
|
|
39
39
|
);
|
|
@@ -43,8 +43,8 @@ export function checkUncompressedSize(
|
|
|
43
43
|
archiveBytes > 0 &&
|
|
44
44
|
uncompressedBytes > archiveBytes * MAX_COMPRESSION_RATIO
|
|
45
45
|
) {
|
|
46
|
-
throw
|
|
47
|
-
|
|
46
|
+
throw createPushyError(
|
|
47
|
+
ERROR_DELTA_FAILED,
|
|
48
48
|
`archive compression ratio exceeds ${MAX_COMPRESSION_RATIO}:1 ` +
|
|
49
49
|
`(${uncompressedBytes}/${archiveBytes})`,
|
|
50
50
|
);
|
|
@@ -78,7 +78,7 @@ export async function ensureFreeSpace(
|
|
|
78
78
|
}
|
|
79
79
|
const needed = Math.max(0, bytesToWrite) + FREE_DISK_MARGIN_BYTES;
|
|
80
80
|
if (free < needed) {
|
|
81
|
-
throw
|
|
81
|
+
throw createPushyError(
|
|
82
82
|
ERROR_FILE_OPERATION_FAILED,
|
|
83
83
|
`insufficient disk space: need ${needed} bytes, have ${free}`,
|
|
84
84
|
);
|
|
@@ -103,8 +103,8 @@ export async function measureExtractedDirectory(
|
|
|
103
103
|
);
|
|
104
104
|
for (const name of names) {
|
|
105
105
|
if (isReservedEntryName(name)) {
|
|
106
|
-
throw
|
|
107
|
-
|
|
106
|
+
throw createPushyError(
|
|
107
|
+
ERROR_DELTA_FAILED,
|
|
108
108
|
`archive contains reserved entry ${name}`,
|
|
109
109
|
);
|
|
110
110
|
}
|
|
@@ -112,8 +112,8 @@ export async function measureExtractedDirectory(
|
|
|
112
112
|
const stat = await fileIo.stat(path);
|
|
113
113
|
acc.entries += 1;
|
|
114
114
|
if (acc.entries > MAX_ENTRIES) {
|
|
115
|
-
throw
|
|
116
|
-
|
|
115
|
+
throw createPushyError(
|
|
116
|
+
ERROR_DELTA_FAILED,
|
|
117
117
|
`archive has too many entries (> ${MAX_ENTRIES})`,
|
|
118
118
|
);
|
|
119
119
|
}
|
|
@@ -122,15 +122,15 @@ export async function measureExtractedDirectory(
|
|
|
122
122
|
continue;
|
|
123
123
|
}
|
|
124
124
|
if (stat.size > MAX_ENTRY_BYTES) {
|
|
125
|
-
throw
|
|
126
|
-
|
|
125
|
+
throw createPushyError(
|
|
126
|
+
ERROR_DELTA_FAILED,
|
|
127
127
|
`archive entry too large: ${name} (${stat.size} bytes)`,
|
|
128
128
|
);
|
|
129
129
|
}
|
|
130
130
|
acc.bytes += stat.size;
|
|
131
131
|
if (acc.bytes > MAX_TOTAL_UNCOMPRESSED_BYTES) {
|
|
132
|
-
throw
|
|
133
|
-
|
|
132
|
+
throw createPushyError(
|
|
133
|
+
ERROR_DELTA_FAILED,
|
|
134
134
|
`archive expands beyond ${MAX_TOTAL_UNCOMPRESSED_BYTES} bytes`,
|
|
135
135
|
);
|
|
136
136
|
}
|