From 3010318ba3fec7d525ea92cc01ffa0c3f10a60b0 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sun, 16 Aug 2026 23:36:57 -0700 Subject: [PATCH] fix(android): isolate debug app installs (#125099) --- apps/android/app/build.gradle.kts | 28 ++++++++++++++++++- .../android/app/src/debug/AndroidManifest.xml | 2 +- .../app/src/debug/res/values/strings.xml | 3 ++ .../java/ai/openclaw/app/AssistantLaunch.kt | 2 +- .../app/src/main/res/xml/shortcuts.xml | 2 +- .../app/benchmark/CronJobNavigationTest.kt | 5 ++-- .../app/benchmark/StartupMacrobenchmark.kt | 3 +- apps/android/scripts/perf-online-benchmark.sh | 8 +++--- apps/android/scripts/perf-startup-hotspots.sh | 4 +-- apps/android/scripts/voice-e2e.sh | 12 ++++---- apps/android/wear/build.gradle.kts | 4 +++ .../wear/src/debug/res/values/strings.xml | 3 ++ package.json | 4 +-- scripts/android-screenshots.sh | 11 ++++---- 14 files changed, 66 insertions(+), 25 deletions(-) create mode 100644 apps/android/app/src/debug/res/values/strings.xml create mode 100644 apps/android/wear/src/debug/res/values/strings.xml diff --git a/apps/android/app/build.gradle.kts b/apps/android/app/build.gradle.kts index 2e8324bc3b9a..1a0ea73f530a 100644 --- a/apps/android/app/build.gradle.kts +++ b/apps/android/app/build.gradle.kts @@ -2,6 +2,7 @@ import com.android.build.api.variant.impl.VariantOutputImpl import org.gradle.api.DefaultTask import org.gradle.api.file.ConfigurableFileCollection import org.gradle.api.file.DirectoryProperty +import org.gradle.api.tasks.Exec import org.gradle.api.tasks.InputFiles import org.gradle.api.tasks.Internal import org.gradle.api.tasks.OutputDirectory @@ -16,6 +17,7 @@ import java.util.Properties import javax.inject.Inject val dnsjavaInetAddressResolverService = "META-INF/services/java.net.spi.InetAddressResolverProvider" +val openClawAndroidApplicationId = "ai.openclaw.app" val openClawAndroidVersionFile = rootProject.file("Config/Version.properties") val thirdPartyLicensesDir = rootProject.file("THIRD_PARTY_LICENSES") val openClawRepositoryRoot = rootProject.projectDir.resolve("../..").canonicalFile @@ -214,7 +216,8 @@ android { } defaultConfig { - applicationId = "ai.openclaw.app" + applicationId = openClawAndroidApplicationId + resValue("string", "application_id", openClawAndroidApplicationId) minSdk = 31 targetSdk = 36 testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" @@ -254,6 +257,9 @@ android { proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro") } debug { + applicationIdSuffix = ".debug" + versionNameSuffix = "-debug" + resValue("string", "application_id", "$openClawAndroidApplicationId.debug") isMinifyEnabled = false } } @@ -269,6 +275,7 @@ android { buildFeatures { compose = true buildConfig = true + resValues = true } androidResources { @@ -333,6 +340,7 @@ android { } androidComponents { + val adbExecutable = sdkComponents.adb onVariants { variant -> variant.sources.assets?.addGeneratedSourceDirectory( stageCanvasA2ui, @@ -352,6 +360,24 @@ androidComponents { } output.outputFileName = outputFileName } + + if (variant.buildType == "debug") { + val variantNameCapitalized = variant.name.replaceFirstChar(Char::titlecase) + tasks.register("run$variantNameCapitalized") { + group = "install" + description = "Installs and launches the ${variant.name} app." + dependsOn("install$variantNameCapitalized") + commandLine( + adbExecutable.get().asFile.absolutePath, + "shell", + "am", + "start", + "-W", + "-n", + "${variant.applicationId.get()}/$openClawAndroidApplicationId.MainActivity", + ) + } + } } } kotlin { diff --git a/apps/android/app/src/debug/AndroidManifest.xml b/apps/android/app/src/debug/AndroidManifest.xml index e9ad8273c165..b821a118852f 100644 --- a/apps/android/app/src/debug/AndroidManifest.xml +++ b/apps/android/app/src/debug/AndroidManifest.xml @@ -22,7 +22,7 @@ android:permission="${applicationId}.permission.RUN_VOICE_E2E" android:exported="false"> - + + OpenClaw Dev + diff --git a/apps/android/app/src/main/java/ai/openclaw/app/AssistantLaunch.kt b/apps/android/app/src/main/java/ai/openclaw/app/AssistantLaunch.kt index b0c4823cec7c..f66c97b52716 100644 --- a/apps/android/app/src/main/java/ai/openclaw/app/AssistantLaunch.kt +++ b/apps/android/app/src/main/java/ai/openclaw/app/AssistantLaunch.kt @@ -10,7 +10,7 @@ import java.util.Locale const val actionAskOpenClaw = "ai.openclaw.app.action.ASK_OPENCLAW" /** Debug action that opens the Voice tab directly for Android E2E automation. */ -const val actionOpenVoiceE2e = "ai.openclaw.app.debug.OPEN_VOICE_E2E" +val actionOpenVoiceE2e = "${BuildConfig.APPLICATION_ID}.OPEN_VOICE_E2E" /** Intent extra that carries an optional assistant prompt for app actions. */ const val extraAssistantPrompt = "prompt" diff --git a/apps/android/app/src/main/res/xml/shortcuts.xml b/apps/android/app/src/main/res/xml/shortcuts.xml index c6485febaa93..5dff723cea54 100644 --- a/apps/android/app/src/main/res/xml/shortcuts.xml +++ b/apps/android/app/src/main/res/xml/shortcuts.xml @@ -5,7 +5,7 @@ app:queryPatterns="@array/ask_openclaw_query_patterns"> adb device serial - --package package name (default: ai.openclaw.app) - --activity launch activity (default: .MainActivity) + --package package name (default: ai.openclaw.app.debug) + --activity launch activity (default: ai.openclaw.app.MainActivity) --skip-install skip :app:installPlayDebug --launch-runs launch-to-connected runs (default: 4) --screen-loops screen benchmark loops (default: 6) diff --git a/apps/android/scripts/perf-startup-hotspots.sh b/apps/android/scripts/perf-startup-hotspots.sh index ae06f0e2b44c..67a38bb16aae 100755 --- a/apps/android/scripts/perf-startup-hotspots.sh +++ b/apps/android/scripts/perf-startup-hotspots.sh @@ -4,8 +4,8 @@ set -euo pipefail SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)" ANDROID_DIR="$(cd -- "$SCRIPT_DIR/.." && pwd)" -PACKAGE="ai.openclaw.app" -ACTIVITY=".MainActivity" +PACKAGE="ai.openclaw.app.debug" +ACTIVITY="ai.openclaw.app.MainActivity" DURATION_SECONDS="10" OUTPUT_PERF_DATA="" diff --git a/apps/android/scripts/voice-e2e.sh b/apps/android/scripts/voice-e2e.sh index b09ea6c3273f..9f02ebb4b41f 100755 --- a/apps/android/scripts/voice-e2e.sh +++ b/apps/android/scripts/voice-e2e.sh @@ -3,10 +3,12 @@ set -euo pipefail ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../.." && pwd)" ANDROID_DIR="$ROOT_DIR/apps/android" -PACKAGE_NAME="ai.openclaw.app" -RECEIVER="$PACKAGE_NAME/.VoiceE2eReceiver" -RUN_ACTION="ai.openclaw.app.debug.RUN_VOICE_E2E" -OPEN_ACTION="ai.openclaw.app.debug.OPEN_VOICE_E2E" +APP_NAMESPACE="ai.openclaw.app" +PACKAGE_NAME="$APP_NAMESPACE.debug" +MAIN_ACTIVITY="$PACKAGE_NAME/$APP_NAMESPACE.MainActivity" +RECEIVER="$PACKAGE_NAME/$APP_NAMESPACE.VoiceE2eReceiver" +RUN_ACTION="$PACKAGE_NAME.RUN_VOICE_E2E" +OPEN_ACTION="$PACKAGE_NAME.OPEN_VOICE_E2E" PORT=18789 HOST="127.0.0.1" MODE="both" @@ -146,7 +148,7 @@ fi adb shell pm grant "$PACKAGE_NAME" android.permission.RECORD_AUDIO >/dev/null 2>&1 || true adb shell am force-stop "$PACKAGE_NAME" >/dev/null -adb shell am start -a "$OPEN_ACTION" -n "$PACKAGE_NAME/.MainActivity" >/dev/null +adb shell am start -a "$OPEN_ACTION" -n "$MAIN_ACTIVITY" >/dev/null adb logcat -c run_mode() { diff --git a/apps/android/wear/build.gradle.kts b/apps/android/wear/build.gradle.kts index c8fb696504d6..9b3b6b27e12f 100644 --- a/apps/android/wear/build.gradle.kts +++ b/apps/android/wear/build.gradle.kts @@ -51,6 +51,10 @@ android { } buildTypes { + debug { + applicationIdSuffix = ".debug" + versionNameSuffix = "-debug" + } release { if (phoneReleaseSigning != null) { signingConfig = phoneReleaseSigning diff --git a/apps/android/wear/src/debug/res/values/strings.xml b/apps/android/wear/src/debug/res/values/strings.xml new file mode 100644 index 000000000000..e360013c5491 --- /dev/null +++ b/apps/android/wear/src/debug/res/values/strings.xml @@ -0,0 +1,3 @@ + + OpenClaw Dev + diff --git a/package.json b/package.json index 8fec330d86c6..09ede0897398 100644 --- a/package.json +++ b/package.json @@ -1482,8 +1482,8 @@ "android:install:third-party": "node --import tsx scripts/run-android-gradle.mts :app:installThirdPartyDebug", "android:lint": "cd apps/android && ./gradlew :app:ktlintCheck :benchmark:ktlintCheck :wear:ktlintCheck :wear-shared:ktlintCheck", "android:lint:android": "node --import tsx scripts/run-android-gradle.mts :app:lintPlayDebug :app:lintThirdPartyDebug :wear:lintDebug :wear-shared:lintDebug", - "android:run": "node --import tsx scripts/run-android-gradle.mts :app:installPlayDebug -- adb shell am start -n ai.openclaw.app/.MainActivity", - "android:run:third-party": "node --import tsx scripts/run-android-gradle.mts :app:installThirdPartyDebug -- adb shell am start -n ai.openclaw.app/.MainActivity", + "android:run": "node --import tsx scripts/run-android-gradle.mts :app:runPlayDebug", + "android:run:third-party": "node --import tsx scripts/run-android-gradle.mts :app:runThirdPartyDebug", "android:release": "bash scripts/android-release.sh", "android:release:archive": "bash -lc 'source ./scripts/lib/android-fastlane.sh && cd apps/android && run_android_fastlane android play_store_archive'", "android:release:auth:check": "bash -lc 'source ./scripts/lib/android-fastlane.sh && cd apps/android && run_android_fastlane android auth_check'", diff --git a/scripts/android-screenshots.sh b/scripts/android-screenshots.sh index ba4fbee069cf..82a9f1cc5c5d 100644 --- a/scripts/android-screenshots.sh +++ b/scripts/android-screenshots.sh @@ -46,7 +46,8 @@ DRY_RUN=0 SCENES=(home chat settings gateway voice-wake) OUTPUT_TYPE="phoneScreenshots" GRADLE_ASSEMBLE_TASK=":app:assemblePlayDebug" -ACTIVITY_COMPONENT="ai.openclaw.app/.MainActivity" +APP_PACKAGE="ai.openclaw.app.debug" +ACTIVITY_COMPONENT="$APP_PACKAGE/ai.openclaw.app.MainActivity" EMULATOR_PID="" EMULATOR_LOG="" STARTED_EMULATOR=0 @@ -147,7 +148,7 @@ case "$FORM_FACTOR" in SCENES=(chat voice controls) OUTPUT_TYPE="wearScreenshots" GRADLE_ASSEMBLE_TASK=":wear:assembleDebug" - ACTIVITY_COMPONENT="ai.openclaw.app/ai.openclaw.wear.MainActivity" + ACTIVITY_COMPONENT="$APP_PACKAGE/ai.openclaw.wear.MainActivity" ARTIFACT_DIR="${ARTIFACT_ROOT}/wear" ;; *) @@ -729,8 +730,8 @@ elif [[ "$SKIP_BUILD" != "1" ]]; then ) fi -"$ADB_BIN" -s "$ADB_SERIAL" shell pm clear ai.openclaw.app >/dev/null -"$ADB_BIN" -s "$ADB_SERIAL" shell pm grant ai.openclaw.app android.permission.RECORD_AUDIO >/dev/null +"$ADB_BIN" -s "$ADB_SERIAL" shell pm clear "$APP_PACKAGE" >/dev/null +"$ADB_BIN" -s "$ADB_SERIAL" shell pm grant "$APP_PACKAGE" android.permission.RECORD_AUDIO >/dev/null "$ADB_BIN" -s "$ADB_SERIAL" logcat -c >/dev/null 2>&1 || true for scene in "${SCENES[@]}"; do @@ -739,7 +740,7 @@ for scene in "${SCENES[@]}"; do artifact_path="${ARTIFACT_DIR}/screenshots/openclaw-${scene}.jpg" ui_dump_path="${ARTIFACT_DIR}/ui-dumps/openclaw-${scene}.xml" activity_start_path="${ARTIFACT_DIR}/activity-start/openclaw-${scene}.txt" - "$ADB_BIN" -s "$ADB_SERIAL" shell am force-stop ai.openclaw.app >/dev/null + "$ADB_BIN" -s "$ADB_SERIAL" shell am force-stop "$APP_PACKAGE" >/dev/null "$ADB_BIN" -s "$ADB_SERIAL" shell input keyevent 224 >/dev/null 2>&1 || true "$ADB_BIN" -s "$ADB_SERIAL" shell wm dismiss-keyguard >/dev/null 2>&1 || true "$ADB_BIN" -s "$ADB_SERIAL" shell am start -W \