Android App: Connection Runbook and Command Surface
Learn how to install, pair, and use the OpenClaw Android companion app, including Connect, Chat, and Voice commands. Essential for Android users needing a mobile node for their Gateway.
Read this when
- Pairing or reconnecting the Android node
- Debugging Android gateway discovery or auth
- Mirroring or controlling an Android device from a remote Mac
- Verifying chat history parity across clients
Note
The Android app is officially distributed through Google Play and as a signed, standalone APK attached to supported GitHub Releases. It operates as a companion node and depends on an active OpenClaw Gateway. Source code lives in apps/android with build instructions provided.
Support snapshot
- Function: companion node app (the Gateway cannot run on Android).
- Gateway dependency: mandatory (deploy it on macOS, Linux, or Windows via WSL2).
- Setup: grab it from Google Play or sideload
OpenClaw-Android.apkfrom a supported GitHub Release, get the Gateway running with Getting Started, then complete Pairing. - Gateway details: Runbook and Configuration.
- Communication: Gateway protocol (nodes plus control plane).
- Settings → OpenClaw launches a dedicated Gateway configuration wizard when the operator link has
operator.adminand the Gateway advertisesopenclaw.chat. That wizard's conversation is isolated from regular Chat, masks secret responses on the device, and hands off to Chat only after you press Open Chat.
System-level services (launchd/systemd) run on the Gateway host, not on Android. See Gateway.
Simultaneous gateway sessions
After pairing each Gateway once, head to Settings → Gateway. The checkmark identifies the active Gateway, and each toggle decides whether a non-active Gateway's operator session remains connected. Enabled Gateways establish their own connections while the app runs in the foreground, so switching focus doesn't disconnect the others. Only the focused Gateway holds the Android node session and device access; this stops multiple Gateways from sending camera, location, screen, or notification commands to the same handset. Once the app goes to the background, Android may suspend the secondary connections.
Wear OS companion
The Wear OS companion relies on the paired phone's authenticated Gateway connection; the watch never sees or stores Gateway credentials. It lets you pick agents and sessions, view limited transcripts, send typed or dictated responses, halt an in-progress run, launch realtime Talk within the chosen session, and toggle the phone's Gateway connection. It also provides local reply alerts, dark or light themes, and optional spoken replies. Agent and Gateway features are negotiated by capability to handle staggered phone/watch updates. Realtime Talk sends microphone and playback audio over a temporary Wear OS Data Layer channel, which stops if the selected phone, Gateway link, or audio path drops.
Install outside Google Play
Standard final and correction GitHub Releases ship with a universal OpenClaw-Android.apk and OpenClaw-Android-SHA256SUMS.txt. The APK comes from the release tag, is signed with the OpenClaw Android release key, and includes GitHub Actions provenance.
Pick a release that lists both files, then download and verify that exact tag before sideloading:
release_tag=vYYYY.M.PATCH
gh release download "$release_tag" \
--repo openclaw/openclaw \
--pattern OpenClaw-Android.apk \
--pattern OpenClaw-Android-SHA256SUMS.txt
sha256sum --check OpenClaw-Android-SHA256SUMS.txt
gh attestation verify OpenClaw-Android.apk \
--repo openclaw/openclaw \
--signer-workflow openclaw/openclaw/.github/workflows/android-release.yml \
--source-ref "refs/tags/${release_tag}" \
--deny-self-hosted-runners
Warning
Google Play and standalone APK installations follow separate update channels and may use different signing keys. Android might require removing the current app before switching channels, which wipes its local data. Stick with one channel for routine updates.
Mirror and control Android from a remote Mac
scrcpy shows an Android screen in a macOS window and relays keyboard and mouse input through Android Debug Bridge (ADB). This is an operator-side tool, unrelated to the OpenClaw node link. It helps when the Android device and the Mac sit in different locations but share a private Tailscale network.
Before you begin
-
Put Tailscale on both the Android device and the Mac, and join both to the same tailnet.
-
On Android, turn on Developer options and USB debugging. Android 16 puts Wireless debugging under Settings > System > Developer options. Refer to Android developer options.
-
Set up scrcpy and ADB on the Mac:
brew install scrcpy brew install --cask android-platform-tools -
Have the Android device available for the first connection. Android must approve each Mac's ADB key before that Mac can take control.
Enable ADB over TCP
For the first-time setup, plug the Android device into a trusted computer via USB and accept its debugging prompt. Then execute:
adb devices
adb tcpip 5555
After that, USB can be unplugged. If port 5555 stops responding after a reboot or debugging reset,
redo this local setup step. On Android 11 and newer, initial trust can also be set with
Wireless debugging > Pair device with pairing code and adb pair.
Allow only the controller Mac
Tailnets with restrictive grants must explicitly permit the controller Mac to reach TCP port 5555 on the Android device. Add a narrow rule to the tailnet policy, swapping the example addresses for the two devices' stable Tailscale IPs:
{
grants: [
{
src: ["<remote-mac-tailnet-ip>"],
dst: ["<android-tailnet-ip>"],
ip: ["tcp:5555"],
},
],
}
Check Tailscale grants for host aliases and other selectors. Never expose this port to the public internet or via Funnel: an authorized ADB client gains broad device control.
Connect and start mirroring
On the remote Mac:
adb connect <android-tailnet-ip>:5555
adb devices
scrcpy --serial <android-tailnet-ip>:5555
The first adb connect from this Mac triggers an authorization dialog on Android. Unlock the device,
verify the key fingerprint, and pick Always allow from this computer only when the Mac is
trusted. A successful adb devices entry ends with device; unauthorized signals that the on-device prompt
has not been accepted yet.
Once the scrcpy window appears, use it directly or aim a macOS screen-automation tool like Peekaboo at it. scrcpy handles display and input; Tailscale only supplies the private network route.
Troubleshooting
Connection timed out: confirm the tailnet grant for TCP 5555. A successfultailscale pingshows peer reachability, not that policy allows this TCP port. Trync -vz <android-tailnet-ip> 5555from the Mac.unauthorized: unlock Android and approve the remote Mac's ADB key, or remove the old workstation under Wireless debugging > Paired devices and re-pair it.Connection refused: reconnect locally and runadb tcpip 5555once more.- Multiple devices shown: keep the explicit
--serial <android-tailnet-ip>:5555argument.
When done, close scrcpy and disconnect ADB:
adb disconnect <android-tailnet-ip>:5555
Connection runbook
Android node app ⇄ (mDNS/NSD + WebSocket) ⇄ Gateway
Android reaches the Gateway WebSocket directly and relies on device pairing (role: node).
For Tailscale or public hosts, Android needs a secure endpoint:
- Preferred: Tailscale Serve / Funnel with
https://<magicdns>/wss://<magicdns> - Also accepted: any other
wss://Gateway URL backed by a real TLS endpoint - Cleartext
ws://stays supported on private LAN addresses /.localhosts, pluslocalhost,127.0.0.1, and the Android emulator bridge (10.0.2.2); non-loopback setups automatically fall back to limited operator access
Prerequisites
- A gateway hosted on a separate machine (or reachable through SSH).
- The Android device or emulator must be able to connect to the gateway's WebSocket:
- On the same LAN via mDNS/NSD, or
- On the same Tailscale tailnet with Wide-Area Bonjour or unicast DNS-SD (details below), or
- Through a manually specified gateway host and port (as a fallback)
- Pairing from a tailnet or public mobile network does not rely on raw tailnet IP
ws://endpoints. Instead, use Tailscale Serve or anotherwss://URL. - The
openclawCLI must be available on the gateway machine (or over SSH) to accept pairing requests.
1. Start the Gateway
openclaw gateway --port 18789 --verbose
Check the logs for output resembling:
listening on ws://0.0.0.0:18789
For Android access over Tailscale from a remote location, choose Serve or Funnel over a raw tailnet bind:
openclaw gateway --tailscale serve
This provides Android with a secure wss:// / https:// endpoint. A plain gateway.bind: "tailnet" setup is insufficient for first-time remote Android pairing unless TLS is separately terminated.
2. Verify discovery (optional)
Run the following on the gateway machine:
dns-sd -B _openclaw-gw._tcp local.
Additional troubleshooting notes: Bonjour.
If a wide-area discovery domain was also configured, verify against:
openclaw gateway discover --json
That output shows local. along with the configured wide-area domain in a single operation, using the resolved service endpoint instead of TXT-only hints.
Cross-network discovery via unicast DNS-SD
Android's NSD/mDNS discovery is confined to a single network. When the Android node and the gateway reside on different networks but are linked through Tailscale, rely on Wide-Area Bonjour or unicast DNS-SD. Discovery alone is not enough for tailnet or public Android pairing; the discovered route still requires a secure endpoint (wss:// or Tailscale Serve):
- Create a DNS-SD zone (example
openclaw.internal.) on the gateway host and publish_openclaw-gw._tcprecords. - Set up Tailscale split DNS for your chosen domain, pointing it at that DNS server.
For details and an example CoreDNS configuration: Bonjour.
3. Connect from Android
Inside the Android app:
- A foreground service (with a persistent notification) keeps the gateway connection alive.
- Navigate to the Connect tab.
- Choose Setup Code or Manual mode.
- When discovery is blocked, specify the host and port manually under Advanced controls. For private LAN hosts,
ws://remains functional. For Tailscale or public hosts, enable TLS and use awss://or Tailscale Serve endpoint.
Once the first pairing succeeds, Android automatically reconnects to the active paired gateway on launch (best-effort for discovered gateways, which must be reachable on the network).
Official setup codes register Android as a node and, by default, grant full Gateway operator
access over wss://. Plaintext non-loopback ws:// setup
automatically applies limited access to protect bearer tokens. Settings → Gateway
displays either Full or Limited access. For a limited connection, set up
wss:// or Tailscale Serve, create a new full-access code in Control UI or
with openclaw qr, then scan or paste it on that page and reconnect. Operators
preferring the reduced profile can choose Limited access in Control UI or execute
openclaw qr --limited.
Manage paired gateways
The app maintains a registry of every gateway it has paired with, letting you keep operator sessions active and shift focus without re-pairing:
- Settings → Gateway lists paired gateways, with the focused one flagged. Tapping an entry makes it the focus; the other enabled operator sessions stay connected.
- Each switch determines whether a non-focused Gateway remains connected while the app is in the foreground. The focused Gateway stays enabled and holds the phone's node connection and device capabilities.
- When more than one gateway is paired, the Connect tab shows a quick switcher.
- Credentials, device tokens, TLS trust, chat history, and queued offline messages are stored per Gateway. Switching focus never mixes state between Gateways, and offline-queued messages are delivered only to the Gateway they were intended for.
- Forget removes a gateway's registry entry along with its credentials, device tokens, TLS pin, and cached chats.
Presence alive beacons
After the authenticated node session is established, and when the app moves to the background while the foreground service remains connected, Android invokes node.event with event: "node.presence.alive". The gateway logs this as lastSeenAtMs/lastSeenReason on the paired node/device metadata only after the authenticated node device identity is known.
The app considers the beacon successfully recorded only when the gateway response includes handled: true. Older gateways may acknowledge node.event with { "ok": true }; that response is compatible but does not count as a durable last-seen update.
4. Approve pairing (CLI)
On the gateway machine:
openclaw devices list
openclaw devices approve <requestId>
openclaw devices reject <requestId>
Pairing specifics: Pairing.
Optional: if the Android node always connects from a tightly controlled subnet, you can opt in to first-time node auto-approval with explicit CIDRs or exact IPs:
{
gateway: {
nodes: {
pairing: {
autoApproveCidrs: ["192.168.1.0/24"],
},
},
},
}
This feature is off by default. It applies only to fresh role: node pairing with no requested scopes. Operator/browser pairing and any role, scope, metadata, or public-key change still demand manual approval.
5. Verify the node is connected
openclaw nodes status
openclaw gateway call node.list --params "{}"
6. Chat + history
The Android Chat tab supports session selection (default main, plus other existing sessions):
- History:
chat.history(display-normalized, with inline directive tags, plain-text tool-call XML payloads (<tool_call>,<function_call>,<tool_calls>,<function_calls>, and truncated variants), and leaked ASCII/full-width model control tokens stripped; silent-token assistant rows such as exactNO_REPLY/no_replyare omitted; oversized rows may be replaced with placeholders) - Long replies: tap View all on a capped assistant reply to load the full formatted text inline. Attachments remain in the conversation, and message actions use the expanded text. Tap Show less or press Back to restore the preview; reopening reuses the loaded reply. Loading, retryable failures, unavailable messages, and required reconnects or Gateway updates appear in the message rather than an alert. Synthetic message-tool and commentary previews keep their existing display and actions but do not offer View all, because their copied transcript ID cannot retrieve that synthesized text. This also recognizes the older capped-preview format from released Gateways such as v2026.7.1-2. Android requests up to 1,000,000 characters per text field, matching the Gateway's default retrieval limit; oversized or still-capped results show The full message is too large to display. instead of an incomplete reply.
- Large code blocks scroll within a bounded viewport, with Start of code, End of code, and Copy code controls. Selection stays within the displayed text segment; Copy code copies the entire block. Reading within the code pauses automatic transcript following; Jump to latest resumes it. The separate message Select text action opens a plain-text selection reader; long answers use bounded pages, and selection applies to the displayed page.
- Archiving the open session returns to the app's main chat only if that same session is still selected. Switching sessions, agents, or Gateways while the archive finishes preserves your newer selection.
- Offline history: cached transcripts update in the order live histories are accepted, so a delayed reconnect health check cannot restore an older snapshot. Switching sessions preserves queued cache updates for the session you left.
- Send:
chat.send - Durable sending: every send (text, picked images, and voice notes) is journaled to a per-gateway on-device outbox before any network attempt, so app termination cannot lose submitted input. Sends queued while offline deliver in order on reconnect with stable idempotency keys, and a send is retired only after the turn is visible in canonical
chat.history, an acknowledgement alone is not treated as proof of delivery. Ambiguous outcomes (lost acknowledgement, app killed mid-send, gateway restart before the transcript write) surface as visible rows with explicit Retry/Delete instead of auto-resending. If refreshed history changes branches, earlier queued input keeps its text and attachments but requires explicit retry; input admitted after that history is displayed can send normally when reconnecting to the same branch. Slash commands never auto-replay across a reconnect; they park for explicit retry. The queue is bounded (50 messages and 48 MB of attachment bytes per gateway) and unsent rows expire after 48 hours. Composer drafts that were never submitted are not process-durable. - Image input works through the picker and Android Sharesheet. Assistant-generated images resolve through the paired Gateway connection, render inline with a full-screen preview, and retain only their small artifact references in the offline transcript cache. Downloads are capped at 12 MiB and decoded to bounded display bitmaps.
- Push updates (best-effort):
chat.subscribe->event:"chat" - Listen: long-press an assistant message and choose Listen to hear it; audio renders via gateway
tts.speakwith the configured TTS provider chain, and on-device system TTS is used when the gateway cannot render audio. Playback stops on session switch, new chat, app backgrounding, or chat close.
7. Camera
Camera operations are restricted to foreground use and require permission. camera.snap captures JPEG stills, while camera.clip records MP4 video. Parameter details and CLI helpers live in the Camera node reference.
8. Voice + expanded Android command surface
- Android's bottom navigation offers Home, Chat, and Settings. Voice entry happens through the Chat composer; a dedicated Voice tab does not exist.
- The microphone icon in the composer triggers on-device speech recognition, dropping a transcript into the draft. Holding the same icon records a voice-note attachment. Failures surface explicitly: unrecognized speech, missing permissions, busy or network errors, and silence all produce visible feedback rather than being ignored.
- The Chat waveform starts continuous Talk. Dictation, voice-note capture, and Talk cannot run at the same time; they share one microphone path.
- Talk Mode escalates the existing foreground service from
connectedDevicetoconnectedDevice|microphonebefore recording begins, then steps it back down when Talk stops. The node service listsFOREGROUND_SERVICE_CONNECTED_DEVICEwithCHANGE_NETWORK_STATE; on Android 14 and newer, theFOREGROUND_SERVICE_MICROPHONEdeclaration, theRECORD_AUDIOruntime permission, and the microphone service type are also mandatory at runtime. - Android Talk defaults to native speech recognition, Gateway chat, and
talk.speakvia the configured gateway Talk provider. Local system TTS only kicks in whentalk.speakis missing. - Realtime Gateway relay in Android Talk activates solely when
talk.realtime.modeequalsrealtimeandtalk.realtime.transportequalsgateway-relay. - The
voiceWakecapability is not advertised on Android. Voice input comes from Chat dictation, a voice note, or Talk instead. - Further Android command families, subject to device, permission, and user settings:
device.status,device.info,device.permissions,device.healthdevice.appsappears only with Settings > Phone Capabilities > Installed Apps enabled; launcher-visible apps show by default, andincludeNonLaunchableyields the complete list.notifications.list,notifications.actions(see Notification forwarding below)photos.latestcontacts.search,contacts.addcalendar.events,calendar.addcallLog.searchsms.searchmotion.activity,motion.pedometer
9. Workspace files (read-only)
The Home screen shows a Files card that explores the active agent's workspace through the read-only agents.workspace.list / agents.workspace.get gateway RPCs. It supports folder navigation, text and image previews, and sharing via Android's share sheet. Writes are unsupported, and the gateway caps preview sizes.
Review command approvals
An operator connection with operator.admin, or a paired operator.approvals connection that the Gateway explicitly targets, can inspect pending exec requests under Settings -> Approvals. Before enabling its buttons, the app loads the Gateway's sanitized approval record, surfaces any security warning and the exact decision options that request offers, then sends the approval ID and owner kind back to the Gateway.
Approval state stays in sync with the Control UI and supported chat surfaces. The first committed answer wins; Android shows that canonical result even if another surface answered first. When a resolve response goes missing or the Gateway drops the connection, the app keeps the action locked and re-reads the approval before presenting another choice.
Older Gateways lacking the unified approval methods fall back to the shipped exec-specific methods. Pending review remains functional, but retained terminal state and the richer cross-surface result call for an updated Gateway.
Answer agent questions
For operator connections with operator.questions (or operator.admin), Chat renders pending Gateway questions as native cards. Each card supports single- or multi-select choices, option descriptions, free-text Other responses, and a countdown to expiry. Reconnects pull pending questions fresh from the Gateway. A card locks when this device answers it, another surface answers first, or the question expires or gets cancelled.
Assistant entrypoints
Android lets users launch OpenClaw through the system assistant trigger (Google Assistant). Pressing and holding the home button (or another ACTION_ASSIST trigger) opens the app; saying "Hey Google, ask OpenClaw <prompt>" matches the app's declared App Actions query pattern and places the prompt into the chat composer without sending it automatically.
This relies on Android App Actions (the shortcuts.xml capability) declared in the app manifest. Gateway-side configuration is unnecessary; the assistant intent is processed entirely within the Android app.
Note
App Actions availability hinges on the device, Google Play Services version, and whether OpenClaw is set as the default assistant app.
Notification forwarding
Android can push device notifications to the gateway as node.event items. Configuration happens on the device, through the app's Settings sheet, not in gateway/openclaw.json config.
| Setting | Description |
|---|---|
| Forward Notification Events | Master toggle. Off by default; requires Notification Listener Access to be granted first. |
| Package Filter | Allowlist (only listed package IDs forwarded) or Blocklist (default: all packages except listed IDs). OpenClaw's own package is always excluded in Blocklist mode to prevent forwarding loops. |
| Quiet Hours | Local HH:mm start/end window that suppresses forwarding. Disabled by default; defaults to 22:00-07:00 once enabled. |
| Max Events / Minute | Per-device rate limit on forwarded notifications. Default 20. |
| Route Session Key | Optional. Pins forwarded notification events into a specific session instead of the device's default notification route. |
Note
Notification forwarding requires the Android Notification Listener permission. The app prompts for this during setup.
WhatsApp, WhatsApp Business, Telegram, Telegram X, Discord, and Signal notifications are always excluded. Their messages are already owned by native OpenClaw channel sessions; forwarding the Android notification as a separate node event could route a reply through the wrong conversation.