@hot-updater/react-native 0.36.7 → 1.0.0-rc.0
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 +357 -6
- package/android/src/main/java/com/hotupdater/BundleMetadata.kt +268 -34
- package/android/src/main/java/com/hotupdater/HotUpdaterImpl.kt +75 -9
- package/android/src/main/java/com/hotupdater/NativeConfigUtils.kt +2 -16
- package/android/src/main/java/com/hotupdater/ReleaseCatalogCacheService.kt +166 -0
- package/android/src/main/java/com/hotupdater/SignatureVerifier.kt +1 -2
- package/android/src/newarch/HotUpdater.kt +1 -1
- package/android/src/newarch/HotUpdaterModule.kt +116 -0
- package/android/src/oldarch/HotUpdater.kt +1 -1
- package/android/src/oldarch/HotUpdaterModule.kt +128 -0
- package/android/src/oldarch/HotUpdaterSpec.kt +38 -0
- package/android/src/test/java/com/hotupdater/BundleFileStorageServiceTest.kt +280 -2
- package/android/src/test/java/com/hotupdater/HotUpdaterImplTest.kt +57 -2
- package/android/src/test/java/com/hotupdater/ReleaseCatalogCacheServiceTest.kt +118 -0
- package/ios/HotUpdater/Internal/BundleFileStorageService.swift +491 -30
- package/ios/HotUpdater/Internal/BundleMetadata.swift +229 -6
- package/ios/HotUpdater/Internal/HotUpdater.mm +122 -0
- package/ios/HotUpdater/Internal/HotUpdaterImpl.swift +176 -7
- package/ios/HotUpdater/Internal/ReleaseCatalogCacheService.swift +213 -0
- package/lib/commonjs/catalogCacheNative.js +44 -0
- package/lib/commonjs/catalogCacheNative.js.map +1 -0
- package/lib/commonjs/catalogCacheNative.spec.js +31 -0
- package/lib/commonjs/catalogCacheNative.spec.js.map +1 -0
- package/lib/commonjs/checkForUpdate.js +231 -60
- package/lib/commonjs/checkForUpdate.js.map +1 -1
- package/lib/commonjs/checkForUpdate.releaseCatalog.spec.js +604 -0
- package/lib/commonjs/checkForUpdate.releaseCatalog.spec.js.map +1 -0
- package/lib/commonjs/error.js +8 -1
- package/lib/commonjs/error.js.map +1 -1
- package/lib/commonjs/{fetchUpdateInfo.js → fetchJSON.js} +11 -14
- package/lib/commonjs/fetchJSON.js.map +1 -0
- package/lib/commonjs/httpClient.js +153 -0
- package/lib/commonjs/httpClient.js.map +1 -0
- package/lib/commonjs/httpClient.spec.js +102 -0
- package/lib/commonjs/httpClient.spec.js.map +1 -0
- package/lib/commonjs/index.js +48 -71
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/index.spec.js +117 -161
- package/lib/commonjs/index.spec.js.map +1 -1
- package/lib/commonjs/iosInterop.spec.js +18 -0
- package/lib/commonjs/iosInterop.spec.js.map +1 -0
- package/lib/commonjs/native.js +222 -55
- package/lib/commonjs/native.js.map +1 -1
- package/lib/commonjs/native.spec.js +226 -12
- package/lib/commonjs/native.spec.js.map +1 -1
- package/lib/commonjs/notifyAppReadyAnalytics.boundary.spec.js +218 -0
- package/lib/commonjs/notifyAppReadyAnalytics.boundary.spec.js.map +1 -0
- package/lib/commonjs/notifyAppReadyAnalytics.js +133 -0
- package/lib/commonjs/notifyAppReadyAnalytics.js.map +1 -0
- package/lib/commonjs/notifyAppReadyAnalytics.spec.js +246 -0
- package/lib/commonjs/notifyAppReadyAnalytics.spec.js.map +1 -0
- package/lib/commonjs/notifyAppReadyAnalytics.test-utils.js +24 -0
- package/lib/commonjs/notifyAppReadyAnalytics.test-utils.js.map +1 -0
- package/lib/commonjs/notifyAppReadyAnalytics.transport.spec.js +117 -0
- package/lib/commonjs/notifyAppReadyAnalytics.transport.spec.js.map +1 -0
- package/lib/commonjs/releaseCatalogCache.js +179 -0
- package/lib/commonjs/releaseCatalogCache.js.map +1 -0
- package/lib/commonjs/releaseCatalogCache.spec.js +195 -0
- package/lib/commonjs/releaseCatalogCache.spec.js.map +1 -0
- package/lib/commonjs/releaseCatalogCacheNativeContract.spec.js +64 -0
- package/lib/commonjs/releaseCatalogCacheNativeContract.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/types.js +0 -12
- package/lib/commonjs/types.js.map +1 -1
- package/lib/commonjs/wrap.js +7 -82
- package/lib/commonjs/wrap.js.map +1 -1
- package/lib/commonjs/wrap.spec.js +203 -56
- package/lib/commonjs/wrap.spec.js.map +1 -1
- package/lib/commonjs/wrap.types.js +6 -0
- package/lib/commonjs/wrap.types.js.map +1 -0
- package/lib/module/catalogCacheNative.js +36 -0
- package/lib/module/catalogCacheNative.js.map +1 -0
- package/lib/module/catalogCacheNative.spec.js +31 -0
- package/lib/module/catalogCacheNative.spec.js.map +1 -0
- package/lib/module/checkForUpdate.js +233 -62
- package/lib/module/checkForUpdate.js.map +1 -1
- package/lib/module/checkForUpdate.releaseCatalog.spec.js +603 -0
- package/lib/module/checkForUpdate.releaseCatalog.spec.js.map +1 -0
- package/lib/module/error.js +6 -0
- package/lib/module/error.js.map +1 -1
- package/lib/module/{fetchUpdateInfo.js → fetchJSON.js} +9 -12
- package/lib/module/fetchJSON.js.map +1 -0
- package/lib/module/httpClient.js +148 -0
- package/lib/module/httpClient.js.map +1 -0
- package/lib/module/httpClient.spec.js +102 -0
- package/lib/module/httpClient.spec.js.map +1 -0
- package/lib/module/index.js +49 -66
- package/lib/module/index.js.map +1 -1
- package/lib/module/index.spec.js +117 -161
- package/lib/module/index.spec.js.map +1 -1
- package/lib/module/iosInterop.spec.js +18 -0
- package/lib/module/iosInterop.spec.js.map +1 -0
- package/lib/module/native.js +206 -51
- package/lib/module/native.js.map +1 -1
- package/lib/module/native.spec.js +226 -12
- package/lib/module/native.spec.js.map +1 -1
- package/lib/module/notifyAppReadyAnalytics.boundary.spec.js +217 -0
- package/lib/module/notifyAppReadyAnalytics.boundary.spec.js.map +1 -0
- package/lib/module/notifyAppReadyAnalytics.js +128 -0
- package/lib/module/notifyAppReadyAnalytics.js.map +1 -0
- package/lib/module/notifyAppReadyAnalytics.spec.js +245 -0
- package/lib/module/notifyAppReadyAnalytics.spec.js.map +1 -0
- package/lib/module/notifyAppReadyAnalytics.test-utils.js +18 -0
- package/lib/module/notifyAppReadyAnalytics.test-utils.js.map +1 -0
- package/lib/module/notifyAppReadyAnalytics.transport.spec.js +116 -0
- package/lib/module/notifyAppReadyAnalytics.transport.spec.js.map +1 -0
- package/lib/module/releaseCatalogCache.js +171 -0
- package/lib/module/releaseCatalogCache.js.map +1 -0
- package/lib/module/releaseCatalogCache.spec.js +195 -0
- package/lib/module/releaseCatalogCache.spec.js.map +1 -0
- package/lib/module/releaseCatalogCacheNativeContract.spec.js +64 -0
- package/lib/module/releaseCatalogCacheNativeContract.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/types.js +0 -12
- package/lib/module/types.js.map +1 -1
- package/lib/module/wrap.js +6 -81
- package/lib/module/wrap.js.map +1 -1
- package/lib/module/wrap.spec.js +203 -56
- package/lib/module/wrap.spec.js.map +1 -1
- package/lib/module/wrap.types.js +4 -0
- package/lib/module/wrap.types.js.map +1 -0
- package/lib/typescript/commonjs/catalogCacheNative.d.ts +4 -0
- package/lib/typescript/commonjs/catalogCacheNative.d.ts.map +1 -0
- package/lib/typescript/commonjs/checkForUpdate.d.ts +14 -4
- package/lib/typescript/commonjs/checkForUpdate.d.ts.map +1 -1
- package/lib/typescript/commonjs/error.d.ts +3 -0
- package/lib/typescript/commonjs/error.d.ts.map +1 -1
- package/lib/typescript/commonjs/fetchJSON.d.ts +6 -0
- package/lib/typescript/commonjs/fetchJSON.d.ts.map +1 -0
- package/lib/typescript/commonjs/httpClient.d.ts +56 -0
- package/lib/typescript/commonjs/httpClient.d.ts.map +1 -0
- package/lib/typescript/commonjs/index.d.ts +32 -41
- package/lib/typescript/commonjs/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/native.d.ts +83 -18
- package/lib/typescript/commonjs/native.d.ts.map +1 -1
- package/lib/typescript/commonjs/notifyAppReadyAnalytics.d.ts +13 -0
- package/lib/typescript/commonjs/notifyAppReadyAnalytics.d.ts.map +1 -0
- package/lib/typescript/commonjs/notifyAppReadyAnalytics.test-utils.d.ts +8 -0
- package/lib/typescript/commonjs/notifyAppReadyAnalytics.test-utils.d.ts.map +1 -0
- package/lib/typescript/commonjs/releaseCatalogCache.d.ts +25 -0
- package/lib/typescript/commonjs/releaseCatalogCache.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 +40 -5
- package/lib/typescript/commonjs/specs/NativeHotUpdater.d.ts.map +1 -1
- package/lib/typescript/commonjs/types.d.ts +0 -118
- package/lib/typescript/commonjs/types.d.ts.map +1 -1
- package/lib/typescript/commonjs/wrap.d.ts +2 -191
- package/lib/typescript/commonjs/wrap.d.ts.map +1 -1
- package/lib/typescript/commonjs/wrap.types.d.ts +65 -0
- package/lib/typescript/commonjs/wrap.types.d.ts.map +1 -0
- package/lib/typescript/module/catalogCacheNative.d.ts +4 -0
- package/lib/typescript/module/catalogCacheNative.d.ts.map +1 -0
- package/lib/typescript/module/checkForUpdate.d.ts +14 -4
- package/lib/typescript/module/checkForUpdate.d.ts.map +1 -1
- package/lib/typescript/module/error.d.ts +3 -0
- package/lib/typescript/module/error.d.ts.map +1 -1
- package/lib/typescript/module/fetchJSON.d.ts +6 -0
- package/lib/typescript/module/fetchJSON.d.ts.map +1 -0
- package/lib/typescript/module/httpClient.d.ts +56 -0
- package/lib/typescript/module/httpClient.d.ts.map +1 -0
- package/lib/typescript/module/index.d.ts +32 -41
- package/lib/typescript/module/index.d.ts.map +1 -1
- package/lib/typescript/module/native.d.ts +83 -18
- package/lib/typescript/module/native.d.ts.map +1 -1
- package/lib/typescript/module/notifyAppReadyAnalytics.d.ts +13 -0
- package/lib/typescript/module/notifyAppReadyAnalytics.d.ts.map +1 -0
- package/lib/typescript/module/notifyAppReadyAnalytics.test-utils.d.ts +8 -0
- package/lib/typescript/module/notifyAppReadyAnalytics.test-utils.d.ts.map +1 -0
- package/lib/typescript/module/releaseCatalogCache.d.ts +25 -0
- package/lib/typescript/module/releaseCatalogCache.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 +40 -5
- package/lib/typescript/module/specs/NativeHotUpdater.d.ts.map +1 -1
- package/lib/typescript/module/types.d.ts +0 -118
- package/lib/typescript/module/types.d.ts.map +1 -1
- package/lib/typescript/module/wrap.d.ts +2 -191
- package/lib/typescript/module/wrap.d.ts.map +1 -1
- package/lib/typescript/module/wrap.types.d.ts +65 -0
- package/lib/typescript/module/wrap.types.d.ts.map +1 -0
- package/package.json +6 -24
- package/src/catalogCacheNative.spec.ts +63 -0
- package/src/catalogCacheNative.ts +74 -0
- package/src/checkForUpdate.releaseCatalog.spec.ts +596 -0
- package/src/checkForUpdate.ts +368 -97
- package/src/error.ts +7 -0
- package/src/{fetchUpdateInfo.ts → fetchJSON.ts} +8 -15
- package/src/httpClient.spec.ts +132 -0
- package/src/httpClient.ts +261 -0
- package/src/index.spec.ts +110 -203
- package/src/index.ts +70 -93
- package/src/iosInterop.spec.ts +30 -0
- package/src/native.spec.ts +226 -10
- package/src/native.ts +385 -83
- package/src/notifyAppReadyAnalytics.boundary.spec.ts +234 -0
- package/src/notifyAppReadyAnalytics.spec.ts +243 -0
- package/src/notifyAppReadyAnalytics.test-utils.ts +31 -0
- package/src/notifyAppReadyAnalytics.transport.spec.ts +130 -0
- package/src/notifyAppReadyAnalytics.ts +194 -0
- package/src/releaseCatalogCache.spec.ts +282 -0
- package/src/releaseCatalogCache.ts +341 -0
- package/src/releaseCatalogCacheNativeContract.spec.ts +80 -0
- package/src/sdkVersion.ts +1 -1
- package/src/specs/NativeHotUpdater.ts +51 -5
- package/src/types.ts +0 -140
- package/src/wrap.spec.ts +251 -56
- package/src/wrap.tsx +23 -318
- package/src/wrap.types.ts +78 -0
- package/android/src/main/java/com/hotupdater/StringResourceUtils.kt +0 -45
- package/app.plugin.js +0 -1
- package/ios/HotUpdater/Internal/HotUpdaterCrashHandler.h +0 -7
- package/ios/HotUpdater/Internal/HotUpdaterCrashHandler.mm +0 -4
- package/lib/commonjs/DefaultResolver.js +0 -50
- package/lib/commonjs/DefaultResolver.js.map +0 -1
- package/lib/commonjs/DefaultResolver.spec.js +0 -144
- package/lib/commonjs/DefaultResolver.spec.js.map +0 -1
- package/lib/commonjs/fetchUpdateInfo.js.map +0 -1
- package/lib/module/DefaultResolver.js +0 -46
- package/lib/module/DefaultResolver.js.map +0 -1
- package/lib/module/DefaultResolver.spec.js +0 -144
- package/lib/module/DefaultResolver.spec.js.map +0 -1
- package/lib/module/fetchUpdateInfo.js.map +0 -1
- package/lib/typescript/commonjs/DefaultResolver.d.ts +0 -11
- package/lib/typescript/commonjs/DefaultResolver.d.ts.map +0 -1
- package/lib/typescript/commonjs/fetchUpdateInfo.d.ts +0 -7
- package/lib/typescript/commonjs/fetchUpdateInfo.d.ts.map +0 -1
- package/lib/typescript/module/DefaultResolver.d.ts +0 -11
- package/lib/typescript/module/DefaultResolver.d.ts.map +0 -1
- package/lib/typescript/module/fetchUpdateInfo.d.ts +0 -7
- package/lib/typescript/module/fetchUpdateInfo.d.ts.map +0 -1
- package/plugin/build/transformers.js +0 -314
- package/plugin/build/withHotUpdater.js +0 -228
- package/src/DefaultResolver.spec.ts +0 -193
- package/src/DefaultResolver.ts +0 -63
|
@@ -10,6 +10,7 @@ import java.io.File
|
|
|
10
10
|
import java.io.FileInputStream
|
|
11
11
|
import java.io.FileOutputStream
|
|
12
12
|
import java.net.URL
|
|
13
|
+
import java.util.UUID
|
|
13
14
|
|
|
14
15
|
data class ChangedAssetDescriptor(
|
|
15
16
|
val fileUrl: String?,
|
|
@@ -98,6 +99,30 @@ interface BundleStorageService {
|
|
|
98
99
|
progressCallback: (UpdateProgressPayload) -> Unit,
|
|
99
100
|
)
|
|
100
101
|
|
|
102
|
+
fun stageReleaseSelection(selection: PersistedSelection): Boolean = false
|
|
103
|
+
|
|
104
|
+
fun acceptReleaseCatalog(
|
|
105
|
+
catalogId: String,
|
|
106
|
+
scopeKey: String,
|
|
107
|
+
generation: Long,
|
|
108
|
+
catalogHash: String,
|
|
109
|
+
channel: String,
|
|
110
|
+
selectionContextHash: String,
|
|
111
|
+
): Boolean = false
|
|
112
|
+
|
|
113
|
+
fun getActiveUpdateState(): Map<String, Any?> = emptyMap()
|
|
114
|
+
|
|
115
|
+
fun isReleaseSelectionCurrent(
|
|
116
|
+
catalogId: String,
|
|
117
|
+
scopeKey: String,
|
|
118
|
+
generation: Long,
|
|
119
|
+
catalogHash: String,
|
|
120
|
+
channel: String,
|
|
121
|
+
selectionContextHash: String,
|
|
122
|
+
): Boolean = false
|
|
123
|
+
|
|
124
|
+
suspend fun commitReleaseSelection(selection: PersistedSelection): Boolean = false
|
|
125
|
+
|
|
101
126
|
/**
|
|
102
127
|
* Marks the current launch as successful after the first content appeared.
|
|
103
128
|
*/
|
|
@@ -108,6 +133,30 @@ interface BundleStorageService {
|
|
|
108
133
|
*/
|
|
109
134
|
fun notifyAppReady(): Map<String, Any?>
|
|
110
135
|
|
|
136
|
+
/**
|
|
137
|
+
* Returns the stable install ID for this app installation.
|
|
138
|
+
*/
|
|
139
|
+
fun getInstallId(): String
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Returns the persisted nullable user id for this app installation.
|
|
143
|
+
*/
|
|
144
|
+
fun getUserId(): String?
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Returns the persisted nullable username for this app installation.
|
|
148
|
+
*/
|
|
149
|
+
fun getUsername(): String?
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Persists the optional user envelope for analytics.
|
|
153
|
+
* Passing null clears the stored user identity.
|
|
154
|
+
*/
|
|
155
|
+
fun setUser(
|
|
156
|
+
userId: String?,
|
|
157
|
+
username: String?,
|
|
158
|
+
)
|
|
159
|
+
|
|
111
160
|
/**
|
|
112
161
|
* Gets the crashed bundle history
|
|
113
162
|
* @return CrashedHistory containing crashed bundles
|
|
@@ -167,11 +216,15 @@ class BundleFileStorageService(
|
|
|
167
216
|
private val decompressService: DecompressService,
|
|
168
217
|
private val preferences: PreferencesService,
|
|
169
218
|
private val isolationKey: String,
|
|
219
|
+
private val defaultChannelProvider: () -> String = { HotUpdaterImpl.getChannel(context) },
|
|
170
220
|
) : BundleStorageService {
|
|
171
221
|
companion object {
|
|
172
222
|
private const val TAG = "BundleStorage"
|
|
173
223
|
}
|
|
174
224
|
|
|
225
|
+
private val releaseStateLock = Any()
|
|
226
|
+
private val pendingInstallSelections = java.util.concurrent.ConcurrentHashMap<String, PersistedSelection>()
|
|
227
|
+
|
|
175
228
|
private fun emitArchiveProgress(
|
|
176
229
|
progressCallback: (UpdateProgressPayload) -> Unit,
|
|
177
230
|
progress: Double,
|
|
@@ -487,6 +540,10 @@ class BundleFileStorageService(
|
|
|
487
540
|
|
|
488
541
|
private var currentLaunchReport: LaunchReport? = null
|
|
489
542
|
|
|
543
|
+
@Volatile
|
|
544
|
+
private var currentInstallationIdentity: InstallationIdentity? = null
|
|
545
|
+
private val installationIdentityLock = Any()
|
|
546
|
+
|
|
490
547
|
@Volatile
|
|
491
548
|
private var activeBundleMetadataSnapshot: ActiveBundleMetadataSnapshot? = null
|
|
492
549
|
private val activeBundleMetadataLock = Any()
|
|
@@ -504,6 +561,8 @@ class BundleFileStorageService(
|
|
|
504
561
|
|
|
505
562
|
private fun getLaunchReportFile(): File = File(getBundleStoreDir(), LaunchReport.LAUNCH_REPORT_FILENAME)
|
|
506
563
|
|
|
564
|
+
private fun getInstallationIdentityFile(): File = File(getBundleStoreDir(), InstallationIdentity.IDENTITY_FILENAME)
|
|
565
|
+
|
|
507
566
|
// MARK: - Metadata Operations
|
|
508
567
|
|
|
509
568
|
private fun loadMetadataOrNull(): BundleMetadata? = BundleMetadata.loadFromFile(getMetadataFile(), isolationKey)
|
|
@@ -530,6 +589,39 @@ class BundleFileStorageService(
|
|
|
530
589
|
report.saveToFile(file)
|
|
531
590
|
}
|
|
532
591
|
|
|
592
|
+
private fun resolveUpdateStrategy(): String =
|
|
593
|
+
try {
|
|
594
|
+
if (HotUpdaterImpl.getFingerprintHash(context).isNullOrEmpty()) {
|
|
595
|
+
"appVersion"
|
|
596
|
+
} else {
|
|
597
|
+
"fingerprint"
|
|
598
|
+
}
|
|
599
|
+
} catch (_: Exception) {
|
|
600
|
+
"appVersion"
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
private fun loadInstallationIdentity(): InstallationIdentity? =
|
|
604
|
+
currentInstallationIdentity ?: InstallationIdentity.loadFromFile(getInstallationIdentityFile())?.also {
|
|
605
|
+
currentInstallationIdentity = it
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
private fun saveInstallationIdentity(identity: InstallationIdentity): Boolean {
|
|
609
|
+
currentInstallationIdentity = identity
|
|
610
|
+
return identity.saveToFile(getInstallationIdentityFile())
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
private fun getOrCreateInstallationIdentity(): InstallationIdentity =
|
|
614
|
+
synchronized(installationIdentityLock) {
|
|
615
|
+
loadInstallationIdentity()?.let { return@synchronized it }
|
|
616
|
+
|
|
617
|
+
InstallationIdentity(installId = UUID.randomUUID().toString()).also {
|
|
618
|
+
saveInstallationIdentity(it)
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
private fun getKnownLaunchBundleId(metadata: BundleMetadata): String =
|
|
623
|
+
getCurrentVerifiedBundleId(metadata) ?: HotUpdaterImpl.getMinBundleId()
|
|
624
|
+
|
|
533
625
|
private fun createInitialMetadata(): BundleMetadata {
|
|
534
626
|
val currentBundleId = extractBundleIdFromCurrentURL()
|
|
535
627
|
Log.d(TAG, "Creating initial metadata with stagingBundleId: $currentBundleId")
|
|
@@ -925,10 +1017,34 @@ class BundleFileStorageService(
|
|
|
925
1017
|
): BundleMetadata {
|
|
926
1018
|
val currentVerifiedBundleId =
|
|
927
1019
|
getCurrentVerifiedBundleId(metadata)?.takeIf { it != bundleId }
|
|
1020
|
+
val incomingSelection = pendingInstallSelections.remove(bundleId)
|
|
1021
|
+
if (incomingSelection != null && !isSelectionCurrent(incomingSelection)) {
|
|
1022
|
+
throw IllegalStateException("Release catalog selection is stale")
|
|
1023
|
+
}
|
|
1024
|
+
val currentVerifiedSelection =
|
|
1025
|
+
when {
|
|
1026
|
+
metadata.stagingBundleId != null && !metadata.verificationPending -> metadata.stagingSelection
|
|
1027
|
+
metadata.stableBundleId != null -> metadata.stableSelection
|
|
1028
|
+
else -> null
|
|
1029
|
+
}?.takeIf { it.bundleId != bundleId }
|
|
1030
|
+
val stagingSelection = incomingSelection ?: PersistedSelection.legacyBundle(bundleId)
|
|
1031
|
+
val fromSelection =
|
|
1032
|
+
currentVerifiedSelection
|
|
1033
|
+
?: PersistedSelection.legacyBundle(HotUpdaterImpl.getMinBundleId())
|
|
928
1034
|
|
|
929
1035
|
return metadata.copy(
|
|
930
1036
|
stableBundleId = currentVerifiedBundleId,
|
|
931
1037
|
stagingBundleId = bundleId,
|
|
1038
|
+
stableSelection = currentVerifiedSelection,
|
|
1039
|
+
stagingSelection = stagingSelection,
|
|
1040
|
+
pendingUpdateStrategy = resolveUpdateStrategy(),
|
|
1041
|
+
pendingTransition =
|
|
1042
|
+
PendingSelectionTransition(
|
|
1043
|
+
fromReleaseId = fromSelection.releaseId,
|
|
1044
|
+
fromBundleId = fromSelection.bundleId,
|
|
1045
|
+
toReleaseId = stagingSelection.releaseId,
|
|
1046
|
+
toBundleId = stagingSelection.bundleId,
|
|
1047
|
+
),
|
|
932
1048
|
verificationPending = true,
|
|
933
1049
|
updatedAt = System.currentTimeMillis(),
|
|
934
1050
|
)
|
|
@@ -955,16 +1071,30 @@ class BundleFileStorageService(
|
|
|
955
1071
|
metadata.copy(
|
|
956
1072
|
stableBundleId = null,
|
|
957
1073
|
stagingBundleId = fallbackBundleId,
|
|
1074
|
+
stableSelection = null,
|
|
1075
|
+
stagingSelection = metadata.stableSelection,
|
|
1076
|
+
pendingUpdateStrategy = null,
|
|
1077
|
+
pendingTransition = null,
|
|
958
1078
|
verificationPending = false,
|
|
959
1079
|
updatedAt = System.currentTimeMillis(),
|
|
960
1080
|
)
|
|
1081
|
+
|
|
961
1082
|
saveMetadata(updatedMetadata)
|
|
962
1083
|
|
|
963
1084
|
val fallbackBundleUrl = fallbackBundleId?.let { getBundleUrlForId(it) }
|
|
964
1085
|
setBundleURL(fallbackBundleUrl)
|
|
965
1086
|
|
|
966
1087
|
File(getBundleStoreDir(), stagingBundleId).deleteRecursively()
|
|
967
|
-
saveLaunchReport(
|
|
1088
|
+
saveLaunchReport(
|
|
1089
|
+
LaunchReport(
|
|
1090
|
+
status = "RECOVERED",
|
|
1091
|
+
fromReleaseId = metadata.pendingTransition?.toReleaseId,
|
|
1092
|
+
fromBundleId = stagingBundleId,
|
|
1093
|
+
toReleaseId = metadata.stableSelection?.releaseId,
|
|
1094
|
+
toBundleId = fallbackBundleId ?: HotUpdaterImpl.getMinBundleId(),
|
|
1095
|
+
updateStrategy = metadata.pendingUpdateStrategy ?: resolveUpdateStrategy(),
|
|
1096
|
+
),
|
|
1097
|
+
)
|
|
968
1098
|
return true
|
|
969
1099
|
}
|
|
970
1100
|
|
|
@@ -1025,6 +1155,172 @@ class BundleFileStorageService(
|
|
|
1025
1155
|
)
|
|
1026
1156
|
}
|
|
1027
1157
|
|
|
1158
|
+
private fun releaseScopeStateKey(
|
|
1159
|
+
catalogId: String,
|
|
1160
|
+
scopeKey: String,
|
|
1161
|
+
): String = "$catalogId|$scopeKey"
|
|
1162
|
+
|
|
1163
|
+
private fun selectionContextValue(
|
|
1164
|
+
channel: String,
|
|
1165
|
+
selectionContextHash: String,
|
|
1166
|
+
): String = "$channel\n$selectionContextHash"
|
|
1167
|
+
|
|
1168
|
+
override fun acceptReleaseCatalog(
|
|
1169
|
+
catalogId: String,
|
|
1170
|
+
scopeKey: String,
|
|
1171
|
+
generation: Long,
|
|
1172
|
+
catalogHash: String,
|
|
1173
|
+
channel: String,
|
|
1174
|
+
selectionContextHash: String,
|
|
1175
|
+
): Boolean =
|
|
1176
|
+
synchronized(releaseStateLock) {
|
|
1177
|
+
val metadata = loadMetadataOrNull() ?: createInitialMetadata()
|
|
1178
|
+
val stateKey = releaseScopeStateKey(catalogId, scopeKey)
|
|
1179
|
+
val highestSeen = metadata.highestSeenCatalogs[stateKey]
|
|
1180
|
+
if (highestSeen != null && generation < highestSeen.generation) {
|
|
1181
|
+
return@synchronized false
|
|
1182
|
+
}
|
|
1183
|
+
if (
|
|
1184
|
+
highestSeen != null &&
|
|
1185
|
+
generation == highestSeen.generation &&
|
|
1186
|
+
catalogHash != highestSeen.catalogHash
|
|
1187
|
+
) {
|
|
1188
|
+
return@synchronized false
|
|
1189
|
+
}
|
|
1190
|
+
|
|
1191
|
+
val highWaters = metadata.highestSeenCatalogs.toMutableMap()
|
|
1192
|
+
highWaters[stateKey] = CatalogHighWater(generation, catalogHash)
|
|
1193
|
+
val contexts = metadata.currentSelectionContexts.toMutableMap()
|
|
1194
|
+
contexts[stateKey] = selectionContextValue(channel, selectionContextHash)
|
|
1195
|
+
saveMetadata(
|
|
1196
|
+
metadata.copy(
|
|
1197
|
+
highestSeenCatalogs = highWaters,
|
|
1198
|
+
currentSelectionContexts = contexts,
|
|
1199
|
+
updatedAt = System.currentTimeMillis(),
|
|
1200
|
+
),
|
|
1201
|
+
)
|
|
1202
|
+
}
|
|
1203
|
+
|
|
1204
|
+
private fun activeSelection(metadata: BundleMetadata): PersistedSelection? =
|
|
1205
|
+
metadata.stagingSelection
|
|
1206
|
+
?: metadata.stagingBundleId?.let(PersistedSelection::legacyBundle)
|
|
1207
|
+
?: metadata.stableSelection
|
|
1208
|
+
?: metadata.stableBundleId?.let(PersistedSelection::legacyBundle)
|
|
1209
|
+
|
|
1210
|
+
override fun getActiveUpdateState(): Map<String, Any?> =
|
|
1211
|
+
synchronized(releaseStateLock) {
|
|
1212
|
+
val metadata = loadMetadataOrNull()
|
|
1213
|
+
mapOf(
|
|
1214
|
+
"activeSelection" to metadata?.let(::activeSelection)?.toMap(),
|
|
1215
|
+
"stableSelection" to metadata?.stableSelection?.toMap(),
|
|
1216
|
+
"verificationPending" to (metadata?.verificationPending ?: false),
|
|
1217
|
+
"highestSeenCatalogs" to
|
|
1218
|
+
(metadata?.highestSeenCatalogs ?: emptyMap()).mapValues { (_, value) ->
|
|
1219
|
+
mapOf(
|
|
1220
|
+
"generation" to value.generation.toDouble(),
|
|
1221
|
+
"catalogHash" to value.catalogHash,
|
|
1222
|
+
)
|
|
1223
|
+
},
|
|
1224
|
+
)
|
|
1225
|
+
}
|
|
1226
|
+
|
|
1227
|
+
override fun isReleaseSelectionCurrent(
|
|
1228
|
+
catalogId: String,
|
|
1229
|
+
scopeKey: String,
|
|
1230
|
+
generation: Long,
|
|
1231
|
+
catalogHash: String,
|
|
1232
|
+
channel: String,
|
|
1233
|
+
selectionContextHash: String,
|
|
1234
|
+
): Boolean =
|
|
1235
|
+
synchronized(releaseStateLock) {
|
|
1236
|
+
val metadata = loadMetadataOrNull() ?: return@synchronized false
|
|
1237
|
+
val stateKey = releaseScopeStateKey(catalogId, scopeKey)
|
|
1238
|
+
metadata.highestSeenCatalogs[stateKey] == CatalogHighWater(generation, catalogHash) &&
|
|
1239
|
+
metadata.currentSelectionContexts[stateKey] ==
|
|
1240
|
+
selectionContextValue(channel, selectionContextHash)
|
|
1241
|
+
}
|
|
1242
|
+
|
|
1243
|
+
private fun isSelectionCurrent(selection: PersistedSelection): Boolean {
|
|
1244
|
+
val catalogId = selection.catalogId ?: return false
|
|
1245
|
+
val scopeKey = selection.scopeKey ?: return false
|
|
1246
|
+
val generation = selection.generation ?: return false
|
|
1247
|
+
val catalogHash = selection.catalogHash ?: return false
|
|
1248
|
+
val selectionContextHash = selection.selectionContextHash ?: return false
|
|
1249
|
+
return isReleaseSelectionCurrent(
|
|
1250
|
+
catalogId = catalogId,
|
|
1251
|
+
scopeKey = scopeKey,
|
|
1252
|
+
generation = generation,
|
|
1253
|
+
catalogHash = catalogHash,
|
|
1254
|
+
channel = selection.channel,
|
|
1255
|
+
selectionContextHash = selectionContextHash,
|
|
1256
|
+
)
|
|
1257
|
+
}
|
|
1258
|
+
|
|
1259
|
+
override fun stageReleaseSelection(selection: PersistedSelection): Boolean {
|
|
1260
|
+
if (!isSelectionCurrent(selection)) return false
|
|
1261
|
+
pendingInstallSelections[selection.bundleId] = selection
|
|
1262
|
+
return true
|
|
1263
|
+
}
|
|
1264
|
+
|
|
1265
|
+
override suspend fun commitReleaseSelection(selection: PersistedSelection): Boolean =
|
|
1266
|
+
withContext(Dispatchers.IO) {
|
|
1267
|
+
synchronized(releaseStateLock) {
|
|
1268
|
+
if (!isSelectionCurrent(selection)) return@synchronized false
|
|
1269
|
+
val metadata = loadMetadataOrNull() ?: createInitialMetadata()
|
|
1270
|
+
val nextMetadata =
|
|
1271
|
+
if (selection.kind == "BUNDLE") {
|
|
1272
|
+
when {
|
|
1273
|
+
metadata.stagingSelection?.bundleId == selection.bundleId ||
|
|
1274
|
+
metadata.stagingBundleId == selection.bundleId -> {
|
|
1275
|
+
metadata.copy(
|
|
1276
|
+
stagingBundleId = selection.bundleId,
|
|
1277
|
+
stagingSelection = selection,
|
|
1278
|
+
updatedAt = System.currentTimeMillis(),
|
|
1279
|
+
)
|
|
1280
|
+
}
|
|
1281
|
+
|
|
1282
|
+
metadata.stableSelection?.bundleId == selection.bundleId ||
|
|
1283
|
+
metadata.stableBundleId == selection.bundleId -> {
|
|
1284
|
+
metadata.copy(
|
|
1285
|
+
stableBundleId = selection.bundleId,
|
|
1286
|
+
stableSelection = selection,
|
|
1287
|
+
updatedAt = System.currentTimeMillis(),
|
|
1288
|
+
)
|
|
1289
|
+
}
|
|
1290
|
+
|
|
1291
|
+
else -> {
|
|
1292
|
+
return@synchronized false
|
|
1293
|
+
}
|
|
1294
|
+
}
|
|
1295
|
+
} else {
|
|
1296
|
+
setBundleURL(null)
|
|
1297
|
+
metadata.copy(
|
|
1298
|
+
stableBundleId = null,
|
|
1299
|
+
stableSelection = null,
|
|
1300
|
+
stagingBundleId = null,
|
|
1301
|
+
stagingSelection = selection,
|
|
1302
|
+
pendingUpdateStrategy = null,
|
|
1303
|
+
pendingTransition = null,
|
|
1304
|
+
verificationPending = false,
|
|
1305
|
+
updatedAt = System.currentTimeMillis(),
|
|
1306
|
+
)
|
|
1307
|
+
}
|
|
1308
|
+
if (!saveMetadata(nextMetadata)) return@synchronized false
|
|
1309
|
+
|
|
1310
|
+
if (selection.channel == defaultChannelProvider()) {
|
|
1311
|
+
preferences.setItem("HotUpdaterChannel", null)
|
|
1312
|
+
} else {
|
|
1313
|
+
preferences.setItem("HotUpdaterChannel", selection.channel)
|
|
1314
|
+
}
|
|
1315
|
+
if (selection.kind != "BUNDLE") {
|
|
1316
|
+
getBundleStoreDir().listFiles()?.forEach { file ->
|
|
1317
|
+
if (file.isDirectory) file.deleteRecursively()
|
|
1318
|
+
}
|
|
1319
|
+
}
|
|
1320
|
+
true
|
|
1321
|
+
}
|
|
1322
|
+
}
|
|
1323
|
+
|
|
1028
1324
|
// MARK: - Crashed History
|
|
1029
1325
|
|
|
1030
1326
|
private fun loadCrashedHistory(): CrashedHistory = CrashedHistory.loadFromFile(getCrashedHistoryFile())
|
|
@@ -1049,24 +1345,70 @@ class BundleFileStorageService(
|
|
|
1049
1345
|
return
|
|
1050
1346
|
}
|
|
1051
1347
|
|
|
1348
|
+
val transition = metadata.pendingTransition
|
|
1349
|
+
val fromBundleId = transition?.fromBundleId ?: getKnownLaunchBundleId(metadata)
|
|
1052
1350
|
saveMetadata(
|
|
1053
1351
|
metadata.copy(
|
|
1352
|
+
stableBundleId = null,
|
|
1353
|
+
stableSelection = null,
|
|
1354
|
+
pendingUpdateStrategy = null,
|
|
1355
|
+
pendingTransition = null,
|
|
1054
1356
|
verificationPending = false,
|
|
1055
1357
|
updatedAt = System.currentTimeMillis(),
|
|
1056
1358
|
),
|
|
1057
1359
|
)
|
|
1360
|
+
saveLaunchReport(
|
|
1361
|
+
LaunchReport(
|
|
1362
|
+
status = "UPDATE_APPLIED",
|
|
1363
|
+
fromReleaseId = transition?.fromReleaseId,
|
|
1364
|
+
fromBundleId = fromBundleId,
|
|
1365
|
+
toReleaseId = transition?.toReleaseId,
|
|
1366
|
+
toBundleId = stagingBundleId,
|
|
1367
|
+
updateStrategy = metadata.pendingUpdateStrategy ?: resolveUpdateStrategy(),
|
|
1368
|
+
),
|
|
1369
|
+
)
|
|
1058
1370
|
}
|
|
1059
1371
|
|
|
1060
1372
|
// MARK: - notifyAppReady
|
|
1061
1373
|
|
|
1062
1374
|
override fun notifyAppReady(): Map<String, Any?> {
|
|
1063
|
-
val report = loadLaunchReport()
|
|
1375
|
+
val report = loadLaunchReport()
|
|
1376
|
+
if (report == null) {
|
|
1377
|
+
val metadata = loadMetadataOrNull()
|
|
1378
|
+
if (metadata?.verificationPending == true && metadata.stagingBundleId != null) {
|
|
1379
|
+
return mapOf("status" to "PENDING")
|
|
1380
|
+
}
|
|
1381
|
+
return mapOf("status" to "UNCHANGED")
|
|
1382
|
+
}
|
|
1064
1383
|
return buildMap {
|
|
1065
1384
|
put("status", report.status)
|
|
1066
|
-
report.
|
|
1385
|
+
report.fromReleaseId?.let { put("fromReleaseId", it) }
|
|
1386
|
+
report.fromBundleId?.let { put("fromBundleId", it) }
|
|
1387
|
+
report.toReleaseId?.let { put("toReleaseId", it) }
|
|
1388
|
+
report.toBundleId?.let { put("toBundleId", it) }
|
|
1389
|
+
report.updateStrategy?.let { put("updateStrategy", it) }
|
|
1067
1390
|
}
|
|
1068
1391
|
}
|
|
1069
1392
|
|
|
1393
|
+
override fun getInstallId(): String = getOrCreateInstallationIdentity().installId
|
|
1394
|
+
|
|
1395
|
+
override fun getUserId(): String? = getOrCreateInstallationIdentity().userId
|
|
1396
|
+
|
|
1397
|
+
override fun getUsername(): String? = getOrCreateInstallationIdentity().username
|
|
1398
|
+
|
|
1399
|
+
override fun setUser(
|
|
1400
|
+
userId: String?,
|
|
1401
|
+
username: String?,
|
|
1402
|
+
) = synchronized(installationIdentityLock) {
|
|
1403
|
+
saveInstallationIdentity(
|
|
1404
|
+
getOrCreateInstallationIdentity().copy(
|
|
1405
|
+
userId = userId?.trim()?.takeIf { it.isNotEmpty() },
|
|
1406
|
+
username = username?.trim()?.takeIf { it.isNotEmpty() },
|
|
1407
|
+
),
|
|
1408
|
+
)
|
|
1409
|
+
Unit
|
|
1410
|
+
}
|
|
1411
|
+
|
|
1070
1412
|
// MARK: - Bundle URL Operations
|
|
1071
1413
|
|
|
1072
1414
|
override fun setBundleURL(localPath: String?): Boolean {
|
|
@@ -1128,7 +1470,6 @@ class BundleFileStorageService(
|
|
|
1128
1470
|
TAG,
|
|
1129
1471
|
"updateBundle bundleId $bundleId fileUrl $fileUrl fileHash $fileHash manifestUrl $manifestUrl",
|
|
1130
1472
|
)
|
|
1131
|
-
|
|
1132
1473
|
// If no URL is provided, reset to fallback and clean up all bundles
|
|
1133
1474
|
if (fileUrl.isNullOrEmpty()) {
|
|
1134
1475
|
Log.d(TAG, "fileUrl is null or empty, resetting to fallback bundle")
|
|
@@ -1141,7 +1482,12 @@ class BundleFileStorageService(
|
|
|
1141
1482
|
}
|
|
1142
1483
|
|
|
1143
1484
|
// 2. Reset metadata to initial state (clear all bundle references)
|
|
1144
|
-
val
|
|
1485
|
+
val previousMetadata = loadMetadataOrNull()
|
|
1486
|
+
val metadata =
|
|
1487
|
+
createInitialMetadata().copy(
|
|
1488
|
+
highestSeenCatalogs = previousMetadata?.highestSeenCatalogs ?: emptyMap(),
|
|
1489
|
+
currentSelectionContexts = previousMetadata?.currentSelectionContexts ?: emptyMap(),
|
|
1490
|
+
)
|
|
1145
1491
|
val saveResult = saveMetadata(metadata)
|
|
1146
1492
|
if (!saveResult) {
|
|
1147
1493
|
Log.w(TAG, "Failed to reset metadata")
|
|
@@ -1932,11 +2278,16 @@ class BundleFileStorageService(
|
|
|
1932
2278
|
}
|
|
1933
2279
|
|
|
1934
2280
|
val clearedMetadata =
|
|
1935
|
-
BundleMetadata(
|
|
2281
|
+
(loadMetadataOrNull() ?: BundleMetadata()).copy(
|
|
1936
2282
|
isolationKey = isolationKey,
|
|
1937
2283
|
stableBundleId = null,
|
|
1938
2284
|
stagingBundleId = null,
|
|
2285
|
+
stableSelection = null,
|
|
2286
|
+
stagingSelection = null,
|
|
2287
|
+
pendingUpdateStrategy = null,
|
|
2288
|
+
pendingTransition = null,
|
|
1939
2289
|
verificationPending = false,
|
|
2290
|
+
updatedAt = System.currentTimeMillis(),
|
|
1940
2291
|
)
|
|
1941
2292
|
|
|
1942
2293
|
if (!saveMetadata(clearedMetadata)) {
|