- OAuth authentication via Authentik - WebSocket connection to OpenClaw gateway - Configurable gateway URL with first-run setup - User preferences sync across devices - Multi-user support with custom assistant names - ElevenLabs TTS integration (local + remote) - FCM push notifications for alarms - Voice input via Google Speech API - No hardcoded secrets or internal IPs in tracked files
9.8 KiB
Auto-Reconnection Feature
Alfred mobile app now automatically reconnects when the connection is lost!
Overview
The app now includes intelligent auto-reconnection with exponential backoff to handle network issues gracefully. When the WebSocket connection drops, the app will automatically attempt to reconnect without requiring user intervention.
Features
✅ Automatic Reconnection
- Triggered on any connection failure:
- Network interruptions
- Server disconnections
- "Software caused connection abort" errors
- WiFi/cellular switching
- Proxy restarts
📈 Exponential Backoff
- Smart retry timing:
- Attempt 1: 1 second delay
- Attempt 2: 2 seconds delay
- Attempt 3: 4 seconds delay
- Attempt 4: 8 seconds delay
- Attempt 5: 16 seconds delay
- Attempts 6+: 30 seconds delay (max)
🔢 Max Retry Limit
- 10 reconnection attempts before giving up
- Prevents infinite retry loops
- Battery-friendly
📊 UI Feedback
- Connection status shows:
- "Connecting..." - Initial connection
- "Connected ✅" - Successfully connected
- "Disconnected" - Connection lost
- "Reconnecting... (attempt X, Ys)" - Auto-reconnecting
- "Error: Connection lost - max retries exceeded" - Gave up after 10 attempts
How It Works
Connection Loss Detected
│
▼
┌────────────────────┐
│ shouldReconnect? │
│ (enabled) │
└────────┬───────────┘
│ yes
▼
┌────────────────────┐
│ Check attempt # │
│ < 10 attempts? │
└────────┬───────────┘
│ yes
▼
┌────────────────────┐
│ Calculate delay │
│ (exponential) │
└────────┬───────────┘
│
▼
┌────────────────────┐
│ Schedule retry │
│ (Handler) │
└────────┬───────────┘
│
▼
┌────────────────────┐
│ Wait delay... │
└────────┬───────────┘
│
▼
┌────────────────────┐
│ Attempt connect │
└────────┬───────────┘
│
├─ Success → Reset counter, connected ✅
│
└─ Failure → Loop back to retry
Code Details
GatewayClient.kt Changes
New State Variables:
private var shouldReconnect = true
private var reconnectAttempts = 0
private val maxReconnectAttempts = 10
private val baseReconnectDelayMs = 1000L
private val maxReconnectDelayMs = 30000L
private var reconnectHandler: Handler? = null
Reconnection Logic:
private fun scheduleReconnect() {
if (reconnectAttempts >= maxReconnectAttempts) {
Log.e(TAG, "Max reconnection attempts reached")
listener.onError("Connection lost - max retries exceeded")
shouldReconnect = false
return
}
val delay = minOf(
baseReconnectDelayMs * (1 shl reconnectAttempts),
maxReconnectDelayMs
)
reconnectAttempts++
listener.onReconnecting(reconnectAttempts, delay)
reconnectHandler?.postDelayed({
if (shouldReconnect && !isConnected) {
connect()
}
}, delay)
}
Reset on Success:
private fun resetReconnectionState() {
reconnectAttempts = 0
reconnectHandler?.removeCallbacksAndMessages(null)
reconnectHandler = null
}
GatewayListener Interface
New Callback:
interface GatewayListener {
// ... existing callbacks ...
fun onReconnecting(attempt: Int, delayMs: Long)
}
MainScreen.kt Implementation
override fun onReconnecting(attempt: Int, delayMs: Long) {
val delaySec = delayMs / 1000
connectionStatus = "Reconnecting... (attempt $attempt, ${delaySec}s)"
Log.d("MainScreen", "Reconnecting: attempt $attempt, delay ${delayMs}ms")
}
Testing
Test Scenarios
1. Restart OAuth Proxy
# Kill and restart the proxy
pkill -f "node server.js"
cd ~/.openclaw/workspace/alfred-proxy
node server.js > /tmp/alfred-proxy.log 2>&1 &
Expected behavior:
- App shows "Disconnected"
- After ~1 second: "Reconnecting... (attempt 1, 1s)"
- After ~3 seconds: "Connected ✅"
2. Network Switch (WiFi ↔ Cellular)
- Turn off WiFi on mobile device
- App automatically reconnects via cellular
- Or vice versa
3. Temporary Network Loss
- Enable airplane mode for 5 seconds
- Disable airplane mode
- App reconnects automatically
4. Extended Network Outage
- Enable airplane mode for 5 minutes
- App will attempt 10 reconnections over ~2 minutes
- Shows "Connection lost - max retries exceeded"
- Disable airplane mode
- Restart app to reconnect
Manual Testing
-
Connect the app
- Status should show "Connected ✅"
-
Kill the proxy
pkill -f "node server.js"- Watch the status bar for reconnection attempts
-
Restart the proxy
cd ~/.openclaw/workspace/alfred-proxy node server.js > /tmp/alfred-proxy.log 2>&1 &- App should reconnect within a few seconds
-
Check logs
adb logcat GatewayClient:D MainScreen:D *:S- Should see reconnection attempts and backoff delays
Edge Cases
When Reconnection is Disabled
User-initiated disconnect:
- Logging out
- App closure
- Manual disconnect
The shouldReconnect flag is set to false in these cases to prevent unwanted reconnection attempts.
Max Retries Reached
After 10 failed attempts:
shouldReconnectis set tofalse- Error message displayed
- User must restart the app to reconnect
Why 10 attempts?
- Total time: ~2 minutes of reconnection attempts
- Balance between persistence and battery life
- Prevents infinite loops
State Cleanup
On successful connection:
reconnectAttemptsreset to 0- Reconnection handler cleared
- Fresh state for next potential disconnection
On manual disconnect:
shouldReconnectset to false- All handlers cancelled
- Clean shutdown
Benefits
User Experience
- ✅ Seamless reconnection after temporary network issues
- ✅ No manual intervention required
- ✅ Clear status feedback
- ✅ Works across network type changes (WiFi ↔ Cellular)
Battery Efficiency
- ✅ Exponential backoff reduces connection attempts over time
- ✅ Max retry limit prevents infinite loops
- ✅ Handler cleanup prevents memory leaks
Reliability
- ✅ Handles "Software caused connection abort" errors
- ✅ Resilient to proxy/server restarts
- ✅ Graceful degradation (max retries)
Monitoring
Logs to Watch
GatewayClient:
D/GatewayClient: WebSocket failure
D/GatewayClient: Scheduling reconnect attempt 1 in 1000ms
D/GatewayClient: Attempting reconnection (attempt 1)
D/GatewayClient: Connect successful!
MainScreen:
D/MainScreen: Reconnecting: attempt 1, delay 1000ms
D/MainScreen: Reconnecting: attempt 2, delay 2000ms
Status Bar Messages
| Status | Meaning |
|---|---|
| Connecting... | Initial connection in progress |
| Connected ✅ | Successfully connected |
| Disconnected | Connection lost |
| Reconnecting... (attempt 1, 1s) | First reconnection attempt |
| Reconnecting... (attempt 5, 16s) | Fifth attempt with backoff |
| Error: Connection lost - max retries exceeded | Gave up after 10 attempts |
Configuration
Adjustable Parameters
Edit GatewayClient.kt to customize:
// Maximum reconnection attempts
private val maxReconnectAttempts = 10 // Change to 5, 20, etc.
// Initial retry delay
private val baseReconnectDelayMs = 1000L // Change to 2000L (2s), etc.
// Maximum retry delay
private val maxReconnectDelayMs = 30000L // Change to 60000L (60s), etc.
Exponential Backoff Formula
delay = min(
baseReconnectDelayMs * (2 ^ reconnectAttempts),
maxReconnectDelayMs
)
Example with current settings:
- Attempt 1: min(1000 * 2^0, 30000) = 1000ms
- Attempt 2: min(1000 * 2^1, 30000) = 2000ms
- Attempt 3: min(1000 * 2^2, 30000) = 4000ms
- Attempt 4: min(1000 * 2^3, 30000) = 8000ms
- Attempt 5: min(1000 * 2^4, 30000) = 16000ms
- Attempt 6: min(1000 * 2^5, 30000) = 30000ms (capped)
- Attempts 7-10: 30000ms (capped)
Troubleshooting
App not reconnecting
Check logs:
adb logcat | grep -E "(GatewayClient|MainScreen)"
Possible causes:
- Max retries exceeded (restart app)
shouldReconnectset to false- Handler not scheduled
Reconnecting too slowly
Reduce backoff delays:
private val baseReconnectDelayMs = 500L // 500ms instead of 1s
private val maxReconnectDelayMs = 10000L // 10s instead of 30s
Too many reconnection attempts
Reduce max attempts:
private val maxReconnectAttempts = 5 // 5 instead of 10
Memory leaks
Ensure cleanup:
- Check
disconnect()callsresetReconnectionState() - Verify
reconnectHandler?.removeCallbacksAndMessages(null) - Look for uncancelled handlers in logs
Future Enhancements
Potential improvements:
- Jitter - Add randomness to backoff to prevent thundering herd
- Network detection - Skip retries when network is unavailable
- Manual retry button - User can trigger reconnection after max retries
- Smarter reset - Reset counter after stable connection (e.g., 5 minutes)
- Configurable via settings - User-adjustable retry behavior
- Background reconnection - Continue attempting when app is backgrounded
Version
1.0.0 - Initial auto-reconnection implementation (February 2026)