rmnet_data1 on the OnePlus is the carrier's IMS/VoLTE network: it carries IMS&MMTEL but neither INTERNET nor NOT_RESTRICTED, so binding needs CONNECTIVITY_USE_RESTRICTED_NETWORKS - signature-level, unobtainable. EPERM there is permanent and says nothing about any network's health, but the constraint detector counted it, so a phone with VoLTE reported 'measurement blocked' on every run and every verdict came out INCONCLUSIVE. Capabilities now decide: networks an app may never bind are recorded as app_usable:false in networks[] and skipped, rather than reported as something the run failed to do. Also retires the 'while it tears down' wording, which was a guess that turned out to be wrong - the refusal outlasts the VPN indefinitely, so the text now names the VPN as the usual cause without asserting a timeline it cannot know. App version 0.2.4. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
92 lines
4.0 KiB
Kotlin
92 lines
4.0 KiB
Kotlin
// SPDX-FileCopyrightText: 2026 Echolot contributors
|
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
plugins {
|
|
// AGP 9 built-in Kotlin — no kotlin.android here (see core-probe note).
|
|
alias(libs.plugins.android.application)
|
|
alias(libs.plugins.kotlin.compose)
|
|
alias(libs.plugins.kotlin.serialization)
|
|
}
|
|
|
|
// The app's version is SemVer and lives here, once. versionCode is derived from it rather than
|
|
// maintained alongside: Play/F-Droid need a monotonically increasing integer, but a second number
|
|
// that a human has to remember to bump is a number that eventually disagrees with the first — and
|
|
// the version is now load-bearing, since the server decides whether to serve us by it.
|
|
//
|
|
// major*1_000_000 + minor*10_000 + patch*10 leaves room for 9 patch-level rebuilds (the trailing
|
|
// digit) without disturbing the mapping, and stays inside the 2_100_000_000 ceiling until major 2100.
|
|
val appVersionName = "0.2.4"
|
|
|
|
fun versionCodeOf(semver: String): Int {
|
|
val (major, minor, patch) = semver.substringBefore('-').split(".").map(String::toInt)
|
|
return major * 1_000_000 + minor * 10_000 + patch * 10
|
|
}
|
|
|
|
android {
|
|
namespace = "app.echo_lot.app"
|
|
compileSdk = 36
|
|
|
|
defaultConfig {
|
|
applicationId = "app.echo_lot.app"
|
|
minSdk = 26
|
|
targetSdk = 36
|
|
versionCode = versionCodeOf(appVersionName)
|
|
versionName = appVersionName
|
|
// Automation: `adb shell am start -n app.echo_lot.app/.MainActivity --ez autorun true`
|
|
// runs a measurement immediately and POSTs the report here (dev collection endpoint).
|
|
// Empty: the collection endpoint this pointed at was the adb-beacon receiver, which held
|
|
// 0.0.0.0:443 in cleartext. That service is gone and echolot-server owns 443 with TLS, so
|
|
// posting plaintext there now fails as "client sent an HTTP request to an HTTPS server" —
|
|
// an alarming error for a debugging convenience that is no longer needed, since autorun
|
|
// reports are read straight off the device with `run-as cat`.
|
|
//
|
|
// Deliberately not repointed at /v1/runs. That is the consent-gated upload, and a
|
|
// debugging shortcut must not be able to satisfy it by accident.
|
|
buildConfigField("String", "REPORT_UPLOAD_URL", "\"\"")
|
|
buildConfigField("String", "REPORT_UPLOAD_SECRET", "\"\"")
|
|
// The bare SemVer, without the debug build's "-dev" suffix stripped away by the server's
|
|
// parser anyway — sent to servers so they can apply their compatibility window.
|
|
buildConfigField("String", "APP_SEMVER", "\"$appVersionName\"")
|
|
}
|
|
buildTypes {
|
|
release { isMinifyEnabled = false }
|
|
debug {
|
|
// The dev build is a separate app: own package (installs alongside a real
|
|
// Echolot), own label and a DEV-badged icon (src/debug/res).
|
|
applicationIdSuffix = ".dev"
|
|
versionNameSuffix = "-dev"
|
|
}
|
|
}
|
|
compileOptions {
|
|
sourceCompatibility = JavaVersion.VERSION_17
|
|
targetCompatibility = JavaVersion.VERSION_17
|
|
}
|
|
buildFeatures {
|
|
compose = true
|
|
buildConfig = true
|
|
}
|
|
}
|
|
|
|
dependencies {
|
|
implementation(project(":core-measurement"))
|
|
implementation(project(":core-protocol"))
|
|
implementation(project(":core-engine"))
|
|
implementation(project(":core-probe"))
|
|
implementation(project(":core-shizuku"))
|
|
implementation(project(":core-privacy"))
|
|
implementation(project(":core-archive"))
|
|
|
|
implementation(libs.kotlinx.serialization.json)
|
|
implementation(libs.kotlinx.coroutines.android)
|
|
implementation(libs.androidx.core.ktx)
|
|
implementation(libs.androidx.lifecycle.runtime.ktx)
|
|
implementation(libs.androidx.lifecycle.viewmodel.compose)
|
|
implementation(libs.androidx.activity.compose)
|
|
implementation(platform(libs.androidx.compose.bom))
|
|
implementation(libs.androidx.ui)
|
|
implementation(libs.androidx.ui.graphics)
|
|
implementation(libs.androidx.ui.tooling.preview)
|
|
implementation(libs.androidx.material3)
|
|
debugImplementation(libs.androidx.ui.tooling)
|
|
}
|