react-native-update 10.59.2 → 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.
- package/README-CN.md +7 -7
- package/README.md +7 -6
- package/android/build.gradle +25 -33
- package/android/jni/Android.mk +16 -24
- package/android/lib/arm64-v8a/librnpushy.so +0 -0
- package/android/lib/armeabi-v7a/librnpushy.so +0 -0
- package/android/lib/x86/librnpushy.so +0 -0
- package/android/lib/x86_64/librnpushy.so +0 -0
- package/android/proguard.pro +9 -7
- package/android/src/main/java/cn/reactnative/modules/update/ApkInstaller.java +2 -7
- package/android/src/main/java/cn/reactnative/modules/update/ArchiveLimits.java +23 -6
- package/android/src/main/java/cn/reactnative/modules/update/BundledResourceCopier.java +50 -51
- package/android/src/main/java/cn/reactnative/modules/update/CleanupKeepNames.java +12 -13
- package/android/src/main/java/cn/reactnative/modules/update/CopyGroupResult.java +3 -0
- package/android/src/main/java/cn/reactnative/modules/update/CrashHold.java +22 -30
- package/android/src/main/java/cn/reactnative/modules/update/DownloadTask.java +181 -332
- package/android/src/main/java/cn/reactnative/modules/update/DownloadTaskParams.java +3 -1
- package/android/src/main/java/cn/reactnative/modules/update/ErrorCodes.java +27 -0
- package/android/src/main/java/cn/reactnative/modules/update/FileOperationException.java +1 -1
- package/android/src/main/java/cn/reactnative/modules/update/FlowBridge.java +32 -8
- package/android/src/main/java/cn/reactnative/modules/update/HttpClients.java +63 -0
- package/android/src/main/java/cn/reactnative/modules/update/HttpUtils.java +0 -11
- package/android/src/main/java/cn/reactnative/modules/update/InstallRecord.java +66 -116
- package/android/src/main/java/cn/reactnative/modules/update/NativeCore.java +9 -1
- package/android/src/main/java/cn/reactnative/modules/update/PatchFailedException.java +1 -1
- package/android/src/main/java/cn/reactnative/modules/update/PushyRuntime.java +32 -60
- package/android/src/main/java/cn/reactnative/modules/update/ReactReloadManager.java +8 -23
- package/android/src/main/java/cn/reactnative/modules/update/SafeZipFile.java +42 -80
- package/android/src/main/java/cn/reactnative/modules/update/StateSerialRunner.java +22 -39
- package/android/src/main/java/cn/reactnative/modules/update/SyncCoordinator.java +176 -299
- package/android/src/main/java/cn/reactnative/modules/update/Threads.java +35 -0
- package/android/src/main/java/cn/reactnative/modules/update/UpdateContext.java +242 -174
- package/android/src/main/java/cn/reactnative/modules/update/UpdateEventEmitter.java +1 -1
- package/android/src/main/java/cn/reactnative/modules/update/UpdateFileUtils.java +75 -17
- package/android/src/main/java/cn/reactnative/modules/update/{UpdateModuleImpl.java → UpdateModule.java} +113 -95
- package/android/src/main/java/cn/reactnative/modules/update/UpdatePackage.java +16 -19
- package/android/src/oldarch/cn/reactnative/modules/update/NativePushySpec.java +69 -0
- package/cpp/android_jni/archive_limits_jni.cpp +83 -0
- package/cpp/android_jni/install_record_jni.cpp +89 -0
- package/cpp/android_jni/jni_log.h +31 -0
- package/cpp/android_jni/jni_natives.h +40 -0
- package/cpp/android_jni/jni_registration.cpp +133 -0
- package/cpp/android_jni/jni_util.h +160 -0
- package/cpp/android_jni/update_core_android.cpp +450 -0
- package/cpp/android_jni/update_flow_jni.cpp +133 -0
- package/{android/jni → cpp/hpatch}/hpatch.c +5 -5
- package/{android/jni → cpp/hpatch}/hpatch.h +4 -1
- package/cpp/patch_core/archive_limits.cpp +194 -0
- package/cpp/patch_core/archive_limits.h +99 -10
- package/cpp/patch_core/archive_patch_core.cpp +96 -12
- package/cpp/patch_core/archive_patch_core.h +49 -0
- package/cpp/patch_core/digest.h +5 -4
- package/cpp/patch_core/error_codes.h +5 -3
- package/cpp/patch_core/install_record.cpp +430 -0
- package/cpp/patch_core/install_record.h +163 -9
- package/cpp/patch_core/patch_core.cpp +62 -16
- package/cpp/patch_core/patch_core.h +27 -7
- package/cpp/patch_core/patch_manifest.cpp +201 -0
- package/cpp/patch_core/patch_manifest.h +74 -0
- package/cpp/patch_core/state_ops.h +3 -3
- package/cpp/update_flow_core/flow_json.cpp +82 -8
- package/cpp/update_flow_core/flow_json.h +21 -0
- package/cpp/update_flow_core/native_config.cpp +456 -0
- package/cpp/update_flow_core/native_config.h +84 -0
- package/cpp/update_flow_core/native_round.cpp +326 -0
- package/cpp/update_flow_core/native_round.h +90 -0
- package/cpp/update_flow_core/update_flow_core.h +2 -2
- package/harmony/hvigor-plugin.ts +11 -19
- package/harmony/pushy/src/main/cpp/CMakeLists.txt +17 -26
- package/harmony/pushy/src/main/cpp/PushyPackage.h +2 -2
- package/harmony/pushy/src/main/cpp/PushyTurboModule.cpp +55 -94
- package/harmony/pushy/src/main/cpp/pushy.cpp +694 -517
- package/harmony/pushy/src/main/ets/ArchiveLimits.ts +48 -72
- package/harmony/pushy/src/main/ets/DownloadTask.ts +164 -404
- package/harmony/pushy/src/main/ets/DownloadTaskParams.ts +1 -3
- package/harmony/pushy/src/main/ets/ErrorCodes.ts +1 -14
- package/harmony/pushy/src/main/ets/EventHub.ts +1 -1
- package/harmony/pushy/src/main/ets/InstallRecord.ts +52 -151
- package/harmony/pushy/src/main/ets/Logger.ts +20 -34
- package/harmony/pushy/src/main/ets/NativePatchCore.ts +116 -42
- package/harmony/pushy/src/main/ets/PushyConfiguration.ts +8 -85
- package/harmony/pushy/src/main/ets/PushyTurboModule.ts +11 -25
- package/harmony/pushy/src/main/ets/SaveFile.ts +99 -18
- package/harmony/pushy/src/main/ets/SyncCoordinator.ts +136 -335
- package/harmony/pushy/src/main/ets/Texts.ts +0 -4
- package/harmony/pushy/src/main/ets/UpdateContext.ts +106 -119
- package/harmony/pushy.har +0 -0
- package/ios/RCTPushy/RCTPushy+Launch.mm +185 -0
- package/ios/RCTPushy/RCTPushy.h +12 -0
- package/ios/RCTPushy/RCTPushy.mm +87 -2634
- package/ios/RCTPushy/RCTPushyCommon.h +34 -0
- package/ios/RCTPushy/RCTPushyConfiguration.h +0 -8
- package/ios/RCTPushy/RCTPushyConfiguration.mm +40 -121
- package/ios/RCTPushy/RCTPushyDownloader.mm +40 -37
- package/ios/RCTPushy/RCTPushyInstallRecord.mm +61 -0
- package/ios/RCTPushy/RCTPushyInstaller.mm +689 -0
- package/ios/RCTPushy/RCTPushyInternal.h +144 -0
- package/ios/RCTPushy/RCTPushyOrchestrator.mm +1001 -0
- package/ios/RCTPushy/RCTPushyState.mm +314 -0
- package/package.json +25 -23
- package/react-native-update.podspec +73 -82
- package/src/client.ts +361 -451
- package/src/context.ts +1 -1
- package/src/core.ts +35 -8
- package/src/endpoint.ts +0 -2
- package/src/error.ts +13 -32
- package/src/i18n.ts +18 -83
- package/src/locales/en.ts +0 -4
- package/src/locales/zh.ts +0 -4
- package/src/provider.tsx +60 -53
- package/src/telemetry.ts +0 -13
- package/src/type.ts +0 -15
- package/src/updateFlowCore.ts +116 -10
- package/src/utils.ts +49 -42
- package/android/src/main/java/cn/reactnative/modules/update/ArchivePatchPlanResult.java +0 -6
- package/android/src/main/java/cn/reactnative/modules/update/PushyConfiguration.java +0 -104
- package/android/src/main/java/cn/reactnative/modules/update/UiThreadRunner.java +0 -37
- package/android/src/newarch/cn/reactnative/modules/update/UpdateModule.java +0 -121
- package/android/src/oldarch/cn/reactnative/modules/update/UpdateModule.java +0 -125
- package/cpp/patch_core/jni_natives.h +0 -27
- package/cpp/patch_core/jni_registration.cpp +0 -103
- package/cpp/patch_core/jni_util.h +0 -60
- package/cpp/patch_core/patch_core_android.cpp +0 -97
- package/cpp/patch_core/update_core_android.cpp +0 -485
- package/cpp/update_flow_core/update_flow_jni.cpp +0 -150
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
#include "native_round.h"
|
|
2
|
+
|
|
3
|
+
#include <cmath>
|
|
4
|
+
#include <utility>
|
|
5
|
+
|
|
6
|
+
#include "../patch_core/hbc_transform_wire.h"
|
|
7
|
+
#include "../patch_core/obscured_text.h"
|
|
8
|
+
#include "native_config.h"
|
|
9
|
+
#include "update_flow_core.h"
|
|
10
|
+
|
|
11
|
+
namespace flowcore {
|
|
12
|
+
|
|
13
|
+
using flowjson::Value;
|
|
14
|
+
|
|
15
|
+
namespace {
|
|
16
|
+
|
|
17
|
+
// Stored encoded (see obscured_text.h).
|
|
18
|
+
const std::string& QueryPath() {
|
|
19
|
+
static const std::string value = pushy::text::Reveal("7514fcd4ad805d55263e08fc99");
|
|
20
|
+
return value;
|
|
21
|
+
}
|
|
22
|
+
const std::string& InfoForceBoot() {
|
|
23
|
+
static const std::string value =
|
|
24
|
+
pushy::text::Reveal("3c18e6d2aba9674a360d19ead5a695");
|
|
25
|
+
return value;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// The lists NextQueryStep walks, in order; the state's "list" names the
|
|
29
|
+
// current one ("" once the query is over).
|
|
30
|
+
const char kEndpoints[] = "endpoints";
|
|
31
|
+
const char kQueryUrls[] = "queryUrls";
|
|
32
|
+
const char kRemote[] = "remote";
|
|
33
|
+
|
|
34
|
+
bool HasPrefixIgnoringAsciiCase(const std::string& text, const char* prefix) {
|
|
35
|
+
size_t i = 0;
|
|
36
|
+
for (; prefix[i] != '\0'; i++) {
|
|
37
|
+
if (i >= text.size()) {
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
char c = text[i];
|
|
41
|
+
if (c >= 'A' && c <= 'Z') {
|
|
42
|
+
c = static_cast<char>(c - 'A' + 'a');
|
|
43
|
+
}
|
|
44
|
+
if (c != prefix[i]) {
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return true;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
bool IsHttpsUrl(const std::string& url) {
|
|
52
|
+
return HasPrefixIgnoringAsciiCase(url, "https://");
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
bool IsCleartextUrl(const std::string& url) {
|
|
56
|
+
return HasPrefixIgnoringAsciiCase(url, "http://");
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
std::string StripTrailingSlashes(std::string url) {
|
|
60
|
+
while (!url.empty() && url.back() == '/') {
|
|
61
|
+
url.pop_back();
|
|
62
|
+
}
|
|
63
|
+
return url;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
Value StringOr(const Value& value, const char* fallback) {
|
|
67
|
+
return value.IsString() ? value : Value::String(fallback);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// A non-negative integral state counter; anything else reads as 0.
|
|
71
|
+
size_t Counter(const Value& value) {
|
|
72
|
+
const double number = value.AsNumber();
|
|
73
|
+
if (!value.IsNumber() || !std::isfinite(number) || number < 0) {
|
|
74
|
+
return 0;
|
|
75
|
+
}
|
|
76
|
+
return number >= 1e15 ? static_cast<size_t>(1e15)
|
|
77
|
+
: static_cast<size_t>(number);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// { packageVersion, currentVersion, uuid, rolledBackVersion } — what
|
|
81
|
+
// HandleResponse keys its decision on.
|
|
82
|
+
Value Identity(const Value& config, const Value& env) {
|
|
83
|
+
const Value& configured = config.Get("packageVersion");
|
|
84
|
+
Value identity = Value::Object();
|
|
85
|
+
identity.Set("packageVersion",
|
|
86
|
+
configured.IsString() && !configured.AsString().empty()
|
|
87
|
+
? configured
|
|
88
|
+
: env.Get("packageVersion"));
|
|
89
|
+
identity.Set("currentVersion", env.Get("currentVersion"));
|
|
90
|
+
identity.Set("uuid", StringOr(env.Get("uuid"), ""));
|
|
91
|
+
identity.Set("rolledBackVersion", env.Get("rolledBackVersion"));
|
|
92
|
+
return identity;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
std::string RequestBody(const Value& config, const Value& env) {
|
|
96
|
+
const Value identity = Identity(config, env);
|
|
97
|
+
Value cInfo = Value::Object();
|
|
98
|
+
cInfo.Set("rnu", StringOr(config.Get("rnu"), ""));
|
|
99
|
+
cInfo.Set("rn", StringOr(config.Get("rn"), ""));
|
|
100
|
+
cInfo.Set("os", env.Get("os"));
|
|
101
|
+
cInfo.Set("uuid", identity.Get("uuid"));
|
|
102
|
+
Value input = Value::Object();
|
|
103
|
+
input.Set("packageVersion", identity.Get("packageVersion"));
|
|
104
|
+
input.Set("currentVersion", env.Get("currentVersion"));
|
|
105
|
+
input.Set("buildTime", env.Get("buildTime"));
|
|
106
|
+
input.Set("cInfo", std::move(cInfo));
|
|
107
|
+
input.Set("supportedDiffVersion",
|
|
108
|
+
Value::Number(pushy::hbc::kSupportedDiffVersion));
|
|
109
|
+
input.Set("bundleHash", env.Get("bundleHash"));
|
|
110
|
+
return flowjson::Stringify(BuildRequestBody(input));
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
bool Contains(const Value& list, const std::string& text) {
|
|
114
|
+
for (const Value& entry : list.elements()) {
|
|
115
|
+
if (entry.AsString() == text) {
|
|
116
|
+
return true;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return false;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
Value PlanAttempts(const Value& attempts, bool hasOrigin, bool httpsOnly) {
|
|
123
|
+
Value planned = Value::Array();
|
|
124
|
+
for (const Value& attempt : attempts.elements()) {
|
|
125
|
+
// diff patches the running version; with none running it cannot apply.
|
|
126
|
+
if (attempt.Get("type").AsString() == "diff" && !hasOrigin) {
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
Value urls = Value::Array();
|
|
130
|
+
for (const Value& url : attempt.Get("urls").elements()) {
|
|
131
|
+
if (url.IsString() && !url.AsString().empty() &&
|
|
132
|
+
!(httpsOnly && IsCleartextUrl(url.AsString()))) {
|
|
133
|
+
urls.Push(url);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
if (urls.Size() > 0) {
|
|
137
|
+
Value kept = Value::Object();
|
|
138
|
+
kept.Set("type", attempt.Get("type"));
|
|
139
|
+
kept.Set("urls", std::move(urls));
|
|
140
|
+
planned.Push(std::move(kept));
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
return planned;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
Value VersionInfo(const Value& info) {
|
|
147
|
+
Value hashInfo = Value::Object();
|
|
148
|
+
for (const char* key : {"name", "description", "metaInfo"}) {
|
|
149
|
+
if (info.Get(key).IsString()) {
|
|
150
|
+
hashInfo.Set(key, info.Get(key));
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
// A forceBoot activation is the brick-rescue path: JS reports it once
|
|
154
|
+
// this version survives to markSuccess. Only the server's directive counts
|
|
155
|
+
// — a silent-strategy activation is ordinary delivery.
|
|
156
|
+
if (info.Get("config").Get("forceBoot").Truthy()) {
|
|
157
|
+
hashInfo.Set(InfoForceBoot(), Value::Bool(true));
|
|
158
|
+
}
|
|
159
|
+
return hashInfo;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
} // namespace
|
|
163
|
+
|
|
164
|
+
bool IsHttpsOnly(const Value& endpoints) {
|
|
165
|
+
if (endpoints.elements().empty()) {
|
|
166
|
+
return false;
|
|
167
|
+
}
|
|
168
|
+
for (const Value& endpoint : endpoints.elements()) {
|
|
169
|
+
if (!endpoint.IsString() || !IsHttpsUrl(endpoint.AsString())) {
|
|
170
|
+
return false;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
return true;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
Value PrepareRound(const std::string& configJson, const Value& env,
|
|
177
|
+
double randomSample) {
|
|
178
|
+
RunnableConfig gate = ParseRunnableConfig(configJson);
|
|
179
|
+
if (!gate.runnable() || !env.IsObject()) {
|
|
180
|
+
return Value::Undefined();
|
|
181
|
+
}
|
|
182
|
+
const Value& config = gate.config;
|
|
183
|
+
const Value& queryUrls = config.Get(kQueryUrls);
|
|
184
|
+
Value query = Value::Object();
|
|
185
|
+
query.Set("appKey", Value::String(gate.appKey));
|
|
186
|
+
query.Set("httpsOnly", Value::Bool(IsHttpsOnly(config.Get(kEndpoints))));
|
|
187
|
+
query.Set(kEndpoints,
|
|
188
|
+
OrderEndpointCandidates(config.Get(kEndpoints), randomSample));
|
|
189
|
+
query.Set(kQueryUrls, queryUrls.IsArray() ? queryUrls : Value::Array());
|
|
190
|
+
query.Set("list", Value::String(kEndpoints));
|
|
191
|
+
query.Set("next", Value::Number(0));
|
|
192
|
+
query.Set("tried", Value::Array());
|
|
193
|
+
query.Set("spent", Value::Number(0));
|
|
194
|
+
|
|
195
|
+
Value round = Value::Object();
|
|
196
|
+
round.Set("body", Value::String(RequestBody(config, env)));
|
|
197
|
+
round.Set("query", std::move(query));
|
|
198
|
+
return round;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
Value NextQueryStep(const Value& previous, const Value& responseText) {
|
|
202
|
+
if (!previous.IsObject()) {
|
|
203
|
+
return Value::Undefined();
|
|
204
|
+
}
|
|
205
|
+
Value state = previous;
|
|
206
|
+
if (state.Get("url").IsString()) {
|
|
207
|
+
const bool post = state.Get("post").Truthy();
|
|
208
|
+
state.Remove("url");
|
|
209
|
+
state.Remove("post");
|
|
210
|
+
if (responseText.IsString()) {
|
|
211
|
+
if (post && IsValidResponse(responseText.AsString())) {
|
|
212
|
+
state.Set("found", Value::Bool(true));
|
|
213
|
+
} else if (!post) {
|
|
214
|
+
bool ok = false;
|
|
215
|
+
Value remote = flowjson::Parse(responseText.AsString(), &ok);
|
|
216
|
+
if (ok && remote.IsArray()) {
|
|
217
|
+
state.Set("list", Value::String(kRemote));
|
|
218
|
+
state.Set(kRemote, std::move(remote));
|
|
219
|
+
state.Set("next", Value::Number(0));
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
if (state.Get("found").Truthy()) {
|
|
225
|
+
return state;
|
|
226
|
+
}
|
|
227
|
+
const bool httpsOnly = state.Get("httpsOnly").Truthy();
|
|
228
|
+
for (;;) {
|
|
229
|
+
const std::string list = state.Get("list").AsString();
|
|
230
|
+
if (list != kEndpoints && list != kQueryUrls && list != kRemote) {
|
|
231
|
+
state.Set("list", Value::String(""));
|
|
232
|
+
return state;
|
|
233
|
+
}
|
|
234
|
+
const bool isListOfLists = list == kQueryUrls;
|
|
235
|
+
const Value& entries = state.Get(list);
|
|
236
|
+
size_t next = Counter(state.Get("next"));
|
|
237
|
+
std::string target;
|
|
238
|
+
while (target.empty() && next < entries.Size()) {
|
|
239
|
+
const Value& entry = entries.At(next++);
|
|
240
|
+
if (!entry.IsString()) {
|
|
241
|
+
continue;
|
|
242
|
+
}
|
|
243
|
+
if (isListOfLists) {
|
|
244
|
+
target = entry.AsString();
|
|
245
|
+
continue;
|
|
246
|
+
}
|
|
247
|
+
std::string base = StripTrailingSlashes(entry.AsString());
|
|
248
|
+
if (base.empty() || Contains(state.Get("tried"), base)) {
|
|
249
|
+
continue;
|
|
250
|
+
}
|
|
251
|
+
// A remotely injected endpoint must not downgrade an https
|
|
252
|
+
// configuration to plaintext.
|
|
253
|
+
if (list == kRemote && httpsOnly && !IsHttpsUrl(base)) {
|
|
254
|
+
continue;
|
|
255
|
+
}
|
|
256
|
+
target = std::move(base);
|
|
257
|
+
}
|
|
258
|
+
state.Set("next", Value::Number(static_cast<double>(next)));
|
|
259
|
+
if (target.empty()) {
|
|
260
|
+
// The configured endpoints are followed by the list discovery; a
|
|
261
|
+
// fetched remote list (or running out of lists) ends the query.
|
|
262
|
+
state.Set("list", Value::String(list == kEndpoints ? kQueryUrls : ""));
|
|
263
|
+
state.Set("next", Value::Number(0));
|
|
264
|
+
continue;
|
|
265
|
+
}
|
|
266
|
+
const size_t spent = Counter(state.Get("spent"));
|
|
267
|
+
if (spent >= static_cast<size_t>(kMaxQueryRequests)) {
|
|
268
|
+
state.Set("list", Value::String(""));
|
|
269
|
+
return state;
|
|
270
|
+
}
|
|
271
|
+
state.Set("spent", Value::Number(static_cast<double>(spent + 1)));
|
|
272
|
+
if (isListOfLists) {
|
|
273
|
+
state.Set("url", Value::String(target));
|
|
274
|
+
state.Set("post", Value::Bool(false));
|
|
275
|
+
} else {
|
|
276
|
+
Value tried = state.Get("tried").IsArray() ? state.Get("tried")
|
|
277
|
+
: Value::Array();
|
|
278
|
+
tried.Push(Value::String(target));
|
|
279
|
+
state.Set("tried", std::move(tried));
|
|
280
|
+
state.Set("url", Value::String(target + QueryPath() +
|
|
281
|
+
state.Get("appKey").AsString()));
|
|
282
|
+
state.Set("post", Value::Bool(true));
|
|
283
|
+
}
|
|
284
|
+
return state;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
Value HandleRoundResponse(const std::string& responseText,
|
|
289
|
+
const std::string& configJson, const Value& env,
|
|
290
|
+
const std::string& requestBody,
|
|
291
|
+
double responseAtSeconds) {
|
|
292
|
+
bool ok = false;
|
|
293
|
+
const Value config = flowjson::Parse(configJson, &ok);
|
|
294
|
+
if (!ok || !config.IsObject() || !env.IsObject()) {
|
|
295
|
+
return Value::Undefined();
|
|
296
|
+
}
|
|
297
|
+
const Value& afterDownload = config.Get("afterDownload");
|
|
298
|
+
const Value decision = HandleResponse(
|
|
299
|
+
responseText, Identity(config, env), false,
|
|
300
|
+
afterDownload.IsString() ? afterDownload.AsString() : std::string());
|
|
301
|
+
|
|
302
|
+
Value round = Value::Object();
|
|
303
|
+
round.Set("action", decision.Get("action"));
|
|
304
|
+
if (decision.Get("action").AsString() == "download") {
|
|
305
|
+
round.Set("hash", decision.Get("hash"));
|
|
306
|
+
round.Set("attempts",
|
|
307
|
+
PlanAttempts(decision.Get("attempts"),
|
|
308
|
+
env.Get("currentVersion").Truthy(),
|
|
309
|
+
IsHttpsOnly(config.Get(kEndpoints))));
|
|
310
|
+
round.Set("activate", decision.Get("activate"));
|
|
311
|
+
round.Set("hashInfo", VersionInfo(decision.Get("info")));
|
|
312
|
+
} else {
|
|
313
|
+
round.Set("reason", decision.Get("reason"));
|
|
314
|
+
}
|
|
315
|
+
// Scoped to the exact request and config that produced the response (§10.3);
|
|
316
|
+
// freshness is anchored to the response's arrival.
|
|
317
|
+
Value cache = Value::Object();
|
|
318
|
+
cache.Set("ts", Value::Number(responseAtSeconds));
|
|
319
|
+
cache.Set("body", Value::String(responseText));
|
|
320
|
+
cache.Set("request", Value::String(requestBody));
|
|
321
|
+
cache.Set("config", Value::String(configJson));
|
|
322
|
+
round.Set("cache", Value::String(flowjson::Stringify(cache)));
|
|
323
|
+
return round;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
} // namespace flowcore
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include <string>
|
|
4
|
+
|
|
5
|
+
#include "flow_json.h"
|
|
6
|
+
|
|
7
|
+
// The pure steps of the native cold-start round (NATIVE_CHECKUPDATE_DESIGN
|
|
8
|
+
// §10), one implementation for the Android, iOS and Harmony orchestrators:
|
|
9
|
+
// what to send, which endpoint to ask next, and everything a commit needs
|
|
10
|
+
// from the answer. HTTP, timeouts, locks, downloads and persistence stay
|
|
11
|
+
// native. JS has no counterpart (its client builds its own request through
|
|
12
|
+
// src/updateFlowCore.ts buildCheckRequestBody, which BuildRequestBody
|
|
13
|
+
// mirrors); tests/native-round-vectors.json pins this layer.
|
|
14
|
+
//
|
|
15
|
+
// `config` is the persisted config text (already through
|
|
16
|
+
// ParseRunnableConfig). `env` is what the platform knows about this launch,
|
|
17
|
+
// as a JSON object:
|
|
18
|
+
// packageVersion the binary's package version
|
|
19
|
+
// currentVersion the running version exactly as the platform's JS
|
|
20
|
+
// constant reports it (Android null, iOS absent,
|
|
21
|
+
// Harmony "" when none), so the request matches the one
|
|
22
|
+
// JS builds and JS can reuse the response cache
|
|
23
|
+
// uuid missing -> ""
|
|
24
|
+
// rolledBackVersion the launch-path rollback snapshot
|
|
25
|
+
// buildTime passed through
|
|
26
|
+
// os "<platform> <Platform.Version>", as JS cInfo.os
|
|
27
|
+
// bundleHash omitted from the request when empty
|
|
28
|
+
// Keep the core internal to whichever binary links it (see podspec Core).
|
|
29
|
+
#pragma GCC visibility push(hidden)
|
|
30
|
+
|
|
31
|
+
namespace flowcore {
|
|
32
|
+
|
|
33
|
+
// Total HTTP requests one query may issue: endpoint queries and remote list
|
|
34
|
+
// fetches alike.
|
|
35
|
+
constexpr int kMaxQueryRequests = 8;
|
|
36
|
+
|
|
37
|
+
// {"body": "<checkUpdate request text>", "query": <first NextQueryStep
|
|
38
|
+
// state>}. The body is BuildRequestBody over the config and env: a non-empty
|
|
39
|
+
// string config.packageVersion overrides env.packageVersion, cInfo.rnu/rn are
|
|
40
|
+
// the config's strings ("" otherwise), diffV is this core's
|
|
41
|
+
// kSupportedDiffVersion. randomSample ∈ [0, 1) picks the first endpoint
|
|
42
|
+
// (OrderEndpointCandidates). Undefined when the config is not runnable or
|
|
43
|
+
// env is not an object.
|
|
44
|
+
flowjson::Value PrepareRound(const std::string& configJson,
|
|
45
|
+
const flowjson::Value& env, double randomSample);
|
|
46
|
+
|
|
47
|
+
// One step of the sequential endpoint fallback (§5.1). Pass the previous
|
|
48
|
+
// state and the text the request it named returned (Undefined when it failed
|
|
49
|
+
// or none was made). The result is the next state, with
|
|
50
|
+
// "url" (and "post"): issue that request — a POST of the round's body for
|
|
51
|
+
// an endpoint query, a GET for a remote endpoint list — then call again;
|
|
52
|
+
// "found": true: the text just passed in is the valid check response;
|
|
53
|
+
// neither: no endpoint answered; the query is over.
|
|
54
|
+
// Policy: the configured endpoints in sampled order, then the first
|
|
55
|
+
// queryUrls list that fetches and parses as a JSON array, whose entries are
|
|
56
|
+
// queried in turn (one list is enough). Bases are stripped of trailing '/'
|
|
57
|
+
// and asked once each; entries that are not strings are skipped; a remote
|
|
58
|
+
// entry must be https when every configured endpoint is (IsHttpsOnly), so the
|
|
59
|
+
// network cannot downgrade the round. A response counts only when
|
|
60
|
+
// IsValidResponse accepts it. At most kMaxQueryRequests requests. Undefined
|
|
61
|
+
// for a state that is not an object.
|
|
62
|
+
flowjson::Value NextQueryStep(const flowjson::Value& state,
|
|
63
|
+
const flowjson::Value& responseText);
|
|
64
|
+
|
|
65
|
+
// Every configured endpoint is an https URL (ASCII case-insensitive); false
|
|
66
|
+
// for an empty or missing list.
|
|
67
|
+
bool IsHttpsOnly(const flowjson::Value& endpoints);
|
|
68
|
+
|
|
69
|
+
// HandleResponse for the round, plus what each commit needs:
|
|
70
|
+
// {action: "none", reason, cache}
|
|
71
|
+
// {action: "download", hash, attempts, activate, hashInfo, cache}
|
|
72
|
+
// attempts are planned for this device: no diff attempt without a running
|
|
73
|
+
// version (env.currentVersion falsy), no cleartext http:// URL when the
|
|
74
|
+
// config IsHttpsOnly; strategies left without a URL are dropped. hashInfo is
|
|
75
|
+
// the version info to persist next to the download (the JS side's
|
|
76
|
+
// setLocalHashInfo): the info's string name/description/metaInfo, plus
|
|
77
|
+
// forceBootRescue when the server's config.forceBoot is truthy (the same
|
|
78
|
+
// truthiness that activates it). cache is the response-cache text JS reuses
|
|
79
|
+
// ({ts, body, request, config}). afterDownload is the config's string
|
|
80
|
+
// (otherwise ""). Undefined when the config is not a JSON object or env is
|
|
81
|
+
// not an object.
|
|
82
|
+
flowjson::Value HandleRoundResponse(const std::string& responseText,
|
|
83
|
+
const std::string& configJson,
|
|
84
|
+
const flowjson::Value& env,
|
|
85
|
+
const std::string& requestBody,
|
|
86
|
+
double responseAtSeconds);
|
|
87
|
+
|
|
88
|
+
} // namespace flowcore
|
|
89
|
+
|
|
90
|
+
#pragma GCC visibility pop
|
|
@@ -81,8 +81,8 @@ bool ShouldActivateAfterDownload(const flowjson::Value& info,
|
|
|
81
81
|
const std::string& afterDownload);
|
|
82
82
|
|
|
83
83
|
// Composes Parse → ResolveResult → DecideDownload: one call from the
|
|
84
|
-
// raw checkUpdate response text to a download decision
|
|
85
|
-
//
|
|
84
|
+
// raw checkUpdate response text to a download decision (the native round
|
|
85
|
+
// reaches it through HandleRoundResponse, native_round.h). identity is the union of
|
|
86
86
|
// both composed functions' needs: { packageVersion, currentVersion?, uuid,
|
|
87
87
|
// rolledBackVersion? }. afterDownload is the client's persisted activation
|
|
88
88
|
// policy; a download decision carries `activate` (ShouldActivateAfterDownload
|
package/harmony/hvigor-plugin.ts
CHANGED
|
@@ -156,26 +156,18 @@ export function reactNativeUpdatePlugin() {
|
|
|
156
156
|
console.log(`Build time written to ${metaFilePath} (${buildMode})`);
|
|
157
157
|
};
|
|
158
158
|
|
|
159
|
-
if (typeof safeNode.registerTask
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
dependencies: ['default@PreBuild'],
|
|
165
|
-
postDependencies: ['default@ProcessResource'],
|
|
166
|
-
});
|
|
167
|
-
return;
|
|
168
|
-
} catch (e) {
|
|
169
|
-
console.warn(
|
|
170
|
-
`reactNativeUpdatePlugin: task registration failed (${
|
|
171
|
-
e instanceof Error ? e.message : String(e)
|
|
172
|
-
}), writing meta.json at apply time`,
|
|
173
|
-
);
|
|
174
|
-
}
|
|
159
|
+
if (typeof safeNode.registerTask !== 'function') {
|
|
160
|
+
throw new Error(
|
|
161
|
+
'reactNativeUpdatePlugin: this hvigor cannot register build tasks; ' +
|
|
162
|
+
'apply the plugin from a module hvigorfile with hvigor 5 or later.',
|
|
163
|
+
);
|
|
175
164
|
}
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
165
|
+
safeNode.registerTask({
|
|
166
|
+
name: TASK_NAME,
|
|
167
|
+
run: writeMeta,
|
|
168
|
+
dependencies: ['default@PreBuild'],
|
|
169
|
+
postDependencies: ['default@ProcessResource'],
|
|
170
|
+
});
|
|
179
171
|
},
|
|
180
172
|
};
|
|
181
173
|
}
|
|
@@ -7,38 +7,31 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
|
|
7
7
|
|
|
8
8
|
set(CMAKE_VERBOSE_MAKEFILE on)
|
|
9
9
|
|
|
10
|
+
# The shared native sources are compiled in place from the repository root
|
|
11
|
+
# (or the npm package root, which ships cpp/ and the android/jni submodules).
|
|
10
12
|
set(REPO_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/../../../../../)
|
|
11
|
-
set(STAGED_NATIVE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/android-generated)
|
|
12
|
-
set(ANDROID_JNI_DIR ${REPO_ROOT}/android/jni)
|
|
13
|
-
if(NOT EXISTS ${ANDROID_JNI_DIR}/hpatch.c)
|
|
14
|
-
set(ANDROID_JNI_DIR ${STAGED_NATIVE_DIR}/jni)
|
|
15
|
-
endif()
|
|
16
13
|
set(PATCH_CORE_DIR ${REPO_ROOT}/cpp/patch_core)
|
|
17
|
-
if(NOT EXISTS ${PATCH_CORE_DIR}/patch_core.cpp)
|
|
18
|
-
set(PATCH_CORE_DIR ${STAGED_NATIVE_DIR}/patch_core)
|
|
19
|
-
endif()
|
|
20
14
|
set(UPDATE_FLOW_CORE_DIR ${REPO_ROOT}/cpp/update_flow_core)
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
15
|
+
set(HPATCH_DIR ${REPO_ROOT}/cpp/hpatch)
|
|
16
|
+
set(HDIFFPATCH_DIR ${REPO_ROOT}/android/jni/HDiffPatch)
|
|
17
|
+
set(LZMA_DIR ${REPO_ROOT}/android/jni/lzma)
|
|
24
18
|
|
|
25
19
|
# Always compile from source. A prebuilt-import branch used to live here, but
|
|
26
20
|
# it had no symbol verification: a stale libs/<abi>/librnpushy.so missing a
|
|
27
21
|
# newly added export (e.g. getSupportedDiffVersion) would silently ship and
|
|
28
22
|
# crash getConstants at runtime.
|
|
29
|
-
|
|
30
|
-
|
|
23
|
+
#
|
|
24
|
+
# The platform-neutral cores are picked up by directory (non-recursive, so
|
|
25
|
+
# tests/ stays out; the Android JNI glue lives in cpp/android_jni), so a new
|
|
26
|
+
# core source needs no edit here. CONFIGURE_DEPENDS re-globs on every build.
|
|
27
|
+
file(GLOB CORE_SOURCES CONFIGURE_DEPENDS
|
|
28
|
+
${PATCH_CORE_DIR}/*.cpp
|
|
29
|
+
${UPDATE_FLOW_CORE_DIR}/*.cpp
|
|
30
|
+
${HPATCH_DIR}/*.c
|
|
31
|
+
)
|
|
31
32
|
set(HDP_SOURCES
|
|
32
33
|
${CMAKE_CURRENT_SOURCE_DIR}/pushy.cpp
|
|
33
|
-
${
|
|
34
|
-
${PATCH_CORE_DIR}/digest.cpp
|
|
35
|
-
${PATCH_CORE_DIR}/hbc_transform.cpp
|
|
36
|
-
${PATCH_CORE_DIR}/hbc_transform_wire.cpp
|
|
37
|
-
${PATCH_CORE_DIR}/patch_core.cpp
|
|
38
|
-
${PATCH_CORE_DIR}/state_core.cpp
|
|
39
|
-
${UPDATE_FLOW_CORE_DIR}/flow_json.cpp
|
|
40
|
-
${UPDATE_FLOW_CORE_DIR}/update_flow_core.cpp
|
|
41
|
-
${ANDROID_JNI_DIR}/hpatch.c
|
|
34
|
+
${CORE_SOURCES}
|
|
42
35
|
${HDIFFPATCH_DIR}/libHDiffPatch/HPatch/patch.c
|
|
43
36
|
${HDIFFPATCH_DIR}/file_for_patch.c
|
|
44
37
|
${LZMA_DIR}/C/LzmaDec.c
|
|
@@ -49,14 +42,12 @@ add_library(rnpushy SHARED
|
|
|
49
42
|
${HDP_SOURCES}
|
|
50
43
|
)
|
|
51
44
|
|
|
45
|
+
# pushy.cpp includes the cores by name; the cores themselves (and hpatch's
|
|
46
|
+
# HDiffPatch/lzma includes) resolve file-relative.
|
|
52
47
|
target_include_directories(rnpushy PRIVATE
|
|
53
48
|
${CMAKE_CURRENT_SOURCE_DIR}
|
|
54
49
|
${PATCH_CORE_DIR}
|
|
55
50
|
${UPDATE_FLOW_CORE_DIR}
|
|
56
|
-
${ANDROID_JNI_DIR}
|
|
57
|
-
${HDIFFPATCH_DIR}
|
|
58
|
-
${HDIFFPATCH_DIR}/libHDiffPatch/HPatch
|
|
59
|
-
${LZMA_DIR}/C
|
|
60
51
|
)
|
|
61
52
|
|
|
62
53
|
target_link_libraries(rnpushy PUBLIC
|