Skip to content

Commit

Permalink
Merge pull request #30 from ooni/engine-bridge
Browse files Browse the repository at this point in the history
Engine bridge interface + Android implementation
  • Loading branch information
sdsantos authored Aug 5, 2024
2 parents abdedaa + 16291b5 commit 092afc7
Show file tree
Hide file tree
Showing 12 changed files with 346 additions and 30 deletions.
Original file line number Diff line number Diff line change
@@ -1,8 +1,16 @@
package org.ooni.engine

import oonimkall.CheckInConfig
import oonimkall.CheckInConfigWebConnectivity
import oonimkall.CheckInInfoWebConnectivity
import oonimkall.HTTPRequest
import oonimkall.Logger
import oonimkall.Oonimkall
import oonimkall.SessionConfig

class AndroidOonimkallBridge : OonimkallBridge {
override fun startTask(settingsSerialized: String): OonimkallBridge.Task {
val task = oonimkall.Oonimkall.startTask(settingsSerialized)
val task = Oonimkall.startTask(settingsSerialized)
return object : OonimkallBridge.Task {
override fun interrupt() {
task.interrupt()
Expand All @@ -13,4 +21,102 @@ class AndroidOonimkallBridge : OonimkallBridge {
override fun waitForNextEvent() = task.waitForNextEvent()
}
}

override fun newSession(sessionConfig: OonimkallBridge.SessionConfig): OonimkallBridge.Session {
val session = Oonimkall.newSession(sessionConfig.toMk())

return object : OonimkallBridge.Session {
override fun submitMeasurement(measurement: String): OonimkallBridge.SubmitMeasurementResults {
val context = session.newContextWithTimeout(CONTEXT_TIMEOUT)
val results = session.submit(context, measurement)
return OonimkallBridge.SubmitMeasurementResults(
updatedMeasurement = results.updatedMeasurement,
updatedReportId = results.updatedReportID,
)
}

override fun checkIn(config: OonimkallBridge.CheckInConfig): OonimkallBridge.CheckInResults {
val context = session.newContextWithTimeout(CONTEXT_TIMEOUT)
val info = session.checkIn(context, config.toMk())
return OonimkallBridge.CheckInResults(
reportId = info.webConnectivity?.reportID,
urls = info.webConnectivity.getUrlInfos(),
)
}

override fun httpDo(request: OonimkallBridge.HTTPRequest): OonimkallBridge.HTTPResponse {
val context = session.newContextWithTimeout(CONTEXT_TIMEOUT)
val response = session.httpDo(context, request.toMk())
return OonimkallBridge.HTTPResponse(body = response.body)
}
}
}

private fun OonimkallBridge.SessionConfig.toMk() =
SessionConfig().also {
it.softwareName = softwareName
it.softwareVersion = softwareVersion

it.assetsDir = assetsDir
it.stateDir = stateDir
it.tempDir = tempDir
it.tunnelDir = tunnelDir

it.probeServicesURL = probeServicesURL
it.proxy = proxy

it.logger =
logger?.let { logger ->
object : Logger {
override fun debug(msg: String?) = logger.debug(msg)

override fun info(msg: String?) = logger.info(msg)

override fun warn(msg: String?) = logger.warn(msg)
}
}
it.verbose = verbose
}

private fun OonimkallBridge.CheckInConfig.toMk() =
CheckInConfig().also {
it.charging = charging
it.onWiFi = onWiFi
it.platform = platform
it.runType = runType
it.softwareName = softwareName
it.softwareVersion = softwareVersion
it.webConnectivity =
CheckInConfigWebConnectivity().also {
webConnectivityCategories.forEach { category ->
it.addCategory(category)
}
}
}

private fun OonimkallBridge.HTTPRequest.toMk() =
HTTPRequest().also {
it.method = method
it.url = url
}

private fun CheckInInfoWebConnectivity?.getUrlInfos(): List<OonimkallBridge.UrlInfo> {
if (this == null) return emptyList()

return (0 until size()).mapNotNull { index ->
at(index).let { urlInfo ->
if (urlInfo.url == null) return@mapNotNull null

OonimkallBridge.UrlInfo(
url = urlInfo.url,
categoryCode = urlInfo.categoryCode,
countryCode = urlInfo.countryCode,
)
}
}
}

companion object {
private const val CONTEXT_TIMEOUT = -1L
}
}
3 changes: 3 additions & 0 deletions composeApp/src/commonMain/kotlin/org/ooni/engine/Engine.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.channelFlow
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import org.ooni.engine.models.EventResult
import org.ooni.engine.models.TaskEvent
import org.ooni.engine.models.TaskSettings
import kotlin.math.roundToInt

class Engine(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.ooni.engine

interface OonimkallBridge {
@Throws(Exception::class)
fun startTask(settingsSerialized: String): Task

interface Task {
Expand All @@ -10,4 +11,79 @@ interface OonimkallBridge {

fun waitForNextEvent(): String
}

@Throws(Exception::class)
fun newSession(sessionConfig: SessionConfig): Session

interface Logger {
fun debug(msg: String?)

fun info(msg: String?)

fun warn(msg: String?)
}

interface SessionConfig {
val softwareName: String
val softwareVersion: String

val proxy: String?
val probeServicesURL: String?

val assetsDir: String
val stateDir: String
val tempDir: String
val tunnelDir: String

val logger: Logger?
val verbose: Boolean
}

interface Session {
@Throws(Exception::class)
fun submitMeasurement(measurement: String): SubmitMeasurementResults

@Throws(Exception::class)
fun checkIn(config: CheckInConfig): CheckInResults

@Throws(Exception::class)
fun httpDo(request: HTTPRequest): HTTPResponse
}

data class SubmitMeasurementResults(
val updatedMeasurement: String?,
val updatedReportId: String?,
)

data class CheckInConfig(
val charging: Boolean,
val onWiFi: Boolean,
// "android" or "ios"
val platform: String,
// "timed"
val runType: String,
val softwareName: String,
val softwareVersion: String,
val webConnectivityCategories: List<String>,
)

data class CheckInResults(
val reportId: String?,
val urls: List<UrlInfo>,
)

data class UrlInfo(
val url: String,
val categoryCode: String?,
val countryCode: String?,
)

data class HTTPRequest(
val method: String,
val url: String,
)

data class HTTPResponse(
val body: String?,
)
}
24 changes: 0 additions & 24 deletions composeApp/src/commonMain/kotlin/org/ooni/engine/TaskSettings.kt

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package org.ooni.engine
package org.ooni.engine.models

import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package org.ooni.engine.models

import kotlinx.serialization.KSerializer
import kotlinx.serialization.Serializable
import kotlinx.serialization.descriptors.PrimitiveKind
import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder

@Serializable(with = NetworkTypeSerializer::class)
enum class NetworkType(val value: String) {
VPN("vpn"),
Wifi("wifi"),
Mobile("mobile"),
NoInternet("no_internet"),
}

object NetworkTypeSerializer : KSerializer<NetworkType> {
override val descriptor =
PrimitiveSerialDescriptor("NetworkType", PrimitiveKind.STRING)

override fun serialize(
encoder: Encoder,
value: NetworkType,
) {
encoder.encodeString(value.value)
}

override fun deserialize(decoder: Decoder): NetworkType {
val string = decoder.decodeString()
return NetworkType.entries.firstOrNull { it.value == string } ?: NetworkType.NoInternet
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package org.ooni.engine
package org.ooni.engine.models

sealed interface TaskEvent {
data class Log(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package org.ooni.engine.models

import kotlinx.serialization.KSerializer
import kotlinx.serialization.Serializable
import kotlinx.serialization.descriptors.PrimitiveKind
import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder

@Serializable(with = TaskLogLevelSerializer::class)
enum class TaskLogLevel(val value: String) {
Debug("DEBUG2"),
Info("INFO"),
}

object TaskLogLevelSerializer : KSerializer<TaskLogLevel> {
override val descriptor =
PrimitiveSerialDescriptor("TaskLogLevel", PrimitiveKind.STRING)

override fun serialize(
encoder: Encoder,
value: TaskLogLevel,
) {
encoder.encodeString(value.value)
}

override fun deserialize(decoder: Decoder): TaskLogLevel {
val string = decoder.decodeString()
return TaskLogLevel.entries.firstOrNull { it.value == string } ?: TaskLogLevel.Info
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package org.ooni.engine.models

import kotlinx.serialization.KSerializer
import kotlinx.serialization.Serializable
import kotlinx.serialization.descriptors.PrimitiveKind
import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder

@Serializable(with = TaskOriginSerializer::class)
enum class TaskOrigin(val value: String) {
AutoRun("autorun"),
OoniRun("ooni-run"),
}

object TaskOriginSerializer : KSerializer<TaskOrigin> {
override val descriptor =
PrimitiveSerialDescriptor("TaskOrigin", PrimitiveKind.STRING)

override fun serialize(
encoder: Encoder,
value: TaskOrigin,
) {
encoder.encodeString(value.value)
}

override fun deserialize(decoder: Decoder): TaskOrigin {
val string = decoder.decodeString()
return TaskOrigin.entries.firstOrNull { it.value == string } ?: TaskOrigin.OoniRun
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package org.ooni.engine.models

import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

@Serializable
data class TaskSettings(
// Required
@SerialName("name") val name: String,
@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("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,
) {
@Serializable
data class Options(
// upload results or not
@SerialName("no_collector") val noCollector: Boolean,
// built from the flavors + debug or not + -unattended if autorun
@SerialName("software_name") val softwareName: String,
@SerialName("software_version") val softwareVersion: String,
)

@Serializable
data class Annotations(
@SerialName("network_type") val networkType: NetworkType,
// OONI or DW
@SerialName("flavor") val flavor: String,
// "autorun" or "ooni-run"
@SerialName("origin") val origin: TaskOrigin,
)
}
Loading

0 comments on commit 092afc7

Please sign in to comment.