Skip to content

Commit

Permalink
Merge branch 'main' of github.com:ooni/probe-multiplatform into issue…
Browse files Browse the repository at this point in the history
…s/26
  • Loading branch information
aanorbel committed Aug 6, 2024
2 parents 8e37f16 + 951882a commit 5b25633
Show file tree
Hide file tree
Showing 6 changed files with 305 additions and 119 deletions.
211 changes: 135 additions & 76 deletions composeApp/src/commonMain/kotlin/org/ooni/engine/Engine.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,55 +2,42 @@ package org.ooni.engine

import co.touchlab.kermit.Logger
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.IO
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.channelFlow
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.withContext
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import org.ooni.engine.OonimkallBridge.SubmitMeasurementResults
import org.ooni.engine.models.TaskEvent
import org.ooni.engine.models.TaskEventResult
import org.ooni.engine.models.TaskLogLevel
import org.ooni.engine.models.TaskOrigin
import org.ooni.engine.models.TaskSettings
import org.ooni.probe.config.Config
import org.ooni.probe.shared.Platform
import org.ooni.probe.shared.PlatformInfo

class Engine(
private val bridge: OonimkallBridge,
private val json: Json,
private val baseFilePath: String,
private val cacheDir: String,
private val taskEventMapper: TaskEventMapper,
private val networkTypeFinder: NetworkTypeFinder,
private val platformInfo: PlatformInfo,
private val backgroundDispatcher: CoroutineDispatcher,
) {
fun startTask(taskSettings: TaskSettings): Flow<TaskEvent> =
fun startTask(
name: String,
inputs: List<String>?,
taskOrigin: TaskOrigin,
): Flow<TaskEvent> =
channelFlow {
val finalSettings =
taskSettings.copy(
stateDir = "$baseFilePath/state",
tunnelDir = "$baseFilePath/tunnel",
tempDir = cacheDir,
assetsDir = "$baseFilePath/assets",
)

val response = httpDo(finalSettings)
response?.let {
Logger.d(it)
}

val checkinResults = checkIn(finalSettings)

val task =
bridge.startTask(
json.encodeToString(
checkinResults?.urls?.map { it.url }?.let {
finalSettings.copy(
inputs = it.take(1),
options =
finalSettings.options.copy(
maxRuntime = 90,
),
)
} ?: finalSettings,
),
)
val taskSettings = buildTaskSettings(name, inputs, taskOrigin)
val settingsSerialized = json.encodeToString(taskSettings)
val task = bridge.startTask(settingsSerialized)

while (!task.isDone()) {
val eventJson = task.waitForNextEvent()
Expand All @@ -63,62 +50,134 @@ class Engine(
task.interrupt()
}
}
}

fun session(finalSettings: TaskSettings): OonimkallBridge.Session {
return bridge.newSession(
OonimkallBridge.SessionConfig(
softwareName = finalSettings.options.softwareName,
softwareVersion = finalSettings.options.softwareVersion,
proxy = null,
probeServicesURL = "https://api.prod.ooni.io",
assetsDir = finalSettings.assetsDir.toString(),
stateDir = finalSettings.stateDir.toString(),
tempDir = finalSettings.tempDir.toString(),
tunnelDir = finalSettings.tunnelDir.toString(),
logger =
object : OonimkallBridge.Logger {
override fun debug(msg: String?) {
msg?.let { Logger.d(it) }
}
}.flowOn(backgroundDispatcher)

override fun info(msg: String?) {
msg?.let { Logger.d(it) }
}

override fun warn(msg: String?) {
msg?.let { Logger.d(it) }
}
},
verbose = true,
),
)
}
suspend fun submitMeasurements(
measurement: String,
taskOrigin: TaskOrigin = TaskOrigin.OoniRun,
): SubmitMeasurementResults =
withContext(backgroundDispatcher) {
val sessionConfig = buildSessionConfig(taskOrigin)
session(sessionConfig).submitMeasurement(measurement)
}

suspend fun checkIn(finalSettings: TaskSettings): OonimkallBridge.CheckInResults? {
return withContext(Dispatchers.IO) {
return@withContext session(finalSettings).checkIn(
suspend fun checkIn(
categories: List<String>,
taskOrigin: TaskOrigin,
): OonimkallBridge.CheckInResults =
withContext(backgroundDispatcher) {
val sessionConfig = buildSessionConfig(taskOrigin)
session(sessionConfig).checkIn(
OonimkallBridge.CheckInConfig(
charging = true,
onWiFi = true,
platform = "android",
runType = "autorun",
softwareName = "ooniprobe-android-unattended",
softwareVersion = "3.8.8",
webConnectivityCategories = listOf("NEWS"),
platform = platformInfo.platform.value,
runType = taskOrigin.value,
softwareName = sessionConfig.softwareName,
softwareVersion = sessionConfig.softwareVersion,
webConnectivityCategories = categories,
),
)
}
}

suspend fun httpDo(finalSettings: TaskSettings): String? {
return withContext(Dispatchers.IO) {
return@withContext session(finalSettings).httpDo(
suspend fun httpDo(
method: String,
url: String,
taskOrigin: TaskOrigin = TaskOrigin.OoniRun,
): String? =
withContext(backgroundDispatcher) {
session(buildSessionConfig(taskOrigin)).httpDo(
OonimkallBridge.HTTPRequest(
url = "https://api.dev.ooni.io/api/v2/oonirun/links/10426",
method = "GET",
method = method,
url = url,
),
).body
}

private fun session(sessionConfig: OonimkallBridge.SessionConfig): OonimkallBridge.Session {
return bridge.newSession(sessionConfig)
}

private fun buildTaskSettings(
name: String,
inputs: List<String>?,
taskOrigin: TaskOrigin,
) = TaskSettings(
name = name,
inputs = inputs.orEmpty(),
disabledEvents =
listOf(
"status.queued",
"status.update.websites",
"failure.report_close",
),
// TODO: fetch from preferences
logLevel = TaskLogLevel.Info,
stateDir = "$baseFilePath/state",
tunnelDir = "$baseFilePath/tunnel",
tempDir = cacheDir,
assetsDir = "$baseFilePath/assets",
options =
TaskSettings.Options(
// TODO: fetch from preferences
noCollector = true,
softwareName = buildSoftwareName(taskOrigin),
softwareVersion = platformInfo.version,
// TODO: fetch from preferences
maxRuntime = -1,
),
annotations =
TaskSettings.Annotations(
networkType = networkTypeFinder(),
flavor = buildSoftwareName(taskOrigin),
origin = taskOrigin,
),
// TODO: fetch from preferences
proxy = null,
)

private fun buildSessionConfig(taskOrigin: TaskOrigin) =
OonimkallBridge.SessionConfig(
softwareName = buildSoftwareName(taskOrigin),
softwareVersion = platformInfo.version,
// TODO: fetch from preferences
proxy = null,
probeServicesURL = Config.OONI_API_BASE_URL,
stateDir = "$baseFilePath/state",
tunnelDir = "$baseFilePath/tunnel",
tempDir = cacheDir,
assetsDir = "$baseFilePath/assets",
logger = oonimkallLogger,
verbose = false,
)

private fun buildSoftwareName(taskOrigin: TaskOrigin) =
Config.BASE_SOFTWARE_NAME +
"-" +
platformInfo.platform.value +
"-" +
(if (taskOrigin == TaskOrigin.AutoRun) "-" + "unattended" else "")

private val Platform.value
get() =
when (this) {
Platform.Android -> "android"
Platform.Ios -> "ios"
}

private val oonimkallLogger by lazy {
object : OonimkallBridge.Logger {
override fun debug(msg: String?) {
msg?.let { Logger.d(it) }
}

override fun info(msg: String?) {
msg?.let { Logger.d(it) }
}

override fun warn(msg: String?) {
msg?.let { Logger.d(it) }
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,15 @@ data class TaskSettings(
@SerialName("inputs") val inputs: List<String>,
@SerialName("version") val version: Int = 1,
@SerialName("log_level") val logLevel: TaskLogLevel,
@SerialName("disabled_events") val disabledEvents: List<String> = emptyList(),
@SerialName("disabled_events") val disabledEvents: List<String>,
@SerialName("state_dir") val stateDir: String,
@SerialName("temp_dir") val tempDir: String,
@SerialName("tunnel_dir") val tunnelDir: String,
@SerialName("assets_dir") val assetsDir: String,
@SerialName("options") val options: Options,
@SerialName("annotations") val annotations: Annotations,
// Optional
@SerialName("proxy") val proxy: String? = null,
@SerialName("state_dir") val stateDir: String? = null,
@SerialName("temp_dir") val tempDir: String? = null,
@SerialName("tunnel_dir") val tunnelDir: String? = null,
@SerialName("assets_dir") val assetsDir: String? = null,
@SerialName("proxy") val proxy: String?,
) {
@Serializable
data class Options(
Expand All @@ -27,7 +27,7 @@ data class TaskSettings(
// built from the flavors + debug or not + -unattended if autorun
@SerialName("software_name") val softwareName: String,
@SerialName("software_version") val softwareVersion: String,
@SerialName("max_runtime") val maxRuntime: Int? = null,
@SerialName("max_runtime") val maxRuntime: Int,
)

@Serializable
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package org.ooni.probe.di
import androidx.annotation.VisibleForTesting
import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.Preferences
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.IO
import kotlinx.serialization.json.Json
import org.ooni.engine.Engine
import org.ooni.engine.NetworkTypeFinder
Expand All @@ -25,15 +27,33 @@ class Dependencies(
private val cacheDir: String,
private val dataStore: DataStore<Preferences>,
) {
// Commong

private val backgroundDispatcher = Dispatchers.IO

// Data

private val json by lazy { buildJson() }

// Engine

private val networkTypeFinder by lazy { NetworkTypeFinder { NetworkType.Unknown("") } } // TODO

private val taskEventMapper by lazy { TaskEventMapper(networkTypeFinder, json) }
private val engine by lazy { Engine(oonimkallBridge, json, baseFileDir, cacheDir, taskEventMapper) }

private val engine by lazy {
Engine(
bridge = oonimkallBridge,
json = json,
baseFilePath = baseFileDir,
cacheDir = cacheDir,
taskEventMapper = taskEventMapper,
networkTypeFinder = networkTypeFinder,
platformInfo = platformInfo,
backgroundDispatcher = backgroundDispatcher,
)
}

private val preferenceManager by lazy { SettingsRepository(dataStore) }

// ViewModels
Expand Down
Loading

0 comments on commit 5b25633

Please sign in to comment.