1 package expo.modules.updates.launcher 2 3 import android.content.Context 4 import android.os.AsyncTask 5 import android.util.Log 6 import expo.modules.updates.UpdatesConfiguration 7 import expo.modules.updates.db.entity.AssetEntity 8 import expo.modules.updates.db.entity.UpdateEntity 9 import expo.modules.updates.loader.EmbeddedLoader 10 import expo.modules.updates.manifest.BareUpdateManifest 11 import expo.modules.updates.manifest.EmbeddedManifest 12 import org.apache.commons.io.FileUtils 13 import java.io.File 14 15 /** 16 * Implementation of [Launcher] which always uses the update embedded in the application package, 17 * avoiding SQLite and the expo-updates file store entirely. 18 * 19 * This is only used in rare cases when the database/file system is corrupt or otherwise 20 * inaccessible, but we still want to avoid crashing. The exported property `isEmergencyLaunch` 21 * on [UpdatesModule] should be `true` whenever this class is used. 22 */ 23 class NoDatabaseLauncher @JvmOverloads constructor( 24 context: Context, 25 configuration: UpdatesConfiguration, 26 fatalException: Exception? = null 27 ) : Launcher { 28 override var bundleAssetName: String? = null 29 override val launchedUpdate: UpdateEntity? 30 get() = null 31 override val launchAssetFile: String? 32 get() = null 33 override var localAssetFiles: Map<AssetEntity, String>? = null 34 private set 35 override val isUsingEmbeddedAssets: Boolean 36 get() = localAssetFiles == null 37 writeErrorToLognull38 private fun writeErrorToLog(context: Context, fatalException: Exception) { 39 try { 40 val errorLogFile = File(context.filesDir, ERROR_LOG_FILENAME) 41 val exceptionString = fatalException.toString() 42 FileUtils.writeStringToFile(errorLogFile, exceptionString, "UTF-8", true) 43 } catch (e: Exception) { 44 Log.e(TAG, "Failed to write fatal error to log", e) 45 } 46 } 47 48 companion object { 49 private val TAG = NoDatabaseLauncher::class.java.simpleName 50 51 private const val ERROR_LOG_FILENAME = "expo-error.log" 52 consumeErrorLognull53 fun consumeErrorLog(context: Context): String? { 54 return try { 55 val errorLogFile = File(context.filesDir, ERROR_LOG_FILENAME) 56 if (!errorLogFile.exists()) { 57 return null 58 } 59 val logContents = FileUtils.readFileToString(errorLogFile, "UTF-8") 60 errorLogFile.delete() 61 logContents 62 } catch (e: Exception) { 63 Log.e(TAG, "Failed to read error log", e) 64 null 65 } 66 } 67 } 68 69 init { 70 val embeddedUpdateManifest = EmbeddedManifest.get(context, configuration) 71 ?: throw RuntimeException("Failed to launch with embedded update because the embedded manifest was null") 72 73 if (embeddedUpdateManifest is BareUpdateManifest) { 74 bundleAssetName = EmbeddedLoader.BARE_BUNDLE_FILENAME 75 localAssetFiles = null 76 } else { 77 bundleAssetName = EmbeddedLoader.BUNDLE_FILENAME <lambda>null78 localAssetFiles = mutableMapOf<AssetEntity, String>().apply { 79 for (asset in embeddedUpdateManifest.assetEntityList) { 80 this[asset] = "asset:///" + asset.embeddedAssetFilename 81 } 82 } 83 } 84 85 if (fatalException != null) { <lambda>null86 AsyncTask.execute { writeErrorToLog(context, fatalException) } 87 } 88 } 89 } 90