- Connect to /ws endpoint instead of /ws/node?token=... - Handle connect.challenge event and send proper handshake - Implement protocol v3 frame types (req/res/event) - Add node.invoke command handling for gateway RPC - Create DeviceIdentity for challenge signing (EC P-256 via Keystore) - Declare caps: notifications, calls, voice - Declare commands: notification.action/dismiss, call.answer/reject/speak/hangup - Send proper responses for node.invoke requests |
||
|---|---|---|
| app | ||
| gradle/wrapper | ||
| README.md | ||
| build.gradle.kts | ||
| gradle.properties | ||
| gradlew | ||
| settings.gradle.kts | ||
README.md
ClawdNode Android
AI-powered phone assistant that connects to Clawdbot Gateway. Enables Claude to answer calls, screen notifications, and act on your behalf.
Features (v0.1)
Notification Interception
- Captures all notifications from all apps
- Forwards to Gateway: app name, title, text, available actions
- Can trigger actions (Reply, Mark read, etc.) via Gateway commands
- Can dismiss notifications remotely
Call Screening & Voice
- Intercepts incoming calls before ring
- Sends caller info to Gateway for Claude to decide
- Answer calls programmatically
- Speak into calls via TTS (Text-to-Speech)
- Listen to caller via STT (Speech-to-Text)
- Full voice conversation loop with Claude as the brain
Security
- Tailscale-only — no public internet exposure
- Encrypted credential storage (EncryptedSharedPreferences)
- Local audit log of all actions
- All permissions clearly explained
Protocol
Events (Phone → Gateway)
// Notification received
{"type": "notification", "id": "com.whatsapp:123:1706400000", "app": "WhatsApp", "package": "com.whatsapp", "title": "Mom", "text": "Call me when you can", "actions": ["Reply", "Mark read"]}
// Incoming call
{"type": "call_incoming", "call_id": "tel:+1234567890", "number": "+1234567890", "contact": "Mom"}
// Caller speech (transcribed)
{"type": "call_audio", "call_id": "tel:+1234567890", "transcript": "Hi, I'm calling about the appointment", "is_final": true}
// Call ended
{"type": "call_ended", "call_id": "tel:+1234567890", "duration": 45, "outcome": "completed"}
Commands (Gateway → Phone)
// Take screenshot
{"cmd": "screenshot"}
// Trigger notification action
{"cmd": "notification_action", "id": "com.whatsapp:123:...", "action": "Reply", "text": "I'll call you back in 30 min"}
// Dismiss notification
{"cmd": "notification_dismiss", "id": "com.whatsapp:123:..."}
// Answer incoming call with greeting
{"cmd": "call_answer", "call_id": "tel:+1234567890", "greeting": "Hello, this is Johan's phone. Who's calling?"}
// Reject call
{"cmd": "call_reject", "call_id": "tel:+1234567890"}
// Speak into active call
{"cmd": "call_speak", "call_id": "tel:+1234567890", "text": "Thank you for calling. I'll let Johan know about the appointment."}
// Hang up
{"cmd": "call_hangup", "call_id": "tel:+1234567890"}
Setup
1. Build the APK
# Clone and build
cd clawdnode-android
./gradlew assembleDebug
# APK will be at:
# app/build/outputs/apk/debug/app-debug.apk
Or open in Android Studio and build.
2. Install on Phone
adb install app/build/outputs/apk/debug/app-debug.apk
Or transfer APK and install manually (enable "Unknown sources").
3. Configure Gateway
- Open ClawdNode app
- Enter Gateway URL:
http://<tailscale-ip>:18789 - Enter Gateway Token: (from your Clawdbot config)
- Save Configuration
4. Grant Permissions
The app needs several permissions:
- Notification Access — System settings, enable ClawdNode
- Call Screening Role — Become the call screener
- Runtime Permissions:
- Phone state
- Call log
- Answer calls
- Record audio (for STT)
- Contacts (for caller ID)
5. Test Connection
- Status should show "✓ Connected to Gateway"
- Send a test notification to your phone
- Check Gateway logs for the notification event
Voice Flow Example
1. Call comes in from unknown number
2. ClawdNode sends: {"type": "call_incoming", "number": "+1234567890", "contact": null}
3. Claude decides to answer and screen
4. Gateway sends: {"cmd": "call_answer", "greeting": "Hi, this is Johan's assistant. Who's calling?"}
5. ClawdNode answers, plays TTS greeting
6. Caller speaks: "Hi, I'm calling from Dr. Smith's office about tomorrow's appointment"
7. ClawdNode sends: {"type": "call_audio", "transcript": "Hi, I'm calling from Dr. Smith's office..."}
8. Claude processes, decides to confirm details
9. Gateway sends: {"cmd": "call_speak", "text": "Thank you for calling. Can you confirm the time?"}
10. ... conversation continues ...
11. Gateway sends: {"cmd": "call_hangup"}
12. Claude sends summary to Johan via Signal
Project Structure
app/src/main/java/com/inou/clawdnode/
├── ClawdNodeApp.kt # Application class, initialization
├── service/
│ ├── NodeService.kt # Foreground service, command routing
│ └── GatewayClient.kt # WebSocket connection
├── notifications/
│ └── NotificationListener.kt # Notification capture & actions
├── calls/
│ ├── CallScreener.kt # Call screening service
│ └── VoiceCallService.kt # InCallService for voice interaction
├── security/
│ ├── TokenStore.kt # Encrypted credential storage
│ └── AuditLog.kt # Local audit trail
├── protocol/
│ └── Messages.kt # Event/Command data classes
└── ui/
└── MainActivity.kt # Setup UI
Requirements
- Android 10+ (API 29) — required for CallScreeningService
- Tailscale installed and connected
- Clawdbot Gateway running and accessible
Security Notes
- Gateway connection is via Tailscale mesh network only
- Credentials are stored using Android's EncryptedSharedPreferences
- All actions are logged locally with timestamps
- No data leaves your network (except to Gateway)
License
MIT — Use freely, contribute back.