apps/android/README.md
OpenClaw Android is the officially released Google Play app. It connects to an OpenClaw Gateway as a companion node for chat, voice, approvals, screen, and device-aware automation.
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/.
Standalone release APK verification also requires that key's public certificate SHA-256 fingerprint to match Config/ReleaseSigning.json.
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.
Regular final and correction OpenClaw releases publish the signed third-party APK as OpenClaw-Android.apk with a checksum manifest and GitHub Actions provenance. .github/workflows/android-release.yml is the only automated GitHub Release upload path; OpenClaw Release Publish dispatches it while the canonical release is still a draft and blocks publication until the uploaded asset contract verifies.
The protected android-release environment supplies MATCH_PASSWORD; the repository's read-only GitHub App token checks out encrypted material from openclaw/apps-signing. The workflow builds the exact release tag, refuses to replace different existing bytes, and re-downloads the APK for checksum, certificate, and provenance verification.
pnpm android:release:archive is for local archive validation only. It is not a
fallback upload path after pnpm android:release:upload fails.
Agent-driven Google Play uploads must use pnpm android:release:upload as the
only release path. If that command fails, stop and fix the failing screenshot,
metadata, signing, validation, archive, or upload step before trying again. Do
not upload archived artifacts through direct Fastlane lanes, Gradle artifacts,
Google Play API commands, or Play Console mutation commands.
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:lintPlayDebug :app:lintThirdPartyDebug
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)ACCESS_FINE_LOCATION / ACCESS_COARSE_LOCATION for foreground checks.ACCESS_BACKGROUND_LOCATION plus FOREGROUND_SERVICE_LOCATION for user-enabled Always checks.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, READ_EXTERNAL_STORAGE, and background location; hides SMS, Call Log, Photos, and Always location surfaces.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, and offers explicit Always location opt-in through Android settings.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:
Maintainer: @obviyus. For issues/questions/contributions, please open an issue or reach out on Discord.