Skip to content

Commit

Permalink
feat: add engine bridge on ios.
Browse files Browse the repository at this point in the history
  • Loading branch information
aanorbel committed Aug 1, 2024
1 parent f80472d commit ada86c0
Show file tree
Hide file tree
Showing 3 changed files with 344 additions and 10 deletions.
99 changes: 98 additions & 1 deletion composeApp/src/commonMain/kotlin/org/ooni/engine/Engine.kt
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
package org.ooni.engine

import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.IO
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.channelFlow
import kotlinx.coroutines.withContext
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import org.ooni.engine.models.EventResult
Expand All @@ -25,7 +28,25 @@ class Engine(
assetsDir = baseFilePath,
)

val task = bridge.startTask(json.encodeToString(finalSettings))
val response = httpDo(finalSettings)
println(response)

val checkinResults = checkIn(finalSettings)

val task =
bridge.startTask(
json.encodeToString(
checkinResults?.urls?.map { it.url }?.let {
finalSettings.copy(
inputs = it,
options =
finalSettings.options.copy(
maxRuntime = 90,
),
)
} ?: finalSettings,
),
)

while (!task.isDone()) {
val eventJson = task.waitForNextEvent()
Expand All @@ -40,6 +61,82 @@ class Engine(
}
}

fun session(finalSettings: TaskSettings): OonimkallBridge.Session {
return bridge.newSession(
object : OonimkallBridge.SessionConfig {
override val softwareName: String
get() = finalSettings.options.softwareName
override val softwareVersion: String
get() = finalSettings.options.softwareVersion
override val proxy: String?
get() = null
override val probeServicesURL: String?
get() = "https://api.prod.ooni.io"
override val assetsDir: String
get() = finalSettings.assetsDir.toString()
override val stateDir: String
get() = finalSettings.stateDir.toString()
override val tempDir: String
get() = finalSettings.tempDir.toString()
override val tunnelDir: String
get() = finalSettings.tunnelDir.toString()
override val logger: OonimkallBridge.Logger?
get() =
object : OonimkallBridge.Logger {
override fun debug(msg: String?) {
println("DEBUG: $msg")
}

override fun info(msg: String?) {
println("INFO: $msg")
}

override fun warn(msg: String?) {
println("WARN: $msg")
}
}
override val verbose: Boolean
get() = true
},
)
}

suspend fun checkIn(finalSettings: TaskSettings): OonimkallBridge.CheckInResults? {
return withContext(Dispatchers.IO) {
return@withContext session(finalSettings).checkIn(
object : OonimkallBridge.CheckInConfig {
override val charging: Boolean
get() = true
override val onWiFi: Boolean
get() = true
override val platform: String
get() = "android"
override val runType: String
get() = "autorun"
override val softwareName: String
get() = "ooniprobe-android-unattended"
override val softwareVersion: String
get() = "3.8.8"
override val webConnectivityCategories: List<String>
get() = listOf("NEWS")
},
)
}
}

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

private fun EventResult.toTaskEvent(): TaskEvent? =
when (key) {
"status.started" -> TaskEvent.Started
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,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,
)

@Serializable
Expand Down
254 changes: 245 additions & 9 deletions iosApp/iosApp/engine/IosOonimkallBridge.swift
Original file line number Diff line number Diff line change
@@ -1,23 +1,259 @@
import composeApp
import Oonimkall

class IosOonimkallBridge : OonimkallBridge {
let CONTEXT_TIMEOUT: Int64 = -1

class IosOonimkallBridge: OonimkallBridge {

func startTask(settingsSerialized: String) throws -> OonimkallBridgeTask {
var error: NSError?
let task = OonimkallStartTask(settingsSerialized, &error)!

class Task : OonimkallBridgeTask {
class Task: OonimkallBridgeTask {
var task: OonimkallTask
init(task: OonimkallTask) { self.task = task }
func isDone() -> Bool { task.isDone() }
func interrupt() { task.interrupt() }
func waitForNextEvent() -> String { task.waitForNextEvent() }

init(task: OonimkallTask) {
self.task = task
}

func isDone() -> Bool {
task.isDone()
}

func interrupt() {
task.interrupt()
}

func waitForNextEvent() -> String {
task.waitForNextEvent()
}
}

return Task(task: task)
}

func doNewSession(sessionConfig: OonimkallBridgeSessionConfig) throws -> OonimkallBridgeSession {
fatalError("Not Implemented")
class IosSession: OonimkallBridgeSession {
private let sessionConfig: OonimkallSessionConfig

init(sessionConfig: OonimkallSessionConfig) {
self.sessionConfig = sessionConfig
}

func checkIn(config: OonimkallBridgeCheckInConfig) throws -> any OonimkallBridgeCheckInResults {
var error: NSError?
let ses = OonimkallNewSession(sessionConfig, &error)
// throw error if any
if error != nil {
throw error!
}
guard let context = ses?.newContext(withTimeout: CONTEXT_TIMEOUT) else {
throw NSError()
}
do {
let info = try ses?.check(in: context, config: config.toMk())
return IosCheckInResults(info: info!)
} catch {
throw error
}
}

func httpDo(request: OonimkallBridgeHTTPRequest) throws -> OonimkallBridgeHTTPResponse {
var error: NSError?
let ses = OonimkallNewSession(sessionConfig, &error)
// throw error if any
if error != nil {
throw error!
}
guard let context = ses?.newContext(withTimeout: CONTEXT_TIMEOUT) else {
throw NSError()
}
do {
let response = try ses?.httpDo(context, jreq: request.toMk())
return IosHTTPResponse(response: response)
} catch {
throw error
}
}

func submitMeasurement(measurement: String) throws -> any OonimkallBridgeSubmitMeasurementResults {
var error: NSError?
let ses = OonimkallNewSession(sessionConfig, &error)
// throw error if any
if error != nil {
throw error!
}
guard let context = ses?.newContext(withTimeout: CONTEXT_TIMEOUT) else {
throw NSError()
}
do {

let result: OonimkallSubmitMeasurementResults? = try ses?.submit(context, measurement: measurement)
return IosSubmitMeasurementResults(results: result)
} catch {
throw error
}
}
}

return IosSession(sessionConfig: sessionConfig.toMk())
}
}


extension OonimkallBridgeSessionConfig {
func toMk() -> OonimkallSessionConfig {

let config: OonimkallSessionConfig = OonimkallSessionConfig()
config.softwareName = softwareName
config.softwareVersion = softwareVersion
config.assetsDir = assetsDir
config.stateDir = stateDir
config.tempDir = tempDir
config.tunnelDir = tunnelDir
if let probeServicesURL = probeServicesURL {
config.probeServicesURL = probeServicesURL
}
if let proxy = proxy {
config.proxy = proxy
}
// Problem setting logger
if let logger = logger {
let applicationLogger = IosLogger(logger: logger)
// config.logger = applicationLogger
}
config.verbose = verbose
return config
}
}

extension OonimkallBridgeCheckInConfig {
func toMk() -> OonimkallCheckInConfig {
let config = OonimkallCheckInConfig()
config.charging = charging
config.onWiFi = onWiFi
config.platform = platform
config.runType = runType
config.softwareName = softwareName
config.softwareVersion = softwareVersion
config.webConnectivity = OonimkallCheckInConfigWebConnectivity()
webConnectivityCategories.forEach { category in
config.webConnectivity?.addCategory(category)
}
return config
}
}

extension OonimkallBridgeHTTPRequest {
func toMk() -> OonimkallHTTPRequest {
let request = OonimkallHTTPRequest()
request.method = method
request.url = url
return request
}
}

class IosHTTPResponse: OonimkallBridgeHTTPResponse {
private let response: OonimkallHTTPResponse?

init(response: OonimkallHTTPResponse?) {
self.response = response
}

var body: String? {
response?.body
}
}

class IosSubmitMeasurementResults: OonimkallBridgeSubmitMeasurementResults {
private let results: OonimkallSubmitMeasurementResults?

init(results: OonimkallSubmitMeasurementResults?) {
self.results = results
}

var updatedMeasurement: String? {
results?.updatedMeasurement
}

var updatedReportId: String? {
results?.updatedReportID
}
}

@objc
class IosLogger: OonimkallLogger {
private let logger: OonimkallBridgeLogger?

override init(ref: Any) {
self.logger = 0 as? any OonimkallBridgeLogger
super.init(ref: ref)
}

init(logger: OonimkallBridgeLogger) {
self.logger = logger
super.init()
}

override func debug(_ msg: String?) {
logger?.debug(msg: msg)
}

override func info(_ msg: String?) {
logger?.info(msg: msg)
}

override func warn(_ msg: String?) {
logger?.warn(msg: msg)
}
}


class IosCheckInResults: OonimkallBridgeCheckInResults {
private let info: OonimkallCheckInInfo

init(info: OonimkallCheckInInfo) {
self.info = info
}

var reportId: String? {
info.webConnectivity?.reportID
}

var urls: [OonimkallBridgeUrlInfo] {

var responseUrls = [OonimkallBridgeUrlInfo]()

let size = info.webConnectivity?.size() ?? 0

for i in 0..<size {

if info.webConnectivity?.at(i) != nil {
let urlInfo: OonimkallURLInfo? = info.webConnectivity?.at(i)
responseUrls.append(IosUrlInfo(urlInfo: urlInfo!))
}
}

return responseUrls
}
}

class IosUrlInfo: OonimkallBridgeUrlInfo {
private let urlInfo: OonimkallURLInfo

init(urlInfo: OonimkallURLInfo) {
self.urlInfo = urlInfo
}

var url: String {
urlInfo.url
}

var categoryCode: String? {
urlInfo.categoryCode
}

var countryCode: String? {
urlInfo.countryCode
}
}

0 comments on commit ada86c0

Please sign in to comment.