diff --git a/flake.nix b/flake.nix index 948455b..70fead2 100644 --- a/flake.nix +++ b/flake.nix @@ -33,30 +33,99 @@ sdk = "${android.androidsdk}/libexec/android-sdk"; # gradle/gradle-daemon-jvm.properties pins the daemon to JDK 21. jdk = pkgs.jdk21; + buildTools = "${sdk}/build-tools/${buildToolsVersion}"; + + # AGP otherwise downloads an aapt2 binary from Maven, which is dynamically linked + # against a generic FHS and does not run on NixOS. Use the SDK's patched one. + # Gradle is also pointed at the Nix JDK so the daemon toolchain criteria are met + # locally instead of downloading a (non-NixOS) JDK through foojay. + gradleOpts = builtins.concatStringsSep " " [ + "-Dorg.gradle.project.android.aapt2FromMavenOverride=${buildTools}/aapt2" + "-Dorg.gradle.java.installations.paths=${jdk.home}" + "-Dorg.gradle.java.installations.auto-download=false" + ]; + + # The MG4 head unit is signed with the public AOSP test platform key, so that is + # the key a system-UID (unstable) build must carry. See tools/README.md and OTA.md. + aospKeys = "https://raw.githubusercontent.com/aosp-mirror/platform_build/android-9.0.0_r1/target/product/security"; + platformCertSha256 = "c8a2e9bccf597c2fb6dc66bee293fc13f2fc47ec77bc6b2b0d52c11f51192ab8"; + + buildCar = pkgs.writeShellApplication { + name = "build-car"; + runtimeInputs = [ jdk pkgs.curl pkgs.openssl pkgs.coreutils pkgs.gnugrep pkgs.gawk ]; + text = '' + root=$(git rev-parse --show-toplevel 2>/dev/null || pwd) + cd "$root" + if [ ! -f app/build.gradle ]; then + echo "build-car: run this from the FatihsMG4 checkout" >&2 + exit 1 + fi + + export ANDROID_HOME=${sdk} ANDROID_SDK_ROOT=${sdk} JAVA_HOME=${jdk.home} + export GRADLE_OPTS="${gradleOpts}" + echo "sdk.dir=$ANDROID_HOME" > local.properties + + # Keys live in tools/, which .gitignore keeps out of the repository. + for f in platform.pk8 platform.x509.pem; do + if [ ! -f "tools/$f" ]; then + echo "Fetching AOSP test key tools/$f" + curl -sSfL -o "tools/$f" "${aospKeys}/$f" + fi + done + cert=$(openssl x509 -in tools/platform.x509.pem -outform der | sha256sum | cut -d' ' -f1) + if [ "$cert" != "${platformCertSha256}" ]; then + echo "build-car: tools/platform.x509.pem has SHA-256 $cert, expected ${platformCertSha256}" >&2 + exit 1 + fi + + ./gradlew assembleUnstableDebug "$@" + version=$(./gradlew -q printUnstableVersion "$@" | tail -1) + + in=app/build/outputs/apk/unstable/debug/app-unstable-debug.apk + mkdir -p build/car + aligned=build/car/aligned.apk + out="build/car/FatihsMG4-unstable-$version.apk" + + "${buildTools}/zipalign" -f -p 4 "$in" "$aligned" + # Options come before the input APK; apksigner rejects the other order. + "${buildTools}/apksigner" sign \ + --key tools/platform.pk8 --cert tools/platform.x509.pem \ + --out "$out" "$aligned" + rm -f "$aligned" "$out.idsig" + + signer=$("${buildTools}/apksigner" verify --print-certs "$out" \ + | awk -F': ' '/Signer #1 certificate SHA-256 digest/ { print $2 }') + if [ "$signer" != "${platformCertSha256}" ]; then + echo "build-car: $out is signed with $signer, expected ${platformCertSha256}" >&2 + rm -f "$out" + exit 1 + fi + sha256sum "$out" | cut -d' ' -f1 > "$out.sha256" + + echo + echo "Signed with the AOSP platform key: $out" + echo "Install on the head unit:" + echo " adb push $out /data/local/tmp/update.apk" + echo " adb shell \"pm install -r -f -d -t /data/local/tmp/update.apk\"" + ''; + }; in { default = pkgs.mkShell { - packages = [ jdk android.androidsdk ]; + packages = [ jdk android.androidsdk buildCar ]; ANDROID_HOME = sdk; ANDROID_SDK_ROOT = sdk; JAVA_HOME = jdk.home; - # AGP otherwise downloads an aapt2 binary from Maven, which is dynamically linked - # against a generic FHS and does not run on NixOS. Use the SDK's patched one. - # Gradle is also pointed at the Nix JDK so the daemon toolchain criteria are met - # locally instead of downloading a (non-NixOS) JDK through foojay. - GRADLE_OPTS = builtins.concatStringsSep " " [ - "-Dorg.gradle.project.android.aapt2FromMavenOverride=${sdk}/build-tools/${buildToolsVersion}/aapt2" - "-Dorg.gradle.java.installations.paths=${jdk.home}" - "-Dorg.gradle.java.installations.auto-download=false" - ]; + GRADLE_OPTS = gradleOpts; shellHook = '' echo "sdk.dir=$ANDROID_HOME" > local.properties echo "FatihsMG4 dev shell — JDK $(java -version 2>&1 | head -1 | cut -d'"' -f2), SDK at $ANDROID_HOME" echo " ./gradlew assembleStableDebug # debug APK" echo " ./gradlew assembleUnstableDebug # build that can update itself (OTA)" + echo " build-car # unstable build signed for the car" ''; }; });