Build the phone's APK as a signed release, and say so in the render report

The phone has been running the debug build: build-apk.sh assembled it, and
nothing in the app or its report said which build a frame time came from.
A debuggable build runs Compose at a fraction of release speed, so the
tuning so far was measured against the wrong number. On the emulator, the
same fixture and gestures: measure 1.4ms mean / 14.7ms worst on debug,
0.8ms / 6.6ms on release.

build-apk.sh now assembles the release variant, signed with a key it
generates once under ~/.config/ai-app (beside the pinned CA, outside any
checkout). The report's header names the build. The one native library is
declared kept-with-symbols so packaging stops warning about an NDK the
build does not need.

Also: ui-sandbox.sh keep now keeps the config too. The server appends
spawned sessions and enrolled tokens to it, so regenerating it left the
transcripts on disk and the registry empty.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
irisandClaude Fable 5.1 committed 2026-09-02 05:21:30 -04:00
1 parent 486842e4aa
commit 8750ff90cb
5 files changed
+97 -10

No files matched your search

+29 -2
View File
@@ -103,8 +103,35 @@ android {
versionCode = 1
versionName = "1.0"
}
packaging { resources { excludes += "/META-INF/{AL2.0,LGPL2.1}" } }
buildTypes { getByName("release") { isMinifyEnabled = false } }
packaging {
resources { excludes += "/META-INF/{AL2.0,LGPL2.1}" }
// The one native library here is AndroidX's, a few hundred kilobytes with its symbols.
// Stripping them needs an NDK the release build would otherwise not use; keeping them
// is declared so AGP stops warning that it could not.
jniLibs { keepDebugSymbols += "**/libandroidx.graphics.path.so" }
}
// A release build must be signed, and the key is per machine rather than per repo: it is
// what the phone recognises the app by, and a secret never lives in a checkout (the mount is
// shared with an untrusted VM). build-apk.sh keeps it beside the pinned CA and points here
// through the environment; without it the release build is unsigned, which is fine for
// everything except installing.
val keystore = System.getenv("AI_APP_KEYSTORE")
signingConfigs {
if (keystore != null) {
create("release") {
storeFile = file(keystore)
storePassword = System.getenv("AI_APP_KEYSTORE_PASSWORD")
keyAlias = "ai-app"
keyPassword = storePassword
}
}
}
buildTypes {
getByName("release") {
isMinifyEnabled = false
if (keystore != null) signingConfig = signingConfigs.getByName("release")
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_21
targetCompatibility = JavaVersion.VERSION_21
@@ -1,5 +1,7 @@
package com.example.aiapp
import android.content.Context
import android.content.pm.ApplicationInfo
import android.os.Build
import android.os.SystemClock
import android.util.Log
@@ -1195,7 +1197,11 @@ fun SessionScreen(settings: ServerSettings, summary: SessionSummary, onBack: ()
debugReport(
device =
"device: ${Build.MODEL} (${Build.MANUFACTURER})," +
" Android ${Build.VERSION.RELEASE}",
" Android ${Build.VERSION.RELEASE}\n" +
// A debuggable build runs Compose at a fraction of
// release speed, so a report that did not say which
// it came from was read as the app's own cost.
"build: ${if (debuggable(context)) "debug" else "release"}",
transcript =
listOf(
" ${items.size} events, ${rows.size} rows," +
@@ -2312,3 +2318,6 @@ private fun PickerButton(current: String, options: List<String>, onPick: (String
}
}
}
private fun debuggable(context: Context) =
context.applicationInfo.flags and ApplicationInfo.FLAG_DEBUGGABLE != 0