Files
echolot/CLAUDE.md
T
Markus RambossekandClaude Opus 5 4de3064f71 reports: build-5 runs on both devices — new toolchain is behaviour-neutral
TB330FU (A15/SDK35) and CPH2747 (A16/SDK36) both re-run on the AGP 9.2 /
Gradle 9.6 / Kotlin 2.2.10 / JDK 25 / targetSdk 36 toolchain. Every probe on
both devices keeps the verdict it had on the previous build (tablet vs build-4,
phone vs build-3), so the upgrade changed no observed capability.

Dual-path Shizuku executor now confirmed on the current toolchain on both:
phone exec_path=UserService, tablet exec_path=newProcess fallback, both 7/7.

Collection-loop gotchas recorded in build-status.md, found while driving the
phone over USB:

- icmp.ping6's verdict is topology-dependent, not build-dependent. A run with
  only 2 networks up reported UNSUPPORTED; with cellular present it is the
  expected "echo reply on cellular only". Always read it against
  link.snapshot's network_count from the same report.
- The Shizuku permission dialog times out after 30 s and the probe logs
  "permission not granted" at ~30027 ms. Grant it before starting a run.
- uiautomator dump can report a stale idle state right after a tap; the
  exported JSON's durationMs values are the ground truth for completion.

CLAUDE.md's collection loop is updated — over USB the run is fully driveable
via am start / input tap / run-as cat, no manual export needed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-30 17:35:53 +02:00

90 lines
5.3 KiB
Markdown

# 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://`. Namespace `app.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 in `docs/` 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 (see `docs/build-status.md` for 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.kt` with 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. Never drive a probe run over *wireless* adb.
- Collection loop over USB, fully driveable by Claude: install → `am start` → tap "Run all
probes" → poll `uiautomator dump` until the button label returns to "Run all probes" → tap
"Export JSON" → the report lands in `cache/reports/` and comes back via
`adb shell run-as app.echo_lot.prober cat …` (no need to drive the share sheet; dismiss it
with BACK). Archive under `echolot-prober/reports/`, record findings in `docs/build-status.md`.
- Bash-tool `adb shell` calls with absolute device paths get mangled by Git Bash path
conversion (`/data/…``C:/Program Files/Git/data/…`); use the PowerShell tool for those.
- Bump `versionCode` on every deployed prober change — it shows on screen and as `proberBuild`
in 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 `newProcess` fallback carries
it. Full history in build-status.md.
- As of the AGP 9.2.0 / Gradle 9.6.0 / Kotlin 2.2.10 bump, JDK 17+ (including 25) works —
AGP 9 requires Gradle 9.1.0+ and Kotlin 2.2.10+ as its minimum KGP version. On machines with
Android Studio, its `jbr` directory works as `JAVA_HOME`.
## Likely next steps
1. Run on physical devices; collect JSON reports across Android versions/vendors.
2. ~~C-over-JNI errqueue shim~~ — not needed; `trace.errqueue_reachable` and `traceroute.udp4`
are SUPPORTED on both known devices via `Os.recvmsg` + `StructMsghdr` reflection.
3. Fold the confirmed capabilities + Shizuku dump-format samples back into the production
`core-probe` / `core-shizuku` modules.