Preserve a pending outbox branch reconciliation across the drain handoff: the drain's finally and the reconcile early-return now recheck both pending requests, so queued messages no longer stall until an unrelated event. Also join the fail-fast fixture transport in SidebarGatewayPickerTest before screenshot history adoption. Forward-port of release/2026.9.758379ef4a5and44628e7e16.
OpenClaw Android App
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.
App features
- Pair with a Gateway using a QR code, setup code, or manual connection. Gateway credentials are stored encrypted.
- Stream chat replies, choose models and reasoning effort, manage session permissions, and expand task progress. The draft has its own full-width row above one control row: +, model, and reasoning on the left; microphone and Talk/send on the right. Session permissions are inside the + menu, with the current access mode shown. The model name opens a compact searchable provider menu above the composer; the configured default is marked on its model row, without settings buttons in the picker. The top-right Chat actions menu includes Context with its live usage ring, which opens context usage, latest tokens, and the cost breakdown. Tap the microphone for dictation. While listening, a Stop icon replaces the microphone; tap it to finish listening. While starting or transcribing, a Close icon cancels that attempt. Long-press for voice messages or Talk, or use the Talk button beside the microphone when the draft is empty. The effort dial opens the live-preview slider and Fast mode.
- Select agents, pin sessions, and browse available native session catalogs from the sidebar. Connecting creates or adopts a dedicated Android session without resetting its history. Native sessions keep their runtime-owned model: Android shows that ownership instead of offering a model change. New session starts independently of the current native thread. Generic child-session forks and new worktrees are unavailable for those sessions; supported message-level forks remain available.
- Search from Overview or Settings to find settings by their displayed name or category, alongside quick actions and recent threads. Local destinations such as Appearance, Profile, and Licenses work without connecting a Gateway. Back from a settings detail returns to the screen that opened search; Desktop appears only when the connected Gateway supports it.
- Choose a theme family, color mode, accent, and app language in Settings → Appearance. Theme and accent edits sync with a connected writable profile. Read-only or unknown-profile edits, including new edits after restarting offline, stay on the device; choose them again after connecting to sync. Already profile-bound edits wait for that profile to reconnect, without discarding or replacing newer device-local choices.
- Choose Text size in Settings → Appearance: 90%, 100% (default), 110%, 125%, or 140%. This device-local choice survives restarting the app and does not sync to your profile. It combines with Android system font scaling, including nonlinear scaling, without changing spacing or typefaces.
- Configure foreground on-device Voice Wake and Gateway-synced wake words in Settings → Voice.
- Talk setup and startup failures stay visible in a dismissible Chat dialog. If the message reports a missing provider or invalid credentials, configure the Talk provider on the Gateway before trying again.
- Use Settings → OpenClaw for guided Gateway setup and repair. New replies stay visible at the end of the conversation; scrolling back preserves your reading position until you return or tap Jump to latest.
- Setup offers optional notifications, microphone, camera, and location access; other permissions are under Additional features. Request all asks for the available runtime permissions without enabling the camera or location features; Android handles consent through its system prompts. Notification access stays separate under Special access. Continue without granting access, or enable phone capabilities individually or later in Settings. Blocked individual setup requests offer Android Settings. Biometric locking, Gateway/chat notifications, and authenticated background presence are supported.
- SMS is available only in third-party builds on supported telephony devices. Setup distinguishes partial read/send access. Android Settings can recover user denial, but installer or device-policy restrictions may leave SMS unavailable; OpenClaw cannot override them. Returning from Settings rereads the actual grants without restarting Gateway setup.
- View the phone's memory and disk meters on the Control UI Devices page. Connected Android nodes report host resource stats immediately and every 60 seconds; disk meters require an available storage sample and a Gateway that supports host stats.
- Manage installed skills and Gateway-verified ClawHub releases, review Skill Workshop proposals, and inspect or edit automations with the required Gateway access.
- Use the Wear OS companion for sessions, replies, aborts, and realtime Talk through the paired phone without storing Gateway credentials on the watch.
Open in Android Studio
- Open the folder
apps/android.
Session colors
Long-press a row on the Threads page and choose Color, then select a swatch or Default to clear it. The eight colors are red, blue, green, yellow, purple, orange, pink, and cyan. Colored sessions show a narrow leading stripe in the sidebar and Threads page, plus a colored ring around the agent avatar in the open chat header. Unset colors add no indicator. Colors sync through the Gateway and remain visible in the local session cache while offline.
Camera attachments
Tap + → Camera to open the viewfinder directly. Switch between Photo and Video inside the camera; there is no separate mode-selection menu. Capture a photo, or start and stop a video recording, to add it to the original draft. Camera access is requested on first use, and video recording requests microphone access when needed. Closing the camera, leaving the app, or rotating the screen cancels capture and preserves the draft. Nothing is sent until you tap Send. Gallery selects both existing images and videos.
Image previews in Chat
Tap an image to open the full image. Pinch to zoom up to 4× and drag to pan; double-tap the image for 2.5× zoom or to reset. The bottom controls zoom out, show the current percentage (tap to reset), and zoom in. Close with X, Android Back, or a stationary tap on empty background. Image taps, drags, and two-finger gestures do not dismiss the preview. Rotation or recreation refits the image rather than restoring offsets from the old viewport.
Completed work in Chat
In the app's own conversation, agent main sessions, and dashboard conversations, completed commentary and tool activity fold into a Worked or Worked for… row. Tap the row to expand or collapse the details. Prompts, final answers, and attachments remain visible. The summary keeps counts of failed, blocked, and unknown tool outcomes visible even when work is collapsed; these describe individual tools, not the overall answer. Failures without a later answer stay visible; earlier tool failure details remain available in expanded work. Active work stays expanded, including a run continued by a steering message. Channel conversations retain their full transcript.
Message information
Tap the subdued timestamp under an assistant message to see its recorded model, token counts, cache reads/writes, and estimated cost when the Gateway supplies them. These are facts for that message's model call, not totals for the whole run. Missing and zero-valued details are omitted; messages without details keep a plain timestamp. The existing offline transcript cache retains these facts. Android does not infer a historical context percentage from the current session's model limit.
Review changes
When the connected Gateway advertises sessions.diff, open a conversation's
three-dot Chat actions menu and choose Review changes. If the action is
missing, update the Gateway before reviewing changes on Android.
The full-screen native viewer loads the same sessions.diff snapshot as the web Review panel.
Close it with Android Back or Close review; pulling down does not dismiss it.
Review shows Uncommitted changes only. The scope label is static; branch-base
and historical commit comparisons are outside this Android viewer's scope.
Tap a file header to collapse it. Line numbers are hidden by default. Start a new
rightward swipe of at least 64dp with the code already at its left edge to reveal them;
swipe left at least 64dp to hide them, then start a fresh swipe to pan the code. A swipe that starts away
from the left edge only pans, even after reaching that edge: lift your finger before swiping again to
reveal numbers. Addition/deletion markers remain visible.
Long-press a code line, then drag to select more lines. Release to choose To
chat (append path:start-end (Before | Uncommitted) or the corresponding
After context, followed by all selected text in a fenced
code block, to your draft without sending it) or
Copy (copy the selected text). A pulse marks the start of selection; a toast
confirms either action. Drag any of the four selection handles to refine the range before
choosing an action. Selection stays in one hunk and uses the starting line's
side: Before for deleted lines, After otherwise. It skips the opposite
side and never includes omitted context. Tap outside the actions to cancel.
Use Copy patch to copy that file's returned patch.
Refresh changes requests a new snapshot; the viewer does not stream updates.
The Gateway owns repository selection and session-start filtering. This is a checkout snapshot, not an exact audit of the assistant's edits. Binary files, truncated patches, stopped workspaces, and unavailable repositories are identified in the viewer. Switching conversation or Gateway closes the review; safe fold layout changes preserve the opening.
Foldable layout
With a full-height vertical separator reported by AndroidX WindowManager, the main app places its sidebar on the reading-direction start plane and the active page on the other plane. Both use the reported bounds, including off-center hinges. The sidebar remains visible after selecting a page or session.
Book panes require at least 280 dp for the sidebar, 320 dp for the active page, and 320 dp of height. Onboarding and layouts without a supported split use the largest rectangular region clear of separating folds and fully occluding hinges. Equal regions prefer the top, then the reading-direction start side. Opening the keyboard does not select a different fallback region for this outer host. Without an intersecting separator, the app keeps its full-window layout and modal sidebar.
In Chat, a full-width horizontal separator can place the conversation header, transcript, and status above the hinge and the same composer below it. Each usable pane must be at least 320 dp wide. The measured space must fit the complete header, a transcript band with a complete text line, and status above, with a complete input line and controls below, accounting for the current font size and padding.
Layout changes retain the draft, cursor, reader position, and existing local capture state owners. If keyboard insets or larger text leave too little space, Chat uses the existing one-region fallback based on the space remaining after insets, then restores the split when it fits.
Command search and gateway trust prompts retain the single-region host.
Gateway trust, QR scan-error, Replace gateway setup, and Forget gateway prompts also stay within one safe region. Their complete contents and actions scroll when space is limited; opening the keyboard does not move them to another region. If the keyboard covers that region entirely, dismiss the keyboard to reach the prompt again.
Chat actions and voice options menus stay in the safe region containing their trigger. These popups remain focusable without becoming keyboard (IME) targets. If folds, insets, or layout changes invalidate an open menu, it closes without choosing an action. Reopen it explicitly when space permits; it does not reopen automatically when the layout recovers. Dismissing the menu does not reset Chat's draft, editor, or reader state.
Chat's + menu opens above the composer as a compact icon list: Camera, Gallery, Files, Location, and Permissions. Permissions shows the current access mode. Camera offers photo and video capture through the in-app camera after Android permission is granted. Confirmed captures are added to the original draft for review, without sending; cancellation keeps the draft unchanged. Temporary captures are removed after import or cancellation. Gallery selects both photos and videos through Android's system picker without whole-library permission. The embedded preview is not used: its Done callback can precede pending URI grants and revocations, so it cannot supply a reliable final selection. Location opens a confirmation view, requests foreground permission, and adds a map link to the draft for review before sending. It respects the app's Precise Location toggle, including changes made while capture is pending, using the same approximate grid as node location responses. Closing a picker preserves the draft; switching conversations retires the opening.
Background tasks and Switch branch sheets initially use the largest safe region with usable sheet space, not the trigger's region. They keep that region while it remains usable. Valid geometry changes retain the same sheet and local state. An invalid opening closes without selecting an option and stays closed until explicitly reopened.
The attachment, model, permissions, context, and effort menus use the same opening and safe-region checks, anchored above the composer when space permits. Search and provider expansion stay inside its bounded viewport; keyboard and fold changes keep it in usable window space.
Background tasks remains an agent-wide, read-only list and detail view. Safe layout changes retain the opening and its reading state. Switching Gateway, agent, or chat closes it; a same-owner disconnect leaves read errors visible with Refresh available.
Switch branch keeps its list and reading position through safe layout changes; the title and rows scroll together in short panes. Changing Gateway, agent, or chat retires the opening. Reading remains available when a run is pending, outbox restoration is incomplete, the current session has outbox items, or a branch switch is already in flight. Mutation rows stay disabled in those states. Closing or retiring the sheet does not cancel an admitted switch, and its completion cannot dismiss a replacement opening.
Other dialogs, sheets, and popup menus are not fold-adapted yet.
Wear OS companion
The wear app is a paired-phone companion with the same application ID and signing identity as the phone app. The watch discovers the phone through Wear OS Data Layer, then uses the phone's existing authenticated operator session. It never receives or stores Gateway tokens, passwords, TLS pins, or device-signing identity.
The watch supports agent and session selection, bounded text-only transcript history, streaming reply state, text and voice replies, abort, realtime Talk within the selected session, paired-phone Gateway controls, local reply notifications, theme and automatic-speech settings, and a launch Tile. Realtime Talk streams watch microphone and playback audio over a temporary Wear OS Data Layer channel; it still uses the phone's authenticated Gateway session and closes when the selected phone or Gateway connection changes. A missing Data Layer event sequence or changed phone-process epoch triggers a fresh history request instead of applying uncertain deltas. Agent and Gateway controls are capability-negotiated so an older paired phone remains usable during staggered updates.
cd apps/android
./gradlew :wear:testDebugUnitTest :wear:assembleDebug :wear:lintDebug :wear:ktlintCheck
Build / Run
Install the repository's Node.js and pnpm dependencies before building. Gradle builds the shared Mermaid renderer automatically and packages its local assets with the app; no CDN or Gateway renderer is needed.
pnpm install
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
Mermaid diagrams
Chat renders completed mermaid code blocks inline. Tap a diagram for a
full-screen view with pinch-to-zoom and panning. The corner menu switches to
source or retries a temporary failure, and the copy button copies the original
Mermaid source. Incomplete streaming blocks remain readable code.
The renderer shares its pinned Mermaid version, sandbox, and SVG sanitizer with
the Control UI. Android keeps bounded bitmap previews in memory and retains the
sanitized SVG for zooming. Math and diagrams share the render queue and lifecycle
owner, with separate lazy WebViews and resource limits. See
packages/mermaid-renderer for the
shared runtime and build contract.
Repository-backed debug Gradle invocations, including pnpm android:run and
pnpm android:screenshots, stamp the full checkout commit and capture one UTC
build timestamp shared by every debug variant in that invocation. Release
tasks still require explicit openclawBuildCommit and
openclawBuildTimestamp properties so signed artifacts remain reproducible.
Android release archives use the pinned version in apps/android/version.json.
Run Android Store Release from main without input parameters, or run
pnpm android:release:upload from a clean local main matching origin/main.
The pipeline selects unused phone and Wear build numbers from Google Play and
generates OpenAI release notes from changes since each form factor's public
release. It saves the plan and notes as release artifacts and uploads the selected
clean source commit. Tracked version defaults and notes stay unchanged; the flow
creates no preparation commits or follow-up PRs.
For local preparation or inspection:
pnpm android:version
pnpm android:version:check
pnpm android:version:pin -- --from-gateway
pnpm android:version:sync
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 phone and Wear OS Google Play screenshots:
pnpm android:screenshots
The screenshot script captures both form factors with retained
OpenClaw_Screenshots_API36 (Pixel 2) and
OpenClaw_Wear_Screenshots_API34 (Wear OS Large Round) AVDs. It creates a
missing AVD, boots it headlessly, waits for Android to finish booting, disables
animations, captures the screenshots, then shuts down the emulator it started.
Install the API 36 Google APIs and API 34 Wear OS system images in the local
Android SDK. Use --form-factor phone|wear with --avd or --device to
explicitly capture one form factor from another emulator.
For local branch-switching proof, launch a debug build with the intent extras
openclaw.screenshotMode=true and openclaw.screenshotScene=branches. This Chat
scene has 12 local branch alternatives and no active run. Switching updates the
selected branch and transcript only in fixture memory, never on a live Gateway.
Start a fresh app process before choosing a scene; restarting only the Activity
reuses the process runtime. Same-scene re-entry retains the selected branch.
For sidebar attention proof, use openclaw.screenshotScene=attention. The native drawer contains inactive sessions with multiple questions and execution, plugin, and Gateway-settings approvals. Tap or keyboard-focus an attention icon to inspect the oldest request. The attention-expiry scene uses successive short deadlines to exercise live removal without another Gateway event. These fixtures use the normal request parsers and lifecycle owners with synthetic in-memory responses; start a fresh app process between scenes.
For completed-work proof, use openclaw.screenshotScene=completed-work,
active-work, or work-boundaries. These scenes use the same Chat screen with
synthetic history in a node-owned app conversation. They cover disclosure
expansion, active work, attachments, and failed tools without a live Gateway.
Start a fresh app process between scenes.
pnpm android:release:archive builds signed release artifacts into apps/android/build/release-artifacts/ and writes .sha256 checksum files:
- Play build:
openclaw-<version>-play-release.aab - Wear build:
openclaw-<version>-wear-release.aab - Third-party build:
openclaw-<version>-third-party-release.apk
pnpm 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. The Android APK Artifact Publish workflow at .github/workflows/android-release.yml is the only automated GitHub Release upload path. When the tagged Android pin matches the stable release train, OpenClaw Release Publish qualifies Android independently and dispatches it after core npm succeeds. A mismatched pin records an explicit skip. Android does not hold npm or GitHub release finalization, so verified APK assets may attach after the release is public.
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 and inspect the store
outcome before trying again. Keep the saved plan and generated notes for
investigation or local archive replay.
Do not upload archived artifacts through direct Fastlane lanes, Gradle artifacts,
Google Play API commands, or Play Console mutation commands.
The release lane uploads the phone and Wear bundles in one atomic Google Play
edit. It publishes the phone bundle to GOOGLE_PLAY_TRACK and maps the Wear
bundle to the corresponding form-factor track (wear:<track>), so the default
internal channel publishes to internal and wear:internal.
See apps/android/VERSIONING.md and apps/android/fastlane/SETUP.md for the release workflow.
Prefer pnpm android:release:archive, which stamps and validates the full Git commit and one UTC build timestamp before signing. Flavor-specific direct Gradle release tasks must pass the same metadata explicitly:
cd apps/android
commit="$(git -C ../.. rev-parse HEAD)"
built_at="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
./gradlew -PopenclawBuildCommit="$commit" -PopenclawBuildTimestamp="$built_at" :app:bundlePlayRelease
./gradlew -PopenclawBuildCommit="$commit" -PopenclawBuildTimestamp="$built_at" :wear:bundleRelease
./gradlew -PopenclawBuildCommit="$commit" -PopenclawBuildTimestamp="$built_at" :app:bundleThirdPartyRelease
Kotlin Lint + Format
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 :wear:ktlintCheck :wear-shared:ktlintCheck
./gradlew :app:ktlintFormat :benchmark:ktlintFormat :wear:ktlintFormat :wear-shared:ktlintFormat
./gradlew :app:lintPlayDebug :app:lintThirdPartyDebug :wear:lintDebug :wear-shared:lintDebug
Set ANDROID_HOME to your installed Android SDK, or set sdk.dir in the local apps/android/local.properties file. For Homebrew's command-line tools, the SDK may be at /opt/homebrew/share/android-commandlinetools.
Macrobenchmark (Startup + Frame Timing)
cd apps/android
./gradlew :benchmark:connectedDebugAndroidTest
Reports are written under:
apps/android/benchmark/build/reports/androidTests/connected/
Perf CLI (low-noise)
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:
- Runs only
StartupMacrobenchmark#coldStartup(10 iterations). - Prints median/min/max/COV in one line.
- Writes timestamped snapshot JSON to
apps/android/benchmark/results/. - Auto-compares with previous local snapshot (or pass explicit baseline:
--baseline <old-benchmarkData.json>).
Hotspot script behavior:
- Ensures debug app installed, captures startup
simpleperfdata for.MainActivity. - Prints top DSOs, top symbols, and key app-path clues (Compose/MainActivity/WebView).
- Writes raw
perf.datapath for deeper follow-up if needed.
Run on a Real Android Phone (USB)
- On phone, enable Developer options + USB debugging.
- Connect by USB and accept the debugging trust prompt on phone.
- Verify ADB can see the device:
adb devices -l
- Install + launch debug build:
pnpm android:install
pnpm android:run
If adb devices -l shows unauthorized, re-plug and accept the trust prompt again.
USB-only gateway testing (no LAN dependency)
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 open Settings → Gateway → Manual Gateway (or Set up manually during first-run setup):
- Host:
127.0.0.1 - Port:
18789 - Connection security: Unencrypted
Hot Reload / Fast Iteration
This app is native Kotlin + Jetpack Compose.
- For Compose UI edits: use Android Studio Live Edit on a debug build (works on physical devices; project
minSdk=31already meets API requirement). - For many non-structural code/resource changes: use Android Studio Apply Changes.
- For structural/native/manifest/Gradle changes: do full reinstall (
pnpm android:run).
Connect / Pair
- Start the gateway (on your main machine):
pnpm openclaw gateway --port 18789 --verbose
- In the Android app:
- Follow the first-run connection screen, or open Settings → Gateway to change a saved connection.
- Scan a QR code, paste a setup code, or enter the Gateway manually.
Gateway credentials and setup codes are masked and accept paste. The app requests password input with autocorrection disabled; this does not guarantee how a keyboard stores or learns from input.
- Approve pairing (on the gateway machine):
openclaw devices list
openclaw devices approve <requestId>
More details: docs/platforms/android.md.
If the gateway cannot be reached, the app keeps the connection error visible during automatic retries. For an address that may use Tailscale, Set up Tailscale opens the Android installation guide. Open Tailscale and connect to the gateway's tailnet. Check that the gateway computer is online and OpenClaw is running, then retry. This advice does not verify Tailscale's connection state or change certificate trust. If an earlier network request is still stopping, the app waits for it before starting another request.
Permissions
- Discovery:
- Android 13+ (
API 33+):NEARBY_WIFI_DEVICES - Android 12 and below:
ACCESS_FINE_LOCATION(required for NSD scanning)
- Android 13+ (
- Location:
- Both flavors:
ACCESS_FINE_LOCATION/ACCESS_COARSE_LOCATIONfor foreground checks. - Third-party flavor only:
ACCESS_BACKGROUND_LOCATIONplusFOREGROUND_SERVICE_LOCATIONfor user-enabledAlwayschecks.
- Both flavors:
- Foreground service notification (Android 13+):
POST_NOTIFICATIONS - Camera:
CAMERAforcamera.snapandcamera.clipRECORD_AUDIOforcamera.clipwhenincludeAudio=true
Google Play Restricted Permissions
As of March 19, 2026, these manifest permissions are the main Google Play policy risk for this app:
READ_SMSSEND_SMSREAD_CALL_LOG
Why these matter:
- Google Play treats SMS and Call Log access as highly restricted. In most cases, Play only allows them for the default SMS app, default Phone app, default Assistant, or a narrow policy exception.
- Review usually involves a
Permissions Declaration Form, policy justification, and demo video evidence in Play Console. - The Play build removes these behind the
playflavor. - Photo library access is also removed from the Play build. Use third-party builds for
photos.latest.
Current OpenClaw Android implication:
- APK / sideload build can keep SMS, Call Log, and recent-photo features.
- Google Play build excludes SMS send/search, Call Log search, and recent-photo access unless the product is intentionally positioned and approved under the relevant policy exception.
- The repo now ships this split as Android product flavors:
play: removesREAD_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, andAlwayslocation surfaces.- Installed-app listing is user controlled.
device.appsis advertised only after the user enables Settings > Phone Capabilities > Installed Apps. The command defaults to launcher-visible apps and does not requireQUERY_ALL_PACKAGES. thirdParty: keeps the full permission set and the existing SMS / Call Log / Photos functionality, and offers explicitAlwayslocation opt-in through Android settings.
Policy links:
- Google Play SMS and Call Log policy
- Google Play sensitive permissions policy hub
- Android default handlers guide
Other Play-restricted surfaces to watch if added later:
ACCESS_BACKGROUND_LOCATIONMANAGE_EXTERNAL_STORAGEQUERY_ALL_PACKAGESREQUEST_INSTALL_PACKAGESAccessibilityService
Reference links:
Integration Capability Test (Preconditioned)
This suite assumes setup is already done manually. It does not install/run/pair automatically.
Pre-req checklist:
- Gateway is running and reachable from the Android app.
- Android app is connected to that gateway and
openclaw nodes statusshows it as paired + connected. - App stays unlocked and in foreground for the whole run.
- Grant runtime permissions for capabilities you expect to pass (camera/mic/location/notification listener/location, etc.).
- No interactive system dialogs should be pending before test start.
- Local operator test client pairing is approved. If first run fails with
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=...orOPENCLAW_ANDROID_NODE_NAME=...
What it does:
- Reads
node.describecommand list from the selected Android node. - Invokes advertised non-interactive commands.
- Skips
screen.recordandtalk.ptt.*in this suite because they require interactive capture. Useapps/android/scripts/voice-e2e.shfor microphone and voice-path proof. - Asserts command contracts (success or expected deterministic error for safe-invalid calls like
sms.sendandnotifications.actions).
Common failure quick-fixes:
pairing requiredbefore tests start:- list pending requests (
openclaw devices list), then approve with the exact ID (openclaw devices approve <requestId>) and rerun.
- list pending requests (
Contributions
Maintainer: @obviyus. For issues/questions/contributions, please open an issue or reach out on Discord.