FairEmail/app/src/main/java/com/bugsnag/android/LastRunInfoStore.kt

98 lines
3.2 KiB
Kotlin

package com.bugsnag.android
import com.bugsnag.android.internal.ImmutableConfig
import java.io.File
import java.util.concurrent.locks.ReentrantReadWriteLock
import kotlin.concurrent.withLock
private const val KEY_VALUE_DELIMITER = "="
private const val KEY_CONSECUTIVE_LAUNCH_CRASHES = "consecutiveLaunchCrashes"
private const val KEY_CRASHED = "crashed"
private const val KEY_CRASHED_DURING_LAUNCH = "crashedDuringLaunch"
/**
* Persists/loads [LastRunInfo] on disk, which allows Bugsnag to determine
* whether the previous application launch crashed or not. This class is thread-safe.
*/
internal class LastRunInfoStore(config: ImmutableConfig) {
val file: File = File(config.persistenceDirectory.value, "last-run-info")
private val logger: Logger = config.logger
private val lock = ReentrantReadWriteLock()
fun persist(lastRunInfo: LastRunInfo) {
lock.writeLock().withLock {
try {
persistImpl(lastRunInfo)
} catch (exc: Throwable) {
logger.w("Unexpectedly failed to persist LastRunInfo.", exc)
}
}
}
private fun persistImpl(lastRunInfo: LastRunInfo) {
val text = KeyValueWriter().apply {
add(KEY_CONSECUTIVE_LAUNCH_CRASHES, lastRunInfo.consecutiveLaunchCrashes)
add(KEY_CRASHED, lastRunInfo.crashed)
add(KEY_CRASHED_DURING_LAUNCH, lastRunInfo.crashedDuringLaunch)
}.toString()
file.writeText(text)
logger.d("Persisted: $text")
}
fun load(): LastRunInfo? {
return lock.readLock().withLock {
try {
loadImpl()
} catch (exc: Throwable) {
logger.w("Unexpectedly failed to load LastRunInfo.", exc)
null
}
}
}
private fun loadImpl(): LastRunInfo? {
if (!file.exists()) {
return null
}
val lines = file.readText().split("\n").filter { it.isNotBlank() }
if (lines.size != 3) {
logger.w("Unexpected number of lines when loading LastRunInfo. Skipping load. $lines")
return null
}
return try {
val consecutiveLaunchCrashes = lines[0].asIntValue(KEY_CONSECUTIVE_LAUNCH_CRASHES)
val crashed = lines[1].asBooleanValue(KEY_CRASHED)
val crashedDuringLaunch = lines[2].asBooleanValue(KEY_CRASHED_DURING_LAUNCH)
val runInfo = LastRunInfo(consecutiveLaunchCrashes, crashed, crashedDuringLaunch)
logger.d("Loaded: $runInfo")
runInfo
} catch (exc: NumberFormatException) {
// unlikely case where information was serialized incorrectly
logger.w("Failed to read consecutiveLaunchCrashes from saved lastRunInfo", exc)
null
}
}
private fun String.asIntValue(key: String) =
substringAfter("$key$KEY_VALUE_DELIMITER").toInt()
private fun String.asBooleanValue(key: String) =
substringAfter("$key$KEY_VALUE_DELIMITER").toBoolean()
}
private class KeyValueWriter {
private val sb = StringBuilder()
fun add(key: String, value: Any) {
sb.append("$key$KEY_VALUE_DELIMITER$value")
sb.append("\n")
}
override fun toString() = sb.toString()
}