@hot-updater/react-native 1.0.0-rc.4 → 1.0.0-rc.41
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/build.gradle +13 -2
- package/android/proguard-rules.pro +8 -2
- package/android/src/bridge/BridgeReactInstance.kt +38 -0
- package/android/src/bridgeless/BridgeReactInstance.kt +24 -0
- package/android/src/main/java/com/hotupdater/ActivityStartGate.kt +94 -0
- package/android/src/main/java/com/hotupdater/BuiltInAssetResolver.kt +286 -0
- package/android/src/main/java/com/hotupdater/BundleFileStorageService.kt +1064 -844
- package/android/src/main/java/com/hotupdater/BundleMetadata.kt +50 -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 +46 -30
- package/android/src/main/java/com/hotupdater/HotUpdaterRecoveryManager.kt +75 -192
- 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/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/newarch/JavaScriptExceptionHooks.kt +48 -0
- package/android/src/newarch/ReactIntegrationManager.kt +6 -32
- 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/android/src/oldarch/JavaScriptExceptionHooks.kt +71 -0
- package/ios/HotUpdater/Internal/BrotliFileDecompressor.swift +167 -0
- package/ios/HotUpdater/Internal/BuiltInAssetResolver.swift +196 -0
- package/ios/HotUpdater/Internal/BundleFileStorageService.swift +1032 -1116
- package/ios/HotUpdater/Internal/BundleMetadata.swift +61 -54
- 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 +32 -67
- package/ios/HotUpdater/Internal/HotUpdaterImpl.swift +229 -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 +96 -0
- package/lib/commonjs/appReady.js.map +1 -0
- package/lib/commonjs/checkForUpdate.js +121 -69
- 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 +5 -14
- package/lib/commonjs/error.js.map +1 -1
- package/lib/commonjs/fetchJSON.js +16 -27
- package/lib/commonjs/fetchJSON.js.map +1 -1
- package/lib/commonjs/fetchUpdateResponse.js +60 -0
- package/lib/commonjs/fetchUpdateResponse.js.map +1 -0
- package/lib/commonjs/httpClient.js +64 -95
- package/lib/commonjs/httpClient.js.map +1 -1
- package/lib/commonjs/index.js +355 -324
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/init.types.js +6 -0
- package/lib/commonjs/init.types.js.map +1 -0
- package/lib/commonjs/native.js +77 -63
- package/lib/commonjs/native.js.map +1 -1
- package/lib/commonjs/pluginHost.js +41 -0
- package/lib/commonjs/pluginHost.js.map +1 -0
- package/lib/commonjs/releaseCatalogCache.js +36 -87
- 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 +145 -0
- package/lib/commonjs/updateError.js.map +1 -0
- package/lib/commonjs/wrap.js +46 -38
- package/lib/commonjs/wrap.js.map +1 -1
- package/lib/module/appReady.js +90 -0
- package/lib/module/appReady.js.map +1 -0
- package/lib/module/checkForUpdate.js +114 -64
- 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 +5 -14
- package/lib/module/error.js.map +1 -1
- package/lib/module/fetchJSON.js +14 -26
- package/lib/module/fetchJSON.js.map +1 -1
- package/lib/module/fetchUpdateResponse.js +55 -0
- package/lib/module/fetchUpdateResponse.js.map +1 -0
- package/lib/module/httpClient.js +63 -94
- package/lib/module/httpClient.js.map +1 -1
- package/lib/module/index.js +335 -327
- package/lib/module/index.js.map +1 -1
- package/lib/module/init.types.js +4 -0
- package/lib/module/init.types.js.map +1 -0
- package/lib/module/native.js +70 -56
- package/lib/module/native.js.map +1 -1
- package/lib/module/pluginHost.js +36 -0
- package/lib/module/pluginHost.js.map +1 -0
- package/lib/module/releaseCatalogCache.js +32 -82
- 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 +139 -0
- package/lib/module/updateError.js.map +1 -0
- package/lib/module/wrap.js +47 -38
- package/lib/module/wrap.js.map +1 -1
- package/lib/typescript/commonjs/appReady.d.ts +30 -0
- package/lib/typescript/commonjs/appReady.d.ts.map +1 -0
- package/lib/typescript/commonjs/checkForUpdate.d.ts +20 -6
- 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 +5 -14
- package/lib/typescript/commonjs/error.d.ts.map +1 -1
- package/lib/typescript/commonjs/fetchJSON.d.ts +5 -5
- package/lib/typescript/commonjs/fetchJSON.d.ts.map +1 -1
- package/lib/typescript/commonjs/fetchUpdateResponse.d.ts +14 -0
- package/lib/typescript/commonjs/fetchUpdateResponse.d.ts.map +1 -0
- package/lib/typescript/commonjs/httpClient.d.ts +5 -34
- package/lib/typescript/commonjs/httpClient.d.ts.map +1 -1
- package/lib/typescript/commonjs/index.d.ts +124 -116
- package/lib/typescript/commonjs/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/init.types.d.ts +21 -0
- package/lib/typescript/commonjs/init.types.d.ts.map +1 -0
- package/lib/typescript/commonjs/native.d.ts +32 -25
- package/lib/typescript/commonjs/native.d.ts.map +1 -1
- package/lib/typescript/commonjs/pluginHost.d.ts +8 -0
- package/lib/typescript/commonjs/pluginHost.d.ts.map +1 -0
- package/lib/typescript/commonjs/releaseCatalogCache.d.ts +8 -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 +35 -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 +2 -3
- package/lib/typescript/commonjs/wrap.d.ts.map +1 -1
- package/lib/typescript/commonjs/wrap.types.d.ts +13 -41
- package/lib/typescript/commonjs/wrap.types.d.ts.map +1 -1
- package/lib/typescript/module/appReady.d.ts +30 -0
- package/lib/typescript/module/appReady.d.ts.map +1 -0
- package/lib/typescript/module/checkForUpdate.d.ts +20 -6
- 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 +5 -14
- package/lib/typescript/module/error.d.ts.map +1 -1
- package/lib/typescript/module/fetchJSON.d.ts +5 -5
- package/lib/typescript/module/fetchJSON.d.ts.map +1 -1
- package/lib/typescript/module/fetchUpdateResponse.d.ts +14 -0
- package/lib/typescript/module/fetchUpdateResponse.d.ts.map +1 -0
- package/lib/typescript/module/httpClient.d.ts +5 -34
- package/lib/typescript/module/httpClient.d.ts.map +1 -1
- package/lib/typescript/module/index.d.ts +124 -116
- package/lib/typescript/module/index.d.ts.map +1 -1
- package/lib/typescript/module/init.types.d.ts +21 -0
- package/lib/typescript/module/init.types.d.ts.map +1 -0
- package/lib/typescript/module/native.d.ts +32 -25
- package/lib/typescript/module/native.d.ts.map +1 -1
- package/lib/typescript/module/pluginHost.d.ts +8 -0
- package/lib/typescript/module/pluginHost.d.ts.map +1 -0
- package/lib/typescript/module/releaseCatalogCache.d.ts +8 -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 +35 -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 +2 -3
- package/lib/typescript/module/wrap.d.ts.map +1 -1
- package/lib/typescript/module/wrap.types.d.ts +13 -41
- package/lib/typescript/module/wrap.types.d.ts.map +1 -1
- package/package.json +8 -4
- package/src/appReady.ts +147 -0
- package/src/checkForUpdate.ts +156 -89
- package/src/clientPlugin.ts +21 -0
- package/src/error.ts +5 -15
- package/src/fetchJSON.ts +14 -30
- package/src/fetchUpdateResponse.ts +60 -0
- package/src/httpClient.ts +89 -160
- package/src/index.ts +413 -425
- package/src/init.types.ts +24 -0
- package/src/native.ts +101 -105
- package/src/pluginHost.ts +52 -0
- package/src/releaseCatalogCache.ts +43 -172
- package/src/sdkVersion.ts +1 -1
- package/src/specs/NativeHotUpdater.ts +34 -48
- package/src/store.ts +2 -23
- package/src/types.ts +1 -1
- package/src/updateError.ts +167 -0
- package/src/wrap.tsx +60 -43
- package/src/wrap.types.ts +18 -46
- 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 -616
- 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 -302
- 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 -883
- 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 -329
- 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 -615
- 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 -301
- 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 -882
- 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 -330
- 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 -612
- package/src/httpClient.spec.ts +0 -132
- package/src/index.spec.ts +0 -337
- package/src/iosInterop.spec.ts +0 -30
- package/src/native.spec.ts +0 -945
- 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 -406
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
package com.hotupdater
|
|
2
2
|
|
|
3
3
|
import android.app.ActivityOptions
|
|
4
|
+
import android.app.Application
|
|
4
5
|
import android.content.Context
|
|
5
6
|
import android.content.Intent
|
|
6
7
|
import android.os.Process
|
|
@@ -18,6 +19,9 @@ class HotUpdaterImpl {
|
|
|
18
19
|
private val preferences: PreferencesService
|
|
19
20
|
private val recoveryManager: HotUpdaterRecoveryManager
|
|
20
21
|
private val releaseCatalogCache: ReleaseCatalogCacheService
|
|
22
|
+
private val installIdentity: InstallIdentityService
|
|
23
|
+
private val keyValueStorage: KeyValueStorageService
|
|
24
|
+
private val activityStartGate: ActivityStartGate
|
|
21
25
|
private var currentLaunchSelection: LaunchSelection? = null
|
|
22
26
|
|
|
23
27
|
/**
|
|
@@ -31,11 +35,14 @@ class HotUpdaterImpl {
|
|
|
31
35
|
this.context = context.applicationContext
|
|
32
36
|
this.bundleStorage = bundleStorage
|
|
33
37
|
this.preferences = preferences
|
|
38
|
+
this.activityStartGate = ActivityStartGate(this.context as? Application)
|
|
34
39
|
this.recoveryManager = HotUpdaterRecoveryManager(this.context)
|
|
35
40
|
this.releaseCatalogCache =
|
|
36
41
|
ReleaseCatalogCacheService(
|
|
37
42
|
File(this.context.noBackupFilesDir, "hot-updater-release-catalog-cache"),
|
|
38
43
|
)
|
|
44
|
+
this.installIdentity = InstallIdentityService.create(this.context)
|
|
45
|
+
this.keyValueStorage = KeyValueStorageService.create(this.context)
|
|
39
46
|
}
|
|
40
47
|
|
|
41
48
|
/**
|
|
@@ -83,14 +90,12 @@ class HotUpdaterImpl {
|
|
|
83
90
|
val fileSystem = FileManagerService(appContext)
|
|
84
91
|
val preferences = createPreferences(appContext)
|
|
85
92
|
val downloadService = OkHttpDownloadService()
|
|
86
|
-
val decompressService = DecompressService()
|
|
87
93
|
val isolationKey = getIsolationKey(appContext)
|
|
88
94
|
|
|
89
95
|
return BundleFileStorageService(
|
|
90
96
|
appContext,
|
|
91
97
|
fileSystem,
|
|
92
98
|
downloadService,
|
|
93
|
-
decompressService,
|
|
94
99
|
preferences,
|
|
95
100
|
isolationKey,
|
|
96
101
|
)
|
|
@@ -268,36 +273,37 @@ class HotUpdaterImpl {
|
|
|
268
273
|
fun getJSBundleFile(): String = prepareLaunchIfNeeded().bundleUrl
|
|
269
274
|
|
|
270
275
|
/**
|
|
271
|
-
* Updates the bundle from
|
|
276
|
+
* Updates the bundle from its manifest and complete asset descriptors.
|
|
272
277
|
* @param bundleId ID of the bundle to update
|
|
273
|
-
* @param
|
|
274
|
-
* @param
|
|
278
|
+
* @param manifestUrl URL of the target manifest
|
|
279
|
+
* @param manifestFileHash Hash or signature used to verify the target manifest
|
|
280
|
+
* @param assets Complete target asset descriptor map
|
|
275
281
|
* @param progressCallback Callback for download progress updates
|
|
282
|
+
* @return how the staged bundle arrived
|
|
276
283
|
* @throws HotUpdaterException if the update fails
|
|
277
284
|
*/
|
|
278
285
|
suspend fun updateBundle(
|
|
279
286
|
bundleId: String,
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
manifestFileHash: String?,
|
|
284
|
-
changedAssets: Map<String, ChangedAssetDescriptor>?,
|
|
287
|
+
manifestUrl: String,
|
|
288
|
+
manifestFileHash: String,
|
|
289
|
+
assets: Map<String, ChangedAssetDescriptor>,
|
|
285
290
|
channel: String?,
|
|
286
291
|
selection: PersistedSelection? = null,
|
|
292
|
+
archiveUrl: String? = null,
|
|
287
293
|
progressCallback: (UpdateProgressPayload) -> Unit,
|
|
288
|
-
) {
|
|
294
|
+
): UpdateBundleResult {
|
|
289
295
|
if (selection != null && !bundleStorage.stageReleaseSelection(selection)) {
|
|
290
|
-
throw
|
|
296
|
+
throw StaleReleaseSelectionException()
|
|
291
297
|
}
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
298
|
+
val result =
|
|
299
|
+
bundleStorage.updateBundle(
|
|
300
|
+
bundleId,
|
|
301
|
+
manifestUrl,
|
|
302
|
+
manifestFileHash,
|
|
303
|
+
assets,
|
|
304
|
+
archiveUrl,
|
|
305
|
+
progressCallback,
|
|
306
|
+
)
|
|
301
307
|
|
|
302
308
|
if (!channel.isNullOrEmpty()) {
|
|
303
309
|
if (channel == getDefaultChannel()) {
|
|
@@ -306,6 +312,7 @@ class HotUpdaterImpl {
|
|
|
306
312
|
preferences.setItem(CHANNEL_STORAGE_KEY, channel)
|
|
307
313
|
}
|
|
308
314
|
}
|
|
315
|
+
return result
|
|
309
316
|
}
|
|
310
317
|
|
|
311
318
|
/**
|
|
@@ -423,17 +430,17 @@ class HotUpdaterImpl {
|
|
|
423
430
|
|
|
424
431
|
suspend fun commitReleaseSelection(selection: PersistedSelection): Boolean = bundleStorage.commitReleaseSelection(selection)
|
|
425
432
|
|
|
426
|
-
fun getInstallId(): String =
|
|
427
|
-
|
|
428
|
-
fun getUserId(): String? = bundleStorage.getUserId()
|
|
433
|
+
fun getInstallId(): String = installIdentity.getInstallId()
|
|
429
434
|
|
|
430
|
-
|
|
435
|
+
/** Reads a value from the client plugins' key-value store. */
|
|
436
|
+
fun getStorageItem(key: String): String? = keyValueStorage.getItem(key)
|
|
431
437
|
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
438
|
+
/** Writes a value to the client plugins' key-value store; null removes the key. */
|
|
439
|
+
fun setStorageItem(
|
|
440
|
+
key: String,
|
|
441
|
+
value: String?,
|
|
435
442
|
) {
|
|
436
|
-
|
|
443
|
+
keyValueStorage.setItem(key, value)
|
|
437
444
|
}
|
|
438
445
|
|
|
439
446
|
/**
|
|
@@ -467,7 +474,6 @@ class HotUpdaterImpl {
|
|
|
467
474
|
|
|
468
475
|
/**
|
|
469
476
|
* Gets the current launched bundle ID.
|
|
470
|
-
* Reads manifest.json first and falls back to the legacy BUNDLE_ID file.
|
|
471
477
|
* Built-in bundle fallback is handled in JS.
|
|
472
478
|
*/
|
|
473
479
|
fun getBundleId(): String? {
|
|
@@ -505,6 +511,16 @@ class HotUpdaterImpl {
|
|
|
505
511
|
|
|
506
512
|
val pendingRecovery = recoveryManager.consumePendingCrashRecovery()
|
|
507
513
|
val selection = bundleStorage.prepareLaunch(pendingRecovery)
|
|
514
|
+
// A headless JS task, such as a background push, loads the bundle without
|
|
515
|
+
// an activity and never renders. Only a launch with an activity may leave
|
|
516
|
+
// an unfinished launch for the next process to roll back.
|
|
517
|
+
activityStartGate.runWhenActivityStarted(
|
|
518
|
+
if (selection.shouldRollbackOnCrash) {
|
|
519
|
+
{ bundleStorage.markLaunchStarted(selection.launchedBundleId) }
|
|
520
|
+
} else {
|
|
521
|
+
null
|
|
522
|
+
},
|
|
523
|
+
)
|
|
508
524
|
recoveryManager.startMonitoring(
|
|
509
525
|
bundleId = selection.launchedBundleId,
|
|
510
526
|
shouldRollback = selection.shouldRollbackOnCrash,
|
|
@@ -9,6 +9,7 @@ import android.os.Looper
|
|
|
9
9
|
import android.os.Process
|
|
10
10
|
import android.os.SystemClock
|
|
11
11
|
import android.util.Log
|
|
12
|
+
import androidx.core.util.AtomicFile
|
|
12
13
|
import com.facebook.react.ReactApplication
|
|
13
14
|
import com.facebook.react.bridge.JSExceptionHandler
|
|
14
15
|
import com.facebook.react.bridge.ReactContext
|
|
@@ -16,6 +17,7 @@ import com.facebook.react.bridge.ReactMarker
|
|
|
16
17
|
import com.facebook.react.bridge.ReactMarkerConstants
|
|
17
18
|
import org.json.JSONObject
|
|
18
19
|
import java.io.File
|
|
20
|
+
import java.io.FileOutputStream
|
|
19
21
|
import java.lang.reflect.Field
|
|
20
22
|
import kotlin.system.exitProcess
|
|
21
23
|
|
|
@@ -28,10 +30,7 @@ internal class HotUpdaterRecoveryManager(
|
|
|
28
30
|
private val crashMarkerFile = File(bundleStoreDir, CRASH_MARKER_FILENAME)
|
|
29
31
|
private val watchdogStateFile = File(bundleStoreDir, WATCHDOG_STATE_FILENAME)
|
|
30
32
|
|
|
31
|
-
private
|
|
32
|
-
private var shouldRollbackOnCrash = false
|
|
33
|
-
private var isMonitoring = false
|
|
34
|
-
private var recoveryRequested = false
|
|
33
|
+
private val launchState = HotUpdaterRecoveryState()
|
|
35
34
|
private var contentAppearedCallback: ((String?) -> Unit)? = null
|
|
36
35
|
private var recoveryRestartCallback: (() -> Boolean)? = null
|
|
37
36
|
|
|
@@ -39,10 +38,7 @@ internal class HotUpdaterRecoveryManager(
|
|
|
39
38
|
Runnable {
|
|
40
39
|
cancelRecoveryWatchdog()
|
|
41
40
|
Log.d(TAG, "Stopping crash monitoring for current launch")
|
|
42
|
-
|
|
43
|
-
recoveryRequested = false
|
|
44
|
-
shouldRollbackOnCrash = false
|
|
45
|
-
currentBundleId = null
|
|
41
|
+
launchState.stop()
|
|
46
42
|
contentAppearedCallback = null
|
|
47
43
|
recoveryRestartCallback = null
|
|
48
44
|
activeManager = null
|
|
@@ -52,7 +48,7 @@ internal class HotUpdaterRecoveryManager(
|
|
|
52
48
|
private val installJsExceptionHooksRunnable =
|
|
53
49
|
object : Runnable {
|
|
54
50
|
override fun run() {
|
|
55
|
-
if (
|
|
51
|
+
if (launchState.snapshot() == null) {
|
|
56
52
|
return
|
|
57
53
|
}
|
|
58
54
|
|
|
@@ -74,7 +70,7 @@ internal class HotUpdaterRecoveryManager(
|
|
|
74
70
|
}
|
|
75
71
|
|
|
76
72
|
return try {
|
|
77
|
-
val recovery = PendingCrashRecovery.
|
|
73
|
+
val recovery = PendingCrashRecovery.loadFromFile(crashMarkerFile) ?: return null
|
|
78
74
|
Log.d(
|
|
79
75
|
TAG,
|
|
80
76
|
"Consumed pending crash marker bundleId=${recovery.launchedBundleId} shouldRollback=${recovery.shouldRollback}",
|
|
@@ -96,11 +92,8 @@ internal class HotUpdaterRecoveryManager(
|
|
|
96
92
|
) {
|
|
97
93
|
crashMarkerFile.parentFile?.mkdirs()
|
|
98
94
|
contentAppearedCallback = onContentAppeared
|
|
99
|
-
currentBundleId = bundleId
|
|
100
|
-
shouldRollbackOnCrash = shouldRollback
|
|
101
|
-
isMonitoring = true
|
|
102
|
-
recoveryRequested = false
|
|
103
95
|
recoveryRestartCallback = onRecoveryRestartRequested
|
|
96
|
+
launchState.start(bundleId, shouldRollback)
|
|
104
97
|
activeManager = this
|
|
105
98
|
|
|
106
99
|
ensureExceptionHandlerInstalled()
|
|
@@ -122,17 +115,13 @@ internal class HotUpdaterRecoveryManager(
|
|
|
122
115
|
}
|
|
123
116
|
|
|
124
117
|
private fun handleContentAppeared() {
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
Log.d(TAG, "First content appeared for bundleId=$currentBundleId")
|
|
118
|
+
val launch = launchState.completeLaunch() ?: return
|
|
119
|
+
Log.d(TAG, "First content appeared for bundleId=${launch.bundleId}")
|
|
130
120
|
ReactMarker.removeListener(contentAppearedListener)
|
|
131
121
|
mainHandler.removeCallbacks(installJsExceptionHooksRunnable)
|
|
132
122
|
mainHandler.removeCallbacks(stopMonitoringRunnable)
|
|
133
|
-
contentAppearedCallback?.invoke(
|
|
134
|
-
|
|
135
|
-
updateNativeLaunchState(currentBundleId, false)
|
|
123
|
+
contentAppearedCallback?.invoke(launch.bundleId)
|
|
124
|
+
updateNativeLaunchState(launch.bundleId, false)
|
|
136
125
|
cancelRecoveryWatchdog()
|
|
137
126
|
mainHandler.postDelayed(stopMonitoringRunnable, MONITORING_GRACE_PERIOD_MS)
|
|
138
127
|
}
|
|
@@ -145,11 +134,10 @@ internal class HotUpdaterRecoveryManager(
|
|
|
145
134
|
previousExceptionHandler = Thread.getDefaultUncaughtExceptionHandler()
|
|
146
135
|
Thread.setDefaultUncaughtExceptionHandler { thread, throwable ->
|
|
147
136
|
val manager = activeManager
|
|
148
|
-
manager?.writeCrashMarker()
|
|
149
|
-
|
|
150
137
|
if (manager?.requestAutomaticRecovery() == true) {
|
|
151
|
-
|
|
152
|
-
|
|
138
|
+
// The separate restart activity kills this process after it starts.
|
|
139
|
+
// A second fatal error must not kill it while that request is in flight.
|
|
140
|
+
return@setDefaultUncaughtExceptionHandler
|
|
153
141
|
}
|
|
154
142
|
|
|
155
143
|
previousExceptionHandler?.uncaughtException(thread, throwable)
|
|
@@ -161,20 +149,21 @@ internal class HotUpdaterRecoveryManager(
|
|
|
161
149
|
exceptionHandlerInstalled = true
|
|
162
150
|
}
|
|
163
151
|
|
|
164
|
-
private fun writeCrashMarker() {
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
}
|
|
168
|
-
|
|
152
|
+
private fun writeCrashMarker(launch: HotUpdaterRecoveryState.Launch) {
|
|
153
|
+
val atomicFile = AtomicFile(crashMarkerFile)
|
|
154
|
+
var output: FileOutputStream? = null
|
|
169
155
|
try {
|
|
170
156
|
val payload =
|
|
171
157
|
JSONObject().apply {
|
|
172
|
-
put("bundleId",
|
|
173
|
-
put("shouldRollback",
|
|
158
|
+
put("bundleId", launch.bundleId ?: JSONObject.NULL)
|
|
159
|
+
put("shouldRollback", launch.shouldRollback)
|
|
174
160
|
}
|
|
175
161
|
crashMarkerFile.parentFile?.mkdirs()
|
|
176
|
-
|
|
162
|
+
output = atomicFile.startWrite()
|
|
163
|
+
output.write(payload.toString().toByteArray(Charsets.UTF_8))
|
|
164
|
+
atomicFile.finishWrite(output)
|
|
177
165
|
} catch (e: Exception) {
|
|
166
|
+
output?.let(atomicFile::failWrite)
|
|
178
167
|
Log.e(TAG, "Failed to write crash marker", e)
|
|
179
168
|
}
|
|
180
169
|
}
|
|
@@ -194,35 +183,28 @@ internal class HotUpdaterRecoveryManager(
|
|
|
194
183
|
cancelRecoveryWatchdogAlarm(appContext)
|
|
195
184
|
}
|
|
196
185
|
|
|
197
|
-
private fun requestAutomaticRecovery(): Boolean {
|
|
198
|
-
|
|
199
|
-
|
|
186
|
+
private fun requestAutomaticRecovery(exception: Exception? = null): Boolean {
|
|
187
|
+
val request = launchState.requestRecovery()
|
|
188
|
+
if (exception != null) {
|
|
189
|
+
Log.e(TAG, "Caught React startup exception for bundleId=${launchState.snapshot()?.bundleId}", exception)
|
|
200
190
|
}
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
return true
|
|
205
|
-
}
|
|
206
|
-
recoveryRequested = true
|
|
191
|
+
if (request == null) {
|
|
192
|
+
launchState.snapshot()?.let(::writeCrashMarker)
|
|
193
|
+
return false
|
|
207
194
|
}
|
|
208
|
-
|
|
195
|
+
if (!request.shouldRestart) return true
|
|
196
|
+
writeCrashMarker(request.launch)
|
|
209
197
|
val started = recoveryRestartCallback?.invoke() == true
|
|
198
|
+
launchState.finishRecoveryRequest(request, started)
|
|
210
199
|
if (!started) {
|
|
211
|
-
synchronized(this) {
|
|
212
|
-
recoveryRequested = false
|
|
213
|
-
}
|
|
214
200
|
Log.w(TAG, "Failed to schedule automatic recovery restart")
|
|
215
201
|
} else {
|
|
216
|
-
Log.i(TAG, "Scheduled automatic recovery restart for bundleId=$
|
|
202
|
+
Log.i(TAG, "Scheduled automatic recovery restart for bundleId=${request.launch.bundleId}")
|
|
217
203
|
}
|
|
218
204
|
return started
|
|
219
205
|
}
|
|
220
206
|
|
|
221
|
-
private fun handleJavaScriptException(exception: Exception): Boolean
|
|
222
|
-
Log.e(TAG, "Caught React startup exception for bundleId=$currentBundleId", exception)
|
|
223
|
-
writeCrashMarker()
|
|
224
|
-
return requestAutomaticRecovery()
|
|
225
|
-
}
|
|
207
|
+
private fun handleJavaScriptException(exception: Exception): Boolean = requestAutomaticRecovery(exception)
|
|
226
208
|
|
|
227
209
|
private fun ensureNativeSignalHandlerInstalled() {
|
|
228
210
|
if (signalHandlerInstalled || !loadNativeLibrary()) {
|
|
@@ -252,141 +234,11 @@ internal class HotUpdaterRecoveryManager(
|
|
|
252
234
|
}
|
|
253
235
|
}
|
|
254
236
|
|
|
237
|
+
// Each architecture's source set reaches its own React instance, so the old
|
|
238
|
+
// architecture's ReactNativeHost is compiled only into old architecture builds.
|
|
255
239
|
private fun installJavaScriptExceptionHooks() {
|
|
256
240
|
val application = appContext as? ReactApplication ?: return
|
|
257
|
-
|
|
258
|
-
if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
|
|
259
|
-
installReactHostExceptionHook(application)
|
|
260
|
-
} else {
|
|
261
|
-
installLegacyExceptionHook(application)
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
private fun installReactHostExceptionHook(application: ReactApplication) {
|
|
266
|
-
val reactHost = getReactHost(application) ?: return
|
|
267
|
-
val reactHostDelegate =
|
|
268
|
-
findField(reactHost.javaClass, "mReactHostDelegate")?.let { field ->
|
|
269
|
-
field.isAccessible = true
|
|
270
|
-
field.get(reactHost)
|
|
271
|
-
}
|
|
272
|
-
?: findField(reactHost.javaClass, "reactHostDelegate")?.let { field ->
|
|
273
|
-
field.isAccessible = true
|
|
274
|
-
field.get(reactHost)
|
|
275
|
-
}
|
|
276
|
-
?: return
|
|
277
|
-
|
|
278
|
-
val delegateIdentity = System.identityHashCode(reactHostDelegate)
|
|
279
|
-
if (patchedReactHostDelegateIds.add(delegateIdentity)) {
|
|
280
|
-
val exceptionHandlerField = findField(reactHostDelegate.javaClass, "exceptionHandler")
|
|
281
|
-
if (exceptionHandlerField == null) {
|
|
282
|
-
patchedReactHostDelegateIds.remove(delegateIdentity)
|
|
283
|
-
return
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
exceptionHandlerField.isAccessible = true
|
|
287
|
-
@Suppress("UNCHECKED_CAST")
|
|
288
|
-
val previousHandler =
|
|
289
|
-
exceptionHandlerField.get(reactHostDelegate) as? (Exception) -> Unit
|
|
290
|
-
|
|
291
|
-
exceptionHandlerField.set(reactHostDelegate) { exception: Exception ->
|
|
292
|
-
if (activeManager?.handleJavaScriptException(exception) != true) {
|
|
293
|
-
previousHandler?.invoke(exception) ?: throw exception
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
val reactContext =
|
|
299
|
-
findMethod(reactHost.javaClass, "getCurrentReactContext")?.invoke(reactHost) as? ReactContext
|
|
300
|
-
reactContext?.let { patchReactContextExceptionHandler(it) }
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
private fun installLegacyExceptionHook(application: ReactApplication) {
|
|
304
|
-
val instanceManager = application.reactNativeHost.reactInstanceManager
|
|
305
|
-
val managerIdentity = System.identityHashCode(instanceManager)
|
|
306
|
-
if (patchedInstanceManagerIds.add(managerIdentity)) {
|
|
307
|
-
val exceptionHandlerField = findField(instanceManager.javaClass, "mJSExceptionHandler")
|
|
308
|
-
if (exceptionHandlerField == null) {
|
|
309
|
-
patchedInstanceManagerIds.remove(managerIdentity)
|
|
310
|
-
return
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
exceptionHandlerField.isAccessible = true
|
|
314
|
-
val previousHandler = exceptionHandlerField.get(instanceManager) as? JSExceptionHandler
|
|
315
|
-
exceptionHandlerField.set(instanceManager, RecoveryJSExceptionHandler(previousHandler))
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
instanceManager.currentReactContext?.let { reactContext ->
|
|
319
|
-
patchReactContextExceptionHandler(reactContext)
|
|
320
|
-
patchCatalystInstanceExceptionHandler(reactContext)
|
|
321
|
-
}
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
private fun patchReactContextExceptionHandler(reactContext: ReactContext) {
|
|
325
|
-
val contextIdentity = System.identityHashCode(reactContext)
|
|
326
|
-
if (!patchedReactContextIds.add(contextIdentity)) {
|
|
327
|
-
return
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
val previousHandler = reactContext.jsExceptionHandler
|
|
331
|
-
if (previousHandler is RecoveryJSExceptionHandler) {
|
|
332
|
-
return
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
reactContext.setJSExceptionHandler(RecoveryJSExceptionHandler(previousHandler))
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
private fun patchCatalystInstanceExceptionHandler(reactContext: ReactContext) {
|
|
339
|
-
val catalystInstance =
|
|
340
|
-
try {
|
|
341
|
-
reactContext.catalystInstance
|
|
342
|
-
} catch (_: Exception) {
|
|
343
|
-
null
|
|
344
|
-
} ?: return
|
|
345
|
-
|
|
346
|
-
val catalystIdentity = System.identityHashCode(catalystInstance)
|
|
347
|
-
if (!patchedCatalystInstanceIds.add(catalystIdentity)) {
|
|
348
|
-
return
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
val exceptionHandlerField = findField(catalystInstance.javaClass, "mJSExceptionHandler")
|
|
352
|
-
if (exceptionHandlerField == null) {
|
|
353
|
-
patchedCatalystInstanceIds.remove(catalystIdentity)
|
|
354
|
-
return
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
exceptionHandlerField.isAccessible = true
|
|
358
|
-
val previousHandler = exceptionHandlerField.get(catalystInstance) as? JSExceptionHandler
|
|
359
|
-
if (previousHandler is RecoveryJSExceptionHandler) {
|
|
360
|
-
return
|
|
361
|
-
}
|
|
362
|
-
exceptionHandlerField.set(catalystInstance, RecoveryJSExceptionHandler(previousHandler))
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
private fun getReactHost(application: ReactApplication): Any? =
|
|
366
|
-
try {
|
|
367
|
-
findMethod(application.javaClass, "getReactHost")?.invoke(application)
|
|
368
|
-
} catch (_: Exception) {
|
|
369
|
-
null
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
private fun findMethod(
|
|
373
|
-
clazz: Class<*>,
|
|
374
|
-
name: String,
|
|
375
|
-
) = runCatching { clazz.getMethod(name) }.getOrNull()
|
|
376
|
-
|
|
377
|
-
private fun findField(
|
|
378
|
-
clazz: Class<*>,
|
|
379
|
-
name: String,
|
|
380
|
-
): Field? {
|
|
381
|
-
var current: Class<*>? = clazz
|
|
382
|
-
while (true) {
|
|
383
|
-
val currentClass = current ?: break
|
|
384
|
-
runCatching { currentClass.getDeclaredField(name) }
|
|
385
|
-
.getOrNull()
|
|
386
|
-
?.let { return it }
|
|
387
|
-
current = currentClass.superclass
|
|
388
|
-
}
|
|
389
|
-
return null
|
|
241
|
+
JavaScriptExceptionHooks.install(application)
|
|
390
242
|
}
|
|
391
243
|
|
|
392
244
|
private fun loadNativeLibrary(): Boolean {
|
|
@@ -413,11 +265,11 @@ internal class HotUpdaterRecoveryManager(
|
|
|
413
265
|
shouldRollback: Boolean,
|
|
414
266
|
)
|
|
415
267
|
|
|
416
|
-
|
|
268
|
+
internal class RecoveryJSExceptionHandler(
|
|
417
269
|
private val previousHandler: JSExceptionHandler?,
|
|
418
270
|
) : JSExceptionHandler {
|
|
419
271
|
override fun handleException(e: Exception) {
|
|
420
|
-
if (
|
|
272
|
+
if (!handleJavaScriptExceptionForRecovery(e)) {
|
|
421
273
|
previousHandler?.handleException(e) ?: throw e
|
|
422
274
|
}
|
|
423
275
|
}
|
|
@@ -450,10 +302,40 @@ internal class HotUpdaterRecoveryManager(
|
|
|
450
302
|
@Volatile
|
|
451
303
|
private var activeManager: HotUpdaterRecoveryManager? = null
|
|
452
304
|
|
|
453
|
-
private val patchedInstanceManagerIds = mutableSetOf<Int>()
|
|
454
|
-
private val patchedCatalystInstanceIds = mutableSetOf<Int>()
|
|
455
305
|
private val patchedReactContextIds = mutableSetOf<Int>()
|
|
456
|
-
|
|
306
|
+
|
|
307
|
+
/** Hands a JavaScript error to the monitored launch; false when it does not recover it. */
|
|
308
|
+
internal fun handleJavaScriptExceptionForRecovery(exception: Exception): Boolean =
|
|
309
|
+
activeManager?.handleJavaScriptException(exception) == true
|
|
310
|
+
|
|
311
|
+
internal fun patchReactContextExceptionHandler(reactContext: ReactContext) {
|
|
312
|
+
val contextIdentity = System.identityHashCode(reactContext)
|
|
313
|
+
if (!patchedReactContextIds.add(contextIdentity)) {
|
|
314
|
+
return
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
val previousHandler = reactContext.jsExceptionHandler
|
|
318
|
+
if (previousHandler is RecoveryJSExceptionHandler) {
|
|
319
|
+
return
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
reactContext.setJSExceptionHandler(RecoveryJSExceptionHandler(previousHandler))
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
internal fun findField(
|
|
326
|
+
clazz: Class<*>,
|
|
327
|
+
name: String,
|
|
328
|
+
): Field? {
|
|
329
|
+
var current: Class<*>? = clazz
|
|
330
|
+
while (true) {
|
|
331
|
+
val currentClass = current ?: break
|
|
332
|
+
runCatching { currentClass.getDeclaredField(name) }
|
|
333
|
+
.getOrNull()
|
|
334
|
+
?.let { return it }
|
|
335
|
+
current = currentClass.superclass
|
|
336
|
+
}
|
|
337
|
+
return null
|
|
338
|
+
}
|
|
457
339
|
|
|
458
340
|
@JvmStatic
|
|
459
341
|
fun handleRecoveryWatchdog(context: Context) {
|
|
@@ -467,7 +349,8 @@ internal class HotUpdaterRecoveryManager(
|
|
|
467
349
|
}
|
|
468
350
|
|
|
469
351
|
val crashMarkerFile = File(getBundleStoreDir(appContext), CRASH_MARKER_FILENAME)
|
|
470
|
-
|
|
352
|
+
val recovery = PendingCrashRecovery.loadFromFile(crashMarkerFile)
|
|
353
|
+
if (recovery?.shouldRollback == true && recovery.launchedBundleId != null) {
|
|
471
354
|
Log.i(TAG, "Recovery watchdog detected crash marker, relaunching app")
|
|
472
355
|
watchdogStateFile.delete()
|
|
473
356
|
cancelRecoveryWatchdogAlarm(appContext)
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
package com.hotupdater
|
|
2
|
+
|
|
3
|
+
internal class HotUpdaterRecoveryState {
|
|
4
|
+
data class Launch(
|
|
5
|
+
val bundleId: String?,
|
|
6
|
+
val shouldRollback: Boolean,
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
data class RecoveryRequest(
|
|
10
|
+
val launch: Launch,
|
|
11
|
+
val shouldRestart: Boolean,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
private enum class RestartState { NONE, REQUESTED, SCHEDULED, FAILED }
|
|
15
|
+
|
|
16
|
+
private var launch: Launch? = null
|
|
17
|
+
private var restartState = RestartState.NONE
|
|
18
|
+
private var contentAppeared = false
|
|
19
|
+
|
|
20
|
+
@Synchronized
|
|
21
|
+
fun start(
|
|
22
|
+
bundleId: String?,
|
|
23
|
+
shouldRollback: Boolean,
|
|
24
|
+
) {
|
|
25
|
+
launch = Launch(bundleId, shouldRollback)
|
|
26
|
+
restartState = RestartState.NONE
|
|
27
|
+
contentAppeared = false
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
@Synchronized
|
|
31
|
+
fun snapshot(): Launch? = launch
|
|
32
|
+
|
|
33
|
+
@Synchronized
|
|
34
|
+
fun stop() {
|
|
35
|
+
launch = null
|
|
36
|
+
restartState = RestartState.NONE
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
@Synchronized
|
|
40
|
+
fun completeLaunch(): Launch? {
|
|
41
|
+
val current = launch ?: return null
|
|
42
|
+
if (contentAppeared || restartState != RestartState.NONE) return null
|
|
43
|
+
contentAppeared = true
|
|
44
|
+
launch = current.copy(shouldRollback = false)
|
|
45
|
+
return current
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
@Synchronized
|
|
49
|
+
fun requestRecovery(): RecoveryRequest? {
|
|
50
|
+
val current = launch?.takeIf { it.shouldRollback } ?: return null
|
|
51
|
+
val shouldRestart = restartState == RestartState.NONE || restartState == RestartState.FAILED
|
|
52
|
+
if (shouldRestart) restartState = RestartState.REQUESTED
|
|
53
|
+
return RecoveryRequest(current, shouldRestart)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
@Synchronized
|
|
57
|
+
fun finishRecoveryRequest(
|
|
58
|
+
request: RecoveryRequest,
|
|
59
|
+
started: Boolean,
|
|
60
|
+
) {
|
|
61
|
+
if (launch !== request.launch) return
|
|
62
|
+
restartState = if (started) RestartState.SCHEDULED else RestartState.FAILED
|
|
63
|
+
}
|
|
64
|
+
}
|
|
@@ -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
|
+
}
|