Upstream kept the head unit on the driver's phone hotspot by re-joining a saved network every few minutes. This car's head unit has its own connection and the Wi-Fi is not used, so the feature goes entirely: WifiAutoConnect, its call on the service tick, the preference key, and the ACCESS_WIFI_STATE / CHANGE_WIFI_STATE permissions. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QLJfZQZeAKAwSqViTmLzki
220 lines
9.0 KiB
TOML
220 lines
9.0 KiB
TOML
# Development environment with mise (the Nix flake, `nix develop`, is the other way in).
|
|
#
|
|
# Committed on purpose: it describes how to build the project, not someone's machine. Paths
|
|
# go through {{env.HOME}} / JAVA_HOME, never hard-coded. For a local override (keystore path,
|
|
# SDK elsewhere), create a mise.local.toml — that one stays ignored by git.
|
|
#
|
|
# mise install installs the JDK
|
|
# mise run bootstrap installs the Android SDK if missing
|
|
# mise run test unit tests
|
|
# mise tasks lists everything
|
|
#
|
|
# Why the JDK is pinned: the machine's default JDK (26) is not supported by AGP and fails the
|
|
# build with an unhelpful class-version error.
|
|
|
|
[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",
|
|
]
|
|
|
|
# SDK version app/build.gradle needs (compileSdk = 36).
|
|
COMPILE_SDK = "36"
|
|
BUILD_TOOLS = "36.0.0"
|
|
|
|
# APK SIGNING secrets — nothing to do with the ABRP API key, which is entered in the app.
|
|
# Set them outside the repository: uncomment in a mise.local.toml (ignored by git) and point
|
|
# at a local keystore, NEVER at a file in the repository. The car build is signed by
|
|
# `build-car` (flake.nix) instead.
|
|
# SIGNING_KEYSTORE = "{{env.HOME}}/.keystores/signing.keystore"
|
|
# SIGNING_STORE_PASSWORD = "…"
|
|
# SIGNING_KEY_ALIAS = "platform"
|
|
# SIGNING_KEY_PASSWORD = "…"
|
|
|
|
# Gradle must run on the JDK mise just installed. Without -Dorg.gradle.java.home, Gradle may
|
|
# reuse a daemon started by another JDK — typically the one bundled with VS Code's Java
|
|
# extension, a JRE 21 without jlink: the build then fails in JdkImageTransform, with a message
|
|
# that has nothing to do with the code. JAVA_HOME is set by [tools] above; $JAVA_HOME is
|
|
# resolved by the shell at run time, so this stays portable between machines.
|
|
# ── Emulator ─────────────────────────────────────────────────────────────────────
|
|
#
|
|
# A WORD on fidelity: the car runs AAOS 9 (API 28). Google publishes NO Automotive system
|
|
# image below API 33 — checked with sdkmanager --list. So no emulator reproduces both the OS
|
|
# and the car API of the MG4. Hence two profiles, each faithful on one axis:
|
|
#
|
|
# emulator-car API 33 Automotive — has a CarPropertyManager, wrong OS version. For the
|
|
# service lifecycle and the car path. SAIC VENDOR properties (0x216xxxxx:
|
|
# SOC, range) do not exist in the emulator: those reads fail, as expected.
|
|
# emulator-screen API 28 tablet — right OS version and right screen (1920x720 @ 160 dpi,
|
|
# 12.8" HD like the MG4), but NO car services. For the UI, layouts and
|
|
# density. (The AOSP API 28 image also shares the car's platform key, so the
|
|
# car build installs there as the system user.)
|
|
#
|
|
# None of this replaces a test on the car.
|
|
# Screen 1920x720 px at 160 dpi, as on the car (app window 1778x720). Reference firmware:
|
|
# 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 = "Installs the Android SDK (platform + build-tools) if missing"
|
|
run = """
|
|
set -e
|
|
if [ -d "$ANDROID_HOME/platforms/android-$COMPILE_SDK" ]; then
|
|
echo "SDK $COMPILE_SDK already present, nothing to do"
|
|
exit 0
|
|
fi
|
|
mkdir -p "$ANDROID_HOME"
|
|
if [ ! -x "$ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager" ]; then
|
|
echo "Downloading the command-line 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 installed, local.properties written"
|
|
"""
|
|
|
|
[tasks.build]
|
|
description = "Builds the debug APK"
|
|
run = './gradlew -Dorg.gradle.java.home=$JAVA_HOME assembleStableDebug'
|
|
|
|
[tasks.test]
|
|
description = "JVM unit tests"
|
|
run = './gradlew -Dorg.gradle.java.home=$JAVA_HOME testStableDebugUnitTest testUnstableDebugUnitTest'
|
|
|
|
[tasks.lint]
|
|
description = "Android lint"
|
|
run = './gradlew -Dorg.gradle.java.home=$JAVA_HOME lintStableDebug lintUnstableDebug'
|
|
|
|
[tasks.permissions]
|
|
description = "Permission drift check (the same as CI)"
|
|
run = "bash .github/security/check-permissions.sh"
|
|
|
|
[tasks.check]
|
|
description = "What CI runs: permissions + lint + tests"
|
|
depends = ["permissions", "lint", "test"]
|
|
|
|
[tasks.install]
|
|
description = "Installs the debug APK on the car connected over ADB"
|
|
run = './gradlew -Dorg.gradle.java.home=$JAVA_HOME installStableDebug'
|
|
|
|
[tasks.logs]
|
|
description = "Follows the app's logs on the car"
|
|
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"
|
|
|
|
[tasks.release]
|
|
description = "Release APKs of both channels (stable without OTA, unstable with 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 = "Installs the emulator and system images and creates both AVDs"
|
|
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 "ERROR: /dev/kvm missing — no hardware acceleration, the emulator would be unusable." >&2
|
|
echo "On WSL2: enable nested virtualisation on the Windows side." >&2
|
|
exit 1
|
|
fi
|
|
if ! id -nG | tr ' ' '\n' | grep -qx kvm; then
|
|
echo "WARNING: user is not in the kvm group. Fix with:" >&2
|
|
echo " sudo usermod -aG kvm $USER # then log in again" >&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 already exists"
|
|
else
|
|
echo no | "$AVDM" create avd -n "$name" -k "$image" -d "$device" --force
|
|
fi
|
|
cfg="$HOME/.android/avd/$name.avd/config.ini"
|
|
# The MG4's screen. Without this the AVD keeps the generic profile's density and the
|
|
# layouts do not look like what the driver sees.
|
|
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 set to ${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, not "host": WSL2 exposes no real GPU (only Mesa llvmpipe). With
|
|
# -gpu host the emulator dies at start-up on "Your GPU cannot be used for hardware
|
|
# rendering", before any window appears — hence a tray icon and no visible emulator.
|
|
# Software rendering is slower, but it starts.
|
|
[tasks.emulator-car]
|
|
description = "Starts the Automotive emulator (API 33 — has a 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 "Car emulator ready."
|
|
'''
|
|
|
|
[tasks.emulator-screen]
|
|
description = "Starts the MG4 screen emulator (API 28, 1920x720 @ 160dpi — no car services)"
|
|
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 "Screen emulator ready."
|
|
'''
|
|
|
|
[tasks.emulator-stop]
|
|
description = "Stops the running emulator"
|
|
run = '"$ANDROID_HOME/platform-tools/adb" emu kill || true'
|
|
|
|
[tasks.run]
|
|
description = "Builds, installs and starts the APK on the emulator or the connected car"
|
|
depends = ["build"]
|
|
run = '''
|
|
set -e
|
|
ADB="$ANDROID_HOME/platform-tools/adb"
|
|
if [ -z "$($ADB devices | sed '1d' | grep -w device || true)" ]; then
|
|
echo "No device connected. Start 'mise run emulator-car' or 'mise run emulator-screen' first," >&2
|
|
echo "or connect the car over ADB." >&2
|
|
exit 1
|
|
fi
|
|
APK=$(ls -t app/build/outputs/apk/stable/debug/*.apk | head -1)
|
|
echo "Installing $APK"
|
|
$ADB install -r -g "$APK"
|
|
$ADB shell am start -n "com.stinnesbeck.nilsmg4/com.evsuite.abrp.MainActivity"
|
|
echo "Started. Logs: mise run logs"
|
|
'''
|