Files
mg4/mise.toml
T
nilsandClaude Opus 5.5 a5db5604d3 Code review fixes: app 2.9.0, server 2.2.0
Telemetry no longer loses queued batches. The server drops a bad value,
or a sample without a plausible time, instead of rejecting the batch,
and answers 200 with what it dropped. The app sends only values inside
the server's ranges (speed as a magnitude, so reversing is fine) and no
samples before the head unit's clock is set.

Server:
- Pairing hands out and accepts a token only after the device is saved;
  a failed save leaves the request pending.
- A paired token serves only the installation id it was paired with
  (403 otherwise); the shared INGEST_TOKEN still serves any.
- Wrong-password lockouts and open pairing requests are limited per
  client address (last X-Forwarded-For entry), so a stranger cannot
  lock the owner out; a car starting again replaces its own request.
- ABRP forwarding skips samples stamped ahead of the server's clock,
  which used to stop it until a restart.

App:
- Settings written to plaintext during a keystore failure move back
  into encrypted storage; the stored installation id wins.
- The installation id is derived from ANDROID_ID on first start, so a
  reinstall finds its history; a stored or imported id still wins.
- The scanned config file is deleted after import (it holds the token).
- GPS stops while live telemetry is off or the server unconfigured, and
  pauses while parked and idle, keeping the fix the car parked with.
- Range estimates use the monotonic clock.
- One HTTP helper (ServerHttp) for telemetry, sync and pairing.
- Last Turkish strings translated; mise logs shows the NilsMG4 tags.

Grafana reads InfluxDB with its own read-only token (new grafana-influx
Secret, created by hand as described in k8s/README.md).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QLJfZQZeAKAwSqViTmLzki
2026-10-18 00:36:19 +02:00

222 lines
9.3 KiB
TOML

# Environnement de développement — EVABRPUploader.
#
# Commité volontairement : il décrit comment construire le projet, pas la machine de
# quelqu'un. Les chemins passent par {{env.HOME}} / JAVA_HOME, jamais en dur.
# Pour une surcharge locale (chemin de keystore, SDK ailleurs), créer un mise.local.toml
# — celui-là reste ignoré par git.
#
# mise install installe le JDK
# mise run bootstrap installe le SDK Android si absent
# mise run test tests unitaires
# mise tasks liste tout
#
# Pourquoi épingler le JDK : le JDK par défaut de la machine (26) n'est pas supporté
# par AGP et fait échouer le build avec une erreur de version de classe peu parlante.
[tools]
java = "17"
[env]
ANDROID_HOME = "{{env.HOME}}/Android/Sdk"
ANDROID_SDK_ROOT = "{{env.HOME}}/Android/Sdk"
_.path = [
"{{env.HOME}}/Android/Sdk/platform-tools",
"{{env.HOME}}/Android/Sdk/cmdline-tools/latest/bin",
]
# Version du SDK requise par app/build.gradle (compileSdk = 36).
COMPILE_SDK = "36"
BUILD_TOOLS = "36.0.0"
# Secrets de SIGNATURE D'APK — sans rapport avec la clé d'API ABRP, qui se saisit dans
# l'application. À définir hors du dépôt : décommenter dans un mise.local.toml (ignoré
# par git) et pointer vers un keystore local, JAMAIS vers un fichier du dépôt.
# SIGNING_KEYSTORE = "{{env.HOME}}/.keystores/signing.keystore"
# SIGNING_STORE_PASSWORD = "…"
# SIGNING_KEY_ALIAS = "platform"
# SIGNING_KEY_PASSWORD = "…"
# Gradle doit tourner sur le JDK que mise vient d'installer. Sans -Dorg.gradle.java.home,
# Gradle peut réutiliser un daemon lancé par un autre JDK — typiquement celui embarqué par
# l'extension Java de VS Code, un JRE 21 sans jlink : le build échoue alors sur
# JdkImageTransform, avec un message qui n'a rien à voir avec le code.
# JAVA_HOME est posé par [tools] ci-dessus : $JAVA_HOME est résolu par le shell au moment
# de l'exécution, donc ceci reste portable d'une machine à l'autre.
# ── Émulateur ────────────────────────────────────────────────────────────────────
#
# ATTENTION sur la fidélité : le véhicule tourne en AAOS 9 (API 28). Google ne publie
# AUCUNE image système Automotive en dessous d'API 33 — vérifié avec sdkmanager --list.
# Il n'existe donc pas d'émulateur qui reproduise à la fois l'OS et l'API voiture du MG4.
# D'où deux profils, chacun fidèle sur un axe :
#
# emulator-car API 33 Automotive — a bien un CarPropertyManager, mauvaise version
# d'OS. Pour tester le cycle de vie du service et le chemin voiture.
# Les propriétés VENDEUR SAIC (0x216xxxxx : SOC, autonomie) n'existent
# pas dans l'émulateur : ces lectures échoueront, c'est normal.
# emulator-screen API 28 tablette — bonne version d'OS et bon écran (1920x720 @ 160dpi,
# 12,8" HD comme le MG4), mais AUCUN service voiture.
# Pour l'UI, les layouts et la densité.
#
# Rien de tout cela ne remplace un essai sur le véhicule.
# Densité cible du projet : 160 dpi.
# Écran 12,8" 1920x720. Firmware de référence : SWI68-29958-1300R69.
EMU_CAR_AVD = "evabrp-car"
EMU_CAR_IMAGE = "system-images;android-33;android-automotive;x86_64"
EMU_SCREEN_AVD = "evabrp-screen"
EMU_SCREEN_IMAGE = "system-images;android-28;google_apis;x86_64"
EMU_WIDTH = "1920"
EMU_HEIGHT = "720"
EMU_DENSITY = "160"
[tasks.bootstrap]
description = "Installe le SDK Android (platform + build-tools) s'il manque"
run = """
set -e
if [ -d "$ANDROID_HOME/platforms/android-$COMPILE_SDK" ]; then
echo "SDK $COMPILE_SDK déjà présent → rien à faire"
exit 0
fi
mkdir -p "$ANDROID_HOME"
if [ ! -x "$ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager" ]; then
echo "Téléchargement des commandline-tools…"
tmp=$(mktemp -d)
curl -sSL -o "$tmp/tools.zip" \
https://dl.google.com/android/repository/commandlinetools-linux-11076708_latest.zip
unzip -q "$tmp/tools.zip" -d "$tmp"
mkdir -p "$ANDROID_HOME/cmdline-tools"
mv "$tmp/cmdline-tools" "$ANDROID_HOME/cmdline-tools/latest"
rm -rf "$tmp"
fi
yes | "$ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager" --sdk_root="$ANDROID_HOME" --licenses > /dev/null
"$ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager" --sdk_root="$ANDROID_HOME" \
"platform-tools" "platforms;android-$COMPILE_SDK" "build-tools;$BUILD_TOOLS"
echo "sdk.dir=$ANDROID_HOME" > local.properties
echo "SDK installé, local.properties écrit"
"""
[tasks.build]
description = "Compile l'APK debug"
run = './gradlew -Dorg.gradle.java.home=$JAVA_HOME assembleStableDebug'
[tasks.test]
description = "Tests unitaires JVM"
run = './gradlew -Dorg.gradle.java.home=$JAVA_HOME testStableDebugUnitTest testUnstableDebugUnitTest'
[tasks.lint]
description = "Analyse lint Android"
run = './gradlew -Dorg.gradle.java.home=$JAVA_HOME lintStableDebug lintUnstableDebug'
[tasks.permissions]
description = "Contrôle de dérive des permissions (le même que la CI)"
run = "bash .github/security/check-permissions.sh"
[tasks.check]
description = "Ce que la CI exécute : permissions + lint + tests"
depends = ["permissions", "lint", "test"]
[tasks.install]
description = "Installe l'APK debug sur le véhicule connecté en ADB"
run = './gradlew -Dorg.gradle.java.home=$JAVA_HOME installStableDebug'
[tasks.logs]
description = "Suit les logs de l'app sur le véhicule"
run = "adb logcat -s AbrpUploadService:V CarPropertyAdapter:V VhalProbe:V SecurePrefs:V OtaUpdater:V UpdateHook:V NilsMG4.Telemetry:V NilsMG4.Mongo:V NilsMG4.SteeringKey:V EVABRP.Boot:V EVABRP.Wifi:V"
[tasks.release]
description = "APK release des deux canaux (stable sans OTA, unstable avec OTA)"
run = './gradlew -Dorg.gradle.java.home=$JAVA_HOME assembleStableRelease assembleUnstableRelease'
[tasks.clean]
run = './gradlew -Dorg.gradle.java.home=$JAVA_HOME clean'
[tasks.emulator-setup]
description = "Installe l'émulateur + les images système et crée les deux AVD"
run = '''
set -e
SDK="$ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager"
AVDM="$ANDROID_HOME/cmdline-tools/latest/bin/avdmanager"
if [ ! -e /dev/kvm ]; then
echo "ERREUR : /dev/kvm absent — pas d'accélération matérielle, l'émulateur sera inutilisable." >&2
echo "Sous WSL2 : activer la virtualisation imbriquée côté Windows." >&2
exit 1
fi
if ! id -nG | tr ' ' '\n' | grep -qx kvm; then
echo "ATTENTION : utilisateur absent du groupe kvm. Corriger avec :" >&2
echo " sudo usermod -aG kvm $USER # puis se reconnecter" >&2
fi
yes | "$SDK" --sdk_root="$ANDROID_HOME" --licenses > /dev/null
"$SDK" --sdk_root="$ANDROID_HOME" "emulator" "platform-tools" "$EMU_CAR_IMAGE" "$EMU_SCREEN_IMAGE"
create_avd() {
name="$1"; image="$2"; device="$3"
if "$AVDM" list avd 2>/dev/null | grep -q "Name: $name"; then
echo "AVD $name déjà présent"
else
echo no | "$AVDM" create avd -n "$name" -k "$image" -d "$device" --force
fi
cfg="$HOME/.android/avd/$name.avd/config.ini"
# Écran du MG4. Sans ça l'AVD garde la densité du profil générique et les layouts
# ne ressemblent pas à ce que voit le conducteur.
sed -i '/^hw.lcd/d;/^disk.dataPartition.size/d' "$cfg"
{
echo "hw.lcd.width=$EMU_WIDTH"
echo "hw.lcd.height=$EMU_HEIGHT"
echo "hw.lcd.density=$EMU_DENSITY"
echo "disk.dataPartition.size=4G"
} >> "$cfg"
echo "AVD $name configuré en ${EMU_WIDTH}x${EMU_HEIGHT} @ ${EMU_DENSITY}dpi"
}
create_avd "$EMU_CAR_AVD" "$EMU_CAR_IMAGE" "automotive_1024p_landscape"
create_avd "$EMU_SCREEN_AVD" "$EMU_SCREEN_IMAGE" "pixel_c"
'''
# swiftshader_indirect, pas « host » : WSL2 n'expose aucun GPU réel (uniquement Mesa
# llvmpipe). Avec -gpu host l'émulateur meurt au démarrage sur « Your GPU cannot be used
# for hardware rendering », avant qu'aucune fenêtre n'apparaisse — d'où l'icône de tray
# sans émulateur visible. Le rendu logiciel est plus lent, mais il démarre.
[tasks.emulator-car]
description = "Lance l'émulateur Automotive (API 33 — a un CarPropertyManager)"
run = '''
set -e
"$ANDROID_HOME/emulator/emulator" -avd "$EMU_CAR_AVD" \
-gpu swiftshader_indirect -no-snapshot-save -no-boot-anim &
"$ANDROID_HOME/platform-tools/adb" wait-for-device
echo "Émulateur voiture prêt."
'''
[tasks.emulator-screen]
description = "Lance l'émulateur écran MG4 (API 28, 1920x720 @ 160dpi — pas de service voiture)"
run = '''
set -e
"$ANDROID_HOME/emulator/emulator" -avd "$EMU_SCREEN_AVD" \
-gpu swiftshader_indirect -no-snapshot-save -no-boot-anim &
"$ANDROID_HOME/platform-tools/adb" wait-for-device
echo "Émulateur écran prêt."
'''
[tasks.emulator-stop]
description = "Arrête l'émulateur en cours"
run = '"$ANDROID_HOME/platform-tools/adb" emu kill || true'
[tasks.run]
description = "Compile, installe et lance l'APK sur l'émulateur ou le véhicule connecté"
depends = ["build"]
run = '''
set -e
ADB="$ANDROID_HOME/platform-tools/adb"
if [ -z "$($ADB devices | sed '1d' | grep -w device || true)" ]; then
echo "Aucun appareil connecté. Lancer d'abord 'mise run emulator-car' ou 'mise run emulator-screen'," >&2
echo "ou brancher le véhicule en ADB." >&2
exit 1
fi
APK=$(ls -t app/build/outputs/apk/stable/debug/*.apk | head -1)
echo "Installation de $APK"
$ADB install -r -g "$APK"
$ADB shell am start -n "com.stinnesbeck.nilsmg4/com.evsuite.abrp.MainActivity"
echo "Lancé. Logs : mise run logs"
'''