From 827653aa291e37ae63c0cfa1eee4fbaa20cea122 Mon Sep 17 00:00:00 2001 From: Lena Date: Sat, 1 Aug 2026 00:00:00 +0000 Subject: build: pin and verify release inputs --- app/build.gradle | 65 ++++++++++++++++++++++++++++++-------------------------- 1 file changed, 35 insertions(+), 30 deletions(-) (limited to 'app/build.gradle') diff --git a/app/build.gradle b/app/build.gradle index 323f340..2c8ec99 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -5,18 +5,18 @@ plugins { import java.security.MessageDigest android { - namespace 'invalid.lena.scrcpy' - compileSdk 35 + namespace = 'invalid.lena.scrcpy' + compileSdk 36 // Pin build-tools explicitly so the aapt2/zipalign/d8 toolchain is // fixed and builds stay reproducible, not whatever AGP defaults to. - // 35.0.0 is the compileSdk's. + // 35.0.0 is AGP 8.13's supported default. buildToolsVersion '35.0.0' defaultConfig { // The published package id. Immutable across releases. applicationId 'invalid.lena.scrcpy' minSdk 31 - targetSdk 35 + targetSdk 36 versionCode 4 versionName '0.4' } @@ -26,8 +26,8 @@ android { // deterministic. This is the single most common reproducible-build // breaker on AGP. dependenciesInfo { - includeInApk false - includeInBundle false + includeInApk = false + includeInBundle = false } // Release signing pulls credentials from the environment. Set all @@ -49,10 +49,9 @@ android { } } - // Native ABI policy: release ships arm64-v8a only. Every targeted - // source device is 64-bit ARM in practice; 32-bit-only devices are - // not supported. The test rig uses an x86_64 emulator, so debug keeps - // x86_64 (and arm64) so ./test e2e still works. + // Ship one ordinary APK containing both supported 64-bit ABIs. arm64 + // covers physical devices; x86_64 covers emulators and ChromeOS without + // introducing split-APK installation logic. buildTypes { debug { applicationIdSuffix '.debug' @@ -62,15 +61,32 @@ android { } } release { + // x86_64 is here so the emulator can install and RUN the + // minified artifact. Without it no tier ever executed the + // variant that ships, and an R8 misconfiguration - a keep + // rule that is too narrow, a reflective edge R8 could not + // see - would first show up on a user's device. It costs a + // second ABI in the APK and buys coverage of the only build + // that matters. + // + // This is why the APK is ~7.5 MB while the dex shrank: it + // carries two copies of libconscrypt_jni.so and libspake2.so, + // stored uncompressed and 16 KiB aligned for Android 15+ page + // sizes. Uncompressed native libraries inflate the download + // and shrink the installation, because they are mapped from + // the APK instead of being extracted. A single-ABI build is + // about 3.5 MB; splits would give users that without giving + // up the emulator coverage, and are the obvious next step if + // download size starts to matter. ndk { - abiFilters 'arm64-v8a' + abiFilters 'arm64-v8a', 'x86_64' } minifyEnabled true - shrinkResources true + shrinkResources = true proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' if (signingReady) { - signingConfig signingConfigs.release + signingConfig = signingConfigs.release } } } @@ -86,20 +102,14 @@ android { // our use case (we only call BC's asn1, crypto, util.encoders): // - picnic post-quantum lookup tables: ~1.2 MB of .properties // - cert-path-reviewer i18n messages: ~92 KB, en+de only - // - kotlin metadata: bleed from spake2-android (Kotlin lib); - // our code is pure Java and doesn't reflect on .kotlin_builtins - // - androidx annotation LICENSE.txt: 10 KB blob, no runtime use // - duplicate META-INF licenses/notices: ordinary AGP cleanup resources.excludes += [ 'META-INF/LICENSE*', 'META-INF/NOTICE*', 'META-INF/versions/9/OSGI-INF/MANIFEST.MF', - 'META-INF/androidx/**', - 'META-INF/kotlin-stdlib*', 'org/bouncycastle/pqc/crypto/picnic/**', 'org/bouncycastle/pqc/legacy/picnic/**', 'org/bouncycastle/x509/CertPathReviewerMessages*.properties', - 'kotlin/**', ] } @@ -122,15 +132,8 @@ dependencies { // we declare it explicitly here rather than patch the vendor tree. implementation 'org.bouncycastle:bcprov-jdk15to18:1.84' - // Bundled Conscrypt. libadb-android's TLS pairing exports keying - // material via Conscrypt. Without a bundled copy it reflects into the - // platform's hidden com.android.org.conscrypt.Conscrypt, which under - // targetSdk 35 is invisible to reflection (getMethod throws - // NoSuchMethodException and pairing fails on real devices). Bundling a - // standalone Conscrypt flips libadb onto the public - // org.conscrypt.Conscrypt API, which is not restricted. Ships a native - // .so (filtered to arm64-v8a in release). - implementation 'org.conscrypt:conscrypt-android:2.5.2' + // Wireless pairing requires the public Conscrypt API and native provider. + implementation 'org.conscrypt:conscrypt-android:2.6.1' testImplementation 'junit:junit:4.13.2' testImplementation 'org.json:json:20240303' @@ -151,14 +154,16 @@ tasks.register('verifyScrcpyServer') { } def expected = expectedServerSum.get() def version = serverVersion.text.trim() - if (!(expected ==~ /[0-9a-f]{64}/) || version.isEmpty()) { + if (!(expected ==~ /[0-9a-f]{64}/) + || !(version ==~ /[0-9]+(\.[0-9]+)*/)) { throw new GradleException('scrcpy server checksum or version is invalid') } def digest = MessageDigest.getInstance('SHA-256') serverJar.withInputStream { input -> byte[] buffer = new byte[64 * 1024] for (int n; (n = input.read(buffer)) >= 0; ) { - if (n > 0) digest.update(buffer, 0, n) + if (n == 0) throw new GradleException('scrcpy server read made no progress') + digest.update(buffer, 0, n) } } def actual = digest.digest().encodeHex().toString() -- cgit v1.2.3