@bravemobile/react-native-code-push 13.0.0 → 13.1.0-beta.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/CodePush.podspec +35 -3
- package/README.md +117 -3
- package/THIRD-PARTY-NOTICES +96 -0
- package/android/app/build.gradle +19 -0
- package/android/app/proguard-rules.pro +5 -0
- package/android/app/src/main/cpp/CMakeLists.txt +38 -0
- package/android/app/src/main/cpp/binarypatch_jni.c +181 -0
- package/android/app/src/main/java/com/microsoft/codepush/react/BinaryPatchAttempt.java +92 -0
- package/android/app/src/main/java/com/microsoft/codepush/react/BinaryPatchResult.java +64 -0
- package/android/app/src/main/java/com/microsoft/codepush/react/CodePush.java +1 -1
- package/android/app/src/main/java/com/microsoft/codepush/react/CodePushBinaryPatch.java +293 -0
- package/android/app/src/main/java/com/microsoft/codepush/react/CodePushConstants.java +25 -0
- package/android/app/src/main/java/com/microsoft/codepush/react/CodePushNativeModule.java +20 -6
- package/android/app/src/main/java/com/microsoft/codepush/react/CodePushUpdateManager.java +168 -4
- package/android/app/src/main/java/com/microsoft/codepush/react/CodePushUpdateUtils.java +13 -0
- package/android/app/src/main/java/com/microsoft/codepush/react/DownloadProgressCallback.java +7 -0
- package/android/app/src/main/java/com/microsoft/codepush/react/DownloadProgressEventDispatcher.java +25 -4
- package/android/app/src/main/java/com/microsoft/codepush/react/HDiffPatchNative.java +52 -0
- package/android/app/src/test/java/com/microsoft/codepush/react/CodePushBinaryPatchTest.java +393 -0
- package/android/app/src/test/java/com/microsoft/codepush/react/CodePushUpdateManagerBinaryPatchTest.java +211 -0
- package/android/app/src/test/java/com/microsoft/codepush/react/CodePushUpdateManagerDownloadTest.java +489 -0
- package/android/app/src/test/java/com/microsoft/codepush/react/DownloadProgressEventDispatcherTest.java +80 -0
- package/android/codepush-export.gradle +115 -0
- package/cli/commands/bundleCommand/bundleCodePush.ts +39 -7
- package/cli/commands/bundleCommand/index.ts +9 -1
- package/cli/commands/releaseCommand/addToReleaseHistory.ts +7 -0
- package/cli/commands/releaseCommand/index.test.ts +118 -0
- package/cli/commands/releaseCommand/index.ts +20 -2
- package/cli/commands/releaseCommand/release.test.ts +582 -0
- package/cli/commands/releaseCommand/release.ts +232 -15
- package/cli/dist/commands/bundleCommand/bundleCodePush.js +26 -7
- package/cli/dist/commands/bundleCommand/index.js +5 -1
- package/cli/dist/commands/releaseCommand/addToReleaseHistory.js +6 -1
- package/cli/dist/commands/releaseCommand/index.js +8 -1
- package/cli/dist/commands/releaseCommand/index.test.js +96 -0
- package/cli/dist/commands/releaseCommand/release.js +165 -17
- package/cli/dist/commands/releaseCommand/release.test.js +367 -0
- package/cli/dist/functions/makeBinaryPatchBundle.js +248 -0
- package/cli/dist/functions/makeBinaryPatchBundle.test.js +321 -0
- package/cli/dist/functions/makeCodePushBundle.js +4 -2
- package/cli/dist/functions/runHermesEmitBinaryCommand.js +48 -1
- package/cli/dist/functions/runHermesEmitBinaryCommand.test.js +100 -0
- package/cli/dist/jest.globalSetup.js +38 -0
- package/cli/dist/utils/binaryPatch.js +86 -0
- package/cli/dist/utils/binaryPatch.test.js +190 -0
- package/cli/functions/makeBinaryPatchBundle.test.ts +410 -0
- package/cli/functions/makeBinaryPatchBundle.ts +367 -0
- package/cli/functions/makeCodePushBundle.ts +4 -2
- package/cli/functions/runHermesEmitBinaryCommand.test.ts +124 -0
- package/cli/functions/runHermesEmitBinaryCommand.ts +57 -1
- package/cli/jest.globalSetup.ts +43 -0
- package/cli/utils/binaryPatch.test.ts +234 -0
- package/cli/utils/binaryPatch.ts +103 -0
- package/cpp/binarypatch/binarypatch_zstd_decompressor.c +187 -0
- package/cpp/binarypatch/binarypatch_zstd_decompressor.h +47 -0
- package/cpp/binarypatch/vendor/HDiffPatch/libHDiffPatch/HPatch/checksum_plugin.h +52 -0
- package/cpp/binarypatch/vendor/HDiffPatch/libHDiffPatch/HPatch/hpatch_mt/hpatch_mt.h +147 -0
- package/cpp/binarypatch/vendor/HDiffPatch/libHDiffPatch/HPatch/patch.c +3315 -0
- package/cpp/binarypatch/vendor/HDiffPatch/libHDiffPatch/HPatch/patch.h +268 -0
- package/cpp/binarypatch/vendor/HDiffPatch/libHDiffPatch/HPatch/patch_private.h +297 -0
- package/cpp/binarypatch/vendor/HDiffPatch/libHDiffPatch/HPatch/patch_types.h +434 -0
- package/cpp/binarypatch/vendor/zstd/common/allocations.h +55 -0
- package/cpp/binarypatch/vendor/zstd/common/bits.h +205 -0
- package/cpp/binarypatch/vendor/zstd/common/bitstream.h +454 -0
- package/cpp/binarypatch/vendor/zstd/common/compiler.h +464 -0
- package/cpp/binarypatch/vendor/zstd/common/cpu.h +249 -0
- package/cpp/binarypatch/vendor/zstd/common/debug.c +30 -0
- package/cpp/binarypatch/vendor/zstd/common/debug.h +107 -0
- package/cpp/binarypatch/vendor/zstd/common/entropy_common.c +340 -0
- package/cpp/binarypatch/vendor/zstd/common/error_private.c +64 -0
- package/cpp/binarypatch/vendor/zstd/common/error_private.h +158 -0
- package/cpp/binarypatch/vendor/zstd/common/fse.h +625 -0
- package/cpp/binarypatch/vendor/zstd/common/fse_decompress.c +315 -0
- package/cpp/binarypatch/vendor/zstd/common/huf.h +277 -0
- package/cpp/binarypatch/vendor/zstd/common/mem.h +422 -0
- package/cpp/binarypatch/vendor/zstd/common/pool.c +371 -0
- package/cpp/binarypatch/vendor/zstd/common/pool.h +81 -0
- package/cpp/binarypatch/vendor/zstd/common/portability_macros.h +171 -0
- package/cpp/binarypatch/vendor/zstd/common/threading.c +182 -0
- package/cpp/binarypatch/vendor/zstd/common/threading.h +142 -0
- package/cpp/binarypatch/vendor/zstd/common/xxhash.c +18 -0
- package/cpp/binarypatch/vendor/zstd/common/xxhash.h +7094 -0
- package/cpp/binarypatch/vendor/zstd/common/zstd_common.c +48 -0
- package/cpp/binarypatch/vendor/zstd/common/zstd_deps.h +123 -0
- package/cpp/binarypatch/vendor/zstd/common/zstd_internal.h +324 -0
- package/cpp/binarypatch/vendor/zstd/common/zstd_trace.h +156 -0
- package/cpp/binarypatch/vendor/zstd/decompress/huf_decompress.c +1944 -0
- package/cpp/binarypatch/vendor/zstd/decompress/zstd_ddict.c +244 -0
- package/cpp/binarypatch/vendor/zstd/decompress/zstd_ddict.h +44 -0
- package/cpp/binarypatch/vendor/zstd/decompress/zstd_decompress.c +2410 -0
- package/cpp/binarypatch/vendor/zstd/decompress/zstd_decompress_block.c +2209 -0
- package/cpp/binarypatch/vendor/zstd/decompress/zstd_decompress_block.h +73 -0
- package/cpp/binarypatch/vendor/zstd/decompress/zstd_decompress_internal.h +240 -0
- package/cpp/binarypatch/vendor/zstd/zstd.h +3206 -0
- package/cpp/binarypatch/vendor/zstd/zstd_errors.h +107 -0
- package/ios/CodePush/CodePush.h +18 -1
- package/ios/CodePush/CodePush.mm +24 -1
- package/ios/CodePush/CodePushBinaryPatch.h +80 -0
- package/ios/CodePush/CodePushBinaryPatch.m +473 -0
- package/ios/CodePush/CodePushDownloadHandler.m +6 -0
- package/ios/CodePush/CodePushPackage.m +183 -2
- package/ios/CodePush/CodePushUpdateUtils.m +49 -1
- package/package.json +10 -3
- package/scripts/binary-patch/build-hdiffpatch.sh +86 -0
- package/scripts/export-embedded-bundle.sh +76 -0
- package/src/CodePush.js +46 -3
- package/src/CodePush.test.js +382 -0
- package/src/package-mixins.js +20 -2
- package/typings/react-native-code-push.d.ts +80 -1
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
package com.microsoft.codepush.react;
|
|
2
2
|
|
|
3
|
+
import android.content.Context;
|
|
3
4
|
import android.os.Build;
|
|
4
5
|
|
|
5
6
|
import org.json.JSONObject;
|
|
6
7
|
|
|
7
8
|
import java.io.BufferedInputStream;
|
|
8
9
|
import java.io.BufferedOutputStream;
|
|
10
|
+
import java.io.ByteArrayOutputStream;
|
|
9
11
|
import java.io.File;
|
|
10
12
|
import java.io.FileOutputStream;
|
|
11
13
|
import java.io.IOException;
|
|
14
|
+
import java.io.InputStream;
|
|
12
15
|
import java.net.HttpURLConnection;
|
|
13
16
|
import java.net.MalformedURLException;
|
|
14
17
|
import java.net.URL;
|
|
@@ -19,9 +22,15 @@ import javax.net.ssl.HttpsURLConnection;
|
|
|
19
22
|
public class CodePushUpdateManager {
|
|
20
23
|
|
|
21
24
|
private String mDocumentsDirectory;
|
|
25
|
+
private final CodePushBinaryPatch mBinaryPatch;
|
|
22
26
|
|
|
23
|
-
public CodePushUpdateManager(String documentsDirectory) {
|
|
27
|
+
public CodePushUpdateManager(String documentsDirectory, Context context) {
|
|
28
|
+
this(documentsDirectory, new CodePushBinaryPatch(new AssetsBaseBundleProvider(context), new HDiffPatchNative()));
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
CodePushUpdateManager(String documentsDirectory, CodePushBinaryPatch binaryPatch) {
|
|
24
32
|
mDocumentsDirectory = documentsDirectory;
|
|
33
|
+
mBinaryPatch = binaryPatch;
|
|
25
34
|
}
|
|
26
35
|
|
|
27
36
|
private String getDownloadFilePath() {
|
|
@@ -32,6 +41,10 @@ public class CodePushUpdateManager {
|
|
|
32
41
|
return CodePushUtils.appendPathComponent(getCodePushPath(), CodePushConstants.UNZIPPED_FOLDER_NAME);
|
|
33
42
|
}
|
|
34
43
|
|
|
44
|
+
private String getBinaryPatchFolderPath() {
|
|
45
|
+
return CodePushUtils.appendPathComponent(getCodePushPath(), CodePushConstants.BINARY_PATCH_FOLDER_NAME);
|
|
46
|
+
}
|
|
47
|
+
|
|
35
48
|
private String getDocumentsDirectory() {
|
|
36
49
|
return mDocumentsDirectory;
|
|
37
50
|
}
|
|
@@ -143,8 +156,92 @@ public class CodePushUpdateManager {
|
|
|
143
156
|
}
|
|
144
157
|
}
|
|
145
158
|
|
|
146
|
-
|
|
147
|
-
|
|
159
|
+
/**
|
|
160
|
+
* @return what the attempt at installing the update from its binary patch archive ended
|
|
161
|
+
* in, or null when the update had no patch archive to attempt. The result belongs
|
|
162
|
+
* to this download alone: the metadata written for the update never carries it.
|
|
163
|
+
*/
|
|
164
|
+
public JSONObject downloadPackage(JSONObject updatePackage, String expectedBundleFileName,
|
|
165
|
+
DownloadProgressCallback progressCallback) throws IOException {
|
|
166
|
+
// A release that was published with a binary patch offers two archives of the same
|
|
167
|
+
// update. The patch is worth trying because it is a fraction of the size, and the
|
|
168
|
+
// full archive is always there when it does not work out.
|
|
169
|
+
String binaryPatchDownloadUrl = updatePackage.optString(CodePushConstants.BINARY_PATCH_DOWNLOAD_URL_KEY, null);
|
|
170
|
+
BinaryPatchAttempt patchAttempt = null;
|
|
171
|
+
if (binaryPatchDownloadUrl != null) {
|
|
172
|
+
patchAttempt = new BinaryPatchAttempt();
|
|
173
|
+
if (tryDownloadBinaryPatchPackage(updatePackage, expectedBundleFileName, progressCallback,
|
|
174
|
+
binaryPatchDownloadUrl, patchAttempt)) {
|
|
175
|
+
return patchAttempt.result();
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
downloadAndInstallPackage(updatePackage, expectedBundleFileName, progressCallback,
|
|
180
|
+
updatePackage.optString(CodePushConstants.DOWNLOAD_URL_KEY, null), false, null);
|
|
181
|
+
|
|
182
|
+
return patchAttempt == null ? null : patchAttempt.result();
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Installs the update from its binary patch archive.
|
|
187
|
+
*
|
|
188
|
+
* Every way this can fail ends the same way, with the update being downloaded in full
|
|
189
|
+
* instead, so none of them is reported to the caller as an error. The fallback happens
|
|
190
|
+
* exactly once without anything having to count it: the full archive is downloaded by
|
|
191
|
+
* a call that is not allowed to take the patch path, so it has no failure of its own to
|
|
192
|
+
* fall back from.
|
|
193
|
+
*
|
|
194
|
+
* @param patchAttempt records what the attempt ended in, for the caller to hand to
|
|
195
|
+
* whoever asked for the download
|
|
196
|
+
* @return true when the update was installed, false when the caller has to download the
|
|
197
|
+
* full archive instead
|
|
198
|
+
*/
|
|
199
|
+
private boolean tryDownloadBinaryPatchPackage(JSONObject updatePackage, String expectedBundleFileName,
|
|
200
|
+
DownloadProgressCallback progressCallback,
|
|
201
|
+
String binaryPatchDownloadUrl, BinaryPatchAttempt patchAttempt) {
|
|
202
|
+
try {
|
|
203
|
+
BinaryPatchResult patchResult = downloadAndInstallPackage(updatePackage, expectedBundleFileName,
|
|
204
|
+
progressCallback, binaryPatchDownloadUrl, true, patchAttempt);
|
|
205
|
+
if (patchResult.succeeded()) {
|
|
206
|
+
return true;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
patchAttempt.recordFallback(patchResult.getFailureReason());
|
|
210
|
+
CodePushUtils.log("Binary patch update failed (" + patchResult.getFailureReason()
|
|
211
|
+
+ "). Downloading the full update instead.");
|
|
212
|
+
} catch (Exception | OutOfMemoryError e) {
|
|
213
|
+
// Applying a patch is the one path that holds a whole bundle in memory, so
|
|
214
|
+
// running out of it is a failure this has to absorb like any other: by the time
|
|
215
|
+
// it lands here the arrays are unreachable, and the full archive is downloaded
|
|
216
|
+
// to disk in chunks rather than held.
|
|
217
|
+
patchAttempt.recordFallbackAfterError();
|
|
218
|
+
CodePushUtils.log(e);
|
|
219
|
+
CodePushUtils.log("The binary patch update could not be completed. Downloading the full update instead.");
|
|
220
|
+
} finally {
|
|
221
|
+
FileUtils.deleteDirectoryAtPath(getBinaryPatchFolderPath());
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
return false;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Downloads an update from one of its archives and installs it.
|
|
229
|
+
*
|
|
230
|
+
* @param isBinaryPatchUpdate whether the archive holds a binary patch of the JS bundle,
|
|
231
|
+
* which has to be applied before the contents are the update.
|
|
232
|
+
* Only an archive downloaded from the binary patch URL is
|
|
233
|
+
* treated that way, so an update being downloaded in full can
|
|
234
|
+
* never end up on the patch path.
|
|
235
|
+
* @param patchAttempt the record the patch is timed into, or null for a full download,
|
|
236
|
+
* which has no patch to time
|
|
237
|
+
* @return the outcome of the patch: a failed result means the update was not installed
|
|
238
|
+
* and the caller has to fall back to the full archive. Downloading the full
|
|
239
|
+
* archive always succeeds or throws.
|
|
240
|
+
*/
|
|
241
|
+
BinaryPatchResult downloadAndInstallPackage(JSONObject updatePackage, String expectedBundleFileName,
|
|
242
|
+
DownloadProgressCallback progressCallback,
|
|
243
|
+
String downloadUrlString, boolean isBinaryPatchUpdate,
|
|
244
|
+
BinaryPatchAttempt patchAttempt) throws IOException {
|
|
148
245
|
String newUpdateHash = updatePackage.optString(CodePushConstants.PACKAGE_HASH_KEY, null);
|
|
149
246
|
String newUpdateFolderPath = getPackageFolderPath(newUpdateHash);
|
|
150
247
|
String newUpdateMetadataPath = CodePushUtils.appendPathComponent(newUpdateFolderPath, CodePushConstants.PACKAGE_FILE_NAME);
|
|
@@ -154,7 +251,6 @@ public class CodePushUpdateManager {
|
|
|
154
251
|
FileUtils.deleteDirectoryAtPath(newUpdateFolderPath);
|
|
155
252
|
}
|
|
156
253
|
|
|
157
|
-
String downloadUrlString = updatePackage.optString(CodePushConstants.DOWNLOAD_URL_KEY, null);
|
|
158
254
|
HttpURLConnection connection = null;
|
|
159
255
|
BufferedInputStream bin = null;
|
|
160
256
|
FileOutputStream fos = null;
|
|
@@ -179,9 +275,18 @@ public class CodePushUpdateManager {
|
|
|
179
275
|
connection.setRequestProperty("Accept-Encoding", "identity");
|
|
180
276
|
bin = new BufferedInputStream(connection.getInputStream());
|
|
181
277
|
|
|
278
|
+
// Announced only once the response is flowing, so a connection that fails to
|
|
279
|
+
// open never announces an empty stream.
|
|
280
|
+
progressCallback.onDownloadStart();
|
|
182
281
|
long totalBytes = connection.getContentLength();
|
|
183
282
|
long receivedBytes = 0;
|
|
184
283
|
|
|
284
|
+
if (totalBytes > 0) {
|
|
285
|
+
// The zero-received event hands the listener this download's total up front,
|
|
286
|
+
// so a fallback download shows as restarting rather than running backwards.
|
|
287
|
+
progressCallback.call(new DownloadProgress(totalBytes, 0));
|
|
288
|
+
}
|
|
289
|
+
|
|
185
290
|
File downloadFolder = new File(getCodePushPath());
|
|
186
291
|
downloadFolder.mkdirs();
|
|
187
292
|
downloadFile = new File(downloadFolder, CodePushConstants.DOWNLOAD_FILE_NAME);
|
|
@@ -232,6 +337,21 @@ public class CodePushUpdateManager {
|
|
|
232
337
|
FileUtils.unzipFile(downloadFile, unzippedFolderPath);
|
|
233
338
|
FileUtils.deleteFileOrFolderSilently(downloadFile);
|
|
234
339
|
|
|
340
|
+
// Rebuild the JS bundle the archive only carries a patch of, which leaves the
|
|
341
|
+
// contents identical to the ones the full archive would have delivered.
|
|
342
|
+
if (isBinaryPatchUpdate) {
|
|
343
|
+
long patchStartTime = System.currentTimeMillis();
|
|
344
|
+
BinaryPatchResult patchResult = mBinaryPatch.restoreBundle(unzippedFolderPath,
|
|
345
|
+
getBinaryPatchFolderPath(), expectedBundleFileName);
|
|
346
|
+
if (!patchResult.succeeded()) {
|
|
347
|
+
return patchResult;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
long applyDurationMs = System.currentTimeMillis() - patchStartTime;
|
|
351
|
+
patchAttempt.recordBundleRestored(applyDurationMs);
|
|
352
|
+
CodePushUtils.log("Restored the update from its binary patch in " + applyDurationMs + " ms.");
|
|
353
|
+
}
|
|
354
|
+
|
|
235
355
|
// Merge contents with current update based on the manifest
|
|
236
356
|
String diffManifestFilePath = CodePushUtils.appendPathComponent(unzippedFolderPath,
|
|
237
357
|
CodePushConstants.DIFF_MANIFEST_FILE_NAME);
|
|
@@ -268,12 +388,22 @@ public class CodePushUpdateManager {
|
|
|
268
388
|
CodePushUtils.setJSONValueForKey(updatePackage, CodePushConstants.RELATIVE_BUNDLE_PATH_KEY, relativeBundlePath);
|
|
269
389
|
}
|
|
270
390
|
} else {
|
|
391
|
+
if (isBinaryPatchUpdate) {
|
|
392
|
+
// Whatever the patch URL served, it is not a patch archive - an error page
|
|
393
|
+
// answered with a 200 looks like this too. Moving it into place would
|
|
394
|
+
// install bytes no hash has ever been checked against, so the full archive
|
|
395
|
+
// is downloaded instead.
|
|
396
|
+
return BinaryPatchResult.failure(BinaryPatchResult.REASON_INVALID_MANIFEST);
|
|
397
|
+
}
|
|
398
|
+
|
|
271
399
|
// File is a jsbundle, move it to a folder with the packageHash as its name
|
|
272
400
|
FileUtils.moveFile(downloadFile, newUpdateFolderPath, expectedBundleFileName);
|
|
273
401
|
}
|
|
274
402
|
|
|
275
403
|
// Save metadata to the folder.
|
|
276
404
|
CodePushUtils.writeJsonToFile(updatePackage, newUpdateMetadataPath);
|
|
405
|
+
|
|
406
|
+
return BinaryPatchResult.success();
|
|
277
407
|
}
|
|
278
408
|
|
|
279
409
|
public void installPackage(JSONObject updatePackage, boolean removePendingUpdate) {
|
|
@@ -349,4 +479,38 @@ public class CodePushUpdateManager {
|
|
|
349
479
|
public void clearUpdates() {
|
|
350
480
|
FileUtils.deleteDirectoryAtPath(getCodePushPath());
|
|
351
481
|
}
|
|
482
|
+
|
|
483
|
+
/** Reads the JS bundle that shipped inside the app binary out of the APK's assets. */
|
|
484
|
+
private static class AssetsBaseBundleProvider implements CodePushBinaryPatch.BaseBundleProvider {
|
|
485
|
+
|
|
486
|
+
private final Context mContext;
|
|
487
|
+
|
|
488
|
+
AssetsBaseBundleProvider(Context context) {
|
|
489
|
+
mContext = context;
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
@Override
|
|
493
|
+
public byte[] readBaseBundle(String bundleFileName) throws IOException {
|
|
494
|
+
// The bundle is stored uncompressed in the APK, so it is read straight into
|
|
495
|
+
// memory: a copy on disk would buy nothing and cost the space the restored
|
|
496
|
+
// bundle needs.
|
|
497
|
+
InputStream assetStream = mContext.getAssets().open(bundleFileName);
|
|
498
|
+
try {
|
|
499
|
+
// An uncompressed asset knows its whole length up front, so the buffer is
|
|
500
|
+
// sized for it: growing one would repeatedly hold two copies of a bundle
|
|
501
|
+
// that is already the largest allocation on this path.
|
|
502
|
+
ByteArrayOutputStream bundleBytes = new ByteArrayOutputStream(
|
|
503
|
+
Math.max(assetStream.available(), CodePushConstants.DOWNLOAD_BUFFER_SIZE));
|
|
504
|
+
byte[] buffer = new byte[CodePushConstants.DOWNLOAD_BUFFER_SIZE];
|
|
505
|
+
int bytesRead;
|
|
506
|
+
while ((bytesRead = assetStream.read(buffer)) > 0) {
|
|
507
|
+
bundleBytes.write(buffer, 0, bytesRead);
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
return bundleBytes.toByteArray();
|
|
511
|
+
} finally {
|
|
512
|
+
assetStream.close();
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
}
|
|
352
516
|
}
|
|
@@ -86,6 +86,19 @@ public class CodePushUpdateUtils {
|
|
|
86
86
|
return String.format("%064x", new java.math.BigInteger(1, hash));
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
+
/**
|
|
90
|
+
* SHA-256 of a single file's bytes, as lowercase hex - the way a binary patch manifest
|
|
91
|
+
* records the bundle it was computed against and the bundle it produces.
|
|
92
|
+
*/
|
|
93
|
+
public static String computeHashForFile(File file) throws IOException {
|
|
94
|
+
return computeHash(new FileInputStream(file));
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/** SHA-256 of the bytes in memory, in the same form as {@link #computeHashForFile}. */
|
|
98
|
+
public static String computeHashForBytes(byte[] data) {
|
|
99
|
+
return computeHash(new ByteArrayInputStream(data));
|
|
100
|
+
}
|
|
101
|
+
|
|
89
102
|
public static void copyNecessaryFilesFromCurrentPackage(String diffManifestFilePath, String currentPackageFolderPath, String newPackageFolderPath) throws IOException {
|
|
90
103
|
if (currentPackageFolderPath == null || !new File(currentPackageFolderPath).exists()) {
|
|
91
104
|
CodePushUtils.log("Unable to copy files from current package during diff update, because currentPackageFolderPath is invalid.");
|
package/android/app/src/main/java/com/microsoft/codepush/react/DownloadProgressCallback.java
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
package com.microsoft.codepush.react;
|
|
2
2
|
|
|
3
3
|
interface DownloadProgressCallback {
|
|
4
|
+
/**
|
|
5
|
+
* A download is starting its own progress stream: received bytes count from zero again,
|
|
6
|
+
* against this download's own total. A binary patch fallback makes this happen twice
|
|
7
|
+
* within one downloaded update.
|
|
8
|
+
*/
|
|
9
|
+
void onDownloadStart();
|
|
10
|
+
|
|
4
11
|
void call(DownloadProgress downloadProgress);
|
|
5
12
|
}
|
package/android/app/src/main/java/com/microsoft/codepush/react/DownloadProgressEventDispatcher.java
CHANGED
|
@@ -1,23 +1,44 @@
|
|
|
1
1
|
package com.microsoft.codepush.react;
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Serializes progress events from the download thread and the UI frame callback into one
|
|
5
|
+
* monotonic stream per download: whatever order the two threads dispatch in, the JS side
|
|
6
|
+
* never sees the same download run backwards. The latest progress lives here rather than
|
|
7
|
+
* with the caller so that reading it and dispatching it happen under one lock.
|
|
8
|
+
*/
|
|
3
9
|
class DownloadProgressEventDispatcher {
|
|
4
10
|
interface EventEmitter {
|
|
5
11
|
void emit(DownloadProgress downloadProgress);
|
|
6
12
|
}
|
|
7
13
|
|
|
8
14
|
private final EventEmitter mEventEmitter;
|
|
15
|
+
private DownloadProgress mLatestDownloadProgress = null;
|
|
9
16
|
private long mLastDispatchedBytes = -1;
|
|
10
17
|
|
|
11
18
|
DownloadProgressEventDispatcher(EventEmitter eventEmitter) {
|
|
12
19
|
mEventEmitter = eventEmitter;
|
|
13
20
|
}
|
|
14
21
|
|
|
15
|
-
synchronized void
|
|
16
|
-
|
|
22
|
+
synchronized void record(DownloadProgress downloadProgress) {
|
|
23
|
+
mLatestDownloadProgress = downloadProgress;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
synchronized void dispatchLatest() {
|
|
27
|
+
if (mLatestDownloadProgress == null || mLatestDownloadProgress.getReceivedBytes() <= mLastDispatchedBytes) {
|
|
17
28
|
return;
|
|
18
29
|
}
|
|
19
30
|
|
|
20
|
-
mEventEmitter.emit(
|
|
21
|
-
mLastDispatchedBytes =
|
|
31
|
+
mEventEmitter.emit(mLatestDownloadProgress);
|
|
32
|
+
mLastDispatchedBytes = mLatestDownloadProgress.getReceivedBytes();
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Forgets the download dispatched so far. The next download counts its bytes from zero,
|
|
37
|
+
* and without the reset the monotonic guard would swallow its events as the previous
|
|
38
|
+
* download running backwards.
|
|
39
|
+
*/
|
|
40
|
+
synchronized void reset() {
|
|
41
|
+
mLatestDownloadProgress = null;
|
|
42
|
+
mLastDispatchedBytes = -1;
|
|
22
43
|
}
|
|
23
44
|
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
package com.microsoft.codepush.react;
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The native HDiffPatch applier, which is what actually turns the bundle inside the app
|
|
5
|
+
* binary into the bundle an update wants to run.
|
|
6
|
+
*
|
|
7
|
+
* The library is loaded on first use rather than when the class is loaded, and a load
|
|
8
|
+
* failure is reported as a result code instead of an error: an app whose build did not
|
|
9
|
+
* produce the library still has to install its updates, it just has to download them in
|
|
10
|
+
* full.
|
|
11
|
+
*/
|
|
12
|
+
public class HDiffPatchNative implements CodePushBinaryPatch.PatchApplier {
|
|
13
|
+
|
|
14
|
+
private static final String LIBRARY_NAME = "codepush-binarypatch";
|
|
15
|
+
|
|
16
|
+
private static boolean sLibraryLoadAttempted = false;
|
|
17
|
+
private static boolean sLibraryLoaded = false;
|
|
18
|
+
|
|
19
|
+
@Override
|
|
20
|
+
public int apply(byte[] base, byte[] patch, String outputPath, long expectedTargetSize) {
|
|
21
|
+
if (!loadLibrary()) {
|
|
22
|
+
return RESULT_LIBRARY_UNAVAILABLE;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return applyPatch(base, patch, outputPath, expectedTargetSize);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
private static synchronized boolean loadLibrary() {
|
|
29
|
+
if (!sLibraryLoadAttempted) {
|
|
30
|
+
sLibraryLoadAttempted = true;
|
|
31
|
+
try {
|
|
32
|
+
System.loadLibrary(LIBRARY_NAME);
|
|
33
|
+
sLibraryLoaded = true;
|
|
34
|
+
} catch (UnsatisfiedLinkError e) {
|
|
35
|
+
CodePushUtils.log("Unable to load the binary patch library: " + e.getMessage());
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return sLibraryLoaded;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Writes the bundle that `patch` produces from `base` to `outputPath`.
|
|
44
|
+
*
|
|
45
|
+
* The base bundle and the patch are passed as arrays because the applier needs random
|
|
46
|
+
* access to both, while the restored bundle is written straight to a file, so the two
|
|
47
|
+
* bundles are never in memory at the same time.
|
|
48
|
+
*
|
|
49
|
+
* @return one of the RESULT_* codes of {@link CodePushBinaryPatch.PatchApplier}
|
|
50
|
+
*/
|
|
51
|
+
private static native int applyPatch(byte[] base, byte[] patch, String outputPath, long expectedTargetSize);
|
|
52
|
+
}
|