summaryrefslogtreecommitdiffhomepage
path: root/android
diff options
context:
space:
mode:
authorJanito Vaqueiro Ferreira Filho <janito@mullvad.net>2020-12-11 15:03:29 +0000
committerJanito Vaqueiro Ferreira Filho <janito@mullvad.net>2020-12-14 13:22:56 +0000
commit7ee3dcc5b46faef4bc0c0aee0231f1a66a12b148 (patch)
treeb813dc144587deff97c57c838bed8e6db866ce15 /android
parent9810fc6f52bd678f74afbf494c1dda851aee3843 (diff)
downloadmullvadvpn-7ee3dcc5b46faef4bc0c0aee0231f1a66a12b148.tar.xz
mullvadvpn-7ee3dcc5b46faef4bc0c0aee0231f1a66a12b148.zip
Refactor `MullvadProblemReport` to be an actor
Diffstat (limited to 'android')
-rw-r--r--android/src/main/kotlin/net/mullvad/mullvadvpn/dataproxy/MullvadProblemReport.kt142
1 files changed, 78 insertions, 64 deletions
diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/dataproxy/MullvadProblemReport.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/dataproxy/MullvadProblemReport.kt
index 12dd4cd43b..ae2d1b2df2 100644
--- a/android/src/main/kotlin/net/mullvad/mullvadvpn/dataproxy/MullvadProblemReport.kt
+++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/dataproxy/MullvadProblemReport.kt
@@ -5,114 +5,128 @@ import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.Deferred
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
-import kotlinx.coroutines.Job
import kotlinx.coroutines.async
-import kotlinx.coroutines.launch
+import kotlinx.coroutines.channels.Channel
+import kotlinx.coroutines.channels.ClosedReceiveChannelException
+import kotlinx.coroutines.channels.actor
+import kotlinx.coroutines.channels.sendBlocking
const val PROBLEM_REPORT_FILE = "problem_report.txt"
class MullvadProblemReport {
+ private sealed class Command {
+ class Collect() : Command()
+ class Load(val logs: CompletableDeferred<String>) : Command()
+ class Send(val result: CompletableDeferred<Boolean>) : Command()
+ class Delete() : Command()
+ }
+
val logDirectory = CompletableDeferred<File>()
val resourcesDirectory = CompletableDeferred<File>()
+ private val commandChannel = spawnActor()
+
private val problemReportPath = GlobalScope.async(Dispatchers.Default) {
File(logDirectory.await(), PROBLEM_REPORT_FILE)
}
- private var collectJob: Deferred<Boolean>? = null
- private var sendJob: Deferred<Boolean>? = null
- private var deleteJob: Job? = null
+ private var isCollected = false
var confirmNoEmail: CompletableDeferred<Boolean>? = null
var userEmail = ""
var userMessage = ""
- val isActive: Boolean
- get() {
- synchronized(this) {
- val collectJob = this.collectJob
- val sendJob = this.sendJob
-
- return (collectJob != null && collectJob.isActive) ||
- (sendJob != null && sendJob.isActive)
- }
- }
-
init {
System.loadLibrary("mullvad_jni")
}
fun collect() {
- synchronized(this) {
- if (!isActive) {
- collectJob = GlobalScope.async(Dispatchers.Default) {
- val logDirectoryPath = logDirectory.await().absolutePath
- val reportPath = problemReportPath.await().absolutePath
-
- deleteReportFile().join()
- collectReport(logDirectoryPath, reportPath)
- }
- }
- }
+ commandChannel.sendBlocking(Command.Collect())
}
suspend fun load(): String {
- if (collectJob == null) {
- collect()
- }
+ val logs = CompletableDeferred<String>()
- if (collectJob?.await() ?: false) {
- return problemReportPath.await().readText()
- } else {
- return "Failed to collect logs for problem report"
- }
+ commandChannel.send(Command.Load(logs))
+
+ return logs.await()
}
fun send(): Deferred<Boolean> {
- synchronized(this) {
- var currentJob = sendJob
+ val result = CompletableDeferred<Boolean>()
- if (currentJob == null || currentJob.isCompleted) {
- currentJob = GlobalScope.async(Dispatchers.Default) {
- val result = (collectJob?.await() ?: false) &&
- sendProblemReport(
- userEmail,
- userMessage,
- problemReportPath.await().absolutePath,
- resourcesDirectory.await().absolutePath
- )
+ commandChannel.sendBlocking(Command.Send(result))
- if (result) {
- deleteReportFile()
- }
+ return result
+ }
- result
- }
+ fun deleteReportFile() {
+ commandChannel.sendBlocking(Command.Delete())
+ }
- sendJob = currentJob
- }
+ private fun spawnActor() = GlobalScope.actor<Command>(Dispatchers.Default, Channel.UNLIMITED) {
+ try {
+ while (true) {
+ val command = channel.receive()
- return currentJob
+ when (command) {
+ is Command.Collect -> doCollect()
+ is Command.Load -> command.logs.complete(doLoad())
+ is Command.Send -> command.result.complete(doSend())
+ is Command.Delete -> doDelete()
+ }
+ }
+ } catch (exception: ClosedReceiveChannelException) {
}
}
- fun deleteReportFile(): Job {
- synchronized(this) {
- val oldDeleteJob = deleteJob
+ private suspend fun doCollect() {
+ val logDirectoryPath = logDirectory.await().absolutePath
+ val reportPath = problemReportPath.await().absolutePath
- val job = GlobalScope.launch(Dispatchers.Default) {
- oldDeleteJob?.join()
- problemReportPath.await().delete()
- collectJob = null
- }
+ doDelete()
+
+ isCollected = collectReport(logDirectoryPath, reportPath)
+ }
- deleteJob = job
+ private suspend fun doLoad(): String {
+ if (!isCollected) {
+ doCollect()
+ }
- return job
+ if (isCollected) {
+ return problemReportPath.await().readText()
+ } else {
+ return "Failed to collect logs for problem report"
}
}
+ private suspend fun doSend(): Boolean {
+ if (!isCollected) {
+ doCollect()
+ }
+
+ val result = isCollected &&
+ sendProblemReport(
+ userEmail,
+ userMessage,
+ problemReportPath.await().absolutePath,
+ resourcesDirectory.await().absolutePath
+ )
+
+ if (result) {
+ doDelete()
+ }
+
+ return result
+ }
+
+ private suspend fun doDelete() {
+ problemReportPath.await().delete()
+ isCollected = false
+ }
+
private external fun collectReport(logDirectory: String, reportPath: String): Boolean
private external fun sendProblemReport(
userEmail: String,