Practical loops for host (Rust) and Android Automotive (Kotlin + libnavi.so).
Build/install details: android-build.md. Hardware checklist:
real-hardware-testing.md.
Where to change zoom, approach, routing, HUD: codebase-map.md.
Callable APIs: API.md.
Debug route simulation (debuggable builds only; feeds the live applyFix
pipeline at maxspeed / highway-fallback pace): route-simulation.md.
| Layer | What breaks | First tool |
|---|---|---|
Rust core (driver-break-core) |
Routing, POI, eco, config | cargo test / RUST_LOG |
UniFFI (navi-ffi → libnavi.so) |
Missing symbols, wrong ABI | Rebuild native + reinstall APK |
Android host (app/) |
HUD, MapLibre, sheets | adb logcat, Android Studio |
| Emulator / AAOS | Yellow border UI, wrong user | ./scripts/launch-navi-emulator.sh |
Application id: no.navi.app. Main activity: no.navi.app/.MainActivity.
# Fast focused modules
cargo test -p driver-break-core poi::
cargo test -p driver-break-core config::eco -- --nocapture
# Ignored (large) integrations — need fixtures under core/target/…
cargo test -p driver-break-core --test kongsvinger_lillehammer_integration \
-- --nocapture --ignored
cargo test -p driver-break-core --test dnt_hiking_integration \
-- --nocapture --ignoredUse --nocapture so println! / logging from the test binary stays on the
terminal. Notes from past runs: test-results.md.
- Open the workspace in VS Code (rust-analyzer) or RustRover.
- Set breakpoints in
core/src/…and debug a singlecargo testtarget (same filters as above). - For UniFFI boundary issues, prefer a failing
cargo test -p navi-ffior an Android instrumented call that hits the same FFI function, then bisect in Rust.
The core does not require a special env for most tests. When adding temporary
diagnostics, prefer eprintln! in tests or structured logs you already use in
that crate — remove noise before committing.
Always rebuild the native library when Rust changed, then install the debug APK:
export ANDROID_HOME="${ANDROID_HOME:-$HOME/Android/Sdk}"
export ANDROID_NDK_HOME="${ANDROID_NDK_HOME:-$ANDROID_HOME/ndk/<version>}"
# Emulator ABI
./scripts/build-android-native.sh x86_64-linux-android debug # or release
./gradlew :app:installDebug
./scripts/launch-navi-emulator.shPhysical arm64 head unit: use aarch64-linux-android instead of x86_64-….
Stale libnavi.so is a common “debug red herring”: Kotlin looks fine, FFI
returns wrong data or crashes. If behaviour and code disagree, re-run
build-android-native.sh and reinstall.
# Clear, then follow Navi-related tags
adb logcat -c
adb logcat -v time \
HudVerification:V NaviTracks:V NaviMapTest:V BearingCrash:V \
AndroidRuntime:E libc:F DEBUG:F *:SUseful tags seen in-tree:
| Tag | Area |
|---|---|
HudVerification |
HUD / settings hooks, screenshots, camera errors |
NaviTracks |
Moving-icon / APRS overlay load |
NaviMapTest |
Corridor / map instrumented tests |
BearingCrash |
MapLibre bearing isolation tests |
AndroidRuntime |
Java/Kotlin uncaught exceptions |
DEBUG / tombstones |
Native crashes (libnavi.so, MapLibre) |
Broader MapLibre / GL noise (when investigating map crashes):
adb logcat -v time | grep -iE 'maplibre|libEGL|GLES|vulkan|navi|UnsatisfiedLink'On Automotive multi-user AVDs, confirm you are looking at the driver user (often user 10):
adb shell am get-current-user
adb shell ps --user 10 | grep navi || trueWhen Tools → Diagnostic logging is on, Navi writes a pipe-delimited session file under shared storage so you can copy it with a plain USB cable (MTP) — no adb or developer tools required.
Preferred path on the device:
/storage/emulated/0/Documents/debug/navi_session_YYYY-MM-DD_HH-mm-ss.log
In a file browser / MTP mount that usually appears as:
Internal storage → Documents → debug → navi_session_….log
(Linux desktops may show a GVFS path such as
/run/user/…/gvfs/mtp:host=…/Intern lagring/Documents/debug/ — that is the
desktop’s mount of the same folder, not something the app hardcodes.)
Scoped storage on Android 10+ does not allow creating a new top-level
/debug folder without MANAGE_EXTERNAL_STORAGE (avoided here). If
Documents/debug is not writable, Navi falls back to Download/debug, then
(only as a last resort) app-private storage.
Basemap / DEM downloads stay in app-private storage; only diagnostic /
plugin debug files use the shared Documents (or Download) debug tree.
Product plugins (when shipped) and host-side plugin helpers that need on-device
debug artifacts must write under the same shared debug folder, in a
subfolder named after the plugin:
/storage/emulated/0/Documents/debug/<plugin-name>/…
USB / MTP:
Internal storage → Documents → debug → <plugin-name> → …
Use a stable plugin id matching the manifest name (e.g. right-to-roam-camping,
safety-resupply). Core Navi sessions stay at the debug/ root
(navi_session_….log); do not mix plugin files into that root. See
plugins.md.
- Enable Tools → Diagnostic logging and use the app so a session file is written (or turn the toggle off to flush/close the current file).
- Connect the device with a USB cable and choose File transfer / MTP (not “Charging only”).
- On the computer, open the device storage → Documents → debug.
- Copy
navi_session_….log(and anydebug/<plugin-name>/trees you need).
Tools → Export diagnostic log still opens Android’s share sheet if you prefer email/Drive/etc.
Categories (only these; not a logcat mirror): GPS, CAMERA, TOGGLE,
SETTING_SAVED, ROUTE_PLAN, ROUTE_PLAN_STAGES, ECO_CALC, POI_FOUND,
PAUSE_PLANNED, INSTRUCTION, FUEL_ADDED, SYSTEM. GPS is rate-limited
(about once per 3 s or 25 m move). Off by default; when off, no file is created
and native per-stage plan timing is also gated off. Older sessions are rotated
(last 10 kept).
Route-plan timing: with logging on, a completed plan writes
status=complete … distance_km=… plan_duration_ms=… on ROUTE_PLAN, plus one
ROUTE_PLAN_STAGES line of per-stage milliseconds (motor vs hiking use
different stage keys). Useful to confirm planning is PBF-load-bound rather than
A*-bound — see README Known issues.
Extreme slowness checklist: if every plan takes tens of seconds across
modes, look for pack_hit=false (and usually large graph_build_ms /
poi_barrier_ms) in the session report. That means indexed packs are missing,
stale, or still converting — A* is typically sub-second. Fix: wait for
background indexing after download, or Tools → Rebuild indexed maps.
.navigph files are deprecated and do not speed plans.
- Open the repo (or the
appmodule) in Android Studio. - Run → Attach Debugger to Android Process →
no.navi.app(pick the correct user process if several appear). - Set breakpoints in Kotlin under
app/src/main/java/no/navi/app/(MainActivity,DriveHud, tests underandroidTest).
Native (Rust / JNI) breakpoints need an NDK debug build of libnavi.so and an
lldb attach workflow; day-to-day Navi debugging is usually logcat + Kotlin
debugger + Rust unit tests, not full JNI stepping.
Compose UI: use Layout Inspector and test tags (top_drive_hud,
bottom_drive_hud, map_settings_sheet, status_toast, …) with
instrumented tests or adb shell uiautomator dump.
./gradlew :app:installDebug :app:installDebugAndroidTest
# One class (APKs stay installed after the run — see
# android.injected.androidTest.leaveApksInstalledAfterRun in gradle.properties)
./gradlew :app:connectedDebugAndroidTest \
-Pandroid.testInstrumentationRunnerArguments.class=no.navi.app.HudVerificationInstrumentedTest
# One method
./gradlew :app:connectedDebugAndroidTest \
-Pandroid.testInstrumentationRunnerArguments.class=no.navi.app.HudVerificationInstrumentedTest#hud_map_tap_does_not_affect_settings_sheetsDo not adb uninstall no.navi.app between debug iterations — that deletes
app-private indexed packs, place index, and PMTiles. Use :app:installDebug
(in-place update) or adb install -r. Uninstall only when switching signing
keys (debug ↔ release) or deliberately testing a fresh install.
Watch the same logcat tags while the suite runs. HUD screenshots are written
under the app external files dir (and sometimes mirrored under
/data/local/tmp/); see android-test-results.md.
NaviMapTestHooks injects camera, headings, altitude, and sheet open requests
for tests — useful when reproducing HUD/map issues without manual UI.
| Symptom | What to try |
|---|---|
| Yellow “bordering activity” screen | ./scripts/launch-navi-emulator.sh (disables display-compat wrapper) |
| App missing / wrong UI on AAOS | Launch with --user 10 (driver); reinstall for that user |
| MapLibre SIGSEGV on bearing (old GLES SDK) | Default is GLES android-sdk:11.13.5 after AAOS re-check PASS; if regression, re-run BearingCrashIsolationTest — see README known issues |
| Gestures / pan seem dead | Overlay must forward touches (see map Canvas pointerInteropFilter); confirm in logcat / HUD map-tap test |
| Icons show fallback / no eco leaf | Clear app files/icons and relaunch so ensureIconsCopied refreshes leaf.svg |
# Java stack
adb logcat -b crash -d
# Native tombstone (path varies by device)
adb shell ls /data/tombstones/ 2>/dev/null
adb bugreport bugreport.zip # heavy; use when filing hardware issuesFor hardware runs, attach full untruncated logcat as required by
real-hardware-testing.md.
Evidence from GitHub Actions run
30334233545
(commit 418f952), job connectedDebugAndroidTest:
| Fact | Value |
|---|---|
| AVD package | system-images;android-30;default;x86_64 |
| Profile | Generic phone AOSP (target: default), not Automotive |
| GPU | -gpu swiftshader_indirect |
| Fail point | ApproachInstructionInstrumentedTest.approachAppearAndUrgencyScreenshots at 3/50 |
| Gradle report | Empty <failure/>; then Process crashed |
| Emulator lines (untruncated excerpt) | See below |
INFO | Created VkInstance:... for application:'maplibre-native' ...
INFO | Created VkDevice:... for application:'maplibre-native' ...
INFO | Destroyed VkDevice:...
INFO | Destroyed VkInstance:... for application:'maplibre-native' ...
no.navi.app.ApproachInstructionInstrumentedTest > approachAppearAndUrgencyScreenshots[...] FAILED
emulator-5554 - 11 Tests 3/50 completed. (0 skipped) (1 failed)
Test run failed to complete. Instrumentation run failed due to Process crashed.
Create→destroy of MapLibre Vulkan in about one second, before any meaningful
approach screenshot work, points at a native crash / aborted MapView on that
phone image — not a Java assertion with a missing stack, and not OOM from a
long suite (positions 1–2 are HideDistance JNI checks with no MapLibre). CI did
not upload logcat/tombstones on that run; local AAOS reproduction of tests 1–3
passed with no new tombstone (only Zygote signal 9 after a clean
run finished: 3 tests, 0 failed).
Mitigation: nightly workflow retargeted to API 33 android-automotive with
-memory 4096, post-boot wm size 1920x1080 (avoid emulator -skin on this
AVD), and scripts/ci-connected-android-test.sh for continuous logcat + report
upload (instrumented-evidence/). Evidence steps use if: always() / an EXIT
trap / host runner/ fallbacks; cancel-in-progress is false so a
diagnostic workflow_dispatch is not killed mid-suite by a later push.
First Automotive confirmation
(30363333076):
process-crash abort did not recur; approach failed
approach box must be compact (was 209px of 320.0px screen) on the default
narrow skin instead — assertion now uses the 420.dp product cap, with the
45% width fraction only on screens ≥ 720px. Note: android-emulator-runner
runs each YAML script: line as its own sh -c, so multi-line inline scripts
cannot share variables (that broke evidence mkdir on
30364223279).
Cancelled mid-suite run
30364672472
still uploaded a 3.7 MB continuous logcat (approach passed after the assert
fix; Basemap was in progress when concurrency cancelled the job).
Complete evidence from
30369028382:
Basemap dies on the Mapterhorn 3D+tilt segment under the 4 GB CI AVD
(guest overcommit; no tombstone); offline+boundary shots pass. CI keeps
-memory 4096 and skips that 3D segment via instrumentation arg
navi.ci=true (host CI= is invisible on-device) — full 3D remains
local/hardware.
- Reproduce on emulator with
installDebug+launch-navi-emulator.sh. - Capture
adb logcatfor the tags above from cold start through the failure. - If FFI/routing related: reproduce with a Rust test or re-run
build-android-native.shand reinstall. - If UI/HUD related: run the relevant
*InstrumentedTestmethod; pull screenshots from the device files dir. - On a real head unit: follow
real-hardware-testing.mdand keep logs + screenshots together.