@hot-updater/react-native 1.0.0-rc.1 → 1.0.0-rc.15
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/android/proguard-rules.pro +17 -2
- package/android/src/main/java/com/hotupdater/BuiltInAssetResolver.kt +286 -0
- package/android/src/main/java/com/hotupdater/BundleFileStorageService.kt +651 -637
- package/android/src/main/java/com/hotupdater/BundleMetadata.kt +10 -0
- package/android/src/main/java/com/hotupdater/HotUpdaterException.kt +0 -13
- package/android/src/main/java/com/hotupdater/HotUpdaterImpl.kt +10 -14
- package/android/src/main/java/com/hotupdater/HotUpdaterRecoveryManager.kt +37 -54
- package/android/src/main/java/com/hotupdater/HotUpdaterRecoveryState.kt +64 -0
- package/android/src/main/java/com/hotupdater/OkHttpDownloadService.kt +19 -0
- package/android/src/main/java/com/hotupdater/TarArchiveInputStream.kt +214 -375
- package/android/src/main/java/com/hotupdater/TarBrArchiveExtractor.kt +119 -0
- package/android/src/newarch/HotUpdater.kt +7 -9
- package/android/src/newarch/HotUpdaterModule.kt +40 -20
- package/android/src/oldarch/HotUpdater.kt +7 -9
- package/android/src/oldarch/HotUpdaterModule.kt +40 -20
- package/android/src/test/java/com/hotupdater/BundleFileStorageServiceTest.kt +1126 -74
- package/android/src/test/java/com/hotupdater/HotUpdaterImplTest.kt +4 -5
- package/android/src/test/java/com/hotupdater/HotUpdaterRecoveryStateTest.kt +97 -0
- package/android/src/test/java/com/hotupdater/PendingCrashRecoveryTest.kt +44 -0
- package/android/src/test/java/com/hotupdater/TarBrArchiveExtractorTest.kt +206 -0
- package/ios/HotUpdater/Internal/BrotliFileDecompressor.swift +167 -0
- package/ios/HotUpdater/Internal/BuiltInAssetResolver.swift +196 -0
- package/ios/HotUpdater/Internal/BundleFileStorageService.swift +773 -1016
- package/ios/HotUpdater/Internal/BundleMetadata.swift +5 -0
- package/ios/HotUpdater/Internal/FileUtilities.swift +64 -0
- package/ios/HotUpdater/Internal/HotUpdater-Bridging-Header.h +0 -1
- package/ios/HotUpdater/Internal/HotUpdater.mm +5 -27
- package/ios/HotUpdater/Internal/HotUpdaterImpl.swift +149 -51
- package/ios/HotUpdater/Internal/NotificationExtension.swift +0 -2
- package/ios/HotUpdater/Internal/TarArchiveExtractor.swift +182 -311
- package/ios/HotUpdater/Internal/URLSessionDownloadService.swift +13 -72
- package/ios/HotUpdater/Recovery/HotUpdaterRecovery.h +16 -0
- package/ios/HotUpdater/Recovery/HotUpdaterRecovery.mm +83 -0
- package/ios/HotUpdater/Recovery/HotUpdaterRecoveryHooks.mm +49 -0
- package/lib/commonjs/checkForUpdate.js +32 -23
- package/lib/commonjs/checkForUpdate.js.map +1 -1
- package/lib/commonjs/checkForUpdate.releaseCatalog.spec.js +97 -28
- package/lib/commonjs/checkForUpdate.releaseCatalog.spec.js.map +1 -1
- package/lib/commonjs/error.js +4 -13
- package/lib/commonjs/error.js.map +1 -1
- package/lib/commonjs/fetchJSON.js +9 -2
- package/lib/commonjs/fetchJSON.js.map +1 -1
- package/lib/commonjs/httpClient.js +46 -29
- package/lib/commonjs/httpClient.js.map +1 -1
- package/lib/commonjs/httpClient.spec.js +57 -5
- package/lib/commonjs/httpClient.spec.js.map +1 -1
- package/lib/commonjs/index.js +31 -14
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/index.spec.js +56 -11
- package/lib/commonjs/index.spec.js.map +1 -1
- package/lib/commonjs/native.js +15 -14
- package/lib/commonjs/native.js.map +1 -1
- package/lib/commonjs/native.spec.js +128 -22
- package/lib/commonjs/native.spec.js.map +1 -1
- package/lib/commonjs/{notifyAppReadyAnalytics.boundary.spec.js → notifyAppReadyInsights.boundary.spec.js} +28 -21
- package/lib/commonjs/notifyAppReadyInsights.boundary.spec.js.map +1 -0
- package/lib/commonjs/notifyAppReadyInsights.js +188 -0
- package/lib/commonjs/notifyAppReadyInsights.js.map +1 -0
- package/lib/commonjs/{notifyAppReadyAnalytics.spec.js → notifyAppReadyInsights.spec.js} +161 -29
- package/lib/commonjs/notifyAppReadyInsights.spec.js.map +1 -0
- package/lib/commonjs/{notifyAppReadyAnalytics.test-utils.js → notifyAppReadyInsights.test-utils.js} +3 -3
- package/lib/commonjs/notifyAppReadyInsights.test-utils.js.map +1 -0
- package/lib/commonjs/{notifyAppReadyAnalytics.transport.spec.js → notifyAppReadyInsights.transport.spec.js} +14 -7
- package/lib/commonjs/notifyAppReadyInsights.transport.spec.js.map +1 -0
- package/lib/commonjs/sdkVersion.js +1 -1
- package/lib/commonjs/sdkVersion.js.map +1 -1
- package/lib/commonjs/specs/NativeHotUpdater.js.map +1 -1
- package/lib/commonjs/store.js +3 -10
- package/lib/commonjs/store.js.map +1 -1
- package/lib/commonjs/store.notification.spec.js +12 -4
- package/lib/commonjs/store.notification.spec.js.map +1 -1
- package/lib/commonjs/store.spec.js +2 -136
- package/lib/commonjs/store.spec.js.map +1 -1
- package/lib/commonjs/wrap.js +11 -12
- package/lib/commonjs/wrap.js.map +1 -1
- package/lib/commonjs/wrap.spec.js +97 -25
- package/lib/commonjs/wrap.spec.js.map +1 -1
- package/lib/module/checkForUpdate.js +32 -23
- package/lib/module/checkForUpdate.js.map +1 -1
- package/lib/module/checkForUpdate.releaseCatalog.spec.js +97 -28
- package/lib/module/checkForUpdate.releaseCatalog.spec.js.map +1 -1
- package/lib/module/error.js +4 -13
- package/lib/module/error.js.map +1 -1
- package/lib/module/fetchJSON.js +7 -1
- package/lib/module/fetchJSON.js.map +1 -1
- package/lib/module/httpClient.js +48 -31
- package/lib/module/httpClient.js.map +1 -1
- package/lib/module/httpClient.spec.js +57 -5
- package/lib/module/httpClient.spec.js.map +1 -1
- package/lib/module/index.js +32 -15
- package/lib/module/index.js.map +1 -1
- package/lib/module/index.spec.js +56 -11
- package/lib/module/index.spec.js.map +1 -1
- package/lib/module/native.js +13 -12
- package/lib/module/native.js.map +1 -1
- package/lib/module/native.spec.js +128 -22
- package/lib/module/native.spec.js.map +1 -1
- package/lib/module/{notifyAppReadyAnalytics.boundary.spec.js → notifyAppReadyInsights.boundary.spec.js} +23 -16
- package/lib/module/notifyAppReadyInsights.boundary.spec.js.map +1 -0
- package/lib/module/notifyAppReadyInsights.js +181 -0
- package/lib/module/notifyAppReadyInsights.js.map +1 -0
- package/lib/module/{notifyAppReadyAnalytics.spec.js → notifyAppReadyInsights.spec.js} +153 -21
- package/lib/module/notifyAppReadyInsights.spec.js.map +1 -0
- package/lib/module/{notifyAppReadyAnalytics.test-utils.js → notifyAppReadyInsights.test-utils.js} +3 -3
- package/lib/module/notifyAppReadyInsights.test-utils.js.map +1 -0
- package/lib/module/{notifyAppReadyAnalytics.transport.spec.js → notifyAppReadyInsights.transport.spec.js} +11 -4
- package/lib/module/notifyAppReadyInsights.transport.spec.js.map +1 -0
- package/lib/module/sdkVersion.js +1 -1
- package/lib/module/sdkVersion.js.map +1 -1
- package/lib/module/specs/NativeHotUpdater.js.map +1 -1
- package/lib/module/store.js +3 -10
- package/lib/module/store.js.map +1 -1
- package/lib/module/store.notification.spec.js +12 -4
- package/lib/module/store.notification.spec.js.map +1 -1
- package/lib/module/store.spec.js +2 -136
- package/lib/module/store.spec.js.map +1 -1
- package/lib/module/wrap.js +11 -12
- package/lib/module/wrap.js.map +1 -1
- package/lib/module/wrap.spec.js +99 -25
- package/lib/module/wrap.spec.js.map +1 -1
- package/lib/typescript/commonjs/checkForUpdate.d.ts +2 -3
- package/lib/typescript/commonjs/checkForUpdate.d.ts.map +1 -1
- package/lib/typescript/commonjs/error.d.ts +4 -13
- package/lib/typescript/commonjs/error.d.ts.map +1 -1
- package/lib/typescript/commonjs/fetchJSON.d.ts +4 -0
- package/lib/typescript/commonjs/fetchJSON.d.ts.map +1 -1
- package/lib/typescript/commonjs/httpClient.d.ts +6 -6
- package/lib/typescript/commonjs/httpClient.d.ts.map +1 -1
- package/lib/typescript/commonjs/index.d.ts +4 -7
- package/lib/typescript/commonjs/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/native.d.ts +5 -9
- package/lib/typescript/commonjs/native.d.ts.map +1 -1
- package/lib/typescript/commonjs/notifyAppReadyInsights.d.ts +25 -0
- package/lib/typescript/commonjs/notifyAppReadyInsights.d.ts.map +1 -0
- package/lib/typescript/commonjs/notifyAppReadyInsights.test-utils.d.ts +8 -0
- package/lib/typescript/commonjs/notifyAppReadyInsights.test-utils.d.ts.map +1 -0
- package/lib/typescript/commonjs/sdkVersion.d.ts +1 -1
- package/lib/typescript/commonjs/sdkVersion.d.ts.map +1 -1
- package/lib/typescript/commonjs/specs/NativeHotUpdater.d.ts +8 -35
- package/lib/typescript/commonjs/specs/NativeHotUpdater.d.ts.map +1 -1
- package/lib/typescript/commonjs/store.d.ts +1 -3
- package/lib/typescript/commonjs/store.d.ts.map +1 -1
- package/lib/typescript/commonjs/wrap.types.d.ts +5 -8
- package/lib/typescript/commonjs/wrap.types.d.ts.map +1 -1
- package/lib/typescript/module/checkForUpdate.d.ts +2 -3
- package/lib/typescript/module/checkForUpdate.d.ts.map +1 -1
- package/lib/typescript/module/error.d.ts +4 -13
- package/lib/typescript/module/error.d.ts.map +1 -1
- package/lib/typescript/module/fetchJSON.d.ts +4 -0
- package/lib/typescript/module/fetchJSON.d.ts.map +1 -1
- package/lib/typescript/module/httpClient.d.ts +6 -6
- package/lib/typescript/module/httpClient.d.ts.map +1 -1
- package/lib/typescript/module/index.d.ts +4 -7
- package/lib/typescript/module/index.d.ts.map +1 -1
- package/lib/typescript/module/native.d.ts +5 -9
- package/lib/typescript/module/native.d.ts.map +1 -1
- package/lib/typescript/module/notifyAppReadyInsights.d.ts +25 -0
- package/lib/typescript/module/notifyAppReadyInsights.d.ts.map +1 -0
- package/lib/typescript/module/notifyAppReadyInsights.test-utils.d.ts +8 -0
- package/lib/typescript/module/notifyAppReadyInsights.test-utils.d.ts.map +1 -0
- package/lib/typescript/module/sdkVersion.d.ts +1 -1
- package/lib/typescript/module/sdkVersion.d.ts.map +1 -1
- package/lib/typescript/module/specs/NativeHotUpdater.d.ts +8 -35
- package/lib/typescript/module/specs/NativeHotUpdater.d.ts.map +1 -1
- package/lib/typescript/module/store.d.ts +1 -3
- package/lib/typescript/module/store.d.ts.map +1 -1
- package/lib/typescript/module/wrap.types.d.ts +5 -8
- package/lib/typescript/module/wrap.types.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/checkForUpdate.releaseCatalog.spec.ts +137 -61
- package/src/checkForUpdate.ts +35 -29
- package/src/error.ts +4 -14
- package/src/fetchJSON.ts +12 -1
- package/src/httpClient.spec.ts +70 -5
- package/src/httpClient.ts +78 -64
- package/src/index.spec.ts +59 -11
- package/src/index.ts +41 -17
- package/src/native.spec.ts +133 -22
- package/src/native.ts +21 -32
- package/src/{notifyAppReadyAnalytics.boundary.spec.ts → notifyAppReadyInsights.boundary.spec.ts} +25 -17
- package/src/{notifyAppReadyAnalytics.spec.ts → notifyAppReadyInsights.spec.ts} +141 -22
- package/src/{notifyAppReadyAnalytics.test-utils.ts → notifyAppReadyInsights.test-utils.ts} +4 -4
- package/src/{notifyAppReadyAnalytics.transport.spec.ts → notifyAppReadyInsights.transport.spec.ts} +13 -5
- package/src/notifyAppReadyInsights.ts +293 -0
- package/src/sdkVersion.ts +1 -1
- package/src/specs/NativeHotUpdater.ts +8 -36
- package/src/store.notification.spec.ts +13 -3
- package/src/store.spec.ts +0 -157
- package/src/store.ts +2 -23
- package/src/wrap.spec.ts +109 -31
- package/src/wrap.tsx +11 -11
- package/src/wrap.types.ts +5 -8
- package/android/src/main/java/com/hotupdater/DecompressService.kt +0 -89
- package/android/src/main/java/com/hotupdater/DecompressionStrategy.kt +0 -26
- package/android/src/main/java/com/hotupdater/TarBrDecompressionStrategy.kt +0 -117
- package/android/src/main/java/com/hotupdater/TarGzDecompressionStrategy.kt +0 -117
- package/android/src/main/java/com/hotupdater/ZipDecompressionStrategy.kt +0 -192
- package/android/src/test/java/com/hotupdater/TarArchiveInputStreamTest.kt +0 -68
- package/ios/HotUpdater/Internal/ArchiveExtractionUtilities.swift +0 -178
- package/ios/HotUpdater/Internal/DecompressService.swift +0 -112
- package/ios/HotUpdater/Internal/DecompressionStrategy.swift +0 -22
- package/ios/HotUpdater/Internal/StreamingTarArchiveExtractor.swift +0 -370
- package/ios/HotUpdater/Internal/TarBrDecompressionStrategy.swift +0 -39
- package/ios/HotUpdater/Internal/TarGzDecompressionStrategy.swift +0 -59
- package/ios/HotUpdater/Internal/ZipArchiveExtractor.swift +0 -482
- package/ios/HotUpdater/Internal/ZipDecompressionStrategy.swift +0 -56
- package/lib/commonjs/notifyAppReadyAnalytics.boundary.spec.js.map +0 -1
- package/lib/commonjs/notifyAppReadyAnalytics.js +0 -133
- package/lib/commonjs/notifyAppReadyAnalytics.js.map +0 -1
- package/lib/commonjs/notifyAppReadyAnalytics.spec.js.map +0 -1
- package/lib/commonjs/notifyAppReadyAnalytics.test-utils.js.map +0 -1
- package/lib/commonjs/notifyAppReadyAnalytics.transport.spec.js.map +0 -1
- package/lib/module/notifyAppReadyAnalytics.boundary.spec.js.map +0 -1
- package/lib/module/notifyAppReadyAnalytics.js +0 -128
- package/lib/module/notifyAppReadyAnalytics.js.map +0 -1
- package/lib/module/notifyAppReadyAnalytics.spec.js.map +0 -1
- package/lib/module/notifyAppReadyAnalytics.test-utils.js.map +0 -1
- package/lib/module/notifyAppReadyAnalytics.transport.spec.js.map +0 -1
- package/lib/typescript/commonjs/notifyAppReadyAnalytics.d.ts +0 -13
- package/lib/typescript/commonjs/notifyAppReadyAnalytics.d.ts.map +0 -1
- package/lib/typescript/commonjs/notifyAppReadyAnalytics.test-utils.d.ts +0 -8
- package/lib/typescript/commonjs/notifyAppReadyAnalytics.test-utils.d.ts.map +0 -1
- package/lib/typescript/module/notifyAppReadyAnalytics.d.ts +0 -13
- package/lib/typescript/module/notifyAppReadyAnalytics.d.ts.map +0 -1
- package/lib/typescript/module/notifyAppReadyAnalytics.test-utils.d.ts +0 -8
- package/lib/typescript/module/notifyAppReadyAnalytics.test-utils.d.ts.map +0 -1
- package/src/notifyAppReadyAnalytics.ts +0 -194
|
@@ -21,6 +21,7 @@ data class BundleMetadata(
|
|
|
21
21
|
val pendingUpdateStrategy: String? = null,
|
|
22
22
|
val pendingTransition: PendingSelectionTransition? = null,
|
|
23
23
|
val verificationPending: Boolean = false,
|
|
24
|
+
val launchInProgress: Boolean = false,
|
|
24
25
|
val highestSeenCatalogs: Map<String, CatalogHighWater> = emptyMap(),
|
|
25
26
|
val currentSelectionContexts: Map<String, String> = emptyMap(),
|
|
26
27
|
val updatedAt: Long = System.currentTimeMillis(),
|
|
@@ -71,6 +72,7 @@ data class BundleMetadata(
|
|
|
71
72
|
pendingTransition =
|
|
72
73
|
json.optJSONObject("pendingTransition")?.let(PendingSelectionTransition::fromJson),
|
|
73
74
|
verificationPending = json.optBoolean("verificationPending", false),
|
|
75
|
+
launchInProgress = json.optBoolean("launchInProgress", false),
|
|
74
76
|
highestSeenCatalogs = highestSeenCatalogs,
|
|
75
77
|
currentSelectionContexts = currentSelectionContexts,
|
|
76
78
|
updatedAt = json.optLong("updatedAt", System.currentTimeMillis()),
|
|
@@ -121,6 +123,7 @@ data class BundleMetadata(
|
|
|
121
123
|
put("pendingUpdateStrategy", pendingUpdateStrategy ?: JSONObject.NULL)
|
|
122
124
|
put("pendingTransition", pendingTransition?.toJson() ?: JSONObject.NULL)
|
|
123
125
|
put("verificationPending", verificationPending)
|
|
126
|
+
put("launchInProgress", launchInProgress)
|
|
124
127
|
put(
|
|
125
128
|
"highestSeenCatalogs",
|
|
126
129
|
JSONObject().apply {
|
|
@@ -394,6 +397,13 @@ data class PendingCrashRecovery(
|
|
|
394
397
|
val shouldRollback: Boolean,
|
|
395
398
|
) {
|
|
396
399
|
companion object {
|
|
400
|
+
internal fun loadFromFile(file: File): PendingCrashRecovery? =
|
|
401
|
+
try {
|
|
402
|
+
fromJson(JSONObject(file.readText()))
|
|
403
|
+
} catch (_: Exception) {
|
|
404
|
+
null
|
|
405
|
+
}
|
|
406
|
+
|
|
397
407
|
fun fromJson(json: JSONObject): PendingCrashRecovery =
|
|
398
408
|
PendingCrashRecovery(
|
|
399
409
|
launchedBundleId =
|
|
@@ -17,12 +17,6 @@ class HotUpdaterException(
|
|
|
17
17
|
"Missing or empty 'bundleId'",
|
|
18
18
|
)
|
|
19
19
|
|
|
20
|
-
fun invalidFileUrl() =
|
|
21
|
-
HotUpdaterException(
|
|
22
|
-
"INVALID_FILE_URL",
|
|
23
|
-
"Invalid 'fileUrl' provided",
|
|
24
|
-
)
|
|
25
|
-
|
|
26
20
|
// Bundle storage errors
|
|
27
21
|
fun directoryCreationFailed() =
|
|
28
22
|
HotUpdaterException(
|
|
@@ -45,13 +39,6 @@ class HotUpdaterException(
|
|
|
45
39
|
"Download incomplete: received $actualSize bytes, expected $expectedSize bytes",
|
|
46
40
|
)
|
|
47
41
|
|
|
48
|
-
fun extractionFormatError(cause: Throwable? = null) =
|
|
49
|
-
HotUpdaterException(
|
|
50
|
-
"EXTRACTION_FORMAT_ERROR",
|
|
51
|
-
"The downloaded bundle file is not a valid compressed archive",
|
|
52
|
-
cause,
|
|
53
|
-
)
|
|
54
|
-
|
|
55
42
|
fun invalidBundle() =
|
|
56
43
|
HotUpdaterException(
|
|
57
44
|
"INVALID_BUNDLE",
|
|
@@ -83,14 +83,12 @@ class HotUpdaterImpl {
|
|
|
83
83
|
val fileSystem = FileManagerService(appContext)
|
|
84
84
|
val preferences = createPreferences(appContext)
|
|
85
85
|
val downloadService = OkHttpDownloadService()
|
|
86
|
-
val decompressService = DecompressService()
|
|
87
86
|
val isolationKey = getIsolationKey(appContext)
|
|
88
87
|
|
|
89
88
|
return BundleFileStorageService(
|
|
90
89
|
appContext,
|
|
91
90
|
fileSystem,
|
|
92
91
|
downloadService,
|
|
93
|
-
decompressService,
|
|
94
92
|
preferences,
|
|
95
93
|
isolationKey,
|
|
96
94
|
)
|
|
@@ -268,22 +266,22 @@ class HotUpdaterImpl {
|
|
|
268
266
|
fun getJSBundleFile(): String = prepareLaunchIfNeeded().bundleUrl
|
|
269
267
|
|
|
270
268
|
/**
|
|
271
|
-
* Updates the bundle from
|
|
269
|
+
* Updates the bundle from its manifest and complete asset descriptors.
|
|
272
270
|
* @param bundleId ID of the bundle to update
|
|
273
|
-
* @param
|
|
274
|
-
* @param
|
|
271
|
+
* @param manifestUrl URL of the target manifest
|
|
272
|
+
* @param manifestFileHash Hash or signature used to verify the target manifest
|
|
273
|
+
* @param assets Complete target asset descriptor map
|
|
275
274
|
* @param progressCallback Callback for download progress updates
|
|
276
275
|
* @throws HotUpdaterException if the update fails
|
|
277
276
|
*/
|
|
278
277
|
suspend fun updateBundle(
|
|
279
278
|
bundleId: String,
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
manifestFileHash: String?,
|
|
284
|
-
changedAssets: Map<String, ChangedAssetDescriptor>?,
|
|
279
|
+
manifestUrl: String,
|
|
280
|
+
manifestFileHash: String,
|
|
281
|
+
assets: Map<String, ChangedAssetDescriptor>,
|
|
285
282
|
channel: String?,
|
|
286
283
|
selection: PersistedSelection? = null,
|
|
284
|
+
archiveUrl: String? = null,
|
|
287
285
|
progressCallback: (UpdateProgressPayload) -> Unit,
|
|
288
286
|
) {
|
|
289
287
|
if (selection != null && !bundleStorage.stageReleaseSelection(selection)) {
|
|
@@ -291,11 +289,10 @@ class HotUpdaterImpl {
|
|
|
291
289
|
}
|
|
292
290
|
bundleStorage.updateBundle(
|
|
293
291
|
bundleId,
|
|
294
|
-
fileUrl,
|
|
295
|
-
fileHash,
|
|
296
292
|
manifestUrl,
|
|
297
293
|
manifestFileHash,
|
|
298
|
-
|
|
294
|
+
assets,
|
|
295
|
+
archiveUrl,
|
|
299
296
|
progressCallback,
|
|
300
297
|
)
|
|
301
298
|
|
|
@@ -467,7 +464,6 @@ class HotUpdaterImpl {
|
|
|
467
464
|
|
|
468
465
|
/**
|
|
469
466
|
* Gets the current launched bundle ID.
|
|
470
|
-
* Reads manifest.json first and falls back to the legacy BUNDLE_ID file.
|
|
471
467
|
* Built-in bundle fallback is handled in JS.
|
|
472
468
|
*/
|
|
473
469
|
fun getBundleId(): String? {
|
|
@@ -9,6 +9,7 @@ import android.os.Looper
|
|
|
9
9
|
import android.os.Process
|
|
10
10
|
import android.os.SystemClock
|
|
11
11
|
import android.util.Log
|
|
12
|
+
import androidx.core.util.AtomicFile
|
|
12
13
|
import com.facebook.react.ReactApplication
|
|
13
14
|
import com.facebook.react.bridge.JSExceptionHandler
|
|
14
15
|
import com.facebook.react.bridge.ReactContext
|
|
@@ -16,6 +17,7 @@ import com.facebook.react.bridge.ReactMarker
|
|
|
16
17
|
import com.facebook.react.bridge.ReactMarkerConstants
|
|
17
18
|
import org.json.JSONObject
|
|
18
19
|
import java.io.File
|
|
20
|
+
import java.io.FileOutputStream
|
|
19
21
|
import java.lang.reflect.Field
|
|
20
22
|
import kotlin.system.exitProcess
|
|
21
23
|
|
|
@@ -28,10 +30,7 @@ internal class HotUpdaterRecoveryManager(
|
|
|
28
30
|
private val crashMarkerFile = File(bundleStoreDir, CRASH_MARKER_FILENAME)
|
|
29
31
|
private val watchdogStateFile = File(bundleStoreDir, WATCHDOG_STATE_FILENAME)
|
|
30
32
|
|
|
31
|
-
private
|
|
32
|
-
private var shouldRollbackOnCrash = false
|
|
33
|
-
private var isMonitoring = false
|
|
34
|
-
private var recoveryRequested = false
|
|
33
|
+
private val launchState = HotUpdaterRecoveryState()
|
|
35
34
|
private var contentAppearedCallback: ((String?) -> Unit)? = null
|
|
36
35
|
private var recoveryRestartCallback: (() -> Boolean)? = null
|
|
37
36
|
|
|
@@ -39,10 +38,7 @@ internal class HotUpdaterRecoveryManager(
|
|
|
39
38
|
Runnable {
|
|
40
39
|
cancelRecoveryWatchdog()
|
|
41
40
|
Log.d(TAG, "Stopping crash monitoring for current launch")
|
|
42
|
-
|
|
43
|
-
recoveryRequested = false
|
|
44
|
-
shouldRollbackOnCrash = false
|
|
45
|
-
currentBundleId = null
|
|
41
|
+
launchState.stop()
|
|
46
42
|
contentAppearedCallback = null
|
|
47
43
|
recoveryRestartCallback = null
|
|
48
44
|
activeManager = null
|
|
@@ -52,7 +48,7 @@ internal class HotUpdaterRecoveryManager(
|
|
|
52
48
|
private val installJsExceptionHooksRunnable =
|
|
53
49
|
object : Runnable {
|
|
54
50
|
override fun run() {
|
|
55
|
-
if (
|
|
51
|
+
if (launchState.snapshot() == null) {
|
|
56
52
|
return
|
|
57
53
|
}
|
|
58
54
|
|
|
@@ -74,7 +70,7 @@ internal class HotUpdaterRecoveryManager(
|
|
|
74
70
|
}
|
|
75
71
|
|
|
76
72
|
return try {
|
|
77
|
-
val recovery = PendingCrashRecovery.
|
|
73
|
+
val recovery = PendingCrashRecovery.loadFromFile(crashMarkerFile) ?: return null
|
|
78
74
|
Log.d(
|
|
79
75
|
TAG,
|
|
80
76
|
"Consumed pending crash marker bundleId=${recovery.launchedBundleId} shouldRollback=${recovery.shouldRollback}",
|
|
@@ -96,11 +92,8 @@ internal class HotUpdaterRecoveryManager(
|
|
|
96
92
|
) {
|
|
97
93
|
crashMarkerFile.parentFile?.mkdirs()
|
|
98
94
|
contentAppearedCallback = onContentAppeared
|
|
99
|
-
currentBundleId = bundleId
|
|
100
|
-
shouldRollbackOnCrash = shouldRollback
|
|
101
|
-
isMonitoring = true
|
|
102
|
-
recoveryRequested = false
|
|
103
95
|
recoveryRestartCallback = onRecoveryRestartRequested
|
|
96
|
+
launchState.start(bundleId, shouldRollback)
|
|
104
97
|
activeManager = this
|
|
105
98
|
|
|
106
99
|
ensureExceptionHandlerInstalled()
|
|
@@ -122,17 +115,13 @@ internal class HotUpdaterRecoveryManager(
|
|
|
122
115
|
}
|
|
123
116
|
|
|
124
117
|
private fun handleContentAppeared() {
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
Log.d(TAG, "First content appeared for bundleId=$currentBundleId")
|
|
118
|
+
val launch = launchState.completeLaunch() ?: return
|
|
119
|
+
Log.d(TAG, "First content appeared for bundleId=${launch.bundleId}")
|
|
130
120
|
ReactMarker.removeListener(contentAppearedListener)
|
|
131
121
|
mainHandler.removeCallbacks(installJsExceptionHooksRunnable)
|
|
132
122
|
mainHandler.removeCallbacks(stopMonitoringRunnable)
|
|
133
|
-
contentAppearedCallback?.invoke(
|
|
134
|
-
|
|
135
|
-
updateNativeLaunchState(currentBundleId, false)
|
|
123
|
+
contentAppearedCallback?.invoke(launch.bundleId)
|
|
124
|
+
updateNativeLaunchState(launch.bundleId, false)
|
|
136
125
|
cancelRecoveryWatchdog()
|
|
137
126
|
mainHandler.postDelayed(stopMonitoringRunnable, MONITORING_GRACE_PERIOD_MS)
|
|
138
127
|
}
|
|
@@ -145,11 +134,10 @@ internal class HotUpdaterRecoveryManager(
|
|
|
145
134
|
previousExceptionHandler = Thread.getDefaultUncaughtExceptionHandler()
|
|
146
135
|
Thread.setDefaultUncaughtExceptionHandler { thread, throwable ->
|
|
147
136
|
val manager = activeManager
|
|
148
|
-
manager?.writeCrashMarker()
|
|
149
|
-
|
|
150
137
|
if (manager?.requestAutomaticRecovery() == true) {
|
|
151
|
-
|
|
152
|
-
|
|
138
|
+
// The separate restart activity kills this process after it starts.
|
|
139
|
+
// A second fatal error must not kill it while that request is in flight.
|
|
140
|
+
return@setDefaultUncaughtExceptionHandler
|
|
153
141
|
}
|
|
154
142
|
|
|
155
143
|
previousExceptionHandler?.uncaughtException(thread, throwable)
|
|
@@ -161,20 +149,21 @@ internal class HotUpdaterRecoveryManager(
|
|
|
161
149
|
exceptionHandlerInstalled = true
|
|
162
150
|
}
|
|
163
151
|
|
|
164
|
-
private fun writeCrashMarker() {
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
}
|
|
168
|
-
|
|
152
|
+
private fun writeCrashMarker(launch: HotUpdaterRecoveryState.Launch) {
|
|
153
|
+
val atomicFile = AtomicFile(crashMarkerFile)
|
|
154
|
+
var output: FileOutputStream? = null
|
|
169
155
|
try {
|
|
170
156
|
val payload =
|
|
171
157
|
JSONObject().apply {
|
|
172
|
-
put("bundleId",
|
|
173
|
-
put("shouldRollback",
|
|
158
|
+
put("bundleId", launch.bundleId ?: JSONObject.NULL)
|
|
159
|
+
put("shouldRollback", launch.shouldRollback)
|
|
174
160
|
}
|
|
175
161
|
crashMarkerFile.parentFile?.mkdirs()
|
|
176
|
-
|
|
162
|
+
output = atomicFile.startWrite()
|
|
163
|
+
output.write(payload.toString().toByteArray(Charsets.UTF_8))
|
|
164
|
+
atomicFile.finishWrite(output)
|
|
177
165
|
} catch (e: Exception) {
|
|
166
|
+
output?.let(atomicFile::failWrite)
|
|
178
167
|
Log.e(TAG, "Failed to write crash marker", e)
|
|
179
168
|
}
|
|
180
169
|
}
|
|
@@ -194,35 +183,28 @@ internal class HotUpdaterRecoveryManager(
|
|
|
194
183
|
cancelRecoveryWatchdogAlarm(appContext)
|
|
195
184
|
}
|
|
196
185
|
|
|
197
|
-
private fun requestAutomaticRecovery(): Boolean {
|
|
198
|
-
|
|
199
|
-
|
|
186
|
+
private fun requestAutomaticRecovery(exception: Exception? = null): Boolean {
|
|
187
|
+
val request = launchState.requestRecovery()
|
|
188
|
+
if (exception != null) {
|
|
189
|
+
Log.e(TAG, "Caught React startup exception for bundleId=${launchState.snapshot()?.bundleId}", exception)
|
|
200
190
|
}
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
return true
|
|
205
|
-
}
|
|
206
|
-
recoveryRequested = true
|
|
191
|
+
if (request == null) {
|
|
192
|
+
launchState.snapshot()?.let(::writeCrashMarker)
|
|
193
|
+
return false
|
|
207
194
|
}
|
|
208
|
-
|
|
195
|
+
if (!request.shouldRestart) return true
|
|
196
|
+
writeCrashMarker(request.launch)
|
|
209
197
|
val started = recoveryRestartCallback?.invoke() == true
|
|
198
|
+
launchState.finishRecoveryRequest(request, started)
|
|
210
199
|
if (!started) {
|
|
211
|
-
synchronized(this) {
|
|
212
|
-
recoveryRequested = false
|
|
213
|
-
}
|
|
214
200
|
Log.w(TAG, "Failed to schedule automatic recovery restart")
|
|
215
201
|
} else {
|
|
216
|
-
Log.i(TAG, "Scheduled automatic recovery restart for bundleId=$
|
|
202
|
+
Log.i(TAG, "Scheduled automatic recovery restart for bundleId=${request.launch.bundleId}")
|
|
217
203
|
}
|
|
218
204
|
return started
|
|
219
205
|
}
|
|
220
206
|
|
|
221
|
-
private fun handleJavaScriptException(exception: Exception): Boolean
|
|
222
|
-
Log.e(TAG, "Caught React startup exception for bundleId=$currentBundleId", exception)
|
|
223
|
-
writeCrashMarker()
|
|
224
|
-
return requestAutomaticRecovery()
|
|
225
|
-
}
|
|
207
|
+
private fun handleJavaScriptException(exception: Exception): Boolean = requestAutomaticRecovery(exception)
|
|
226
208
|
|
|
227
209
|
private fun ensureNativeSignalHandlerInstalled() {
|
|
228
210
|
if (signalHandlerInstalled || !loadNativeLibrary()) {
|
|
@@ -467,7 +449,8 @@ internal class HotUpdaterRecoveryManager(
|
|
|
467
449
|
}
|
|
468
450
|
|
|
469
451
|
val crashMarkerFile = File(getBundleStoreDir(appContext), CRASH_MARKER_FILENAME)
|
|
470
|
-
|
|
452
|
+
val recovery = PendingCrashRecovery.loadFromFile(crashMarkerFile)
|
|
453
|
+
if (recovery?.shouldRollback == true && recovery.launchedBundleId != null) {
|
|
471
454
|
Log.i(TAG, "Recovery watchdog detected crash marker, relaunching app")
|
|
472
455
|
watchdogStateFile.delete()
|
|
473
456
|
cancelRecoveryWatchdogAlarm(appContext)
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
package com.hotupdater
|
|
2
|
+
|
|
3
|
+
internal class HotUpdaterRecoveryState {
|
|
4
|
+
data class Launch(
|
|
5
|
+
val bundleId: String?,
|
|
6
|
+
val shouldRollback: Boolean,
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
data class RecoveryRequest(
|
|
10
|
+
val launch: Launch,
|
|
11
|
+
val shouldRestart: Boolean,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
private enum class RestartState { NONE, REQUESTED, SCHEDULED, FAILED }
|
|
15
|
+
|
|
16
|
+
private var launch: Launch? = null
|
|
17
|
+
private var restartState = RestartState.NONE
|
|
18
|
+
private var contentAppeared = false
|
|
19
|
+
|
|
20
|
+
@Synchronized
|
|
21
|
+
fun start(
|
|
22
|
+
bundleId: String?,
|
|
23
|
+
shouldRollback: Boolean,
|
|
24
|
+
) {
|
|
25
|
+
launch = Launch(bundleId, shouldRollback)
|
|
26
|
+
restartState = RestartState.NONE
|
|
27
|
+
contentAppeared = false
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
@Synchronized
|
|
31
|
+
fun snapshot(): Launch? = launch
|
|
32
|
+
|
|
33
|
+
@Synchronized
|
|
34
|
+
fun stop() {
|
|
35
|
+
launch = null
|
|
36
|
+
restartState = RestartState.NONE
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
@Synchronized
|
|
40
|
+
fun completeLaunch(): Launch? {
|
|
41
|
+
val current = launch ?: return null
|
|
42
|
+
if (contentAppeared || restartState != RestartState.NONE) return null
|
|
43
|
+
contentAppeared = true
|
|
44
|
+
launch = current.copy(shouldRollback = false)
|
|
45
|
+
return current
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
@Synchronized
|
|
49
|
+
fun requestRecovery(): RecoveryRequest? {
|
|
50
|
+
val current = launch?.takeIf { it.shouldRollback } ?: return null
|
|
51
|
+
val shouldRestart = restartState == RestartState.NONE || restartState == RestartState.FAILED
|
|
52
|
+
if (shouldRestart) restartState = RestartState.REQUESTED
|
|
53
|
+
return RecoveryRequest(current, shouldRestart)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
@Synchronized
|
|
57
|
+
fun finishRecoveryRequest(
|
|
58
|
+
request: RecoveryRequest,
|
|
59
|
+
started: Boolean,
|
|
60
|
+
) {
|
|
61
|
+
if (launch !== request.launch) return
|
|
62
|
+
restartState = if (started) RestartState.SCHEDULED else RestartState.FAILED
|
|
63
|
+
}
|
|
64
|
+
}
|
|
@@ -65,6 +65,13 @@ interface DownloadService {
|
|
|
65
65
|
fileSizeCallback: ((Long) -> Unit)? = null,
|
|
66
66
|
progressCallback: (DownloadProgress) -> Unit,
|
|
67
67
|
): DownloadResult
|
|
68
|
+
|
|
69
|
+
suspend fun downloadFileOnce(
|
|
70
|
+
fileUrl: URL,
|
|
71
|
+
destination: File,
|
|
72
|
+
fileSizeCallback: ((Long) -> Unit)? = null,
|
|
73
|
+
progressCallback: (DownloadProgress) -> Unit,
|
|
74
|
+
): DownloadResult = downloadFile(fileUrl, destination, fileSizeCallback, progressCallback)
|
|
68
75
|
}
|
|
69
76
|
|
|
70
77
|
/**
|
|
@@ -185,6 +192,18 @@ class OkHttpDownloadService : DownloadService {
|
|
|
185
192
|
DownloadResult.Error(lastException ?: Exception("Download failed after $MAX_RETRIES attempts"))
|
|
186
193
|
}
|
|
187
194
|
|
|
195
|
+
override suspend fun downloadFileOnce(
|
|
196
|
+
fileUrl: URL,
|
|
197
|
+
destination: File,
|
|
198
|
+
fileSizeCallback: ((Long) -> Unit)?,
|
|
199
|
+
progressCallback: (DownloadProgress) -> Unit,
|
|
200
|
+
): DownloadResult =
|
|
201
|
+
try {
|
|
202
|
+
attemptDownload(fileUrl, destination, fileSizeCallback, progressCallback)
|
|
203
|
+
} catch (error: Exception) {
|
|
204
|
+
DownloadResult.Error(error)
|
|
205
|
+
}
|
|
206
|
+
|
|
188
207
|
private suspend fun attemptDownload(
|
|
189
208
|
fileUrl: URL,
|
|
190
209
|
destination: File,
|