The wireless-adb findings, report-collection workflow, build-number convention, per-device Shizuku matrix, and the JDK constraint now live in the repo so a session on any machine starts with them. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
4.7 KiB
Echolot — context for Claude Code
This repo is the capability prober for the Echolot project: an F/OSS Android app for detecting and debugging local network issues (target audience: network engineers). The prober validates the no-root feasibility matrix on real hardware before the production app is built.
Project facts
- Name: Echolot. Domain echo-lot.app. URI scheme
echolot://. Namespaceapp.echo_lot.*(hyphen→underscore; appIds/packages can't contain hyphens). Prober appId:app.echo_lot.prober. - Stack: native Kotlin + Jetpack Compose. No Flutter (all value is in platform APIs; Flutter would be a Dart skin over a full Kotlin app + F-Droid build friction).
- Tiers:
app(no root),shizuku(ADB-shell privileges via wireless pairing, shipped in v1),root(future module). Every result records which tier produced it. - License: code is GPL-3.0-or-later (
LICENSE), the specs indocs/are CC-BY-4.0 (docs/LICENSE). New source files get the two-line SPDX header the existing ones carry. The server, when it lands, is GPL too — not AGPL (seedocs/build-status.mdfor the reasoning).
Design docs (source of truth, in docs/)
docs/feature-catalog-and-feasibility.md— full feature list + no-root feasibility matrix.docs/measurement-schema.md— the archived/exportable measurement JSON format (observation vs finding separation, two-clock rule, columnar trains, anonymization types).docs/probe-protocol.md— client↔server wire protocol (pinned-TLS control plane, binary UDP data plane with anti-amplification HMAC, STUN, canary DNS reference records).docs/build-status.md— running log of decisions, what is delivered, and the next steps.
The three specs are draft-complete and user-reviewed; treat them as the contract. build-status.md
is the mutable one — update it as work lands.
Keep prober result IDs aligned with the measurement-schema test-type registry.
Layout
Monorepo. The prober is one deliverable; the Go server and the production app land as siblings.
docs/ design docs (above) — apply repo-wide, not just to the prober
echolot-prober/ the capability prober (self-contained Gradle build)
app/src/main/java/app/echo_lot/prober/
MainActivity.kt Compose host, runs probes sequentially
probe/ Probe interface + all app-tier probes + OS ABI helper
shizuku/ AIDL UserService + ShizukuRunner (shell command exec)
export/Report.kt JSON report + share intent
ui/ProberScreen.kt result cards colored by verdict
Conventions
- Every probe returns a
ProbeResult— never throws to the caller (MainActivity wraps anyway). - Uncertain OS API paths (getsockoptInt, recvmsg, nat64Prefix) are reached via reflection/runCatching and reported, not assumed — the prober's purpose is to discover what exists.
- Hardcoded Linux sockopt ABI numbers live in
OsAbi.ktwith rationale; do not "fix" them to OsConstants names that don't exist.
Build
From echolot-prober/: ./gradlew :app:assembleDebug (needs local Android SDK; set sdk.dir in
echolot-prober/local.properties). The Gradle build is rooted there, not at the repo root.
First build downloads AGP/Compose/Shizuku from Google Maven + Maven Central.
Working with test devices (hard-won lessons)
- Prefer USB for adb. Wireless debugging dies constantly: probe runs churn the wifi the debug link rides on, the Lenovo tablet's ZUI power management kills the listener anyway, and the port rotates on every restart. Deploys over wireless work between runs; never drive a probe run over adb.
- Collection loop that works: install over adb → the user runs the probes and exports the
JSON manually → archive it under
echolot-prober/reports/and record findings indocs/build-status.md. - Bump
versionCodeon every deployed prober change — it shows on screen and asproberBuildin the report; that's how a report is matched to a build. - Known devices: OnePlus 15 (CPH2747, A16) — Shizuku UserService works;
Lenovo TB330FU (A15, multi-user) — UserService never binds, the
newProcessfallback carries it. Full history in build-status.md. - Gradle needs JDK 17–21 (
JAVA_HOME); a system JDK 25 breaks AGP 8.7. On machines with Android Studio, itsjbrdirectory works.
Likely next steps
- Run on physical devices; collect JSON reports across Android versions/vendors.
- If
trace.errqueue_reachableis PARTIAL, add the C-over-JNI errqueue shim (recvmsg + cmsg parse) as a:nativemodule and a realtraceroute.udp4probe. - Fold the confirmed capabilities + Shizuku dump-format samples back into the production
core-probe/core-shizukumodules.