react-native-update 10.59.1 → 10.60.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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 +11 -12
- 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 +19 -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 -2628
- 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 +74 -83
- 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
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
package cn.reactnative.modules.update;
|
|
2
2
|
|
|
3
|
-
import java.io.BufferedInputStream;
|
|
4
|
-
import java.io.BufferedOutputStream;
|
|
5
3
|
import java.io.File;
|
|
6
4
|
import java.io.FileOutputStream;
|
|
7
5
|
import java.io.IOException;
|
|
8
6
|
import java.io.InputStream;
|
|
7
|
+
import java.io.OutputStream;
|
|
8
|
+
import java.security.DigestOutputStream;
|
|
9
|
+
import java.security.MessageDigest;
|
|
10
|
+
import java.util.ArrayList;
|
|
11
|
+
import java.util.Collections;
|
|
9
12
|
import java.util.Enumeration;
|
|
10
13
|
import java.util.zip.ZipEntry;
|
|
11
14
|
import java.util.zip.ZipFile;
|
|
@@ -17,73 +20,30 @@ public class SafeZipFile extends ZipFile {
|
|
|
17
20
|
super(file);
|
|
18
21
|
}
|
|
19
22
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
// Bytes written by unzipToFile across the archive: the sum of the central
|
|
23
|
-
// directory's declared sizes is checked up front (inspect), the actual
|
|
23
|
+
// Bytes written by unzipToFile across the archive: the central
|
|
24
|
+
// directory's declared sizes are checked up front (inspect), the actual
|
|
24
25
|
// stream is re-checked here — headers can lie.
|
|
25
26
|
private long totalWritten = 0;
|
|
26
27
|
|
|
27
|
-
static final class Inspection {
|
|
28
|
-
long entries;
|
|
29
|
-
long totalUncompressed;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
28
|
/**
|
|
33
|
-
* Walks the central directory once
|
|
34
|
-
* (entry count, per-entry
|
|
35
|
-
* reserved
|
|
29
|
+
* Walks the central directory once and hands it to the shared accounting
|
|
30
|
+
* (ArchiveLimits.checkEntries: entry count, per-entry and total size,
|
|
31
|
+
* compression ratios, reserved `.pushy-` names) before a single byte is
|
|
32
|
+
* extracted. Returns the total uncompressed size.
|
|
36
33
|
*/
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
long compressed = entry.getCompressedSize();
|
|
49
|
-
if (size > ArchiveLimits.MAX_ENTRY_BYTES) {
|
|
50
|
-
throw new IOException(
|
|
51
|
-
"archive entry too large: " + entry.getName() + " (" + size + " bytes)");
|
|
52
|
-
}
|
|
53
|
-
if (size > 0) {
|
|
54
|
-
result.totalUncompressed += size;
|
|
55
|
-
if (result.totalUncompressed > ArchiveLimits.MAX_TOTAL_UNCOMPRESSED_BYTES) {
|
|
56
|
-
throw new IOException("archive expands beyond "
|
|
57
|
-
+ ArchiveLimits.MAX_TOTAL_UNCOMPRESSED_BYTES + " bytes");
|
|
58
|
-
}
|
|
59
|
-
if (compressed > 0 && size > ArchiveLimits.RATIO_CHECK_MIN_BYTES
|
|
60
|
-
&& size / compressed > ArchiveLimits.MAX_COMPRESSION_RATIO) {
|
|
61
|
-
throw new IOException(
|
|
62
|
-
"archive entry compression ratio too high: " + entry.getName());
|
|
63
|
-
}
|
|
64
|
-
}
|
|
34
|
+
long inspect(long archiveBytes) throws IOException {
|
|
35
|
+
ArrayList<? extends ZipEntry> entries = Collections.list(entries());
|
|
36
|
+
int count = entries.size();
|
|
37
|
+
String[] names = new String[count];
|
|
38
|
+
long[] sizes = new long[count];
|
|
39
|
+
long[] compressedSizes = new long[count];
|
|
40
|
+
for (int i = 0; i < count; i++) {
|
|
41
|
+
ZipEntry entry = entries.get(i);
|
|
42
|
+
names[i] = entry.getName();
|
|
43
|
+
sizes[i] = entry.getSize();
|
|
44
|
+
compressedSizes[i] = entry.getCompressedSize();
|
|
65
45
|
}
|
|
66
|
-
return
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
// Files the SDK itself writes into a version directory to record its own
|
|
70
|
-
// decisions (the ".pushy-complete" install marker today). An archive
|
|
71
|
-
// must never be able to ship one: a package that unzips its own marker
|
|
72
|
-
// next to a bundle would be trusted as a finished install even when the
|
|
73
|
-
// patch step that follows fails.
|
|
74
|
-
static final String RESERVED_ENTRY_PREFIX = ".pushy-";
|
|
75
|
-
|
|
76
|
-
static boolean isReservedEntryName(String name) {
|
|
77
|
-
if (name == null) {
|
|
78
|
-
return false;
|
|
79
|
-
}
|
|
80
|
-
String normalized = name.replace('\\', '/');
|
|
81
|
-
while (normalized.endsWith("/")) {
|
|
82
|
-
normalized = normalized.substring(0, normalized.length() - 1);
|
|
83
|
-
}
|
|
84
|
-
int slash = normalized.lastIndexOf('/');
|
|
85
|
-
String baseName = slash >= 0 ? normalized.substring(slash + 1) : normalized;
|
|
86
|
-
return baseName.startsWith(RESERVED_ENTRY_PREFIX);
|
|
46
|
+
return ArchiveLimits.checkEntries(archiveBytes, names, sizes, compressedSizes);
|
|
87
47
|
}
|
|
88
48
|
|
|
89
49
|
@Override
|
|
@@ -115,15 +75,17 @@ public class SafeZipFile extends ZipFile {
|
|
|
115
75
|
if (null != name && (name.contains("../") || name.contains("..\\"))) {
|
|
116
76
|
throw new SecurityException("illegal entry: " + name);
|
|
117
77
|
}
|
|
118
|
-
if (isReservedEntryName(name)) {
|
|
119
|
-
throw new SecurityException("reserved entry: " + name);
|
|
120
|
-
}
|
|
121
78
|
}
|
|
122
79
|
return entry;
|
|
123
80
|
}
|
|
124
81
|
}
|
|
125
82
|
|
|
126
83
|
public void unzipToPath(ZipEntry ze, File targetPath) throws IOException {
|
|
84
|
+
unzipToPath(ze, targetPath, null);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/** As above; a non-null digest is fed every byte written for the entry. */
|
|
88
|
+
void unzipToPath(ZipEntry ze, File targetPath, MessageDigest digest) throws IOException {
|
|
127
89
|
String name = ze.getName();
|
|
128
90
|
File target = new File(targetPath, name);
|
|
129
91
|
|
|
@@ -133,22 +95,19 @@ public class SafeZipFile extends ZipFile {
|
|
|
133
95
|
if (!canonicalPath.startsWith(targetPath.getCanonicalPath() + File.separator)) {
|
|
134
96
|
throw new SecurityException("Illegal name: " + name);
|
|
135
97
|
}
|
|
136
|
-
if (isReservedEntryName(name)) {
|
|
137
|
-
throw new SecurityException("reserved entry: " + name);
|
|
138
|
-
}
|
|
139
|
-
|
|
140
98
|
if (ze.isDirectory()) {
|
|
141
99
|
target.mkdirs();
|
|
142
100
|
return;
|
|
143
101
|
}
|
|
144
|
-
unzipToFile(ze, target);
|
|
102
|
+
unzipToFile(ze, target, digest);
|
|
145
103
|
}
|
|
146
104
|
|
|
147
105
|
public void unzipToFile(ZipEntry ze, File target) throws IOException {
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
106
|
+
unzipToFile(ze, target, null);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
private void unzipToFile(ZipEntry ze, File target, MessageDigest digest) throws IOException {
|
|
110
|
+
UpdateFileUtils.ensureParentDirectory(target);
|
|
152
111
|
|
|
153
112
|
// The declared size is the budget when known; otherwise the global
|
|
154
113
|
// per-entry cap. Either way the stream may not exceed it.
|
|
@@ -157,12 +116,15 @@ public class SafeZipFile extends ZipFile {
|
|
|
157
116
|
? Math.min(declared, ArchiveLimits.MAX_ENTRY_BYTES)
|
|
158
117
|
: ArchiveLimits.MAX_ENTRY_BYTES;
|
|
159
118
|
long written = 0;
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
119
|
+
// One 64KB buffer, no extra buffered wrappers: every read/write is
|
|
120
|
+
// already a large block.
|
|
121
|
+
try (InputStream input = getInputStream(ze)) {
|
|
122
|
+
try (OutputStream output = digest == null
|
|
123
|
+
? new FileOutputStream(target)
|
|
124
|
+
: new DigestOutputStream(new FileOutputStream(target), digest)) {
|
|
125
|
+
byte[] buffer = new byte[UpdateFileUtils.BUFFER_SIZE];
|
|
164
126
|
int n;
|
|
165
|
-
while ((n = input.read(buffer, 0,
|
|
127
|
+
while ((n = input.read(buffer, 0, buffer.length)) >= 0) {
|
|
166
128
|
written += n;
|
|
167
129
|
totalWritten += n;
|
|
168
130
|
if (written > budget) {
|
|
@@ -1,64 +1,47 @@
|
|
|
1
1
|
package cn.reactnative.modules.update;
|
|
2
2
|
|
|
3
|
-
import android.util.Log;
|
|
4
|
-
import androidx.annotation.Nullable;
|
|
5
3
|
import com.facebook.react.bridge.Promise;
|
|
6
|
-
|
|
4
|
+
import com.facebook.react.bridge.UiThreadUtil;
|
|
7
5
|
import java.util.concurrent.Executor;
|
|
8
|
-
import java.util.concurrent.Executors;
|
|
9
|
-
import java.util.concurrent.ThreadFactory;
|
|
10
6
|
|
|
11
7
|
/**
|
|
12
|
-
* Runs
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
* These operations only read/modify SharedPreferences via a synchronous
|
|
16
|
-
* commit(); they were previously dispatched to the UI thread purely to
|
|
17
|
-
* serialize them. markSuccess in particular runs on every cold start, so doing
|
|
18
|
-
* its blocking disk write on the main thread caused jank/ANR on low-end
|
|
19
|
-
* devices. A single-thread executor preserves the same serialization guarantee
|
|
20
|
-
* while keeping the disk I/O off the UI thread.
|
|
8
|
+
* Runs a module operation on a given executor and rejects its promise with
|
|
9
|
+
* the operation's error code when it throws; the operation resolves the
|
|
10
|
+
* promise itself.
|
|
21
11
|
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
12
|
+
* State-persistence operations (switchVersion / markSuccess / setUuid /
|
|
13
|
+
* setLocalHashInfo / reset / config) go to {@link #STATE_SERIAL}, a single
|
|
14
|
+
* background thread: they only read/modify SharedPreferences via a
|
|
15
|
+
* synchronous commit(), and were once dispatched to the UI thread purely to
|
|
16
|
+
* serialize them. markSuccess runs on every cold start, so its blocking disk
|
|
17
|
+
* write on the main thread caused jank/ANR on low-end devices; one worker
|
|
18
|
+
* keeps the serialization in submission order without that. Only the React
|
|
19
|
+
* reload itself still hops to {@link #UI_THREAD}.
|
|
24
20
|
*/
|
|
25
21
|
final class StateSerialRunner {
|
|
26
22
|
interface Operation {
|
|
27
23
|
void run() throws Throwable;
|
|
28
24
|
}
|
|
29
25
|
|
|
30
|
-
//
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
private static final Executor EXECUTOR = Executors.newSingleThreadExecutor(
|
|
34
|
-
new ThreadFactory() {
|
|
35
|
-
@Override
|
|
36
|
-
public Thread newThread(Runnable r) {
|
|
37
|
-
return new Thread(r, "pushy-state-serial");
|
|
38
|
-
}
|
|
39
|
-
});
|
|
26
|
+
// Named so it is identifiable in thread dumps / ANR traces.
|
|
27
|
+
static final Executor STATE_SERIAL = Threads.single("pushy-state-serial", false);
|
|
28
|
+
static final Executor UI_THREAD = UiThreadUtil::runOnUiThread;
|
|
40
29
|
|
|
41
30
|
private StateSerialRunner() {
|
|
42
31
|
}
|
|
43
32
|
|
|
44
33
|
static void run(
|
|
45
|
-
|
|
34
|
+
Executor executor,
|
|
35
|
+
final Promise promise,
|
|
46
36
|
final String errorCode,
|
|
47
37
|
final String operationName,
|
|
48
38
|
final Operation operation
|
|
49
39
|
) {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
} catch (Throwable error) {
|
|
56
|
-
if (promise != null) {
|
|
57
|
-
promise.reject(errorCode, operationName + " failed", error);
|
|
58
|
-
} else {
|
|
59
|
-
Log.e(Texts.LOG_TAG, operationName + " failed", error);
|
|
60
|
-
}
|
|
61
|
-
}
|
|
40
|
+
executor.execute(() -> {
|
|
41
|
+
try {
|
|
42
|
+
operation.run();
|
|
43
|
+
} catch (Throwable error) {
|
|
44
|
+
promise.reject(errorCode, operationName + " failed", error);
|
|
62
45
|
}
|
|
63
46
|
});
|
|
64
47
|
}
|