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.apk from a supported GitHub Release, get the Gateway running with Getting Started, then complete Pairing.
  • Gateway details: Runbook and Configuration.
  • Settings → OpenClaw launches a dedicated Gateway configuration wizard when the operator link has operator.admin and the Gateway advertises openclaw.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 successful tailscale ping shows peer reachability, not that policy allows this TCP port. Try nc -vz <android-tailnet-ip> 5555 from 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 run adb tcpip 5555 once more.
  • Multiple devices shown: keep the explicit --serial <android-tailnet-ip>:5555 argument.

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 / .local hosts, plus localhost, 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 another wss:// URL.
  • The openclaw CLI 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):

  1. Create a DNS-SD zone (example openclaw.internal.) on the gateway host and publish _openclaw-gw._tcp records.
  2. 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 a wss:// 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 exact NO_REPLY / no_reply are 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.speak with 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 connectedDevice to connectedDevice|microphone before recording begins, then steps it back down when Talk stops. The node service lists FOREGROUND_SERVICE_CONNECTED_DEVICE with CHANGE_NETWORK_STATE; on Android 14 and newer, the FOREGROUND_SERVICE_MICROPHONE declaration, the RECORD_AUDIO runtime permission, and the microphone service type are also mandatory at runtime.
  • Android Talk defaults to native speech recognition, Gateway chat, and talk.speak via the configured gateway Talk provider. Local system TTS only kicks in when talk.speak is missing.
  • Realtime Gateway relay in Android Talk activates solely when talk.realtime.mode equals realtime and talk.realtime.transport equals gateway-relay.
  • The voiceWake capability 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.health
    • device.apps appears only with Settings > Phone Capabilities > Installed Apps enabled; launcher-visible apps show by default, and includeNonLaunchable yields the complete list.
    • notifications.list, notifications.actions (see Notification forwarding below)
    • photos.latest
    • contacts.search, contacts.add
    • calendar.events, calendar.add
    • callLog.search
    • sms.search
    • motion.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.

SettingDescription
Forward Notification EventsMaster toggle. Off by default; requires Notification Listener Access to be granted first.
Package FilterAllowlist (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 HoursLocal HH:mm start/end window that suppresses forwarding. Disabled by default; defaults to 22:00-07:00 once enabled.
Max Events / MinutePer-device rate limit on forwarded notifications. Default 20.
Route Session KeyOptional. 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.

3,491 words · updated Sep 1, 2026