diff --git a/composeApp/src/androidMain/AndroidManifest.xml b/composeApp/src/androidMain/AndroidManifest.xml index 112aadd0..441fbe74 100644 --- a/composeApp/src/androidMain/AndroidManifest.xml +++ b/composeApp/src/androidMain/AndroidManifest.xml @@ -1,6 +1,8 @@ + + - \ No newline at end of file + diff --git a/composeApp/src/androidMain/kotlin/org/ooni/engine/AndroidNetworkTypeFinder.kt b/composeApp/src/androidMain/kotlin/org/ooni/engine/AndroidNetworkTypeFinder.kt new file mode 100644 index 00000000..c1b71d68 --- /dev/null +++ b/composeApp/src/androidMain/kotlin/org/ooni/engine/AndroidNetworkTypeFinder.kt @@ -0,0 +1,24 @@ +package org.ooni.engine + +import android.net.ConnectivityManager +import android.net.NetworkCapabilities +import org.ooni.engine.models.NetworkType + +class AndroidNetworkTypeFinder( + private val connectivityManager: ConnectivityManager?, +) : NetworkTypeFinder { + override fun invoke(): NetworkType { + if (connectivityManager == null) return NetworkType.NoInternet + + val capabilities = + connectivityManager.getNetworkCapabilities(connectivityManager.activeNetwork) + ?: return NetworkType.NoInternet + + return when { + capabilities.hasTransport(NetworkCapabilities.TRANSPORT_VPN) -> NetworkType.VPN + capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) -> NetworkType.Wifi + capabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) -> NetworkType.Mobile + else -> NetworkType.NoInternet + } + } +} diff --git a/composeApp/src/androidMain/kotlin/org/ooni/probe/AndroidApplication.kt b/composeApp/src/androidMain/kotlin/org/ooni/probe/AndroidApplication.kt index 476232df..a1fe912d 100644 --- a/composeApp/src/androidMain/kotlin/org/ooni/probe/AndroidApplication.kt +++ b/composeApp/src/androidMain/kotlin/org/ooni/probe/AndroidApplication.kt @@ -1,9 +1,11 @@ package org.ooni.probe import android.app.Application +import android.net.ConnectivityManager import android.os.Build import app.cash.sqldelight.db.SqlDriver import app.cash.sqldelight.driver.android.AndroidSqliteDriver +import org.ooni.engine.AndroidNetworkTypeFinder import org.ooni.engine.AndroidOonimkallBridge import org.ooni.probe.di.Dependencies import org.ooni.probe.shared.Platform @@ -21,6 +23,8 @@ class AndroidApplication : Application() { baseFileDir = filesDir.absolutePath, cacheDir = cacheDir.absolutePath, databaseDriverFactory = ::buildDatabaseDriver, + networkTypeFinder = + AndroidNetworkTypeFinder(getSystemService(ConnectivityManager::class.java)), ) } diff --git a/composeApp/src/commonMain/kotlin/org/ooni/engine/Engine.kt b/composeApp/src/commonMain/kotlin/org/ooni/engine/Engine.kt index 41df3e2d..3cac9418 100644 --- a/composeApp/src/commonMain/kotlin/org/ooni/engine/Engine.kt +++ b/composeApp/src/commonMain/kotlin/org/ooni/engine/Engine.kt @@ -37,12 +37,19 @@ class Engine( channelFlow { val taskSettings = buildTaskSettings(name, inputs, taskOrigin) val settingsSerialized = json.encodeToString(taskSettings) - val task = bridge.startTask(settingsSerialized) - while (!task.isDone()) { - val eventJson = task.waitForNextEvent() - val taskEventResult = json.decodeFromString(eventJson) - taskEventMapper(taskEventResult)?.let { send(it) } + var task: OonimkallBridge.Task? = null + try { + task = bridge.startTask(settingsSerialized) + + while (!task.isDone()) { + val eventJson = task.waitForNextEvent() + val taskEventResult = json.decodeFromString(eventJson) + taskEventMapper(taskEventResult)?.let { send(it) } + } + } catch (e: Exception) { + task?.interrupt() + throw MkException(e) } invokeOnClose { @@ -57,8 +64,12 @@ class Engine( taskOrigin: TaskOrigin = TaskOrigin.OoniRun, ): SubmitMeasurementResults = withContext(backgroundDispatcher) { - val sessionConfig = buildSessionConfig(taskOrigin) - session(sessionConfig).submitMeasurement(measurement) + try { + val sessionConfig = buildSessionConfig(taskOrigin) + session(sessionConfig).submitMeasurement(measurement) + } catch (e: Exception) { + throw MkException(e) + } } suspend fun checkIn( @@ -66,18 +77,22 @@ class Engine( taskOrigin: TaskOrigin, ): OonimkallBridge.CheckInResults = withContext(backgroundDispatcher) { - val sessionConfig = buildSessionConfig(taskOrigin) - session(sessionConfig).checkIn( - OonimkallBridge.CheckInConfig( - charging = true, - onWiFi = true, - platform = platformInfo.platform.value, - runType = taskOrigin.value, - softwareName = sessionConfig.softwareName, - softwareVersion = sessionConfig.softwareVersion, - webConnectivityCategories = categories, - ), - ) + try { + val sessionConfig = buildSessionConfig(taskOrigin) + session(sessionConfig).checkIn( + OonimkallBridge.CheckInConfig( + charging = true, + onWiFi = true, + platform = platformInfo.platform.value, + runType = taskOrigin.value, + softwareName = sessionConfig.softwareName, + softwareVersion = sessionConfig.softwareVersion, + webConnectivityCategories = categories, + ), + ) + } catch (e: Exception) { + throw MkException(e) + } } suspend fun httpDo( @@ -86,13 +101,17 @@ class Engine( taskOrigin: TaskOrigin = TaskOrigin.OoniRun, ): String? = withContext(backgroundDispatcher) { - session(buildSessionConfig(taskOrigin)) - .httpDo( - OonimkallBridge.HTTPRequest( - method = method, - url = url, - ), - ).body + try { + session(buildSessionConfig(taskOrigin)) + .httpDo( + OonimkallBridge.HTTPRequest( + method = method, + url = url, + ), + ).body + } catch (e: Exception) { + throw MkException(e) + } } private fun session(sessionConfig: OonimkallBridge.SessionConfig): OonimkallBridge.Session = bridge.newSession(sessionConfig) @@ -179,4 +198,6 @@ class Engine( } } } + + class MkException(e: Exception) : Exception(e) } diff --git a/composeApp/src/commonMain/kotlin/org/ooni/probe/di/Dependencies.kt b/composeApp/src/commonMain/kotlin/org/ooni/probe/di/Dependencies.kt index c55c8fdd..9f2ba9ef 100644 --- a/composeApp/src/commonMain/kotlin/org/ooni/probe/di/Dependencies.kt +++ b/composeApp/src/commonMain/kotlin/org/ooni/probe/di/Dependencies.kt @@ -9,7 +9,6 @@ import org.ooni.engine.Engine import org.ooni.engine.NetworkTypeFinder import org.ooni.engine.OonimkallBridge import org.ooni.engine.TaskEventMapper -import org.ooni.engine.models.NetworkType import org.ooni.probe.Database import org.ooni.probe.data.models.TestResult import org.ooni.probe.shared.PlatformInfo @@ -23,8 +22,9 @@ class Dependencies( private val baseFileDir: String, private val cacheDir: String, private val databaseDriverFactory: () -> SqlDriver, + private val networkTypeFinder: NetworkTypeFinder, ) { - // Commong + // Common private val backgroundDispatcher = Dispatchers.IO @@ -35,8 +35,6 @@ class Dependencies( // Engine - private val networkTypeFinder by lazy { NetworkTypeFinder { NetworkType.Unknown("") } } // TODO - private val taskEventMapper by lazy { TaskEventMapper(networkTypeFinder, json) } private val engine by lazy { diff --git a/composeApp/src/commonMain/kotlin/org/ooni/probe/ui/dashboard/DashboardViewModel.kt b/composeApp/src/commonMain/kotlin/org/ooni/probe/ui/dashboard/DashboardViewModel.kt index 74f75207..1a89bb22 100644 --- a/composeApp/src/commonMain/kotlin/org/ooni/probe/ui/dashboard/DashboardViewModel.kt +++ b/composeApp/src/commonMain/kotlin/org/ooni/probe/ui/dashboard/DashboardViewModel.kt @@ -6,6 +6,7 @@ import co.touchlab.kermit.Logger import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.launchIn @@ -33,18 +34,27 @@ class DashboardViewModel( _state.value = _state.value.copy(isRunning = true) - val response = - engine.httpDo( - method = "GET", - url = "https://api.dev.ooni.io/api/v2/oonirun/links/10426", - ) - response?.let { Logger.d(it) } + try { + val response = + engine.httpDo( + method = "GET", + url = "https://api.dev.ooni.io/api/v2/oonirun/links/10426", + ) + response?.let { Logger.d(it) } + } catch (e: Engine.MkException) { + Logger.e("httpDo failed", e) + } val checkInResults = - engine.checkIn( - categories = listOf("NEWS"), - taskOrigin = TaskOrigin.OoniRun, - ) + try { + engine.checkIn( + categories = listOf("NEWS"), + taskOrigin = TaskOrigin.OoniRun, + ) + } catch (e: Engine.MkException) { + Logger.e("checkIn failed", e) + return@flatMapLatest emptyFlow() + } engine .startTask( @@ -62,6 +72,9 @@ class DashboardViewModel( }.onCompletion { _state.update { it.copy(isRunning = false) } } + .catch { + Logger.e("startTask failed", it) + } } } }.launchIn(viewModelScope) diff --git a/composeApp/src/iosMain/kotlin/org/ooni/probe/SetupDependencies.kt b/composeApp/src/iosMain/kotlin/org/ooni/probe/SetupDependencies.kt index acde2781..2d9e8476 100644 --- a/composeApp/src/iosMain/kotlin/org/ooni/probe/SetupDependencies.kt +++ b/composeApp/src/iosMain/kotlin/org/ooni/probe/SetupDependencies.kt @@ -2,6 +2,7 @@ package org.ooni.probe import app.cash.sqldelight.driver.native.NativeSqliteDriver import org.ooni.engine.OonimkallBridge +import org.ooni.engine.models.NetworkType import org.ooni.probe.di.Dependencies import org.ooni.probe.shared.Platform import org.ooni.probe.shared.PlatformInfo @@ -23,6 +24,8 @@ fun setupDependencies(bridge: OonimkallBridge) = baseFileDir = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, true).first().toString(), cacheDir = NSTemporaryDirectory(), databaseDriverFactory = ::buildDatabaseDriver, + // TODO + networkTypeFinder = { NetworkType.Unknown("") }, ) private val platformInfo get() =