apps/android/README.md
Status: extremely alpha. The app is actively being rebuilt from the ground up.
Setup Code + Manual modesapps/android.cd apps/android
./gradlew :app:assemblePlayDebug
./gradlew :app:installPlayDebug
./gradlew :app:testPlayDebugUnitTest
cd ../..
pnpm android:release:archive
Third-party debug flavor:
cd apps/android
./gradlew :app:assembleThirdPartyDebug
./gradlew :app:installThirdPartyDebug
./gradlew :app:testThirdPartyDebugUnitTest
Android release archives use the pinned version in apps/android/version.json. Update it with:
pnpm android:version
pnpm android:version:check
pnpm android:version:pin -- --from-gateway
pnpm android:version:pin -- --version 2026.6.5 --version-code 2026060501
Release-owner signing sync:
pnpm android:release:signing:plan
MATCH_PASSWORD=<signing repo password> pnpm android:release:signing:sync:pull
MATCH_PASSWORD=<signing repo password> pnpm android:release:signing:check
The signing sync pulls encrypted Android upload-key assets from the shared apps-signing repo and materializes decrypted files under apps/android/build/release-signing/.
Generate raw Google Play screenshots:
pnpm android:screenshots
To make screenshot capture own emulator startup, pass a named AVD:
ANDROID_SCREENSHOT_AVD=OpenClaw_QA_API35 pnpm android:screenshots
The screenshot script uses one connected ADB device when available. If none is
connected and ANDROID_SCREENSHOT_AVD is set, it boots that emulator
headlessly, waits for Android to finish booting, disables animations, captures
the screenshots, then shuts down the emulator it started.
pnpm android:release:archive builds signed release artifacts into apps/android/build/release-artifacts/ and writes .sha256 checksum files:
openclaw-<version>-play-release.aabopenclaw-<version>-third-party-release.apkpnpm android:bundle:release is an alias for the same Fastlane archive lane.
See apps/android/VERSIONING.md and apps/android/fastlane/SETUP.md for the release workflow.
Flavor-specific direct Gradle tasks:
cd apps/android
./gradlew :app:bundlePlayRelease
./gradlew :app:bundleThirdPartyRelease
pnpm android:lint
pnpm android:format
Android framework/resource lint (separate pass):
pnpm android:lint:android
Direct Gradle tasks:
cd apps/android
./gradlew :app:ktlintCheck :benchmark:ktlintCheck
./gradlew :app:ktlintFormat :benchmark:ktlintFormat
./gradlew :app:lintDebug
gradlew auto-detects the Android SDK at ~/Library/Android/sdk (macOS default) if ANDROID_SDK_ROOT / ANDROID_HOME are unset.
cd apps/android
./gradlew :benchmark:connectedDebugAndroidTest
Reports are written under:
apps/android/benchmark/build/reports/androidTests/connected/Deterministic startup measurement + hotspot extraction with compact CLI output:
cd apps/android
./scripts/perf-startup-benchmark.sh
./scripts/perf-startup-hotspots.sh
Benchmark script behavior:
StartupMacrobenchmark#coldStartup (10 iterations).apps/android/benchmark/results/.--baseline <old-benchmarkData.json>).Hotspot script behavior:
simpleperf data for .MainActivity.perf.data path for deeper follow-up if needed.adb devices -l
pnpm android:install
pnpm android:run
If adb devices -l shows unauthorized, re-plug and accept the trust prompt again.
Use adb reverse so Android localhost:18789 tunnels to your laptop localhost:18789.
Terminal A (gateway):
pnpm openclaw gateway --port 18789 --verbose
Terminal B (USB tunnel):
adb reverse tcp:18789 tcp:18789
Then in app Connect → Manual:
127.0.0.118789This app is native Kotlin + Jetpack Compose.
minSdk=31 already meets API requirement).pnpm android:run).__openclaw__/canvas/ (see docs/platforms/android.md).pnpm openclaw gateway --port 18789 --verbose
openclaw devices list
openclaw devices approve <requestId>
More details: docs/platforms/android.md.
API 33+): NEARBY_WIFI_DEVICESACCESS_FINE_LOCATION (required for NSD scanning)POST_NOTIFICATIONSCAMERA for camera.snap and camera.clipRECORD_AUDIO for camera.clip when includeAudio=trueAs of March 19, 2026, these manifest permissions are the main Google Play policy risk for this app:
READ_SMSSEND_SMSREAD_CALL_LOGWhy these matter:
Permissions Declaration Form, policy justification, and demo video evidence in Play Console.play flavor.photos.latest.Current OpenClaw Android implication:
play: removes READ_SMS, SEND_SMS, READ_CALL_LOG, READ_MEDIA_IMAGES, READ_MEDIA_VISUAL_USER_SELECTED, and READ_EXTERNAL_STORAGE; hides SMS, Call Log, and Photos surfaces in onboarding, settings, and advertised node capabilities.device.apps is advertised only after the user enables Settings > Phone Capabilities > Installed Apps. The command defaults to launcher-visible apps and does not require QUERY_ALL_PACKAGES.thirdParty: keeps the full permission set and the existing SMS / Call Log / Photos functionality.Policy links:
Other Play-restricted surfaces to watch if added later:
ACCESS_BACKGROUND_LOCATIONMANAGE_EXTERNAL_STORAGEQUERY_ALL_PACKAGESREQUEST_INSTALL_PACKAGESAccessibilityServiceReference links:
This suite assumes setup is already done manually. It does not install/run/pair automatically.
Pre-req checklist:
openclaw nodes status shows it as paired + connected.OPENCLAW_SKIP_CANVAS_HOST=1; startup logs should include canvas host mounted at .../__openclaw__/).pairing required, preview the latest pending request, approve the printed request ID, then rerun:openclaw devices list
openclaw devices approve --latest # preview only; copy the requestId from output
openclaw devices approve <requestId>
Run:
pnpm android:test:integration
Optional overrides:
OPENCLAW_ANDROID_GATEWAY_URL=ws://... (default: from your local OpenClaw config)OPENCLAW_ANDROID_GATEWAY_TOKEN=...OPENCLAW_ANDROID_GATEWAY_PASSWORD=...OPENCLAW_ANDROID_NODE_ID=... or OPENCLAW_ANDROID_NODE_NAME=...What it does:
node.describe command list from the selected Android node.screen.record in this suite (Android requires interactive per-invocation screen-capture consent).sms.send and notifications.actions).Common failure quick-fixes:
pairing required before tests start:
openclaw devices list), then approve with the exact ID (openclaw devices approve <requestId>) and rerun.A2UI host not reachable / A2UI_HOST_UNAVAILABLE:
NODE_BACKGROUND_UNAVAILABLE: canvas unavailable:
This Android app is currently being rebuilt. Maintainer: @obviyus. For issues/questions/contributions, please open an issue or reach out on Discord.