@hot-updater/react-native 1.0.0-rc.16 → 1.0.0-rc.17
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/src/main/java/com/hotupdater/BundleFileStorageService.kt +491 -388
- package/android/src/main/java/com/hotupdater/BundleMetadata.kt +5 -11
- package/android/src/main/java/com/hotupdater/FileManagerService.kt +45 -0
- package/android/src/main/java/com/hotupdater/HotUpdaterException.kt +290 -12
- package/android/src/main/java/com/hotupdater/HotUpdaterImpl.kt +35 -19
- 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 +110 -52
- 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/newarch/HotUpdaterModule.kt +74 -69
- package/android/src/oldarch/HotUpdaterModule.kt +63 -59
- package/android/src/oldarch/HotUpdaterSpec.kt +4 -6
- package/android/src/test/java/com/hotupdater/BsdiffPatchTest.kt +1 -1
- package/android/src/test/java/com/hotupdater/BundleFileStorageServiceTest.kt +446 -61
- package/android/src/test/java/com/hotupdater/HotUpdaterExceptionTest.kt +220 -0
- package/android/src/test/java/com/hotupdater/HotUpdaterImplTest.kt +19 -27
- package/android/src/test/java/com/hotupdater/InstallIdentityServiceTest.kt +71 -0
- package/android/src/test/java/com/hotupdater/KeyValueStorageServiceTest.kt +106 -0
- package/android/src/test/java/com/hotupdater/NoBackupContext.kt +11 -0
- package/android/src/test/java/com/hotupdater/OkHttpDownloadServiceTest.kt +309 -23
- package/android/src/test/java/com/hotupdater/PreviousProcessExitTest.kt +57 -0
- package/ios/HotUpdater/Internal/BundleFileStorageService.swift +238 -157
- package/ios/HotUpdater/Internal/BundleMetadata.swift +3 -53
- package/ios/HotUpdater/Internal/FileManagerService.swift +48 -0
- package/ios/HotUpdater/Internal/HotUpdater.mm +8 -18
- package/ios/HotUpdater/Internal/HotUpdaterImpl.swift +48 -29
- package/ios/HotUpdater/Internal/KeyValueStorageService.swift +71 -0
- package/ios/HotUpdater/Internal/URLSessionDownloadService.swift +108 -1
- package/ios/HotUpdater/Internal/UpdateFailure.swift +390 -0
- package/lib/commonjs/appReady.js +111 -0
- package/lib/commonjs/appReady.js.map +1 -0
- package/lib/commonjs/appReady.spec.js +360 -0
- package/lib/commonjs/appReady.spec.js.map +1 -0
- package/lib/commonjs/{notifyAppReadyInsights.test-utils.js → appReady.test-utils.js} +4 -3
- package/lib/commonjs/appReady.test-utils.js.map +1 -0
- package/lib/commonjs/baseURL.js +16 -0
- package/lib/commonjs/baseURL.js.map +1 -0
- package/lib/commonjs/checkForUpdate.js +104 -56
- package/lib/commonjs/checkForUpdate.js.map +1 -1
- package/lib/commonjs/checkForUpdate.releaseCatalog.spec.js +337 -74
- package/lib/commonjs/checkForUpdate.releaseCatalog.spec.js.map +1 -1
- package/lib/commonjs/clientPlugin.js +49 -0
- package/lib/commonjs/clientPlugin.js.map +1 -0
- package/lib/commonjs/entryImportGraph.spec.js +50 -0
- package/lib/commonjs/entryImportGraph.spec.js.map +1 -0
- package/lib/commonjs/fetchJSON.js +2 -6
- package/lib/commonjs/fetchJSON.js.map +1 -1
- package/lib/commonjs/httpClient.js +18 -165
- package/lib/commonjs/httpClient.js.map +1 -1
- package/lib/commonjs/httpClient.spec.js +26 -199
- package/lib/commonjs/httpClient.spec.js.map +1 -1
- package/lib/commonjs/index.js +49 -36
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/index.spec.js +108 -123
- package/lib/commonjs/index.spec.js.map +1 -1
- package/lib/commonjs/native.js +67 -48
- package/lib/commonjs/native.js.map +1 -1
- package/lib/commonjs/native.spec.js +80 -58
- package/lib/commonjs/native.spec.js.map +1 -1
- package/lib/commonjs/pluginHost.js +208 -0
- package/lib/commonjs/pluginHost.js.map +1 -0
- package/lib/commonjs/pluginHost.spec.js +292 -0
- package/lib/commonjs/pluginHost.spec.js.map +1 -0
- package/lib/commonjs/plugins/insights/eventId.js +129 -0
- package/lib/commonjs/plugins/insights/eventId.js.map +1 -0
- package/lib/commonjs/plugins/insights/eventId.spec.js +47 -0
- package/lib/commonjs/plugins/insights/eventId.spec.js.map +1 -0
- package/lib/commonjs/plugins/insights/index.js +272 -0
- package/lib/commonjs/plugins/insights/index.js.map +1 -0
- package/lib/commonjs/plugins/insights/insights.spec.js +640 -0
- package/lib/commonjs/plugins/insights/insights.spec.js.map +1 -0
- package/lib/commonjs/plugins/insights/sender.js +128 -0
- package/lib/commonjs/plugins/insights/sender.js.map +1 -0
- package/lib/commonjs/plugins/insights/sender.spec.js +265 -0
- package/lib/commonjs/plugins/insights/sender.spec.js.map +1 -0
- package/lib/commonjs/plugins/insights/state.js +145 -0
- package/lib/commonjs/plugins/insights/state.js.map +1 -0
- package/lib/commonjs/releaseCatalogCache.js +13 -2
- package/lib/commonjs/releaseCatalogCache.js.map +1 -1
- package/lib/commonjs/releaseCatalogCache.spec.js +18 -0
- package/lib/commonjs/releaseCatalogCache.spec.js.map +1 -1
- package/lib/commonjs/sdkVersion.js +1 -1
- package/lib/commonjs/specs/NativeHotUpdater.js.map +1 -1
- package/lib/commonjs/updateError.js +132 -0
- package/lib/commonjs/updateError.js.map +1 -0
- package/lib/commonjs/updateError.spec.js +149 -0
- package/lib/commonjs/updateError.spec.js.map +1 -0
- package/lib/commonjs/wrap.js +43 -25
- package/lib/commonjs/wrap.js.map +1 -1
- package/lib/commonjs/wrap.progress.spec.js +195 -0
- package/lib/commonjs/wrap.progress.spec.js.map +1 -0
- package/lib/commonjs/wrap.spec.js +94 -230
- package/lib/commonjs/wrap.spec.js.map +1 -1
- package/lib/module/appReady.js +103 -0
- package/lib/module/appReady.js.map +1 -0
- package/lib/module/appReady.spec.js +359 -0
- package/lib/module/appReady.spec.js.map +1 -0
- package/lib/module/{notifyAppReadyInsights.test-utils.js → appReady.test-utils.js} +4 -3
- package/lib/module/appReady.test-utils.js.map +1 -0
- package/lib/module/baseURL.js +11 -0
- package/lib/module/baseURL.js.map +1 -0
- package/lib/module/checkForUpdate.js +103 -57
- package/lib/module/checkForUpdate.js.map +1 -1
- package/lib/module/checkForUpdate.releaseCatalog.spec.js +337 -74
- package/lib/module/checkForUpdate.releaseCatalog.spec.js.map +1 -1
- package/lib/module/clientPlugin.js +44 -0
- package/lib/module/clientPlugin.js.map +1 -0
- package/lib/module/entryImportGraph.spec.js +50 -0
- package/lib/module/entryImportGraph.spec.js.map +1 -0
- package/lib/module/fetchJSON.js +2 -6
- package/lib/module/fetchJSON.js.map +1 -1
- package/lib/module/httpClient.js +18 -165
- package/lib/module/httpClient.js.map +1 -1
- package/lib/module/httpClient.spec.js +27 -200
- package/lib/module/httpClient.spec.js.map +1 -1
- package/lib/module/index.js +44 -38
- package/lib/module/index.js.map +1 -1
- package/lib/module/index.spec.js +108 -123
- package/lib/module/index.spec.js.map +1 -1
- package/lib/module/native.js +63 -44
- package/lib/module/native.js.map +1 -1
- package/lib/module/native.spec.js +80 -58
- package/lib/module/native.spec.js.map +1 -1
- package/lib/module/pluginHost.js +200 -0
- package/lib/module/pluginHost.js.map +1 -0
- package/lib/module/pluginHost.spec.js +291 -0
- package/lib/module/pluginHost.spec.js.map +1 -0
- package/lib/module/plugins/insights/eventId.js +122 -0
- package/lib/module/plugins/insights/eventId.js.map +1 -0
- package/lib/module/plugins/insights/eventId.spec.js +47 -0
- package/lib/module/plugins/insights/eventId.spec.js.map +1 -0
- package/lib/module/plugins/insights/index.js +268 -0
- package/lib/module/plugins/insights/index.js.map +1 -0
- package/lib/module/plugins/insights/insights.spec.js +639 -0
- package/lib/module/plugins/insights/insights.spec.js.map +1 -0
- package/lib/module/plugins/insights/sender.js +123 -0
- package/lib/module/plugins/insights/sender.js.map +1 -0
- package/lib/module/plugins/insights/sender.spec.js +265 -0
- package/lib/module/plugins/insights/sender.spec.js.map +1 -0
- package/lib/module/plugins/insights/state.js +139 -0
- package/lib/module/plugins/insights/state.js.map +1 -0
- package/lib/module/releaseCatalogCache.js +13 -2
- package/lib/module/releaseCatalogCache.js.map +1 -1
- package/lib/module/releaseCatalogCache.spec.js +18 -0
- package/lib/module/releaseCatalogCache.spec.js.map +1 -1
- package/lib/module/sdkVersion.js +1 -1
- package/lib/module/specs/NativeHotUpdater.js.map +1 -1
- package/lib/module/updateError.js +126 -0
- package/lib/module/updateError.js.map +1 -0
- package/lib/module/updateError.spec.js +149 -0
- package/lib/module/updateError.spec.js.map +1 -0
- package/lib/module/wrap.js +43 -25
- package/lib/module/wrap.js.map +1 -1
- package/lib/module/wrap.progress.spec.js +196 -0
- package/lib/module/wrap.progress.spec.js.map +1 -0
- package/lib/module/wrap.spec.js +93 -229
- package/lib/module/wrap.spec.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/appReady.test-utils.d.ts +9 -0
- package/lib/typescript/commonjs/appReady.test-utils.d.ts.map +1 -0
- package/lib/typescript/commonjs/baseURL.d.ts +4 -0
- package/lib/typescript/commonjs/baseURL.d.ts.map +1 -0
- package/lib/typescript/commonjs/checkForUpdate.d.ts +15 -2
- package/lib/typescript/commonjs/checkForUpdate.d.ts.map +1 -1
- package/lib/typescript/commonjs/clientPlugin.d.ts +185 -0
- package/lib/typescript/commonjs/clientPlugin.d.ts.map +1 -0
- package/lib/typescript/commonjs/fetchJSON.d.ts +2 -3
- package/lib/typescript/commonjs/fetchJSON.d.ts.map +1 -1
- package/lib/typescript/commonjs/httpClient.d.ts +2 -36
- package/lib/typescript/commonjs/httpClient.d.ts.map +1 -1
- package/lib/typescript/commonjs/index.d.ts +3 -6
- package/lib/typescript/commonjs/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/native.d.ts +29 -15
- package/lib/typescript/commonjs/native.d.ts.map +1 -1
- package/lib/typescript/commonjs/pluginHost.d.ts +29 -0
- package/lib/typescript/commonjs/pluginHost.d.ts.map +1 -0
- package/lib/typescript/commonjs/plugins/insights/eventId.d.ts +18 -0
- package/lib/typescript/commonjs/plugins/insights/eventId.d.ts.map +1 -0
- package/lib/typescript/commonjs/plugins/insights/index.d.ts +40 -0
- package/lib/typescript/commonjs/plugins/insights/index.d.ts.map +1 -0
- package/lib/typescript/commonjs/plugins/insights/sender.d.ts +74 -0
- package/lib/typescript/commonjs/plugins/insights/sender.d.ts.map +1 -0
- package/lib/typescript/commonjs/plugins/insights/state.d.ts +46 -0
- package/lib/typescript/commonjs/plugins/insights/state.d.ts.map +1 -0
- package/lib/typescript/commonjs/releaseCatalogCache.d.ts +5 -1
- package/lib/typescript/commonjs/releaseCatalogCache.d.ts.map +1 -1
- package/lib/typescript/commonjs/sdkVersion.d.ts +1 -1
- package/lib/typescript/commonjs/specs/NativeHotUpdater.d.ts +34 -12
- package/lib/typescript/commonjs/specs/NativeHotUpdater.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 -10
- 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/appReady.test-utils.d.ts +9 -0
- package/lib/typescript/module/appReady.test-utils.d.ts.map +1 -0
- package/lib/typescript/module/baseURL.d.ts +4 -0
- package/lib/typescript/module/baseURL.d.ts.map +1 -0
- package/lib/typescript/module/checkForUpdate.d.ts +15 -2
- package/lib/typescript/module/checkForUpdate.d.ts.map +1 -1
- package/lib/typescript/module/clientPlugin.d.ts +185 -0
- package/lib/typescript/module/clientPlugin.d.ts.map +1 -0
- package/lib/typescript/module/fetchJSON.d.ts +2 -3
- package/lib/typescript/module/fetchJSON.d.ts.map +1 -1
- package/lib/typescript/module/httpClient.d.ts +2 -36
- package/lib/typescript/module/httpClient.d.ts.map +1 -1
- package/lib/typescript/module/index.d.ts +3 -6
- package/lib/typescript/module/index.d.ts.map +1 -1
- package/lib/typescript/module/native.d.ts +29 -15
- package/lib/typescript/module/native.d.ts.map +1 -1
- package/lib/typescript/module/pluginHost.d.ts +29 -0
- package/lib/typescript/module/pluginHost.d.ts.map +1 -0
- package/lib/typescript/module/plugins/insights/eventId.d.ts +18 -0
- package/lib/typescript/module/plugins/insights/eventId.d.ts.map +1 -0
- package/lib/typescript/module/plugins/insights/index.d.ts +40 -0
- package/lib/typescript/module/plugins/insights/index.d.ts.map +1 -0
- package/lib/typescript/module/plugins/insights/sender.d.ts +74 -0
- package/lib/typescript/module/plugins/insights/sender.d.ts.map +1 -0
- package/lib/typescript/module/plugins/insights/state.d.ts +46 -0
- package/lib/typescript/module/plugins/insights/state.d.ts.map +1 -0
- package/lib/typescript/module/releaseCatalogCache.d.ts +5 -1
- package/lib/typescript/module/releaseCatalogCache.d.ts.map +1 -1
- package/lib/typescript/module/sdkVersion.d.ts +1 -1
- package/lib/typescript/module/specs/NativeHotUpdater.d.ts +34 -12
- package/lib/typescript/module/specs/NativeHotUpdater.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 -10
- package/lib/typescript/module/wrap.types.d.ts.map +1 -1
- package/package.json +14 -2
- package/plugins/insights/package.json +8 -0
- package/src/appReady.spec.ts +393 -0
- package/src/{notifyAppReadyInsights.test-utils.ts → appReady.test-utils.ts} +7 -7
- package/src/appReady.ts +149 -0
- package/src/baseURL.ts +15 -0
- package/src/checkForUpdate.releaseCatalog.spec.ts +370 -76
- package/src/checkForUpdate.ts +135 -80
- package/src/clientPlugin.ts +231 -0
- package/src/entryImportGraph.spec.ts +65 -0
- package/src/fetchJSON.ts +3 -10
- package/src/httpClient.spec.ts +30 -227
- package/src/httpClient.ts +20 -256
- package/src/index.spec.ts +117 -93
- package/src/index.ts +67 -52
- package/src/native.spec.ts +98 -72
- package/src/native.ts +93 -77
- package/src/pluginHost.spec.ts +297 -0
- package/src/pluginHost.ts +262 -0
- package/src/plugins/insights/eventId.spec.ts +60 -0
- package/src/plugins/insights/eventId.ts +171 -0
- package/src/plugins/insights/index.ts +367 -0
- package/src/plugins/insights/insights.spec.ts +732 -0
- package/src/plugins/insights/sender.spec.ts +287 -0
- package/src/plugins/insights/sender.ts +221 -0
- package/src/plugins/insights/state.ts +184 -0
- package/src/releaseCatalogCache.spec.ts +22 -0
- package/src/releaseCatalogCache.ts +19 -4
- package/src/sdkVersion.ts +1 -1
- package/src/specs/NativeHotUpdater.ts +34 -13
- package/src/updateError.spec.ts +144 -0
- package/src/updateError.ts +153 -0
- package/src/wrap.progress.spec.ts +232 -0
- package/src/wrap.spec.ts +100 -283
- package/src/wrap.tsx +57 -26
- package/src/wrap.types.ts +6 -12
- package/lib/commonjs/notifyAppReadyInsights.boundary.spec.js +0 -225
- package/lib/commonjs/notifyAppReadyInsights.boundary.spec.js.map +0 -1
- package/lib/commonjs/notifyAppReadyInsights.js +0 -188
- package/lib/commonjs/notifyAppReadyInsights.js.map +0 -1
- package/lib/commonjs/notifyAppReadyInsights.spec.js +0 -378
- package/lib/commonjs/notifyAppReadyInsights.spec.js.map +0 -1
- package/lib/commonjs/notifyAppReadyInsights.test-utils.js.map +0 -1
- package/lib/commonjs/notifyAppReadyInsights.transport.spec.js +0 -171
- package/lib/commonjs/notifyAppReadyInsights.transport.spec.js.map +0 -1
- package/lib/commonjs/uuidv7.js +0 -41
- package/lib/commonjs/uuidv7.js.map +0 -1
- package/lib/commonjs/uuidv7.spec.js +0 -30
- package/lib/commonjs/uuidv7.spec.js.map +0 -1
- package/lib/module/notifyAppReadyInsights.boundary.spec.js +0 -224
- package/lib/module/notifyAppReadyInsights.boundary.spec.js.map +0 -1
- package/lib/module/notifyAppReadyInsights.js +0 -181
- package/lib/module/notifyAppReadyInsights.js.map +0 -1
- package/lib/module/notifyAppReadyInsights.spec.js +0 -377
- package/lib/module/notifyAppReadyInsights.spec.js.map +0 -1
- package/lib/module/notifyAppReadyInsights.test-utils.js.map +0 -1
- package/lib/module/notifyAppReadyInsights.transport.spec.js +0 -170
- package/lib/module/notifyAppReadyInsights.transport.spec.js.map +0 -1
- package/lib/module/uuidv7.js +0 -36
- package/lib/module/uuidv7.js.map +0 -1
- package/lib/module/uuidv7.spec.js +0 -30
- package/lib/module/uuidv7.spec.js.map +0 -1
- package/lib/typescript/commonjs/notifyAppReadyInsights.d.ts +0 -25
- 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/commonjs/uuidv7.d.ts +0 -10
- package/lib/typescript/commonjs/uuidv7.d.ts.map +0 -1
- package/lib/typescript/module/notifyAppReadyInsights.d.ts +0 -25
- 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/lib/typescript/module/uuidv7.d.ts +0 -10
- package/lib/typescript/module/uuidv7.d.ts.map +0 -1
- package/src/notifyAppReadyInsights.boundary.spec.ts +0 -242
- package/src/notifyAppReadyInsights.spec.ts +0 -362
- package/src/notifyAppReadyInsights.transport.spec.ts +0 -196
- package/src/notifyAppReadyInsights.ts +0 -293
- package/src/uuidv7.spec.ts +0 -25
- package/src/uuidv7.ts +0 -38
|
@@ -479,21 +479,17 @@ data class LaunchReport(
|
|
|
479
479
|
}
|
|
480
480
|
}
|
|
481
481
|
|
|
482
|
+
/**
|
|
483
|
+
* The persisted install id. See [InstallIdentityService].
|
|
484
|
+
*/
|
|
482
485
|
data class InstallationIdentity(
|
|
483
486
|
val installId: String,
|
|
484
|
-
val userId: String? = null,
|
|
485
|
-
val username: String? = null,
|
|
486
487
|
) {
|
|
487
488
|
companion object {
|
|
488
489
|
private const val TAG = "InstallationIdentity"
|
|
489
|
-
const val IDENTITY_FILENAME = "identity.json"
|
|
490
|
+
const val IDENTITY_FILENAME = "install-identity.json"
|
|
490
491
|
|
|
491
|
-
fun fromJson(json: JSONObject): InstallationIdentity =
|
|
492
|
-
InstallationIdentity(
|
|
493
|
-
installId = json.getString("installId"),
|
|
494
|
-
userId = json.optNullableString("userId"),
|
|
495
|
-
username = json.optNullableString("username"),
|
|
496
|
-
)
|
|
492
|
+
fun fromJson(json: JSONObject): InstallationIdentity = InstallationIdentity(installId = json.getString("installId"))
|
|
497
493
|
|
|
498
494
|
fun loadFromFile(file: File): InstallationIdentity? =
|
|
499
495
|
try {
|
|
@@ -511,8 +507,6 @@ data class InstallationIdentity(
|
|
|
511
507
|
fun toJson(): JSONObject =
|
|
512
508
|
JSONObject().apply {
|
|
513
509
|
put("installId", installId)
|
|
514
|
-
put("userId", userId ?: JSONObject.NULL)
|
|
515
|
-
put("username", username ?: JSONObject.NULL)
|
|
516
510
|
}
|
|
517
511
|
|
|
518
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,35 +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
|
|
|
239
|
+
fun invalidManifestParams() =
|
|
240
|
+
HotUpdaterException(
|
|
241
|
+
"INVALID_MANIFEST",
|
|
242
|
+
"Manifest URL, hash, and assets are required",
|
|
243
|
+
failure = invalidResponse.copy(resource = UpdateFailureResource.MANIFEST),
|
|
244
|
+
)
|
|
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
|
+
|
|
20
256
|
// Bundle storage errors
|
|
21
257
|
fun directoryCreationFailed() =
|
|
22
258
|
HotUpdaterException(
|
|
23
259
|
"DIRECTORY_CREATION_FAILED",
|
|
24
260
|
"Failed to create bundle directory",
|
|
261
|
+
failure = storageFailure,
|
|
25
262
|
)
|
|
26
263
|
|
|
27
|
-
fun downloadFailed(
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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
|
+
)
|
|
33
273
|
|
|
34
274
|
fun incompleteDownload(
|
|
35
275
|
expectedSize: Long,
|
|
@@ -37,12 +277,14 @@ class HotUpdaterException(
|
|
|
37
277
|
) = HotUpdaterException(
|
|
38
278
|
"INCOMPLETE_DOWNLOAD",
|
|
39
279
|
"Download incomplete: received $actualSize bytes, expected $expectedSize bytes",
|
|
280
|
+
failure = UpdateFailure.download(UpdateFailureReason.NETWORK),
|
|
40
281
|
)
|
|
41
282
|
|
|
42
283
|
fun invalidBundle() =
|
|
43
284
|
HotUpdaterException(
|
|
44
285
|
"INVALID_BUNDLE",
|
|
45
286
|
"Bundle missing required platform files (index.ios.bundle or index.android.bundle)",
|
|
287
|
+
failure = invalidResponse,
|
|
46
288
|
)
|
|
47
289
|
|
|
48
290
|
fun insufficientDiskSpace(
|
|
@@ -51,19 +293,24 @@ class HotUpdaterException(
|
|
|
51
293
|
) = HotUpdaterException(
|
|
52
294
|
"INSUFFICIENT_DISK_SPACE",
|
|
53
295
|
"Insufficient disk space: need $required bytes, available $available bytes",
|
|
296
|
+
failure = storageFailure,
|
|
54
297
|
)
|
|
55
298
|
|
|
56
|
-
fun signatureVerificationFailed(
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
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
|
+
)
|
|
62
308
|
|
|
63
309
|
fun moveOperationFailed() =
|
|
64
310
|
HotUpdaterException(
|
|
65
311
|
"MOVE_OPERATION_FAILED",
|
|
66
312
|
"Failed to move bundle files",
|
|
313
|
+
failure = storageFailure,
|
|
67
314
|
)
|
|
68
315
|
|
|
69
316
|
fun bundleInCrashedHistory(bundleId: String) =
|
|
@@ -77,30 +324,35 @@ class HotUpdaterException(
|
|
|
77
324
|
HotUpdaterException(
|
|
78
325
|
"PUBLIC_KEY_NOT_CONFIGURED",
|
|
79
326
|
"Public key not configured for signature verification",
|
|
327
|
+
failure = signatureFailure,
|
|
80
328
|
)
|
|
81
329
|
|
|
82
330
|
fun invalidPublicKeyFormat() =
|
|
83
331
|
HotUpdaterException(
|
|
84
332
|
"INVALID_PUBLIC_KEY_FORMAT",
|
|
85
333
|
"Invalid public key format",
|
|
334
|
+
failure = signatureFailure,
|
|
86
335
|
)
|
|
87
336
|
|
|
88
337
|
fun fileHashMismatch() =
|
|
89
338
|
HotUpdaterException(
|
|
90
339
|
"FILE_HASH_MISMATCH",
|
|
91
340
|
"File hash verification failed",
|
|
341
|
+
failure = UpdateFailure.download(UpdateFailureReason.HASH_MISMATCH),
|
|
92
342
|
)
|
|
93
343
|
|
|
94
344
|
fun fileReadFailed() =
|
|
95
345
|
HotUpdaterException(
|
|
96
346
|
"FILE_READ_FAILED",
|
|
97
347
|
"Failed to read file for verification",
|
|
348
|
+
failure = storageFailure,
|
|
98
349
|
)
|
|
99
350
|
|
|
100
351
|
fun unsignedNotAllowed() =
|
|
101
352
|
HotUpdaterException(
|
|
102
353
|
"UNSIGNED_NOT_ALLOWED",
|
|
103
354
|
"Unsigned bundles are not allowed",
|
|
355
|
+
failure = signatureFailure,
|
|
104
356
|
)
|
|
105
357
|
|
|
106
358
|
fun securityFrameworkError(cause: Throwable? = null) =
|
|
@@ -108,6 +360,7 @@ class HotUpdaterException(
|
|
|
108
360
|
"SECURITY_FRAMEWORK_ERROR",
|
|
109
361
|
"Security framework error occurred",
|
|
110
362
|
cause,
|
|
363
|
+
failure = signatureFailure,
|
|
111
364
|
)
|
|
112
365
|
|
|
113
366
|
// Internal errors
|
|
@@ -117,5 +370,30 @@ class HotUpdaterException(
|
|
|
117
370
|
"An unknown error occurred",
|
|
118
371
|
cause,
|
|
119
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
|
+
)
|
|
120
398
|
}
|
|
121
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
|
/**
|
|
@@ -272,6 +277,7 @@ class HotUpdaterImpl {
|
|
|
272
277
|
* @param manifestFileHash Hash or signature used to verify the target manifest
|
|
273
278
|
* @param assets Complete target asset descriptor map
|
|
274
279
|
* @param progressCallback Callback for download progress updates
|
|
280
|
+
* @return how the staged bundle arrived
|
|
275
281
|
* @throws HotUpdaterException if the update fails
|
|
276
282
|
*/
|
|
277
283
|
suspend fun updateBundle(
|
|
@@ -283,18 +289,19 @@ class HotUpdaterImpl {
|
|
|
283
289
|
selection: PersistedSelection? = null,
|
|
284
290
|
archiveUrl: String? = null,
|
|
285
291
|
progressCallback: (UpdateProgressPayload) -> Unit,
|
|
286
|
-
) {
|
|
292
|
+
): UpdateBundleResult {
|
|
287
293
|
if (selection != null && !bundleStorage.stageReleaseSelection(selection)) {
|
|
288
|
-
throw
|
|
294
|
+
throw StaleReleaseSelectionException()
|
|
289
295
|
}
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
296
|
+
val result =
|
|
297
|
+
bundleStorage.updateBundle(
|
|
298
|
+
bundleId,
|
|
299
|
+
manifestUrl,
|
|
300
|
+
manifestFileHash,
|
|
301
|
+
assets,
|
|
302
|
+
archiveUrl,
|
|
303
|
+
progressCallback,
|
|
304
|
+
)
|
|
298
305
|
|
|
299
306
|
if (!channel.isNullOrEmpty()) {
|
|
300
307
|
if (channel == getDefaultChannel()) {
|
|
@@ -303,6 +310,7 @@ class HotUpdaterImpl {
|
|
|
303
310
|
preferences.setItem(CHANNEL_STORAGE_KEY, channel)
|
|
304
311
|
}
|
|
305
312
|
}
|
|
313
|
+
return result
|
|
306
314
|
}
|
|
307
315
|
|
|
308
316
|
/**
|
|
@@ -371,7 +379,15 @@ class HotUpdaterImpl {
|
|
|
371
379
|
}
|
|
372
380
|
}
|
|
373
381
|
|
|
374
|
-
|
|
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
|
+
}
|
|
375
391
|
|
|
376
392
|
fun acceptReleaseCatalog(
|
|
377
393
|
catalogId: String,
|
|
@@ -420,17 +436,17 @@ class HotUpdaterImpl {
|
|
|
420
436
|
|
|
421
437
|
suspend fun commitReleaseSelection(selection: PersistedSelection): Boolean = bundleStorage.commitReleaseSelection(selection)
|
|
422
438
|
|
|
423
|
-
fun getInstallId(): String =
|
|
424
|
-
|
|
425
|
-
fun getUserId(): String? = bundleStorage.getUserId()
|
|
439
|
+
fun getInstallId(): String = installIdentity.getInstallId()
|
|
426
440
|
|
|
427
|
-
|
|
441
|
+
/** Reads a value from the client plugins' key-value store. */
|
|
442
|
+
fun getStorageItem(key: String): String? = keyValueStorage.getItem(key)
|
|
428
443
|
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
444
|
+
/** Writes a value to the client plugins' key-value store; null removes the key. */
|
|
445
|
+
fun setStorageItem(
|
|
446
|
+
key: String,
|
|
447
|
+
value: String?,
|
|
432
448
|
) {
|
|
433
|
-
|
|
449
|
+
keyValueStorage.setItem(key, value)
|
|
434
450
|
}
|
|
435
451
|
|
|
436
452
|
/**
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
package com.hotupdater
|
|
2
|
+
|
|
3
|
+
import android.content.Context
|
|
4
|
+
import java.io.File
|
|
5
|
+
import java.util.UUID
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Owns the install id, a random id created once per app installation.
|
|
9
|
+
*
|
|
10
|
+
* The id lives outside the bundle store and outside device backups, so a
|
|
11
|
+
* device restored from a backup gets an id of its own.
|
|
12
|
+
*/
|
|
13
|
+
class InstallIdentityService(
|
|
14
|
+
private val identityFile: File,
|
|
15
|
+
) {
|
|
16
|
+
companion object {
|
|
17
|
+
/** The install id kept in `noBackupFilesDir/hot-updater/install-identity.json`. */
|
|
18
|
+
fun create(context: Context): InstallIdentityService =
|
|
19
|
+
InstallIdentityService(NoBackupStorage.file(context, InstallationIdentity.IDENTITY_FILENAME))
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
private var identity: InstallationIdentity? = null
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Returns the install id, creating and persisting a random one when none
|
|
26
|
+
* is stored. The id stays stable for this process even if persisting fails.
|
|
27
|
+
*/
|
|
28
|
+
@Synchronized
|
|
29
|
+
fun getInstallId(): String {
|
|
30
|
+
identity?.let { return it.installId }
|
|
31
|
+
val current =
|
|
32
|
+
InstallationIdentity.loadFromFile(identityFile)
|
|
33
|
+
?: InstallationIdentity(installId = UUID.randomUUID().toString()).also { it.saveToFile(identityFile) }
|
|
34
|
+
identity = current
|
|
35
|
+
return current.installId
|
|
36
|
+
}
|
|
37
|
+
}
|