@hot-updater/react-native 1.0.0-rc.2 → 1.0.0-rc.21
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 +954 -837
- package/android/src/main/java/com/hotupdater/BundleMetadata.kt +15 -11
- package/android/src/main/java/com/hotupdater/FileManagerService.kt +45 -0
- package/android/src/main/java/com/hotupdater/HotUpdaterException.kt +287 -22
- package/android/src/main/java/com/hotupdater/HotUpdaterImpl.kt +43 -31
- 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/InstallIdentityService.kt +37 -0
- package/android/src/main/java/com/hotupdater/KeyValueStorageService.kt +86 -0
- package/android/src/main/java/com/hotupdater/NetworkState.kt +31 -0
- package/android/src/main/java/com/hotupdater/NoBackupStorage.kt +17 -0
- package/android/src/main/java/com/hotupdater/OkHttpDownloadService.kt +127 -50
- package/android/src/main/java/com/hotupdater/PreviousProcessExit.kt +89 -0
- package/android/src/main/java/com/hotupdater/ReactNativeValueConverters.kt +34 -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 +109 -84
- package/android/src/oldarch/HotUpdater.kt +7 -9
- package/android/src/oldarch/HotUpdaterModule.kt +98 -74
- package/android/src/oldarch/HotUpdaterSpec.kt +4 -6
- package/ios/HotUpdater/Internal/BrotliFileDecompressor.swift +167 -0
- package/ios/HotUpdater/Internal/BuiltInAssetResolver.swift +196 -0
- package/ios/HotUpdater/Internal/BundleFileStorageService.swift +950 -1112
- package/ios/HotUpdater/Internal/BundleMetadata.swift +8 -53
- package/ios/HotUpdater/Internal/FileManagerService.swift +48 -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 +13 -45
- package/ios/HotUpdater/Internal/HotUpdaterImpl.swift +190 -73
- package/ios/HotUpdater/Internal/KeyValueStorageService.swift +71 -0
- package/ios/HotUpdater/Internal/NotificationExtension.swift +0 -2
- package/ios/HotUpdater/Internal/TarArchiveExtractor.swift +182 -311
- package/ios/HotUpdater/Internal/URLSessionDownloadService.swift +119 -71
- package/ios/HotUpdater/Internal/UpdateFailure.swift +390 -0
- 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/appReady.js +111 -0
- package/lib/commonjs/appReady.js.map +1 -0
- package/lib/commonjs/checkForUpdate.js +125 -70
- package/lib/commonjs/checkForUpdate.js.map +1 -1
- package/lib/commonjs/clientPlugin.js +13 -0
- package/lib/commonjs/clientPlugin.js.map +1 -0
- package/lib/commonjs/error.js +4 -13
- package/lib/commonjs/error.js.map +1 -1
- package/lib/commonjs/fetchJSON.js +5 -2
- package/lib/commonjs/fetchJSON.js.map +1 -1
- package/lib/commonjs/httpClient.js +55 -92
- package/lib/commonjs/httpClient.js.map +1 -1
- package/lib/commonjs/index.js +68 -20
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/native.js +82 -62
- package/lib/commonjs/native.js.map +1 -1
- package/lib/commonjs/pluginHost.js +44 -0
- package/lib/commonjs/pluginHost.js.map +1 -0
- package/lib/commonjs/releaseCatalogCache.js +20 -61
- package/lib/commonjs/releaseCatalogCache.js.map +1 -1
- 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/updateError.js +132 -0
- package/lib/commonjs/updateError.js.map +1 -0
- package/lib/commonjs/wrap.js +44 -27
- package/lib/commonjs/wrap.js.map +1 -1
- package/lib/module/appReady.js +103 -0
- package/lib/module/appReady.js.map +1 -0
- package/lib/module/checkForUpdate.js +118 -65
- package/lib/module/checkForUpdate.js.map +1 -1
- package/lib/module/clientPlugin.js +4 -0
- package/lib/module/clientPlugin.js.map +1 -0
- package/lib/module/error.js +4 -13
- package/lib/module/error.js.map +1 -1
- package/lib/module/fetchJSON.js +3 -1
- package/lib/module/fetchJSON.js.map +1 -1
- package/lib/module/httpClient.js +53 -90
- package/lib/module/httpClient.js.map +1 -1
- package/lib/module/index.js +55 -22
- package/lib/module/index.js.map +1 -1
- package/lib/module/native.js +74 -54
- package/lib/module/native.js.map +1 -1
- package/lib/module/pluginHost.js +35 -0
- package/lib/module/pluginHost.js.map +1 -0
- package/lib/module/releaseCatalogCache.js +16 -56
- package/lib/module/releaseCatalogCache.js.map +1 -1
- 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/updateError.js +126 -0
- package/lib/module/updateError.js.map +1 -0
- package/lib/module/wrap.js +45 -28
- package/lib/module/wrap.js.map +1 -1
- package/lib/typescript/commonjs/appReady.d.ts +23 -0
- package/lib/typescript/commonjs/appReady.d.ts.map +1 -0
- package/lib/typescript/commonjs/checkForUpdate.d.ts +16 -4
- package/lib/typescript/commonjs/checkForUpdate.d.ts.map +1 -1
- package/lib/typescript/commonjs/clientPlugin.d.ts +2 -0
- package/lib/typescript/commonjs/clientPlugin.d.ts.map +1 -0
- 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 +3 -0
- package/lib/typescript/commonjs/fetchJSON.d.ts.map +1 -1
- package/lib/typescript/commonjs/httpClient.d.ts +3 -32
- package/lib/typescript/commonjs/httpClient.d.ts.map +1 -1
- package/lib/typescript/commonjs/index.d.ts +7 -12
- package/lib/typescript/commonjs/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/native.d.ts +33 -23
- package/lib/typescript/commonjs/native.d.ts.map +1 -1
- package/lib/typescript/commonjs/pluginHost.d.ts +4 -0
- package/lib/typescript/commonjs/pluginHost.d.ts.map +1 -0
- package/lib/typescript/commonjs/releaseCatalogCache.d.ts +6 -14
- package/lib/typescript/commonjs/releaseCatalogCache.d.ts.map +1 -1
- 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 +42 -47
- 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/types.d.ts +1 -1
- package/lib/typescript/commonjs/types.d.ts.map +1 -1
- package/lib/typescript/commonjs/updateError.d.ts +26 -0
- package/lib/typescript/commonjs/updateError.d.ts.map +1 -0
- package/lib/typescript/commonjs/wrap.d.ts.map +1 -1
- package/lib/typescript/commonjs/wrap.types.d.ts +6 -7
- package/lib/typescript/commonjs/wrap.types.d.ts.map +1 -1
- package/lib/typescript/module/appReady.d.ts +23 -0
- package/lib/typescript/module/appReady.d.ts.map +1 -0
- package/lib/typescript/module/checkForUpdate.d.ts +16 -4
- package/lib/typescript/module/checkForUpdate.d.ts.map +1 -1
- package/lib/typescript/module/clientPlugin.d.ts +2 -0
- package/lib/typescript/module/clientPlugin.d.ts.map +1 -0
- 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 +3 -0
- package/lib/typescript/module/fetchJSON.d.ts.map +1 -1
- package/lib/typescript/module/httpClient.d.ts +3 -32
- package/lib/typescript/module/httpClient.d.ts.map +1 -1
- package/lib/typescript/module/index.d.ts +7 -12
- package/lib/typescript/module/index.d.ts.map +1 -1
- package/lib/typescript/module/native.d.ts +33 -23
- package/lib/typescript/module/native.d.ts.map +1 -1
- package/lib/typescript/module/pluginHost.d.ts +4 -0
- package/lib/typescript/module/pluginHost.d.ts.map +1 -0
- package/lib/typescript/module/releaseCatalogCache.d.ts +6 -14
- package/lib/typescript/module/releaseCatalogCache.d.ts.map +1 -1
- 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 +42 -47
- 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/types.d.ts +1 -1
- package/lib/typescript/module/types.d.ts.map +1 -1
- package/lib/typescript/module/updateError.d.ts +26 -0
- package/lib/typescript/module/updateError.d.ts.map +1 -0
- package/lib/typescript/module/wrap.d.ts.map +1 -1
- package/lib/typescript/module/wrap.types.d.ts +6 -7
- package/lib/typescript/module/wrap.types.d.ts.map +1 -1
- package/package.json +7 -4
- package/src/appReady.ts +149 -0
- package/src/checkForUpdate.ts +152 -89
- package/src/clientPlugin.ts +17 -0
- package/src/error.ts +4 -14
- package/src/fetchJSON.ts +5 -1
- package/src/httpClient.ts +78 -157
- package/src/index.ts +87 -29
- package/src/native.ts +109 -104
- package/src/pluginHost.ts +45 -0
- package/src/releaseCatalogCache.ts +26 -140
- package/src/sdkVersion.ts +1 -1
- package/src/specs/NativeHotUpdater.ts +41 -48
- package/src/store.ts +2 -23
- package/src/types.ts +1 -1
- package/src/updateError.ts +153 -0
- package/src/wrap.tsx +59 -28
- package/src/wrap.types.ts +6 -7
- 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/BsdiffPatchTest.kt +0 -46
- package/android/src/test/java/com/hotupdater/BundleFileStorageServiceTest.kt +0 -799
- package/android/src/test/java/com/hotupdater/HotUpdaterImplTest.kt +0 -273
- package/android/src/test/java/com/hotupdater/NativeConfigUtilsTest.kt +0 -20
- package/android/src/test/java/com/hotupdater/OkHttpDownloadServiceTest.kt +0 -111
- package/android/src/test/java/com/hotupdater/RelativePathResolverTest.kt +0 -41
- package/android/src/test/java/com/hotupdater/ReleaseCatalogCacheServiceTest.kt +0 -118
- 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/catalogCacheNative.spec.js +0 -31
- package/lib/commonjs/catalogCacheNative.spec.js.map +0 -1
- package/lib/commonjs/checkForUpdate.releaseCatalog.spec.js +0 -604
- package/lib/commonjs/checkForUpdate.releaseCatalog.spec.js.map +0 -1
- package/lib/commonjs/httpClient.spec.js +0 -102
- package/lib/commonjs/httpClient.spec.js.map +0 -1
- package/lib/commonjs/index.spec.js +0 -298
- package/lib/commonjs/index.spec.js.map +0 -1
- package/lib/commonjs/iosInterop.spec.js +0 -18
- package/lib/commonjs/iosInterop.spec.js.map +0 -1
- package/lib/commonjs/native.spec.js +0 -795
- package/lib/commonjs/native.spec.js.map +0 -1
- package/lib/commonjs/notifyAppReadyInsights.boundary.spec.js +0 -218
- package/lib/commonjs/notifyAppReadyInsights.boundary.spec.js.map +0 -1
- package/lib/commonjs/notifyAppReadyInsights.js +0 -133
- package/lib/commonjs/notifyAppReadyInsights.js.map +0 -1
- package/lib/commonjs/notifyAppReadyInsights.spec.js +0 -246
- package/lib/commonjs/notifyAppReadyInsights.spec.js.map +0 -1
- package/lib/commonjs/notifyAppReadyInsights.test-utils.js +0 -24
- package/lib/commonjs/notifyAppReadyInsights.test-utils.js.map +0 -1
- package/lib/commonjs/notifyAppReadyInsights.transport.spec.js +0 -117
- package/lib/commonjs/notifyAppReadyInsights.transport.spec.js.map +0 -1
- package/lib/commonjs/releaseCatalogCache.spec.js +0 -195
- package/lib/commonjs/releaseCatalogCache.spec.js.map +0 -1
- package/lib/commonjs/releaseCatalogCacheNativeContract.spec.js +0 -64
- package/lib/commonjs/releaseCatalogCacheNativeContract.spec.js.map +0 -1
- package/lib/commonjs/store.notification.spec.js +0 -76
- package/lib/commonjs/store.notification.spec.js.map +0 -1
- package/lib/commonjs/store.spec.js +0 -316
- package/lib/commonjs/store.spec.js.map +0 -1
- package/lib/commonjs/wrap.spec.js +0 -296
- package/lib/commonjs/wrap.spec.js.map +0 -1
- package/lib/module/catalogCacheNative.spec.js +0 -31
- package/lib/module/catalogCacheNative.spec.js.map +0 -1
- package/lib/module/checkForUpdate.releaseCatalog.spec.js +0 -603
- package/lib/module/checkForUpdate.releaseCatalog.spec.js.map +0 -1
- package/lib/module/httpClient.spec.js +0 -102
- package/lib/module/httpClient.spec.js.map +0 -1
- package/lib/module/index.spec.js +0 -297
- package/lib/module/index.spec.js.map +0 -1
- package/lib/module/iosInterop.spec.js +0 -18
- package/lib/module/iosInterop.spec.js.map +0 -1
- package/lib/module/native.spec.js +0 -794
- package/lib/module/native.spec.js.map +0 -1
- package/lib/module/notifyAppReadyInsights.boundary.spec.js +0 -217
- package/lib/module/notifyAppReadyInsights.boundary.spec.js.map +0 -1
- package/lib/module/notifyAppReadyInsights.js +0 -128
- package/lib/module/notifyAppReadyInsights.js.map +0 -1
- package/lib/module/notifyAppReadyInsights.spec.js +0 -245
- package/lib/module/notifyAppReadyInsights.spec.js.map +0 -1
- package/lib/module/notifyAppReadyInsights.test-utils.js +0 -18
- package/lib/module/notifyAppReadyInsights.test-utils.js.map +0 -1
- package/lib/module/notifyAppReadyInsights.transport.spec.js +0 -116
- package/lib/module/notifyAppReadyInsights.transport.spec.js.map +0 -1
- package/lib/module/releaseCatalogCache.spec.js +0 -195
- package/lib/module/releaseCatalogCache.spec.js.map +0 -1
- package/lib/module/releaseCatalogCacheNativeContract.spec.js +0 -64
- package/lib/module/releaseCatalogCacheNativeContract.spec.js.map +0 -1
- package/lib/module/store.notification.spec.js +0 -77
- package/lib/module/store.notification.spec.js.map +0 -1
- package/lib/module/store.spec.js +0 -315
- package/lib/module/store.spec.js.map +0 -1
- package/lib/module/wrap.spec.js +0 -295
- package/lib/module/wrap.spec.js.map +0 -1
- package/lib/typescript/commonjs/notifyAppReadyInsights.d.ts +0 -13
- package/lib/typescript/commonjs/notifyAppReadyInsights.d.ts.map +0 -1
- package/lib/typescript/commonjs/notifyAppReadyInsights.test-utils.d.ts +0 -8
- package/lib/typescript/commonjs/notifyAppReadyInsights.test-utils.d.ts.map +0 -1
- package/lib/typescript/module/notifyAppReadyInsights.d.ts +0 -13
- package/lib/typescript/module/notifyAppReadyInsights.d.ts.map +0 -1
- package/lib/typescript/module/notifyAppReadyInsights.test-utils.d.ts +0 -8
- package/lib/typescript/module/notifyAppReadyInsights.test-utils.d.ts.map +0 -1
- package/src/catalogCacheNative.spec.ts +0 -63
- package/src/checkForUpdate.releaseCatalog.spec.ts +0 -596
- package/src/httpClient.spec.ts +0 -132
- package/src/index.spec.ts +0 -331
- package/src/iosInterop.spec.ts +0 -30
- package/src/native.spec.ts +0 -855
- package/src/notifyAppReadyInsights.boundary.spec.ts +0 -234
- package/src/notifyAppReadyInsights.spec.ts +0 -243
- package/src/notifyAppReadyInsights.test-utils.ts +0 -31
- package/src/notifyAppReadyInsights.transport.spec.ts +0 -130
- package/src/notifyAppReadyInsights.ts +0 -194
- package/src/releaseCatalogCache.spec.ts +0 -282
- package/src/releaseCatalogCacheNativeContract.spec.ts +0 -80
- package/src/store.notification.spec.ts +0 -92
- package/src/store.spec.ts +0 -372
- package/src/wrap.spec.ts +0 -367
|
@@ -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 =
|
|
@@ -469,21 +479,17 @@ data class LaunchReport(
|
|
|
469
479
|
}
|
|
470
480
|
}
|
|
471
481
|
|
|
482
|
+
/**
|
|
483
|
+
* The persisted install id. See [InstallIdentityService].
|
|
484
|
+
*/
|
|
472
485
|
data class InstallationIdentity(
|
|
473
486
|
val installId: String,
|
|
474
|
-
val userId: String? = null,
|
|
475
|
-
val username: String? = null,
|
|
476
487
|
) {
|
|
477
488
|
companion object {
|
|
478
489
|
private const val TAG = "InstallationIdentity"
|
|
479
|
-
const val IDENTITY_FILENAME = "identity.json"
|
|
490
|
+
const val IDENTITY_FILENAME = "install-identity.json"
|
|
480
491
|
|
|
481
|
-
fun fromJson(json: JSONObject): InstallationIdentity =
|
|
482
|
-
InstallationIdentity(
|
|
483
|
-
installId = json.getString("installId"),
|
|
484
|
-
userId = json.optNullableString("userId"),
|
|
485
|
-
username = json.optNullableString("username"),
|
|
486
|
-
)
|
|
492
|
+
fun fromJson(json: JSONObject): InstallationIdentity = InstallationIdentity(installId = json.getString("installId"))
|
|
487
493
|
|
|
488
494
|
fun loadFromFile(file: File): InstallationIdentity? =
|
|
489
495
|
try {
|
|
@@ -501,8 +507,6 @@ data class InstallationIdentity(
|
|
|
501
507
|
fun toJson(): JSONObject =
|
|
502
508
|
JSONObject().apply {
|
|
503
509
|
put("installId", installId)
|
|
504
|
-
put("userId", userId ?: JSONObject.NULL)
|
|
505
|
-
put("username", username ?: JSONObject.NULL)
|
|
506
510
|
}
|
|
507
511
|
|
|
508
512
|
fun saveToFile(file: File): Boolean =
|
|
@@ -2,6 +2,51 @@ package com.hotupdater
|
|
|
2
2
|
|
|
3
3
|
import android.content.Context
|
|
4
4
|
import java.io.File
|
|
5
|
+
import java.io.FileOutputStream
|
|
6
|
+
import java.io.IOException
|
|
7
|
+
import java.io.OutputStream
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* An I/O failure writing to local storage, as opposed to a failure reading or
|
|
11
|
+
* decoding the data being written.
|
|
12
|
+
*/
|
|
13
|
+
class LocalStorageException(
|
|
14
|
+
cause: IOException,
|
|
15
|
+
) : IOException(cause.message, cause)
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* An output stream to a local file whose I/O failures, including opening the
|
|
19
|
+
* file, surface as [LocalStorageException].
|
|
20
|
+
*/
|
|
21
|
+
internal class LocalStorageOutputStream private constructor(
|
|
22
|
+
private val output: OutputStream,
|
|
23
|
+
) : OutputStream() {
|
|
24
|
+
companion object {
|
|
25
|
+
fun open(file: File): LocalStorageOutputStream = LocalStorageOutputStream(storageOperation { FileOutputStream(file) })
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
override fun write(byte: Int) = storageOperation { output.write(byte) }
|
|
29
|
+
|
|
30
|
+
override fun write(
|
|
31
|
+
buffer: ByteArray,
|
|
32
|
+
offset: Int,
|
|
33
|
+
length: Int,
|
|
34
|
+
) = storageOperation { output.write(buffer, offset, length) }
|
|
35
|
+
|
|
36
|
+
override fun flush() = storageOperation { output.flush() }
|
|
37
|
+
|
|
38
|
+
override fun close() = storageOperation { output.close() }
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** Runs a local file operation, reporting its I/O failure as [LocalStorageException]. */
|
|
42
|
+
internal inline fun <T> storageOperation(block: () -> T): T =
|
|
43
|
+
try {
|
|
44
|
+
block()
|
|
45
|
+
} catch (error: LocalStorageException) {
|
|
46
|
+
throw error
|
|
47
|
+
} catch (error: IOException) {
|
|
48
|
+
throw LocalStorageException(error)
|
|
49
|
+
}
|
|
5
50
|
|
|
6
51
|
/**
|
|
7
52
|
* Interface for file system operations
|
|
@@ -1,41 +1,275 @@
|
|
|
1
1
|
package com.hotupdater
|
|
2
2
|
|
|
3
|
+
import java.io.IOException
|
|
4
|
+
import java.io.InterruptedIOException
|
|
5
|
+
import java.net.MalformedURLException
|
|
6
|
+
import java.net.SocketException
|
|
7
|
+
import java.net.UnknownHostException
|
|
8
|
+
import javax.net.ssl.SSLException
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Where an update failed: [DOWNLOAD] covers transferring and verifying the
|
|
12
|
+
* update's files, [INSTALL] producing the bundle on disk.
|
|
13
|
+
* Values match `UpdateErrorStage` in packages/react-native/src/clientPlugin.ts.
|
|
14
|
+
*/
|
|
15
|
+
enum class UpdateFailureStage(
|
|
16
|
+
val value: String,
|
|
17
|
+
) {
|
|
18
|
+
DOWNLOAD("download"),
|
|
19
|
+
INSTALL("install"),
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Why an update failed.
|
|
24
|
+
* Values match `UpdateErrorReason` in packages/react-native/src/clientPlugin.ts.
|
|
25
|
+
*/
|
|
26
|
+
enum class UpdateFailureReason(
|
|
27
|
+
val value: String,
|
|
28
|
+
) {
|
|
29
|
+
NETWORK("network"),
|
|
30
|
+
HTTP("http"),
|
|
31
|
+
INVALID_RESPONSE("invalid_response"),
|
|
32
|
+
HASH_MISMATCH("hash_mismatch"),
|
|
33
|
+
SIGNATURE("signature"),
|
|
34
|
+
PATCH("patch"),
|
|
35
|
+
EXTRACT("extract"),
|
|
36
|
+
STORAGE("storage"),
|
|
37
|
+
UNKNOWN("unknown"),
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** What was being fetched or applied when an update failed. */
|
|
41
|
+
enum class UpdateFailureResource(
|
|
42
|
+
val value: String,
|
|
43
|
+
) {
|
|
44
|
+
MANIFEST("manifest"),
|
|
45
|
+
FILE("file"),
|
|
46
|
+
PATCH("patch"),
|
|
47
|
+
ARCHIVE("archive"),
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** Why no HTTP response arrived, for a [UpdateFailureReason.NETWORK] failure. */
|
|
51
|
+
enum class UpdateFailureTransport(
|
|
52
|
+
val value: String,
|
|
53
|
+
) {
|
|
54
|
+
TIMEOUT("timeout"),
|
|
55
|
+
DNS("dns"),
|
|
56
|
+
TLS("tls"),
|
|
57
|
+
CONNECTION("connection"),
|
|
58
|
+
OFFLINE("offline"),
|
|
59
|
+
CANCELLED("cancelled"),
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* The classification an `updateBundle` rejection carries as `userInfo`.
|
|
64
|
+
* [httpStatus] and [originCode] are reported only for
|
|
65
|
+
* [UpdateFailureReason.HTTP], and [transport] only for
|
|
66
|
+
* [UpdateFailureReason.NETWORK].
|
|
67
|
+
*/
|
|
68
|
+
data class UpdateFailure(
|
|
69
|
+
val stage: UpdateFailureStage,
|
|
70
|
+
val reason: UpdateFailureReason,
|
|
71
|
+
val httpStatus: Int? = null,
|
|
72
|
+
val resource: UpdateFailureResource? = null,
|
|
73
|
+
val transport: UpdateFailureTransport? = null,
|
|
74
|
+
/** The storage origin's XML error `<Code>`, such as `AccessDenied`. */
|
|
75
|
+
val originCode: String? = null,
|
|
76
|
+
) {
|
|
77
|
+
/**
|
|
78
|
+
* The rejection's `userInfo`:
|
|
79
|
+
* `{ stage, reason, resource?, httpStatus?, transport?, originCode? }`.
|
|
80
|
+
*/
|
|
81
|
+
fun toUserInfo(): Map<String, Any> =
|
|
82
|
+
buildMap {
|
|
83
|
+
put("stage", stage.value)
|
|
84
|
+
put("reason", reason.value)
|
|
85
|
+
resource?.let { put("resource", it.value) }
|
|
86
|
+
if (reason == UpdateFailureReason.HTTP) {
|
|
87
|
+
httpStatus?.let { put("httpStatus", it) }
|
|
88
|
+
originCode?.let { put("originCode", it) }
|
|
89
|
+
}
|
|
90
|
+
if (reason == UpdateFailureReason.NETWORK) {
|
|
91
|
+
transport?.let { put("transport", it.value) }
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
companion object {
|
|
96
|
+
fun download(reason: UpdateFailureReason) = UpdateFailure(UpdateFailureStage.DOWNLOAD, reason)
|
|
97
|
+
|
|
98
|
+
fun install(reason: UpdateFailureReason) = UpdateFailure(UpdateFailureStage.INSTALL, reason)
|
|
99
|
+
|
|
100
|
+
fun http(
|
|
101
|
+
status: Int,
|
|
102
|
+
originCode: String? = null,
|
|
103
|
+
) = UpdateFailure(UpdateFailureStage.DOWNLOAD, UpdateFailureReason.HTTP, status, originCode = originCode)
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Classifies the error a download returned. OkHttp rejects a URL it
|
|
107
|
+
* cannot request with IllegalArgumentException, an invalid response.
|
|
108
|
+
* [isOffline] is asked only after a failed host lookup.
|
|
109
|
+
*/
|
|
110
|
+
fun ofDownloadError(
|
|
111
|
+
error: Throwable?,
|
|
112
|
+
isOffline: () -> Boolean = { false },
|
|
113
|
+
): UpdateFailure =
|
|
114
|
+
when (error) {
|
|
115
|
+
is HttpStatusException -> {
|
|
116
|
+
http(error.statusCode, error.originCode)
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
is LocalStorageException -> {
|
|
120
|
+
install(UpdateFailureReason.STORAGE)
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
is MalformedURLException -> {
|
|
124
|
+
download(UpdateFailureReason.INVALID_RESPONSE)
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// The body ended early; the connection itself did not fail.
|
|
128
|
+
is IncompleteDownloadException -> {
|
|
129
|
+
download(UpdateFailureReason.NETWORK)
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
is IOException -> {
|
|
133
|
+
UpdateFailure(
|
|
134
|
+
UpdateFailureStage.DOWNLOAD,
|
|
135
|
+
UpdateFailureReason.NETWORK,
|
|
136
|
+
transport = transportOf(error, isOffline),
|
|
137
|
+
)
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
is IllegalArgumentException -> {
|
|
141
|
+
download(UpdateFailureReason.INVALID_RESPONSE)
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
else -> {
|
|
145
|
+
download(UpdateFailureReason.UNKNOWN)
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Why a request got no response, or null when the error does not say.
|
|
151
|
+
* An [InterruptedIOException] is a socket or OkHttp call timeout, a
|
|
152
|
+
* [SocketException] a refused, unreachable, or reset connection, and
|
|
153
|
+
* OkHttp reports a canceled call as an IOException "Canceled".
|
|
154
|
+
*/
|
|
155
|
+
private fun transportOf(
|
|
156
|
+
error: IOException,
|
|
157
|
+
isOffline: () -> Boolean,
|
|
158
|
+
): UpdateFailureTransport? =
|
|
159
|
+
when {
|
|
160
|
+
error is SSLException -> UpdateFailureTransport.TLS
|
|
161
|
+
error is InterruptedIOException -> UpdateFailureTransport.TIMEOUT
|
|
162
|
+
error is UnknownHostException -> if (isOffline()) UpdateFailureTransport.OFFLINE else UpdateFailureTransport.DNS
|
|
163
|
+
error is SocketException -> UpdateFailureTransport.CONNECTION
|
|
164
|
+
error.message == "Canceled" -> UpdateFailureTransport.CANCELLED
|
|
165
|
+
else -> null
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/** Classifies a failed hash or signature check of a downloaded file. */
|
|
169
|
+
fun ofVerificationError(error: Throwable?): UpdateFailure =
|
|
170
|
+
when (error) {
|
|
171
|
+
is SignatureVerificationException.FileHashMismatch -> download(UpdateFailureReason.HASH_MISMATCH)
|
|
172
|
+
is SignatureVerificationException.FileReadFailed -> install(UpdateFailureReason.STORAGE)
|
|
173
|
+
else -> download(UpdateFailureReason.SIGNATURE)
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Classifies an error the pipeline did not anticipate: a malformed URL
|
|
178
|
+
* is an invalid response, any other I/O error a local storage failure,
|
|
179
|
+
* and anything else unknown in [stage].
|
|
180
|
+
*/
|
|
181
|
+
fun ofUnexpectedError(
|
|
182
|
+
error: Throwable,
|
|
183
|
+
stage: UpdateFailureStage,
|
|
184
|
+
): UpdateFailure =
|
|
185
|
+
when (error) {
|
|
186
|
+
is MalformedURLException -> download(UpdateFailureReason.INVALID_RESPONSE)
|
|
187
|
+
is IOException -> install(UpdateFailureReason.STORAGE)
|
|
188
|
+
else -> UpdateFailure(stage, UpdateFailureReason.UNKNOWN)
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* The Release catalog selection changed while its bundle was being installed.
|
|
195
|
+
* The update was superseded, so this rejection is not an update failure.
|
|
196
|
+
*/
|
|
197
|
+
class StaleReleaseSelectionException : IllegalStateException("Release catalog selection is stale")
|
|
198
|
+
|
|
3
199
|
/**
|
|
4
200
|
* Exception class for Hot Updater errors
|
|
5
201
|
* Matches error codes defined in packages/react-native/src/errors.ts
|
|
202
|
+
*
|
|
203
|
+
* [failure] classifies an update failure for JS. It is null for a rejection
|
|
204
|
+
* that is not an update failure, such as a bundle in the crashed history.
|
|
6
205
|
*/
|
|
7
206
|
class HotUpdaterException(
|
|
8
207
|
val code: String,
|
|
9
208
|
message: String,
|
|
10
209
|
cause: Throwable? = null,
|
|
210
|
+
val failure: UpdateFailure? = null,
|
|
11
211
|
) : Exception(message, cause) {
|
|
212
|
+
/**
|
|
213
|
+
* This failure with [resource] as what was being fetched or applied,
|
|
214
|
+
* unless it already names one or is not an update failure.
|
|
215
|
+
*/
|
|
216
|
+
fun withResource(resource: UpdateFailureResource?): HotUpdaterException {
|
|
217
|
+
val failure = failure
|
|
218
|
+
if (resource == null || failure == null || failure.resource != null) {
|
|
219
|
+
return this
|
|
220
|
+
}
|
|
221
|
+
return HotUpdaterException(code, message.orEmpty(), cause, failure.copy(resource = resource)).also {
|
|
222
|
+
it.stackTrace = stackTrace
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
12
226
|
companion object {
|
|
227
|
+
private val invalidResponse = UpdateFailure.download(UpdateFailureReason.INVALID_RESPONSE)
|
|
228
|
+
private val signatureFailure = UpdateFailure.download(UpdateFailureReason.SIGNATURE)
|
|
229
|
+
private val storageFailure = UpdateFailure.install(UpdateFailureReason.STORAGE)
|
|
230
|
+
|
|
13
231
|
// Parameter validation errors
|
|
14
232
|
fun missingBundleId() =
|
|
15
233
|
HotUpdaterException(
|
|
16
234
|
"MISSING_BUNDLE_ID",
|
|
17
235
|
"Missing or empty 'bundleId'",
|
|
236
|
+
failure = invalidResponse,
|
|
18
237
|
)
|
|
19
238
|
|
|
20
|
-
fun
|
|
239
|
+
fun invalidManifestParams() =
|
|
21
240
|
HotUpdaterException(
|
|
22
|
-
"
|
|
23
|
-
"
|
|
241
|
+
"INVALID_MANIFEST",
|
|
242
|
+
"Manifest URL, hash, and assets are required",
|
|
243
|
+
failure = invalidResponse.copy(resource = UpdateFailureResource.MANIFEST),
|
|
24
244
|
)
|
|
25
245
|
|
|
246
|
+
fun invalidFileUrl(
|
|
247
|
+
parameter: String,
|
|
248
|
+
url: String,
|
|
249
|
+
resource: UpdateFailureResource,
|
|
250
|
+
) = HotUpdaterException(
|
|
251
|
+
"INVALID_FILE_URL",
|
|
252
|
+
"Invalid '$parameter' provided: $url",
|
|
253
|
+
failure = invalidResponse.copy(resource = resource),
|
|
254
|
+
)
|
|
255
|
+
|
|
26
256
|
// Bundle storage errors
|
|
27
257
|
fun directoryCreationFailed() =
|
|
28
258
|
HotUpdaterException(
|
|
29
259
|
"DIRECTORY_CREATION_FAILED",
|
|
30
260
|
"Failed to create bundle directory",
|
|
261
|
+
failure = storageFailure,
|
|
31
262
|
)
|
|
32
263
|
|
|
33
|
-
fun downloadFailed(
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
264
|
+
fun downloadFailed(
|
|
265
|
+
cause: Throwable? = null,
|
|
266
|
+
failure: UpdateFailure = UpdateFailure.ofDownloadError(cause),
|
|
267
|
+
) = HotUpdaterException(
|
|
268
|
+
"DOWNLOAD_FAILED",
|
|
269
|
+
"Failed to download bundle",
|
|
270
|
+
cause,
|
|
271
|
+
failure,
|
|
272
|
+
)
|
|
39
273
|
|
|
40
274
|
fun incompleteDownload(
|
|
41
275
|
expectedSize: Long,
|
|
@@ -43,19 +277,14 @@ class HotUpdaterException(
|
|
|
43
277
|
) = HotUpdaterException(
|
|
44
278
|
"INCOMPLETE_DOWNLOAD",
|
|
45
279
|
"Download incomplete: received $actualSize bytes, expected $expectedSize bytes",
|
|
280
|
+
failure = UpdateFailure.download(UpdateFailureReason.NETWORK),
|
|
46
281
|
)
|
|
47
282
|
|
|
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
283
|
fun invalidBundle() =
|
|
56
284
|
HotUpdaterException(
|
|
57
285
|
"INVALID_BUNDLE",
|
|
58
286
|
"Bundle missing required platform files (index.ios.bundle or index.android.bundle)",
|
|
287
|
+
failure = invalidResponse,
|
|
59
288
|
)
|
|
60
289
|
|
|
61
290
|
fun insufficientDiskSpace(
|
|
@@ -64,19 +293,24 @@ class HotUpdaterException(
|
|
|
64
293
|
) = HotUpdaterException(
|
|
65
294
|
"INSUFFICIENT_DISK_SPACE",
|
|
66
295
|
"Insufficient disk space: need $required bytes, available $available bytes",
|
|
296
|
+
failure = storageFailure,
|
|
67
297
|
)
|
|
68
298
|
|
|
69
|
-
fun signatureVerificationFailed(
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
299
|
+
fun signatureVerificationFailed(
|
|
300
|
+
cause: Throwable? = null,
|
|
301
|
+
failure: UpdateFailure = UpdateFailure.ofVerificationError(cause),
|
|
302
|
+
) = HotUpdaterException(
|
|
303
|
+
"SIGNATURE_VERIFICATION_FAILED",
|
|
304
|
+
"Bundle signature verification failed",
|
|
305
|
+
cause,
|
|
306
|
+
failure,
|
|
307
|
+
)
|
|
75
308
|
|
|
76
309
|
fun moveOperationFailed() =
|
|
77
310
|
HotUpdaterException(
|
|
78
311
|
"MOVE_OPERATION_FAILED",
|
|
79
312
|
"Failed to move bundle files",
|
|
313
|
+
failure = storageFailure,
|
|
80
314
|
)
|
|
81
315
|
|
|
82
316
|
fun bundleInCrashedHistory(bundleId: String) =
|
|
@@ -90,30 +324,35 @@ class HotUpdaterException(
|
|
|
90
324
|
HotUpdaterException(
|
|
91
325
|
"PUBLIC_KEY_NOT_CONFIGURED",
|
|
92
326
|
"Public key not configured for signature verification",
|
|
327
|
+
failure = signatureFailure,
|
|
93
328
|
)
|
|
94
329
|
|
|
95
330
|
fun invalidPublicKeyFormat() =
|
|
96
331
|
HotUpdaterException(
|
|
97
332
|
"INVALID_PUBLIC_KEY_FORMAT",
|
|
98
333
|
"Invalid public key format",
|
|
334
|
+
failure = signatureFailure,
|
|
99
335
|
)
|
|
100
336
|
|
|
101
337
|
fun fileHashMismatch() =
|
|
102
338
|
HotUpdaterException(
|
|
103
339
|
"FILE_HASH_MISMATCH",
|
|
104
340
|
"File hash verification failed",
|
|
341
|
+
failure = UpdateFailure.download(UpdateFailureReason.HASH_MISMATCH),
|
|
105
342
|
)
|
|
106
343
|
|
|
107
344
|
fun fileReadFailed() =
|
|
108
345
|
HotUpdaterException(
|
|
109
346
|
"FILE_READ_FAILED",
|
|
110
347
|
"Failed to read file for verification",
|
|
348
|
+
failure = storageFailure,
|
|
111
349
|
)
|
|
112
350
|
|
|
113
351
|
fun unsignedNotAllowed() =
|
|
114
352
|
HotUpdaterException(
|
|
115
353
|
"UNSIGNED_NOT_ALLOWED",
|
|
116
354
|
"Unsigned bundles are not allowed",
|
|
355
|
+
failure = signatureFailure,
|
|
117
356
|
)
|
|
118
357
|
|
|
119
358
|
fun securityFrameworkError(cause: Throwable? = null) =
|
|
@@ -121,6 +360,7 @@ class HotUpdaterException(
|
|
|
121
360
|
"SECURITY_FRAMEWORK_ERROR",
|
|
122
361
|
"Security framework error occurred",
|
|
123
362
|
cause,
|
|
363
|
+
failure = signatureFailure,
|
|
124
364
|
)
|
|
125
365
|
|
|
126
366
|
// Internal errors
|
|
@@ -130,5 +370,30 @@ class HotUpdaterException(
|
|
|
130
370
|
"An unknown error occurred",
|
|
131
371
|
cause,
|
|
132
372
|
)
|
|
373
|
+
|
|
374
|
+
/**
|
|
375
|
+
* A local storage failure while producing the bundle. It keeps the
|
|
376
|
+
* `UNKNOWN_ERROR` code these failures have always been rejected with.
|
|
377
|
+
*/
|
|
378
|
+
fun storageFailed(message: String) =
|
|
379
|
+
HotUpdaterException(
|
|
380
|
+
"UNKNOWN_ERROR",
|
|
381
|
+
message,
|
|
382
|
+
failure = storageFailure,
|
|
383
|
+
)
|
|
384
|
+
|
|
385
|
+
/**
|
|
386
|
+
* Wraps an error the update pipeline did not anticipate in the
|
|
387
|
+
* `UNKNOWN_ERROR` rejection it has always produced, adding [failure].
|
|
388
|
+
*/
|
|
389
|
+
fun unexpected(
|
|
390
|
+
cause: Throwable,
|
|
391
|
+
failure: UpdateFailure,
|
|
392
|
+
) = HotUpdaterException(
|
|
393
|
+
"UNKNOWN_ERROR",
|
|
394
|
+
cause.message ?: "An unknown error occurred",
|
|
395
|
+
cause,
|
|
396
|
+
failure,
|
|
397
|
+
)
|
|
133
398
|
}
|
|
134
399
|
}
|
|
@@ -18,6 +18,8 @@ class HotUpdaterImpl {
|
|
|
18
18
|
private val preferences: PreferencesService
|
|
19
19
|
private val recoveryManager: HotUpdaterRecoveryManager
|
|
20
20
|
private val releaseCatalogCache: ReleaseCatalogCacheService
|
|
21
|
+
private val installIdentity: InstallIdentityService
|
|
22
|
+
private val keyValueStorage: KeyValueStorageService
|
|
21
23
|
private var currentLaunchSelection: LaunchSelection? = null
|
|
22
24
|
|
|
23
25
|
/**
|
|
@@ -36,6 +38,9 @@ class HotUpdaterImpl {
|
|
|
36
38
|
ReleaseCatalogCacheService(
|
|
37
39
|
File(this.context.noBackupFilesDir, "hot-updater-release-catalog-cache"),
|
|
38
40
|
)
|
|
41
|
+
this.installIdentity = InstallIdentityService.create(this.context)
|
|
42
|
+
this.keyValueStorage = KeyValueStorageService.create(this.context)
|
|
43
|
+
PreviousProcessExit.initialize(this.context)
|
|
39
44
|
}
|
|
40
45
|
|
|
41
46
|
/**
|
|
@@ -83,14 +88,12 @@ class HotUpdaterImpl {
|
|
|
83
88
|
val fileSystem = FileManagerService(appContext)
|
|
84
89
|
val preferences = createPreferences(appContext)
|
|
85
90
|
val downloadService = OkHttpDownloadService()
|
|
86
|
-
val decompressService = DecompressService()
|
|
87
91
|
val isolationKey = getIsolationKey(appContext)
|
|
88
92
|
|
|
89
93
|
return BundleFileStorageService(
|
|
90
94
|
appContext,
|
|
91
95
|
fileSystem,
|
|
92
96
|
downloadService,
|
|
93
|
-
decompressService,
|
|
94
97
|
preferences,
|
|
95
98
|
isolationKey,
|
|
96
99
|
)
|
|
@@ -268,36 +271,37 @@ class HotUpdaterImpl {
|
|
|
268
271
|
fun getJSBundleFile(): String = prepareLaunchIfNeeded().bundleUrl
|
|
269
272
|
|
|
270
273
|
/**
|
|
271
|
-
* Updates the bundle from
|
|
274
|
+
* Updates the bundle from its manifest and complete asset descriptors.
|
|
272
275
|
* @param bundleId ID of the bundle to update
|
|
273
|
-
* @param
|
|
274
|
-
* @param
|
|
276
|
+
* @param manifestUrl URL of the target manifest
|
|
277
|
+
* @param manifestFileHash Hash or signature used to verify the target manifest
|
|
278
|
+
* @param assets Complete target asset descriptor map
|
|
275
279
|
* @param progressCallback Callback for download progress updates
|
|
280
|
+
* @return how the staged bundle arrived
|
|
276
281
|
* @throws HotUpdaterException if the update fails
|
|
277
282
|
*/
|
|
278
283
|
suspend fun updateBundle(
|
|
279
284
|
bundleId: String,
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
manifestFileHash: String?,
|
|
284
|
-
changedAssets: Map<String, ChangedAssetDescriptor>?,
|
|
285
|
+
manifestUrl: String,
|
|
286
|
+
manifestFileHash: String,
|
|
287
|
+
assets: Map<String, ChangedAssetDescriptor>,
|
|
285
288
|
channel: String?,
|
|
286
289
|
selection: PersistedSelection? = null,
|
|
290
|
+
archiveUrl: String? = null,
|
|
287
291
|
progressCallback: (UpdateProgressPayload) -> Unit,
|
|
288
|
-
) {
|
|
292
|
+
): UpdateBundleResult {
|
|
289
293
|
if (selection != null && !bundleStorage.stageReleaseSelection(selection)) {
|
|
290
|
-
throw
|
|
294
|
+
throw StaleReleaseSelectionException()
|
|
291
295
|
}
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
296
|
+
val result =
|
|
297
|
+
bundleStorage.updateBundle(
|
|
298
|
+
bundleId,
|
|
299
|
+
manifestUrl,
|
|
300
|
+
manifestFileHash,
|
|
301
|
+
assets,
|
|
302
|
+
archiveUrl,
|
|
303
|
+
progressCallback,
|
|
304
|
+
)
|
|
301
305
|
|
|
302
306
|
if (!channel.isNullOrEmpty()) {
|
|
303
307
|
if (channel == getDefaultChannel()) {
|
|
@@ -306,6 +310,7 @@ class HotUpdaterImpl {
|
|
|
306
310
|
preferences.setItem(CHANNEL_STORAGE_KEY, channel)
|
|
307
311
|
}
|
|
308
312
|
}
|
|
313
|
+
return result
|
|
309
314
|
}
|
|
310
315
|
|
|
311
316
|
/**
|
|
@@ -374,7 +379,15 @@ class HotUpdaterImpl {
|
|
|
374
379
|
}
|
|
375
380
|
}
|
|
376
381
|
|
|
377
|
-
|
|
382
|
+
/**
|
|
383
|
+
* The launch report for this process, with `previousProcessExit` when
|
|
384
|
+
* Android reports why the previous main process exited.
|
|
385
|
+
*/
|
|
386
|
+
fun notifyAppReady(): Map<String, Any?> {
|
|
387
|
+
val report = bundleStorage.notifyAppReady()
|
|
388
|
+
val previousProcessExit = PreviousProcessExit.get() ?: return report
|
|
389
|
+
return report + ("previousProcessExit" to previousProcessExit)
|
|
390
|
+
}
|
|
378
391
|
|
|
379
392
|
fun acceptReleaseCatalog(
|
|
380
393
|
catalogId: String,
|
|
@@ -423,17 +436,17 @@ class HotUpdaterImpl {
|
|
|
423
436
|
|
|
424
437
|
suspend fun commitReleaseSelection(selection: PersistedSelection): Boolean = bundleStorage.commitReleaseSelection(selection)
|
|
425
438
|
|
|
426
|
-
fun getInstallId(): String =
|
|
427
|
-
|
|
428
|
-
fun getUserId(): String? = bundleStorage.getUserId()
|
|
439
|
+
fun getInstallId(): String = installIdentity.getInstallId()
|
|
429
440
|
|
|
430
|
-
|
|
441
|
+
/** Reads a value from the client plugins' key-value store. */
|
|
442
|
+
fun getStorageItem(key: String): String? = keyValueStorage.getItem(key)
|
|
431
443
|
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
444
|
+
/** Writes a value to the client plugins' key-value store; null removes the key. */
|
|
445
|
+
fun setStorageItem(
|
|
446
|
+
key: String,
|
|
447
|
+
value: String?,
|
|
435
448
|
) {
|
|
436
|
-
|
|
449
|
+
keyValueStorage.setItem(key, value)
|
|
437
450
|
}
|
|
438
451
|
|
|
439
452
|
/**
|
|
@@ -467,7 +480,6 @@ class HotUpdaterImpl {
|
|
|
467
480
|
|
|
468
481
|
/**
|
|
469
482
|
* Gets the current launched bundle ID.
|
|
470
|
-
* Reads manifest.json first and falls back to the legacy BUNDLE_ID file.
|
|
471
483
|
* Built-in bundle fallback is handled in JS.
|
|
472
484
|
*/
|
|
473
485
|
fun getBundleId(): String? {
|