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
|
@@ -39,7 +39,7 @@ final class UpdateEventEmitter {
|
|
|
39
39
|
// emit (this SDK itself triggers reloads); sendEvent runs on the
|
|
40
40
|
// main thread, so a throw here would crash the app for a lost
|
|
41
41
|
// progress tick.
|
|
42
|
-
Log.w(
|
|
42
|
+
Log.w(Texts.LOG_TAG, "sendEvent " + eventName + " failed: " + e.getMessage());
|
|
43
43
|
}
|
|
44
44
|
}
|
|
45
45
|
|
|
@@ -1,13 +1,18 @@
|
|
|
1
1
|
package cn.reactnative.modules.update;
|
|
2
2
|
|
|
3
|
+
import java.io.ByteArrayOutputStream;
|
|
3
4
|
import java.io.File;
|
|
4
5
|
import java.io.FileInputStream;
|
|
5
6
|
import java.io.FileOutputStream;
|
|
6
7
|
import java.io.IOException;
|
|
7
8
|
import java.io.InputStream;
|
|
9
|
+
import java.io.OutputStream;
|
|
10
|
+
import java.nio.charset.StandardCharsets;
|
|
11
|
+
import java.security.MessageDigest;
|
|
8
12
|
|
|
9
13
|
final class UpdateFileUtils {
|
|
10
|
-
|
|
14
|
+
/** The one IO block size: stream copies, digests, downloads, unzip. */
|
|
15
|
+
static final int BUFFER_SIZE = 64 * 1024;
|
|
11
16
|
|
|
12
17
|
private UpdateFileUtils() {
|
|
13
18
|
}
|
|
@@ -57,27 +62,53 @@ final class UpdateFileUtils {
|
|
|
57
62
|
}
|
|
58
63
|
}
|
|
59
64
|
|
|
65
|
+
static MessageDigest newSha256() throws IOException {
|
|
66
|
+
try {
|
|
67
|
+
return MessageDigest.getInstance("SHA-256");
|
|
68
|
+
} catch (java.security.NoSuchAlgorithmException e) {
|
|
69
|
+
throw new IOException("SHA-256 unavailable", e);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** Feeds the whole file into digest (streaming, bounded memory). */
|
|
74
|
+
static void updateDigest(MessageDigest digest, File file) throws IOException {
|
|
75
|
+
try (InputStream in = new FileInputStream(file)) {
|
|
76
|
+
updateDigest(digest, in);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** Feeds the rest of the stream into digest; the caller closes it. */
|
|
81
|
+
static void updateDigest(MessageDigest digest, InputStream in) throws IOException {
|
|
82
|
+
byte[] buffer = new byte[BUFFER_SIZE];
|
|
83
|
+
int read;
|
|
84
|
+
while ((read = in.read(buffer)) != -1) {
|
|
85
|
+
digest.update(buffer, 0, read);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
static String toHex(byte[] digest) {
|
|
90
|
+
StringBuilder hex = new StringBuilder(digest.length * 2);
|
|
91
|
+
for (byte b : digest) {
|
|
92
|
+
hex.append(Character.forDigit((b >> 4) & 0xf, 16));
|
|
93
|
+
hex.append(Character.forDigit(b & 0xf, 16));
|
|
94
|
+
}
|
|
95
|
+
return hex.toString();
|
|
96
|
+
}
|
|
97
|
+
|
|
60
98
|
/** Streaming SHA-256 of a file, lowercase hex. */
|
|
61
99
|
static String sha256Hex(File file) throws IOException {
|
|
62
100
|
try (InputStream in = new FileInputStream(file)) {
|
|
63
|
-
|
|
64
|
-
byte[] buffer = new byte[64 * 1024];
|
|
65
|
-
int read;
|
|
66
|
-
while ((read = in.read(buffer)) != -1) {
|
|
67
|
-
md.update(buffer, 0, read);
|
|
68
|
-
}
|
|
69
|
-
byte[] digest = md.digest();
|
|
70
|
-
StringBuilder hex = new StringBuilder(digest.length * 2);
|
|
71
|
-
for (byte b : digest) {
|
|
72
|
-
hex.append(Character.forDigit((b >> 4) & 0xf, 16));
|
|
73
|
-
hex.append(Character.forDigit(b & 0xf, 16));
|
|
74
|
-
}
|
|
75
|
-
return hex.toString();
|
|
76
|
-
} catch (java.security.NoSuchAlgorithmException e) {
|
|
77
|
-
throw new IOException("SHA-256 unavailable", e);
|
|
101
|
+
return sha256Hex(in);
|
|
78
102
|
}
|
|
79
103
|
}
|
|
80
104
|
|
|
105
|
+
/** Streaming SHA-256 of the rest of the stream, lowercase hex; the caller closes it. */
|
|
106
|
+
static String sha256Hex(InputStream in) throws IOException {
|
|
107
|
+
MessageDigest md = newSha256();
|
|
108
|
+
updateDigest(md, in);
|
|
109
|
+
return toHex(md.digest());
|
|
110
|
+
}
|
|
111
|
+
|
|
81
112
|
static void copyFile(File from, File to) throws IOException {
|
|
82
113
|
ensureParentDirectory(to);
|
|
83
114
|
try (
|
|
@@ -95,11 +126,38 @@ final class UpdateFileUtils {
|
|
|
95
126
|
}
|
|
96
127
|
}
|
|
97
128
|
|
|
98
|
-
|
|
129
|
+
/** Copies the rest of the stream; the caller closes both ends. */
|
|
130
|
+
static void copy(InputStream in, OutputStream out) throws IOException {
|
|
99
131
|
byte[] buffer = new byte[BUFFER_SIZE];
|
|
100
132
|
int count;
|
|
101
133
|
while ((count = in.read(buffer)) != -1) {
|
|
102
134
|
out.write(buffer, 0, count);
|
|
103
135
|
}
|
|
104
136
|
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Reads the whole stream (and closes it), failing once it exceeds
|
|
140
|
+
* maxBytes: small server- or archive-provided documents must not be
|
|
141
|
+
* able to exhaust memory.
|
|
142
|
+
*/
|
|
143
|
+
static byte[] readBytes(InputStream input, long maxBytes) throws IOException {
|
|
144
|
+
try (InputStream in = input; ByteArrayOutputStream out = new ByteArrayOutputStream()) {
|
|
145
|
+
byte[] buffer = new byte[BUFFER_SIZE];
|
|
146
|
+
int count;
|
|
147
|
+
long total = 0;
|
|
148
|
+
while ((count = in.read(buffer)) != -1) {
|
|
149
|
+
total += count;
|
|
150
|
+
if (total > maxBytes) {
|
|
151
|
+
throw new IOException("content exceeds " + maxBytes + " bytes");
|
|
152
|
+
}
|
|
153
|
+
out.write(buffer, 0, count);
|
|
154
|
+
}
|
|
155
|
+
return out.toByteArray();
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/** A small UTF-8 text file (a record, a sidecar), at most maxBytes long. */
|
|
160
|
+
static String readSmallUtf8(File file, long maxBytes) throws IOException {
|
|
161
|
+
return new String(readBytes(new FileInputStream(file), maxBytes), StandardCharsets.UTF_8);
|
|
162
|
+
}
|
|
105
163
|
}
|
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
package cn.reactnative.modules.update;
|
|
2
2
|
|
|
3
|
+
import static cn.reactnative.modules.update.StateSerialRunner.STATE_SERIAL;
|
|
4
|
+
import static cn.reactnative.modules.update.StateSerialRunner.UI_THREAD;
|
|
5
|
+
|
|
3
6
|
import android.util.Log;
|
|
4
7
|
import androidx.annotation.Nullable;
|
|
5
8
|
import com.facebook.react.bridge.Promise;
|
|
6
9
|
import com.facebook.react.bridge.ReactApplicationContext;
|
|
10
|
+
import com.facebook.react.bridge.ReactMethod;
|
|
7
11
|
import com.facebook.react.bridge.ReadableMap;
|
|
8
12
|
import java.util.HashMap;
|
|
9
13
|
import java.util.Map;
|
|
@@ -12,32 +16,33 @@ import org.json.JSONObject;
|
|
|
12
16
|
import org.json.JSONTokener;
|
|
13
17
|
|
|
14
18
|
/**
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
+
* The "Pushy" native module for both architectures. NativePushySpec is the
|
|
20
|
+
* codegen spec under the new architecture and the hand-written stand-in in
|
|
21
|
+
* src/oldarch otherwise; the old bridge discovers methods through the
|
|
22
|
+
* @ReactMethod annotations here, so every spec method carries one. Each
|
|
23
|
+
* state-changing method takes a trailing Promise, so a failed persistence
|
|
24
|
+
* rejects instead of resolving early (CODE_AUDIT 2.3).
|
|
19
25
|
*/
|
|
20
|
-
public class
|
|
26
|
+
public class UpdateModule extends NativePushySpec {
|
|
21
27
|
// Download URL option key, stored encoded.
|
|
22
28
|
private static final String OPTION_URL = Texts.reveal("2f07f0d0ba8e5d572e");
|
|
23
29
|
|
|
24
|
-
|
|
25
|
-
public static final String NAME = "Pushy";
|
|
26
|
-
|
|
27
30
|
private final ReactApplicationContext reactContext;
|
|
28
31
|
private final UpdateContext updateContext;
|
|
29
32
|
|
|
30
|
-
public
|
|
33
|
+
public UpdateModule(ReactApplicationContext reactContext) {
|
|
31
34
|
this(reactContext, UpdateContext.getInstance(reactContext));
|
|
32
35
|
}
|
|
33
36
|
|
|
34
|
-
public
|
|
37
|
+
public UpdateModule(ReactApplicationContext reactContext, UpdateContext updateContext) {
|
|
38
|
+
super(reactContext);
|
|
35
39
|
this.reactContext = reactContext;
|
|
36
40
|
this.updateContext = updateContext;
|
|
37
41
|
UpdateEventEmitter.register(reactContext);
|
|
38
42
|
}
|
|
39
43
|
|
|
40
|
-
|
|
44
|
+
@Override
|
|
45
|
+
protected Map<String, Object> getTypedExportedConstants() {
|
|
41
46
|
final Map<String, Object> constants = new HashMap<String, Object>();
|
|
42
47
|
constants.put("downloadRootDir", updateContext.getRootDir());
|
|
43
48
|
constants.put("packageVersion", updateContext.getPackageVersion());
|
|
@@ -45,6 +50,8 @@ public class UpdateModuleImpl {
|
|
|
45
50
|
String currentVersion = updateContext.getCurrentVersion();
|
|
46
51
|
constants.put("currentVersion", currentVersion);
|
|
47
52
|
constants.put("currentVersionInfo", updateContext.getKv("hash_" + currentVersion));
|
|
53
|
+
// Served from the value cached at launch resolution (no file read on
|
|
54
|
+
// the JS thread) when currentVersion is the launched version.
|
|
48
55
|
constants.put("currentBundleSha256", updateContext.currentBundleSha256(currentVersion));
|
|
49
56
|
constants.put("buildTime", updateContext.getBuildTime());
|
|
50
57
|
constants.put("isUsingBundleUrl", updateContext.getIsUsingBundleUrl());
|
|
@@ -63,7 +70,7 @@ public class UpdateModuleImpl {
|
|
|
63
70
|
// A mismatched librnpushy.so (stale manual copy / build cache)
|
|
64
71
|
// must not crash startup via getConstants; 0 simply means "no v2
|
|
65
72
|
// diff track" and the server degrades gracefully.
|
|
66
|
-
Log.e(
|
|
73
|
+
Log.e(Texts.LOG_TAG, "supportedDiffVersion missing from the native library", e);
|
|
67
74
|
}
|
|
68
75
|
constants.put("supportedDiffVersion", supportedDiffVersion);
|
|
69
76
|
return constants;
|
|
@@ -127,26 +134,13 @@ public class UpdateModuleImpl {
|
|
|
127
134
|
|
|
128
135
|
@Override
|
|
129
136
|
public void onDownloadFailed(Throwable error) {
|
|
130
|
-
promise.reject(
|
|
137
|
+
promise.reject(ErrorCodes.forDownloadFailure(error), error);
|
|
131
138
|
}
|
|
132
139
|
};
|
|
133
140
|
}
|
|
134
141
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
// patch health from network health; a local refusal (reinstalling the
|
|
138
|
-
// running version in place) as FILE_OPERATION_FAILED. DownloadTask
|
|
139
|
-
// classifies them.
|
|
140
|
-
private static String downloadErrorCode(Throwable error) {
|
|
141
|
-
if (error instanceof PatchFailedException) {
|
|
142
|
-
return ErrorCodes.DELTA_FAILED;
|
|
143
|
-
}
|
|
144
|
-
if (error instanceof FileOperationException) {
|
|
145
|
-
return ErrorCodes.FILE_OPERATION_FAILED;
|
|
146
|
-
}
|
|
147
|
-
return ErrorCodes.DOWNLOAD_FAILED;
|
|
148
|
-
}
|
|
149
|
-
|
|
142
|
+
@ReactMethod
|
|
143
|
+
@Override
|
|
150
144
|
public void downloadFullUpdate(final ReadableMap options, final Promise promise) {
|
|
151
145
|
String url = readRequiredString(options, OPTION_URL, promise);
|
|
152
146
|
if (url == null) {
|
|
@@ -156,9 +150,11 @@ public class UpdateModuleImpl {
|
|
|
156
150
|
if (hash == null) {
|
|
157
151
|
return;
|
|
158
152
|
}
|
|
159
|
-
updateContext.downloadFullUpdate(url, hash, downloadListener(promise));
|
|
153
|
+
updateContext.downloadFullUpdate(url, hash, downloadListener(promise), 0);
|
|
160
154
|
}
|
|
161
155
|
|
|
156
|
+
@ReactMethod
|
|
157
|
+
@Override
|
|
162
158
|
public void downloadAndInstallApk(final ReadableMap options, final Promise promise) {
|
|
163
159
|
// Every failure of this path — a missing REQUEST_INSTALL_PACKAGES
|
|
164
160
|
// declaration above all — has to come back as a promise rejection,
|
|
@@ -203,6 +199,8 @@ public class UpdateModuleImpl {
|
|
|
203
199
|
}
|
|
204
200
|
}
|
|
205
201
|
|
|
202
|
+
@ReactMethod
|
|
203
|
+
@Override
|
|
206
204
|
public void downloadPatchFromPackage(final ReadableMap options, final Promise promise) {
|
|
207
205
|
String url = readRequiredString(options, OPTION_URL, promise);
|
|
208
206
|
if (url == null) {
|
|
@@ -212,9 +210,11 @@ public class UpdateModuleImpl {
|
|
|
212
210
|
if (hash == null) {
|
|
213
211
|
return;
|
|
214
212
|
}
|
|
215
|
-
updateContext.downloadPatchFromApk(url, hash, downloadListener(promise));
|
|
213
|
+
updateContext.downloadPatchFromApk(url, hash, downloadListener(promise), 0);
|
|
216
214
|
}
|
|
217
215
|
|
|
216
|
+
@ReactMethod
|
|
217
|
+
@Override
|
|
218
218
|
public void downloadPatchFromPpk(final ReadableMap options, final Promise promise) {
|
|
219
219
|
String url = readRequiredString(options, OPTION_URL, promise);
|
|
220
220
|
if (url == null) {
|
|
@@ -228,79 +228,80 @@ public class UpdateModuleImpl {
|
|
|
228
228
|
if (originHash == null) {
|
|
229
229
|
return;
|
|
230
230
|
}
|
|
231
|
-
updateContext.downloadPatchFromPpk(url, hash, originHash, downloadListener(promise));
|
|
231
|
+
updateContext.downloadPatchFromPpk(url, hash, originHash, downloadListener(promise), 0);
|
|
232
232
|
}
|
|
233
233
|
|
|
234
|
+
@ReactMethod
|
|
235
|
+
@Override
|
|
234
236
|
public void reloadUpdate(final ReadableMap options, final Promise promise) {
|
|
235
237
|
String hash = readRequiredString(options, "hash", promise);
|
|
236
238
|
if (hash == null) {
|
|
237
239
|
return;
|
|
238
240
|
}
|
|
239
|
-
|
|
241
|
+
restart(hash, promise);
|
|
240
242
|
}
|
|
241
243
|
|
|
242
|
-
|
|
244
|
+
@ReactMethod
|
|
245
|
+
@Override
|
|
246
|
+
public void restartApp(Promise promise) {
|
|
247
|
+
restart(null, promise);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
private void restart(@Nullable final String hash, final Promise promise) {
|
|
243
251
|
// Two hops (CODE_AUDIT 2.2): the switch (bundle digest + commit) and
|
|
244
252
|
// the launch resolution run on the state serial thread like every
|
|
245
|
-
// other state write
|
|
246
|
-
// UI thread. A failure at either hop
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
UiThreadRunner.run(promise, ErrorCodes.RESTART_FAILED, "restartApp", new UiThreadRunner.Operation() {
|
|
252
|
-
@Override
|
|
253
|
-
public void run() throws Throwable {
|
|
254
|
-
ReactReloadManager.reload(updateContext, reactContext, bundlePath);
|
|
255
|
-
promise.resolve(true);
|
|
256
|
-
}
|
|
257
|
-
});
|
|
253
|
+
// other state write — nothing there touches React; only the React
|
|
254
|
+
// reload itself is posted to the UI thread. A failure at either hop
|
|
255
|
+
// rejects with RESTART_FAILED.
|
|
256
|
+
StateSerialRunner.run(STATE_SERIAL, promise, ErrorCodes.RESTART_FAILED, "restartApp", () -> {
|
|
257
|
+
if (hash != null) {
|
|
258
|
+
updateContext.switchVersion(hash);
|
|
258
259
|
}
|
|
260
|
+
// The bundle to load; null for the packaged one.
|
|
261
|
+
final String bundlePath = updateContext.getBundleUrl();
|
|
262
|
+
StateSerialRunner.run(UI_THREAD, promise, ErrorCodes.RESTART_FAILED, "restartApp", () -> {
|
|
263
|
+
ReactReloadManager.reload(updateContext, reactContext, bundlePath);
|
|
264
|
+
promise.resolve(true);
|
|
265
|
+
});
|
|
259
266
|
});
|
|
260
267
|
}
|
|
261
268
|
|
|
269
|
+
@ReactMethod
|
|
270
|
+
@Override
|
|
262
271
|
public void setNeedUpdate(final ReadableMap options, final Promise promise) {
|
|
263
272
|
final String hash = readRequiredString(options, "hash", promise);
|
|
264
273
|
if (hash == null) {
|
|
265
274
|
return;
|
|
266
275
|
}
|
|
267
|
-
StateSerialRunner.run(promise, ErrorCodes.SWITCH_VERSION_FAILED, "switchVersionLater",
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
updateContext.switchVersion(hash);
|
|
271
|
-
promise.resolve(true);
|
|
272
|
-
}
|
|
276
|
+
StateSerialRunner.run(STATE_SERIAL, promise, ErrorCodes.SWITCH_VERSION_FAILED, "switchVersionLater", () -> {
|
|
277
|
+
updateContext.switchVersion(hash);
|
|
278
|
+
promise.resolve(true);
|
|
273
279
|
});
|
|
274
280
|
}
|
|
275
281
|
|
|
282
|
+
@ReactMethod
|
|
283
|
+
@Override
|
|
276
284
|
public void markSuccess(final Promise promise) {
|
|
277
|
-
StateSerialRunner.run(promise, ErrorCodes.MARK_SUCCESS_FAILED, "markSuccess",
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
updateContext.markSuccess();
|
|
281
|
-
promise.resolve(true);
|
|
282
|
-
}
|
|
285
|
+
StateSerialRunner.run(STATE_SERIAL, promise, ErrorCodes.MARK_SUCCESS_FAILED, "markSuccess", () -> {
|
|
286
|
+
updateContext.markSuccess();
|
|
287
|
+
promise.resolve(true);
|
|
283
288
|
});
|
|
284
289
|
}
|
|
285
290
|
|
|
291
|
+
@ReactMethod
|
|
292
|
+
@Override
|
|
286
293
|
public void getBundleHash(final Promise promise) {
|
|
287
|
-
// Threading lives in UpdateContext (
|
|
294
|
+
// Threading lives in UpdateContext (its bundle-hash thread); resolve is
|
|
288
295
|
// thread-safe. Never rejects — empty string means "unknown".
|
|
289
|
-
updateContext.getBundleHash(
|
|
290
|
-
@Override
|
|
291
|
-
public void onBundleHash(String hash) {
|
|
292
|
-
promise.resolve(hash);
|
|
293
|
-
}
|
|
294
|
-
});
|
|
296
|
+
updateContext.getBundleHash(promise::resolve);
|
|
295
297
|
}
|
|
296
298
|
|
|
299
|
+
@ReactMethod
|
|
300
|
+
@Override
|
|
297
301
|
public void resetToPackagedBundle(final Promise promise) {
|
|
298
|
-
StateSerialRunner.run(promise, ErrorCodes.RESET_FAILED, "resetToPackagedBundle",
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
updateContext.resetToPackagedBundle();
|
|
302
|
-
promise.resolve(true);
|
|
303
|
-
}
|
|
302
|
+
StateSerialRunner.run(STATE_SERIAL, promise, ErrorCodes.RESET_FAILED, "resetToPackagedBundle", () -> {
|
|
303
|
+
updateContext.resetToPackagedBundle();
|
|
304
|
+
promise.resolve(true);
|
|
304
305
|
});
|
|
305
306
|
}
|
|
306
307
|
|
|
@@ -309,18 +310,19 @@ public class UpdateModuleImpl {
|
|
|
309
310
|
* reuse instead of re-checking (§10.3). Empty string when absent; never
|
|
310
311
|
* rejects.
|
|
311
312
|
*/
|
|
313
|
+
@ReactMethod
|
|
314
|
+
@Override
|
|
312
315
|
public void getNativeCheckCache(final Promise promise) {
|
|
313
|
-
String cached = updateContext.
|
|
316
|
+
String cached = updateContext.getNativeCheckCache();
|
|
314
317
|
promise.resolve(cached == null ? "" : cached);
|
|
315
318
|
}
|
|
316
319
|
|
|
317
320
|
/**
|
|
318
|
-
*
|
|
319
|
-
*
|
|
320
|
-
* parsed on read by the orchestrator; absent config = check disabled.
|
|
321
|
-
* Validated at write time — a corrupt config would otherwise silently
|
|
322
|
-
* disable the native check forever with no signal.
|
|
321
|
+
* JS obtained a valid check response for this config in this process; the
|
|
322
|
+
* delayed native round then skips its duplicate request (§10.3).
|
|
323
323
|
*/
|
|
324
|
+
@ReactMethod
|
|
325
|
+
@Override
|
|
324
326
|
public void markJsCheckCompleted(final String config, final Promise promise) {
|
|
325
327
|
if (requireNonEmpty(config, "config", promise) == null) {
|
|
326
328
|
return;
|
|
@@ -329,6 +331,15 @@ public class UpdateModuleImpl {
|
|
|
329
331
|
promise.resolve(true);
|
|
330
332
|
}
|
|
331
333
|
|
|
334
|
+
/**
|
|
335
|
+
* Provisioning for the native cold-start update check
|
|
336
|
+
* (NATIVE_CHECKUPDATE_DESIGN §10.1): the raw JSON persists as-is and is
|
|
337
|
+
* parsed on read by the orchestrator; absent config = check disabled.
|
|
338
|
+
* Validated at write time — a corrupt config would otherwise silently
|
|
339
|
+
* disable the native check forever with no signal.
|
|
340
|
+
*/
|
|
341
|
+
@ReactMethod
|
|
342
|
+
@Override
|
|
332
343
|
public void syncNativeConfig(final String config, final Promise promise) {
|
|
333
344
|
if (requireNonEmpty(config, "config", promise) == null) {
|
|
334
345
|
return;
|
|
@@ -339,28 +350,26 @@ public class UpdateModuleImpl {
|
|
|
339
350
|
promise.reject(ErrorCodes.INVALID_OPTIONS, "config must be a JSON object string", e);
|
|
340
351
|
return;
|
|
341
352
|
}
|
|
342
|
-
StateSerialRunner.run(promise, ErrorCodes.FILE_OPERATION_FAILED, "syncNativeConfig",
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
updateContext.setNativeConfig(config);
|
|
346
|
-
promise.resolve(true);
|
|
347
|
-
}
|
|
353
|
+
StateSerialRunner.run(STATE_SERIAL, promise, ErrorCodes.FILE_OPERATION_FAILED, "syncNativeConfig", () -> {
|
|
354
|
+
updateContext.setNativeConfig(config);
|
|
355
|
+
promise.resolve(true);
|
|
348
356
|
});
|
|
349
357
|
}
|
|
350
358
|
|
|
359
|
+
@ReactMethod
|
|
360
|
+
@Override
|
|
351
361
|
public void setUuid(final String uuid, final Promise promise) {
|
|
352
362
|
if (requireNonEmpty(uuid, "uuid", promise) == null) {
|
|
353
363
|
return;
|
|
354
364
|
}
|
|
355
|
-
StateSerialRunner.run(promise, ErrorCodes.FILE_OPERATION_FAILED, "setUuid",
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
updateContext.setKv("uuid", uuid);
|
|
359
|
-
promise.resolve(true);
|
|
360
|
-
}
|
|
365
|
+
StateSerialRunner.run(STATE_SERIAL, promise, ErrorCodes.FILE_OPERATION_FAILED, "setUuid", () -> {
|
|
366
|
+
updateContext.setKv("uuid", uuid);
|
|
367
|
+
promise.resolve(true);
|
|
361
368
|
});
|
|
362
369
|
}
|
|
363
370
|
|
|
371
|
+
@ReactMethod
|
|
372
|
+
@Override
|
|
364
373
|
public void setLocalHashInfo(final String hash, final String info, final Promise promise) {
|
|
365
374
|
if (requireNonEmpty(hash, "hash", promise) == null) {
|
|
366
375
|
return;
|
|
@@ -371,15 +380,14 @@ public class UpdateModuleImpl {
|
|
|
371
380
|
}
|
|
372
381
|
// A state write that did not reach disk (SharedPreferences commit
|
|
373
382
|
// false) is FILE_OPERATION_FAILED, distinct from malformed input.
|
|
374
|
-
StateSerialRunner.run(promise, ErrorCodes.FILE_OPERATION_FAILED, "setLocalHashInfo",
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
updateContext.setKv("hash_" + hash, info);
|
|
378
|
-
promise.resolve(true);
|
|
379
|
-
}
|
|
383
|
+
StateSerialRunner.run(STATE_SERIAL, promise, ErrorCodes.FILE_OPERATION_FAILED, "setLocalHashInfo", () -> {
|
|
384
|
+
updateContext.setKv("hash_" + hash, info);
|
|
385
|
+
promise.resolve(true);
|
|
380
386
|
});
|
|
381
387
|
}
|
|
382
388
|
|
|
389
|
+
@ReactMethod
|
|
390
|
+
@Override
|
|
383
391
|
public void getLocalHashInfo(final String hash, final Promise promise) {
|
|
384
392
|
if (requireNonEmpty(hash, "hash", promise) == null) {
|
|
385
393
|
return;
|
|
@@ -398,4 +406,14 @@ public class UpdateModuleImpl {
|
|
|
398
406
|
|
|
399
407
|
promise.resolve(value);
|
|
400
408
|
}
|
|
409
|
+
|
|
410
|
+
@ReactMethod
|
|
411
|
+
@Override
|
|
412
|
+
public void addListener(String eventName) {
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
@ReactMethod
|
|
416
|
+
@Override
|
|
417
|
+
public void removeListeners(double count) {
|
|
418
|
+
}
|
|
401
419
|
}
|
|
@@ -18,29 +18,26 @@ public class UpdatePackage extends TurboReactPackage {
|
|
|
18
18
|
@Nullable
|
|
19
19
|
@Override
|
|
20
20
|
public NativeModule getModule(String name, ReactApplicationContext reactContext) {
|
|
21
|
-
return name.equals(
|
|
21
|
+
return name.equals(UpdateModule.NAME) ? new UpdateModule(reactContext) : null;
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
@Override
|
|
25
25
|
public ReactModuleInfoProvider getReactModuleInfoProvider() {
|
|
26
|
-
return
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
));
|
|
42
|
-
return moduleInfos;
|
|
43
|
-
}
|
|
26
|
+
return () -> {
|
|
27
|
+
final Map<String, ReactModuleInfo> moduleInfos = new HashMap<>();
|
|
28
|
+
boolean isTurboModule = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED;
|
|
29
|
+
moduleInfos.put(
|
|
30
|
+
UpdateModule.NAME,
|
|
31
|
+
new ReactModuleInfo(
|
|
32
|
+
UpdateModule.NAME,
|
|
33
|
+
UpdateModule.NAME,
|
|
34
|
+
false, // canOverrideExistingModule
|
|
35
|
+
false, // needsEagerInit
|
|
36
|
+
true, // hasConstants
|
|
37
|
+
false, // isCxxModule
|
|
38
|
+
isTurboModule // isTurboModule
|
|
39
|
+
));
|
|
40
|
+
return moduleInfos;
|
|
44
41
|
};
|
|
45
42
|
}
|
|
46
43
|
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
package cn.reactnative.modules.update;
|
|
2
|
+
|
|
3
|
+
import com.facebook.react.bridge.Promise;
|
|
4
|
+
import com.facebook.react.bridge.ReactApplicationContext;
|
|
5
|
+
import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
|
6
|
+
import com.facebook.react.bridge.ReadableMap;
|
|
7
|
+
import java.util.Map;
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Old-architecture stand-in for the NativePushySpec that codegen generates
|
|
11
|
+
* from src/NativePushy.ts under the new architecture: same name, methods and
|
|
12
|
+
* constants hook, so a single UpdateModule serves both. The bridge reads the
|
|
13
|
+
* @ReactMethod annotations from UpdateModule itself.
|
|
14
|
+
*/
|
|
15
|
+
public abstract class NativePushySpec extends ReactContextBaseJavaModule {
|
|
16
|
+
public static final String NAME = "Pushy";
|
|
17
|
+
|
|
18
|
+
public NativePushySpec(ReactApplicationContext reactContext) {
|
|
19
|
+
super(reactContext);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
@Override
|
|
23
|
+
public String getName() {
|
|
24
|
+
return NAME;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
protected abstract Map<String, Object> getTypedExportedConstants();
|
|
28
|
+
|
|
29
|
+
@Override
|
|
30
|
+
public final Map<String, Object> getConstants() {
|
|
31
|
+
return getTypedExportedConstants();
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
public abstract void setLocalHashInfo(String hash, String info, Promise promise);
|
|
35
|
+
|
|
36
|
+
public abstract void getLocalHashInfo(String hash, Promise promise);
|
|
37
|
+
|
|
38
|
+
public abstract void setUuid(String uuid, Promise promise);
|
|
39
|
+
|
|
40
|
+
public abstract void syncNativeConfig(String config, Promise promise);
|
|
41
|
+
|
|
42
|
+
public abstract void getNativeCheckCache(Promise promise);
|
|
43
|
+
|
|
44
|
+
public abstract void markJsCheckCompleted(String config, Promise promise);
|
|
45
|
+
|
|
46
|
+
public abstract void reloadUpdate(ReadableMap options, Promise promise);
|
|
47
|
+
|
|
48
|
+
public abstract void restartApp(Promise promise);
|
|
49
|
+
|
|
50
|
+
public abstract void setNeedUpdate(ReadableMap options, Promise promise);
|
|
51
|
+
|
|
52
|
+
public abstract void markSuccess(Promise promise);
|
|
53
|
+
|
|
54
|
+
public abstract void getBundleHash(Promise promise);
|
|
55
|
+
|
|
56
|
+
public abstract void resetToPackagedBundle(Promise promise);
|
|
57
|
+
|
|
58
|
+
public abstract void downloadPatchFromPpk(ReadableMap options, Promise promise);
|
|
59
|
+
|
|
60
|
+
public abstract void downloadPatchFromPackage(ReadableMap options, Promise promise);
|
|
61
|
+
|
|
62
|
+
public abstract void downloadFullUpdate(ReadableMap options, Promise promise);
|
|
63
|
+
|
|
64
|
+
public abstract void downloadAndInstallApk(ReadableMap options, Promise promise);
|
|
65
|
+
|
|
66
|
+
public abstract void addListener(String eventName);
|
|
67
|
+
|
|
68
|
+
public abstract void removeListeners(double count);
|
|
69
|
+
}
|