@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
package/CodePush.podspec
CHANGED
|
@@ -12,11 +12,40 @@ Pod::Spec.new do |s|
|
|
|
12
12
|
s.source = { :git => 'https://github.com/Soomgo-Mobile/react-native-code-push.git', :tag => "v#{s.version}"}
|
|
13
13
|
s.ios.deployment_target = '15.5'
|
|
14
14
|
s.tvos.deployment_target = '15.5'
|
|
15
|
-
s.preserve_paths = '*.js'
|
|
15
|
+
s.preserve_paths = ['*.js', 'cpp/binarypatch/**/*.h']
|
|
16
16
|
s.library = 'z'
|
|
17
|
-
|
|
17
|
+
# The applier that installs a binary patch update is compiled from the C sources the
|
|
18
|
+
# other platform and the host build compile as well, referenced where they live rather
|
|
19
|
+
# than copied here, which is what keeps the appliers of the platforms from drifting
|
|
20
|
+
# apart. Only the decompressing half of zstd is needed, and its assembly fast path is
|
|
21
|
+
# deliberately not vendored, so this list is the same one the other platform builds.
|
|
22
|
+
s.source_files = [
|
|
23
|
+
'ios/CodePush/*.{h,m,mm}',
|
|
24
|
+
'cpp/binarypatch/binarypatch_zstd_decompressor.c',
|
|
25
|
+
'cpp/binarypatch/vendor/HDiffPatch/libHDiffPatch/HPatch/patch.c',
|
|
26
|
+
'cpp/binarypatch/vendor/zstd/common/debug.c',
|
|
27
|
+
'cpp/binarypatch/vendor/zstd/common/entropy_common.c',
|
|
28
|
+
'cpp/binarypatch/vendor/zstd/common/error_private.c',
|
|
29
|
+
'cpp/binarypatch/vendor/zstd/common/fse_decompress.c',
|
|
30
|
+
'cpp/binarypatch/vendor/zstd/common/xxhash.c',
|
|
31
|
+
'cpp/binarypatch/vendor/zstd/common/zstd_common.c',
|
|
32
|
+
'cpp/binarypatch/vendor/zstd/decompress/huf_decompress.c',
|
|
33
|
+
'cpp/binarypatch/vendor/zstd/decompress/zstd_ddict.c',
|
|
34
|
+
'cpp/binarypatch/vendor/zstd/decompress/zstd_decompress.c',
|
|
35
|
+
'cpp/binarypatch/vendor/zstd/decompress/zstd_decompress_block.c'
|
|
36
|
+
]
|
|
18
37
|
s.public_header_files = ['ios/CodePush/CodePush.h']
|
|
19
38
|
|
|
39
|
+
binary_patch_header_search_paths = '"$(PODS_TARGET_SRCROOT)/cpp/binarypatch" "$(PODS_TARGET_SRCROOT)/cpp/binarypatch/vendor/HDiffPatch" "$(PODS_TARGET_SRCROOT)/cpp/binarypatch/vendor/zstd"'
|
|
40
|
+
# ZSTD_DISABLE_ASM: the assembly fast path is intentionally not vendored.
|
|
41
|
+
# _IS_USED_MULTITHREAD=0: patches are applied on the thread that downloads them.
|
|
42
|
+
binary_patch_preprocessor_definitions = '$(inherited) ZSTD_DISABLE_ASM=1 _IS_USED_MULTITHREAD=0'
|
|
43
|
+
|
|
44
|
+
s.pod_target_xcconfig = {
|
|
45
|
+
'HEADER_SEARCH_PATHS' => binary_patch_header_search_paths,
|
|
46
|
+
'GCC_PREPROCESSOR_DEFINITIONS' => binary_patch_preprocessor_definitions
|
|
47
|
+
}
|
|
48
|
+
|
|
20
49
|
# Note: Even though there are copy/pasted versions of some of these dependencies in the repo,
|
|
21
50
|
# we explicitly let CocoaPods pull in the versions below so all dependencies are resolved and
|
|
22
51
|
# linked properly at a parent workspace level.
|
|
@@ -25,9 +54,12 @@ Pod::Spec.new do |s|
|
|
|
25
54
|
|
|
26
55
|
if ENV['RCT_NEW_ARCH_ENABLED'] == '1'
|
|
27
56
|
s.compiler_flags = '-DRCT_NEW_ARCH_ENABLED=1'
|
|
57
|
+
# This replaces pod_target_xcconfig rather than adding to it, so what the applier
|
|
58
|
+
# needs to build has to be carried into it as well.
|
|
28
59
|
s.pod_target_xcconfig = {
|
|
29
60
|
'CLANG_CXX_LANGUAGE_STANDARD' => 'c++17',
|
|
30
|
-
'HEADER_SEARCH_PATHS' => "\"$(PODS_ROOT)/Headers/Public/ReactCodegen\" \"${PODS_CONFIGURATION_BUILD_DIR}/ReactCodegen/ReactCodegen.framework/Headers\""
|
|
61
|
+
'HEADER_SEARCH_PATHS' => "\"$(PODS_ROOT)/Headers/Public/ReactCodegen\" \"${PODS_CONFIGURATION_BUILD_DIR}/ReactCodegen/ReactCodegen.framework/Headers\" #{binary_patch_header_search_paths}",
|
|
62
|
+
'GCC_PREPROCESSOR_DEFINITIONS' => binary_patch_preprocessor_definitions
|
|
31
63
|
}
|
|
32
64
|
|
|
33
65
|
s.dependency 'ReactCodegen'
|
package/README.md
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# @bravemobile/react-native-code-push
|
|
2
2
|
|
|
3
|
-
###
|
|
3
|
+
### CodePush without an update server
|
|
4
4
|
|
|
5
5
|
- **No API Server Needed** – Use static hosting solutions (e.g., AWS S3) without maintaining additional API servers.
|
|
6
|
-
- **Familiar API** – Built on `microsoft/react-native-code-push`,
|
|
6
|
+
- **Familiar API** – Built on `microsoft/react-native-code-push`, with the native module migrated to TurboModule.
|
|
7
7
|
- **Flexible Deployment** – Implement your own release workflow, giving you complete control over the deployment process.
|
|
8
8
|
|
|
9
9
|
### 🚀 New Architecture support
|
|
@@ -36,7 +36,7 @@ npm install @bravemobile/react-native-code-push
|
|
|
36
36
|
1. Edit `android/app/build.gradle` file to remove the `apply from: "../../node_modules/..../codepush.gradle"` line.
|
|
37
37
|
|
|
38
38
|
2. The following changes are optional but recommended for cleaning up the old configuration:
|
|
39
|
-
- Since the deployment key is no longer
|
|
39
|
+
- Since the deployment key is no longer used, it is recommended to remove it from your `Info.plist`, `strings.xml`, or JavaScript code.
|
|
40
40
|
- Thanks to Auto Linking, you can remove the `react-native-code-push` module settings from `settings.gradle`.
|
|
41
41
|
|
|
42
42
|
3. Follow the installation guide starting from **'4. "CodePush-ify" your app'**.
|
|
@@ -81,6 +81,10 @@ Run `cd ios && pod install && cd ..`
|
|
|
81
81
|
|
|
82
82
|
(`npx pod-install`, `bundle exec pod install --project-directory=./ios`, ..)
|
|
83
83
|
|
|
84
|
+
The pod compiles the applier that installs binary patch updates from the C sources it
|
|
85
|
+
carries, so there is nothing else to install: CocoaPods builds them along with the rest
|
|
86
|
+
of the pod.
|
|
87
|
+
|
|
84
88
|
|
|
85
89
|
#### (2) Edit `AppDelegate` Code
|
|
86
90
|
|
|
@@ -294,6 +298,38 @@ Please refer to the [CodePushOptions](https://github.com/Soomgo-Mobile/react-nat
|
|
|
294
298
|
- **onDownloadStart:** Triggered when the bundle download begins.
|
|
295
299
|
- **onDownloadSuccess:** Triggered when the bundle download completes successfully.
|
|
296
300
|
- **onSyncError:** Triggered when an unknown error occurs during the update process. (`CodePush.SyncStatus.UNKNOWN_ERROR` status)
|
|
301
|
+
- **onBinaryPatchResult:** Triggered when an update that was published with a binary patch has been downloaded, with the release label and how the patch went. Unlike the callbacks above, this one can also be passed to a single `sync()` call - see below.
|
|
302
|
+
|
|
303
|
+
`onBinaryPatchResult` is called with `{ status: "applied" | "fallback", fallbackReason?: string, applyDurationMs: number }`.
|
|
304
|
+
A `"fallback"` is not a failed update: the update is downloaded in full instead and installed as usual, so the result
|
|
305
|
+
is there to be observed and nothing more. The library neither stores it nor sends it anywhere - an app that wants it in
|
|
306
|
+
its telemetry sends it itself. A callback that throws never fails the update - the error is only logged to the console.
|
|
307
|
+
A fallback also shows in `downloadProgressCallback`: the full download reports a second progress stream that counts
|
|
308
|
+
`receivedBytes` from zero again, against its own, larger `totalBytes`.
|
|
309
|
+
|
|
310
|
+
Register `onBinaryPatchResult` on `CodePush({ ... })` like the callbacks above and it covers every sync, whatever the `checkFrequency` is
|
|
311
|
+
and including the `CodePush.sync()` calls you make yourself.
|
|
312
|
+
|
|
313
|
+
```typescript
|
|
314
|
+
export default CodePush({
|
|
315
|
+
checkFrequency: CodePush.CheckFrequency.MANUAL, // or something else
|
|
316
|
+
releaseHistoryFetcher: releaseHistoryFetcher,
|
|
317
|
+
onBinaryPatchResult: (label, result) => {
|
|
318
|
+
// Send it to your own telemetry, if you want it there.
|
|
319
|
+
},
|
|
320
|
+
})(MyApp);
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
Pass it to a `sync()` call to override the registered one for that call - to tag the result of one particular sync,
|
|
324
|
+
for instance.
|
|
325
|
+
|
|
326
|
+
```typescript
|
|
327
|
+
CodePush.sync({
|
|
328
|
+
onBinaryPatchResult: (label, result) => {
|
|
329
|
+
// Send it to your own telemetry, if you want it there.
|
|
330
|
+
},
|
|
331
|
+
});
|
|
332
|
+
```
|
|
297
333
|
|
|
298
334
|
|
|
299
335
|
### 5. Configure the CLI Tool
|
|
@@ -396,6 +432,84 @@ module.exports = Config;
|
|
|
396
432
|
```
|
|
397
433
|
|
|
398
434
|
|
|
435
|
+
### 6. Export the Embedded Bundle (Optional)
|
|
436
|
+
|
|
437
|
+
Only needed if you want to release **binary patch updates** (`release --binary-bundle-path`).
|
|
438
|
+
A binary patch is the difference between the update and the JS bundle that is already
|
|
439
|
+
inside the installed app, so releasing one means holding on to that bundle: the exact
|
|
440
|
+
bytes the build you shipped to the store embedded.
|
|
441
|
+
|
|
442
|
+
The library ships a hook for each platform that copies the freshly compiled bundle out of
|
|
443
|
+
the build, together with a `binary-patch-base.json` record describing it.
|
|
444
|
+
|
|
445
|
+
**Android** - apply the Gradle script in your app module's `android/app/build.gradle`:
|
|
446
|
+
|
|
447
|
+
```groovy
|
|
448
|
+
apply plugin: "com.android.application"
|
|
449
|
+
apply plugin: "com.facebook.react"
|
|
450
|
+
|
|
451
|
+
react {
|
|
452
|
+
// ...
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
apply from: "../../node_modules/@bravemobile/react-native-code-push/android/codepush-export.gradle"
|
|
456
|
+
```
|
|
457
|
+
|
|
458
|
+
The line can go anywhere in the file (if you use `ext.codePushExportDir`, set it before the
|
|
459
|
+
`apply from:` line). Every variant that bundles JS then exports to
|
|
460
|
+
`android/app/build/codepush/embedded-bundle/<variant>/` after it is bundled. Pass
|
|
461
|
+
`-PcodePushExportDir=<path>` (or set `ext.codePushExportDir`) to export somewhere else; the
|
|
462
|
+
`<variant>` directory is appended either way.
|
|
463
|
+
|
|
464
|
+
**iOS** - call the export script at the end of the **"Bundle React Native code and images"**
|
|
465
|
+
build phase. In Xcode, open that phase
|
|
466
|
+
in your app target's **Build Phases** tab and append the last line below to its script:
|
|
467
|
+
|
|
468
|
+
```bash
|
|
469
|
+
/bin/sh -c "\"$WITH_ENVIRONMENT\" \"$REACT_NATIVE_XCODE\""
|
|
470
|
+
|
|
471
|
+
"$SRCROOT/../node_modules/@bravemobile/react-native-code-push/scripts/export-embedded-bundle.sh"
|
|
472
|
+
```
|
|
473
|
+
|
|
474
|
+
The export lands in `$BUILD_DIR/codepush/embedded-bundle/$CONFIGURATION-$PLATFORM_NAME/`.
|
|
475
|
+
Set the `CODEPUSH_EXPORT_DIR` environment variable to export somewhere else; the
|
|
476
|
+
`$CONFIGURATION-$PLATFORM_NAME` directory is appended either way.
|
|
477
|
+
|
|
478
|
+
**Archive the export per binary release.** Whatever pipeline builds your store binary
|
|
479
|
+
should keep that build's export somewhere durable, organized by binary version, so a later
|
|
480
|
+
release can fetch the bundle that matches the binary it targets:
|
|
481
|
+
|
|
482
|
+
```bash
|
|
483
|
+
# Android, after ./gradlew :app:assembleRelease
|
|
484
|
+
aws s3 cp --recursive \
|
|
485
|
+
android/app/build/codepush/embedded-bundle/release \
|
|
486
|
+
"s3://your-bucket/binaries/android/$BINARY_VERSION/"
|
|
487
|
+
|
|
488
|
+
# iOS - point the export at a path the pipeline knows, since $BUILD_DIR only exists inside the build
|
|
489
|
+
export CODEPUSH_EXPORT_DIR="$PWD/codepush-export"
|
|
490
|
+
xcodebuild -workspace ios/YourApp.xcworkspace -scheme YourApp -configuration Release archive # ...
|
|
491
|
+
aws s3 cp --recursive \
|
|
492
|
+
"$CODEPUSH_EXPORT_DIR/Release-iphoneos" \
|
|
493
|
+
"s3://your-bucket/binaries/ios/$BINARY_VERSION/"
|
|
494
|
+
```
|
|
495
|
+
|
|
496
|
+
To release a patch later, download the export and pass the bundle's path to
|
|
497
|
+
`--binary-bundle-path`:
|
|
498
|
+
|
|
499
|
+
```bash
|
|
500
|
+
aws s3 cp --recursive "s3://your-bucket/binaries/android/1.0.0/" ./binary/
|
|
501
|
+
npx code-push release -b 1.0.0 -v 1.0.1 -p android \
|
|
502
|
+
--binary-bundle-path ./binary/index.android.bundle
|
|
503
|
+
```
|
|
504
|
+
|
|
505
|
+
`release` also uses the record to verify the base bundle it was handed - see
|
|
506
|
+
[Verifying the base bundle](cli/README.md#verifying-the-base-bundle) for details.
|
|
507
|
+
|
|
508
|
+
> [!NOTE]
|
|
509
|
+
> Applying these hooks automatically through the Expo config plugin (`app.plugin.js`) is
|
|
510
|
+
> not implemented yet.
|
|
511
|
+
|
|
512
|
+
|
|
399
513
|
## 🚀 CLI Tool Usage
|
|
400
514
|
|
|
401
515
|
> [!TIP]
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
THIRD PARTY NOTICES
|
|
2
|
+
===================
|
|
3
|
+
|
|
4
|
+
This package includes third party source code. The notices below apply to that
|
|
5
|
+
code only; the rest of this project is covered by LICENSE.md.
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
-------------------------------------------------------------------------------
|
|
9
|
+
HDiffPatch
|
|
10
|
+
-------------------------------------------------------------------------------
|
|
11
|
+
|
|
12
|
+
Version: v5.1.3
|
|
13
|
+
Source: https://github.com/sisong/HDiffPatch/tree/v5.1.3
|
|
14
|
+
License: MIT
|
|
15
|
+
Vendored: cpp/binarypatch/vendor/HDiffPatch/
|
|
16
|
+
|
|
17
|
+
Only the files required to apply a patch on device are vendored, from
|
|
18
|
+
libHDiffPatch/HPatch: patch.c, patch.h, patch_private.h, patch_types.h,
|
|
19
|
+
checksum_plugin.h and hpatch_mt/hpatch_mt.h. The diff side of HDiffPatch is not
|
|
20
|
+
vendored; the `hdiffz` command line tool is built from upstream sources by
|
|
21
|
+
scripts/binary-patch/build-hdiffpatch.sh and is not distributed with this package.
|
|
22
|
+
|
|
23
|
+
MIT License
|
|
24
|
+
|
|
25
|
+
HDiffPatch
|
|
26
|
+
Copyright (c) 2012-2025 housisong
|
|
27
|
+
|
|
28
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
29
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
30
|
+
in the Software without restriction, including without limitation the rights
|
|
31
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
32
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
33
|
+
furnished to do so, subject to the following conditions:
|
|
34
|
+
|
|
35
|
+
The above copyright notice and this permission notice shall be included in all
|
|
36
|
+
copies or substantial portions of the Software.
|
|
37
|
+
|
|
38
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
39
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
40
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
41
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
42
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
43
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
44
|
+
SOFTWARE.
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
-------------------------------------------------------------------------------
|
|
48
|
+
Zstandard (zstd)
|
|
49
|
+
-------------------------------------------------------------------------------
|
|
50
|
+
|
|
51
|
+
Version: 1.5.7
|
|
52
|
+
Commit: 68c88c7c7ad22b5e6882a5296ef96d27dc8750c4
|
|
53
|
+
Source: https://github.com/sisong/zstd
|
|
54
|
+
(the fork HDiffPatch builds against, derived from
|
|
55
|
+
https://github.com/facebook/zstd)
|
|
56
|
+
The files here are a snapshot of that fork; the copies in this
|
|
57
|
+
repository are the version the appliers are built from, and
|
|
58
|
+
scripts/binary-patch/build-hdiffpatch.sh pins the same commit so the
|
|
59
|
+
`hdiffz` it builds compresses with the code that is vendored here.
|
|
60
|
+
License: BSD-3-Clause
|
|
61
|
+
Vendored: cpp/binarypatch/vendor/zstd/
|
|
62
|
+
|
|
63
|
+
Only the decompression path is vendored (lib/common and lib/decompress). The
|
|
64
|
+
amd64 assembly implementation of the Huffman decoder is not vendored because the
|
|
65
|
+
appliers are compiled with ZSTD_DISABLE_ASM=1.
|
|
66
|
+
|
|
67
|
+
BSD License
|
|
68
|
+
|
|
69
|
+
For Zstandard software
|
|
70
|
+
|
|
71
|
+
Copyright (c) Meta Platforms, Inc. and affiliates. All rights reserved.
|
|
72
|
+
|
|
73
|
+
Redistribution and use in source and binary forms, with or without modification,
|
|
74
|
+
are permitted provided that the following conditions are met:
|
|
75
|
+
|
|
76
|
+
* Redistributions of source code must retain the above copyright notice, this
|
|
77
|
+
list of conditions and the following disclaimer.
|
|
78
|
+
|
|
79
|
+
* Redistributions in binary form must reproduce the above copyright notice,
|
|
80
|
+
this list of conditions and the following disclaimer in the documentation
|
|
81
|
+
and/or other materials provided with the distribution.
|
|
82
|
+
|
|
83
|
+
* Neither the name Facebook, nor Meta, nor the names of its contributors may
|
|
84
|
+
be used to endorse or promote products derived from this software without
|
|
85
|
+
specific prior written permission.
|
|
86
|
+
|
|
87
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
88
|
+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
89
|
+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
90
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
|
91
|
+
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
92
|
+
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
93
|
+
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
|
94
|
+
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
95
|
+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
96
|
+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
package/android/app/build.gradle
CHANGED
|
@@ -32,8 +32,27 @@ android {
|
|
|
32
32
|
defaultConfig {
|
|
33
33
|
consumerProguardFiles 'proguard-rules.pro'
|
|
34
34
|
}
|
|
35
|
+
|
|
36
|
+
// Builds the binary patch applier. Its sources are the shared ones at the root of the
|
|
37
|
+
// repository, which the CMake file points at; nothing is compiled twice.
|
|
38
|
+
externalNativeBuild {
|
|
39
|
+
cmake {
|
|
40
|
+
path "src/main/cpp/CMakeLists.txt"
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
testOptions {
|
|
45
|
+
// The unit tests exercise plain logic, so the android framework classes they touch
|
|
46
|
+
// in passing (logging above all) are allowed to do nothing instead of throwing.
|
|
47
|
+
unitTests.returnDefaultValues = true
|
|
48
|
+
}
|
|
35
49
|
}
|
|
36
50
|
|
|
37
51
|
dependencies {
|
|
38
52
|
implementation "com.facebook.react:react-native:+"
|
|
53
|
+
|
|
54
|
+
testImplementation "junit:junit:4.13.2"
|
|
55
|
+
// org.json ships as stubs in the android framework jar, and the unit tests parse real
|
|
56
|
+
// manifests, so they need an implementation that actually parses.
|
|
57
|
+
testImplementation "org.json:json:20231013"
|
|
39
58
|
}
|
|
@@ -37,3 +37,8 @@
|
|
|
37
37
|
|
|
38
38
|
# Can't find referenced class org.bouncycastle.**
|
|
39
39
|
-dontwarn com.nimbusds.jose.**
|
|
40
|
+
|
|
41
|
+
# The binary patch applier is found by name from the native library.
|
|
42
|
+
-keepclasseswithmembernames class com.microsoft.codepush.react.HDiffPatchNative {
|
|
43
|
+
native <methods>;
|
|
44
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
cmake_minimum_required(VERSION 3.13)
|
|
2
|
+
|
|
3
|
+
project(codepush-binarypatch C)
|
|
4
|
+
|
|
5
|
+
# The applier sources live outside the platform directories because the host build and
|
|
6
|
+
# the other platform compile the very same files. Referencing them where they are is
|
|
7
|
+
# what keeps the appliers of the platforms from drifting apart, so they are deliberately
|
|
8
|
+
# not copied into this directory.
|
|
9
|
+
set(BINARY_PATCH_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../../../../cpp/binarypatch)
|
|
10
|
+
|
|
11
|
+
add_library(codepush-binarypatch SHARED
|
|
12
|
+
${CMAKE_CURRENT_SOURCE_DIR}/binarypatch_jni.c
|
|
13
|
+
${BINARY_PATCH_DIR}/binarypatch_zstd_decompressor.c
|
|
14
|
+
${BINARY_PATCH_DIR}/vendor/HDiffPatch/libHDiffPatch/HPatch/patch.c
|
|
15
|
+
${BINARY_PATCH_DIR}/vendor/zstd/common/debug.c
|
|
16
|
+
${BINARY_PATCH_DIR}/vendor/zstd/common/entropy_common.c
|
|
17
|
+
${BINARY_PATCH_DIR}/vendor/zstd/common/error_private.c
|
|
18
|
+
${BINARY_PATCH_DIR}/vendor/zstd/common/fse_decompress.c
|
|
19
|
+
${BINARY_PATCH_DIR}/vendor/zstd/common/xxhash.c
|
|
20
|
+
${BINARY_PATCH_DIR}/vendor/zstd/common/zstd_common.c
|
|
21
|
+
${BINARY_PATCH_DIR}/vendor/zstd/decompress/huf_decompress.c
|
|
22
|
+
${BINARY_PATCH_DIR}/vendor/zstd/decompress/zstd_ddict.c
|
|
23
|
+
${BINARY_PATCH_DIR}/vendor/zstd/decompress/zstd_decompress.c
|
|
24
|
+
${BINARY_PATCH_DIR}/vendor/zstd/decompress/zstd_decompress_block.c)
|
|
25
|
+
|
|
26
|
+
target_include_directories(codepush-binarypatch PRIVATE
|
|
27
|
+
${BINARY_PATCH_DIR}
|
|
28
|
+
${BINARY_PATCH_DIR}/vendor/HDiffPatch
|
|
29
|
+
${BINARY_PATCH_DIR}/vendor/zstd)
|
|
30
|
+
|
|
31
|
+
# ZSTD_DISABLE_ASM: the assembly fast path is intentionally not vendored.
|
|
32
|
+
# _IS_USED_MULTITHREAD=0: patches are applied on the thread that downloads them.
|
|
33
|
+
target_compile_definitions(codepush-binarypatch PRIVATE
|
|
34
|
+
ZSTD_DISABLE_ASM=1
|
|
35
|
+
_IS_USED_MULTITHREAD=0)
|
|
36
|
+
|
|
37
|
+
find_library(log-lib log)
|
|
38
|
+
target_link_libraries(codepush-binarypatch ${log-lib})
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* JNI entry point of the CodePush binary patch applier.
|
|
3
|
+
*
|
|
4
|
+
* The applier itself is the shared C code one directory tree up, which the host build and
|
|
5
|
+
* the other platform compile as well; this file only moves data across the JNI boundary
|
|
6
|
+
* and turns a failure into the result code `HDiffPatchNative` hands back to Java.
|
|
7
|
+
*
|
|
8
|
+
* Memory contract, the same one the host build documents:
|
|
9
|
+
* - the base bundle is held whole, because the patches are produced with `hdiffz -m`,
|
|
10
|
+
* which patches with random access to the base data
|
|
11
|
+
* - the patch is held whole, being far smaller than either bundle
|
|
12
|
+
* - the restored bundle is written sequentially to a file, so a patch session never
|
|
13
|
+
* holds two bundles at once
|
|
14
|
+
*
|
|
15
|
+
* A patch carries no checksum of the base data and its zstd streams carry no content
|
|
16
|
+
* checksum, so a successful apply here is not proof of a correct result. The caller
|
|
17
|
+
* verifies the base and target hashes; this file cannot.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
#include <jni.h>
|
|
21
|
+
#include <stdlib.h>
|
|
22
|
+
#include <stdio.h>
|
|
23
|
+
#include <string.h>
|
|
24
|
+
|
|
25
|
+
#include <android/log.h>
|
|
26
|
+
|
|
27
|
+
#include "binarypatch_zstd_decompressor.h"
|
|
28
|
+
#include "libHDiffPatch/HPatch/patch.h"
|
|
29
|
+
|
|
30
|
+
#define LOG_TAG "ReactNative"
|
|
31
|
+
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
|
|
32
|
+
|
|
33
|
+
/* Mirrored by CodePushBinaryPatch.PatchApplier; the two lists have to stay in step. */
|
|
34
|
+
#define RESULT_OK 0
|
|
35
|
+
#define RESULT_INVALID_ARGUMENT 1
|
|
36
|
+
#define RESULT_IO_ERROR 2
|
|
37
|
+
#define RESULT_INVALID_HEADER 3
|
|
38
|
+
#define RESULT_UNSUPPORTED_COMPRESSION 4
|
|
39
|
+
#define RESULT_SIZE_MISMATCH 5
|
|
40
|
+
#define RESULT_APPLY_FAILED 6
|
|
41
|
+
|
|
42
|
+
/* Scratch buffer handed to patch_decompress_with_cache() to reduce stream reads. */
|
|
43
|
+
#define APPLY_CACHE_SIZE (1024 * 1024)
|
|
44
|
+
|
|
45
|
+
typedef struct {
|
|
46
|
+
FILE* file;
|
|
47
|
+
hpatch_StreamPos_t writtenSize;
|
|
48
|
+
} TSequentialFileWriter;
|
|
49
|
+
|
|
50
|
+
static hpatch_BOOL _write_sequential(const hpatch_TStreamOutput* stream,
|
|
51
|
+
hpatch_StreamPos_t writeToPos,
|
|
52
|
+
const unsigned char* data,
|
|
53
|
+
const unsigned char* data_end) {
|
|
54
|
+
TSequentialFileWriter* self = (TSequentialFileWriter*)stream->streamImport;
|
|
55
|
+
const size_t length = (size_t)(data_end - data);
|
|
56
|
+
/* patch_decompress_with_cache() only ever appends; anything else is a bug. */
|
|
57
|
+
if (writeToPos != self->writtenSize) {
|
|
58
|
+
return hpatch_FALSE;
|
|
59
|
+
}
|
|
60
|
+
if (fwrite(data, 1, length, self->file) != length) {
|
|
61
|
+
return hpatch_FALSE;
|
|
62
|
+
}
|
|
63
|
+
self->writtenSize += length;
|
|
64
|
+
return hpatch_TRUE;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
JNIEXPORT jint JNICALL
|
|
68
|
+
Java_com_microsoft_codepush_react_HDiffPatchNative_applyPatch(JNIEnv* env,
|
|
69
|
+
jclass clazz,
|
|
70
|
+
jbyteArray base,
|
|
71
|
+
jbyteArray patch,
|
|
72
|
+
jstring outputPath,
|
|
73
|
+
jlong expectedTargetSize) {
|
|
74
|
+
jbyte* baseData = NULL;
|
|
75
|
+
jbyte* patchData = NULL;
|
|
76
|
+
const char* outputPathChars = NULL;
|
|
77
|
+
unsigned char* cache = NULL;
|
|
78
|
+
jsize baseSize = 0;
|
|
79
|
+
jsize patchSize = 0;
|
|
80
|
+
hpatch_compressedDiffInfo diffInfo;
|
|
81
|
+
hpatch_TDecompress decompressor;
|
|
82
|
+
hpatch_TStreamInput baseStream;
|
|
83
|
+
hpatch_TStreamInput patchStream;
|
|
84
|
+
hpatch_TStreamOutput targetStream;
|
|
85
|
+
TSequentialFileWriter writer;
|
|
86
|
+
hpatch_BOOL applied;
|
|
87
|
+
int result = RESULT_OK;
|
|
88
|
+
|
|
89
|
+
(void)clazz;
|
|
90
|
+
|
|
91
|
+
if ((base == NULL) || (patch == NULL) || (outputPath == NULL) || (expectedTargetSize <= 0)) {
|
|
92
|
+
return RESULT_INVALID_ARGUMENT;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
baseSize = (*env)->GetArrayLength(env, base);
|
|
96
|
+
patchSize = (*env)->GetArrayLength(env, patch);
|
|
97
|
+
baseData = (*env)->GetByteArrayElements(env, base, NULL);
|
|
98
|
+
patchData = (*env)->GetByteArrayElements(env, patch, NULL);
|
|
99
|
+
outputPathChars = (*env)->GetStringUTFChars(env, outputPath, NULL);
|
|
100
|
+
if ((baseData == NULL) || (patchData == NULL) || (outputPathChars == NULL)) {
|
|
101
|
+
LOGE("[CodePush] out of memory while reading the binary patch inputs");
|
|
102
|
+
result = RESULT_IO_ERROR;
|
|
103
|
+
goto cleanup;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
binarypatch_zstd_decompressor_init(&decompressor);
|
|
107
|
+
|
|
108
|
+
if (!getCompressedDiffInfo_mem(&diffInfo, (const unsigned char*)patchData,
|
|
109
|
+
(const unsigned char*)patchData + patchSize)) {
|
|
110
|
+
LOGE("[CodePush] the binary patch header could not be read");
|
|
111
|
+
result = RESULT_INVALID_HEADER;
|
|
112
|
+
goto cleanup;
|
|
113
|
+
}
|
|
114
|
+
if ((strlen(diffInfo.compressType) > 0) && !decompressor.is_can_open(diffInfo.compressType)) {
|
|
115
|
+
LOGE("[CodePush] the binary patch uses an unsupported codec: %s", diffInfo.compressType);
|
|
116
|
+
result = RESULT_UNSUPPORTED_COMPRESSION;
|
|
117
|
+
goto cleanup;
|
|
118
|
+
}
|
|
119
|
+
if (diffInfo.oldDataSize != (hpatch_StreamPos_t)baseSize) {
|
|
120
|
+
LOGE("[CodePush] the binary patch expects a %llu byte base bundle, this one is %llu bytes",
|
|
121
|
+
(unsigned long long)diffInfo.oldDataSize, (unsigned long long)baseSize);
|
|
122
|
+
result = RESULT_SIZE_MISMATCH;
|
|
123
|
+
goto cleanup;
|
|
124
|
+
}
|
|
125
|
+
if (diffInfo.newDataSize != (hpatch_StreamPos_t)expectedTargetSize) {
|
|
126
|
+
LOGE("[CodePush] the binary patch produces %llu bytes, the manifest promises %llu",
|
|
127
|
+
(unsigned long long)diffInfo.newDataSize, (unsigned long long)expectedTargetSize);
|
|
128
|
+
result = RESULT_SIZE_MISMATCH;
|
|
129
|
+
goto cleanup;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
cache = (unsigned char*)malloc(APPLY_CACHE_SIZE);
|
|
133
|
+
if (cache == NULL) {
|
|
134
|
+
LOGE("[CodePush] out of memory while allocating the binary patch cache");
|
|
135
|
+
result = RESULT_IO_ERROR;
|
|
136
|
+
goto cleanup;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
writer.file = fopen(outputPathChars, "wb");
|
|
140
|
+
writer.writtenSize = 0;
|
|
141
|
+
if (writer.file == NULL) {
|
|
142
|
+
LOGE("[CodePush] the restored bundle could not be opened for writing");
|
|
143
|
+
result = RESULT_IO_ERROR;
|
|
144
|
+
goto cleanup;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
mem_as_hStreamInput(&baseStream, (const unsigned char*)baseData, (const unsigned char*)baseData + baseSize);
|
|
148
|
+
mem_as_hStreamInput(&patchStream, (const unsigned char*)patchData, (const unsigned char*)patchData + patchSize);
|
|
149
|
+
memset(&targetStream, 0, sizeof(targetStream));
|
|
150
|
+
targetStream.streamImport = &writer;
|
|
151
|
+
targetStream.streamSize = diffInfo.newDataSize;
|
|
152
|
+
targetStream.write = _write_sequential;
|
|
153
|
+
|
|
154
|
+
applied = patch_decompress_with_cache(&targetStream, &baseStream, &patchStream, &decompressor,
|
|
155
|
+
cache, cache + APPLY_CACHE_SIZE);
|
|
156
|
+
if (fclose(writer.file) != 0) {
|
|
157
|
+
LOGE("[CodePush] the restored bundle could not be flushed to disk");
|
|
158
|
+
result = RESULT_IO_ERROR;
|
|
159
|
+
goto cleanup;
|
|
160
|
+
}
|
|
161
|
+
if (!applied) {
|
|
162
|
+
LOGE("[CodePush] applying the binary patch failed (decError=%d)", (int)decompressor.decError);
|
|
163
|
+
result = RESULT_APPLY_FAILED;
|
|
164
|
+
goto cleanup;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
cleanup:
|
|
168
|
+
free(cache);
|
|
169
|
+
if (outputPathChars != NULL) {
|
|
170
|
+
(*env)->ReleaseStringUTFChars(env, outputPath, outputPathChars);
|
|
171
|
+
}
|
|
172
|
+
/* JNI_ABORT: neither array is written to, so nothing has to be copied back. */
|
|
173
|
+
if (patchData != NULL) {
|
|
174
|
+
(*env)->ReleaseByteArrayElements(env, patch, patchData, JNI_ABORT);
|
|
175
|
+
}
|
|
176
|
+
if (baseData != NULL) {
|
|
177
|
+
(*env)->ReleaseByteArrayElements(env, base, baseData, JNI_ABORT);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
return (jint)result;
|
|
181
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
package com.microsoft.codepush.react;
|
|
2
|
+
|
|
3
|
+
import org.json.JSONObject;
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* The record of one attempt at installing an update from its binary patch archive: whether
|
|
7
|
+
* the update was installed from the patch, why the full archive had to be downloaded when it
|
|
8
|
+
* was not, and how long the work took.
|
|
9
|
+
*
|
|
10
|
+
* The record exists for an app that wants to judge a patch rollout with its own telemetry, so
|
|
11
|
+
* it travels to whoever asked for the download and nowhere else. It is not part of the
|
|
12
|
+
* update's metadata, it is never written to disk, and nothing here sends it anywhere.
|
|
13
|
+
*/
|
|
14
|
+
class BinaryPatchAttempt {
|
|
15
|
+
|
|
16
|
+
private static final String APPLY_DURATION_MS_KEY = "applyDurationMs";
|
|
17
|
+
private static final String FALLBACK_REASON_KEY = "fallbackReason";
|
|
18
|
+
private static final String STATUS_KEY = "status";
|
|
19
|
+
|
|
20
|
+
private static final String STATUS_APPLIED = "applied";
|
|
21
|
+
private static final String STATUS_FALLBACK = "fallback";
|
|
22
|
+
|
|
23
|
+
private final long mStartTimeMs = System.currentTimeMillis();
|
|
24
|
+
|
|
25
|
+
/** How long the applier took, once it has restored the bundle. */
|
|
26
|
+
private long mApplyDurationMs = -1;
|
|
27
|
+
|
|
28
|
+
private boolean mFellBack;
|
|
29
|
+
private String mFallbackReason;
|
|
30
|
+
private long mAttemptDurationMs;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* The applier restored the bundle. Whether the update installs is decided by the checks
|
|
34
|
+
* that follow, so this is not yet the attempt succeeding.
|
|
35
|
+
*/
|
|
36
|
+
void recordBundleRestored(long applyDurationMs) {
|
|
37
|
+
mApplyDurationMs = applyDurationMs;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* The attempt ended in one of the reasons the appliers report.
|
|
42
|
+
*
|
|
43
|
+
* The attempt is timed here rather than when it is read, because what happens next is the
|
|
44
|
+
* full archive being downloaded and that is not time the patch spent.
|
|
45
|
+
*/
|
|
46
|
+
void recordFallback(String failureReason) {
|
|
47
|
+
mFellBack = true;
|
|
48
|
+
mFallbackReason = failureReason;
|
|
49
|
+
mAttemptDurationMs = System.currentTimeMillis() - mStartTimeMs;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* The attempt ended in an error rather than in a verdict of its own.
|
|
54
|
+
*
|
|
55
|
+
* An error raised after the bundle was restored is the restored update failing the checks
|
|
56
|
+
* that every update passes before it is installed. Before that point the appliers have no
|
|
57
|
+
* word for what happened - the patch archive not being downloadable, say - and inventing
|
|
58
|
+
* one here would put a value on the wire that no platform reports, so the fallback is
|
|
59
|
+
* reported without a reason.
|
|
60
|
+
*/
|
|
61
|
+
void recordFallbackAfterError() {
|
|
62
|
+
recordFallback(bundleWasRestored() ? BinaryPatchResult.REASON_PACKAGE_VERIFICATION_FAILED : null);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
private boolean bundleWasRestored() {
|
|
66
|
+
return mApplyDurationMs >= 0;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* What the attempt ended in, as the app reads it.
|
|
71
|
+
*
|
|
72
|
+
* An attempt that never fell back is one the update was installed from, and it reports the
|
|
73
|
+
* time the applier took. A fallback reports how long the attempt ran before it was given
|
|
74
|
+
* up on, because there is no completed apply to time.
|
|
75
|
+
*/
|
|
76
|
+
JSONObject result() {
|
|
77
|
+
JSONObject result = new JSONObject();
|
|
78
|
+
if (!mFellBack) {
|
|
79
|
+
CodePushUtils.setJSONValueForKey(result, STATUS_KEY, STATUS_APPLIED);
|
|
80
|
+
CodePushUtils.setJSONValueForKey(result, APPLY_DURATION_MS_KEY, Math.max(mApplyDurationMs, 0));
|
|
81
|
+
return result;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
CodePushUtils.setJSONValueForKey(result, STATUS_KEY, STATUS_FALLBACK);
|
|
85
|
+
if (mFallbackReason != null) {
|
|
86
|
+
CodePushUtils.setJSONValueForKey(result, FALLBACK_REASON_KEY, mFallbackReason);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
CodePushUtils.setJSONValueForKey(result, APPLY_DURATION_MS_KEY, mAttemptDurationMs);
|
|
90
|
+
return result;
|
|
91
|
+
}
|
|
92
|
+
}
|