# 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:**
```kotlin
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:**
```kotlin
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:**
```kotlin
private fun resetReconnectionState() {
    reconnectAttempts = 0
    reconnectHandler?.removeCallbacksAndMessages(null)
    reconnectHandler = null
}
```

### GatewayListener Interface

**New Callback:**
```kotlin
interface GatewayListener {
    // ... existing callbacks ...
    fun onReconnecting(attempt: Int, delayMs: Long)
}
```

### MainScreen.kt Implementation

```kotlin
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**
```bash
# 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

1. **Connect the app**
   - Status should show "Connected ✅"

2. **Kill the proxy**
   ```bash
   pkill -f "node server.js"
   ```
   - Watch the status bar for reconnection attempts

3. **Restart the proxy**
   ```bash
   cd ~/.openclaw/workspace/alfred-proxy
   node server.js > /tmp/alfred-proxy.log 2>&1 &
   ```
   - App should reconnect within a few seconds

4. **Check logs**
   ```bash
   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:
- `shouldReconnect` is set to `false`
- 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:**
- `reconnectAttempts` reset to 0
- Reconnection handler cleared
- Fresh state for next potential disconnection

**On manual disconnect:**
- `shouldReconnect` set 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:

```kotlin
// 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

```kotlin
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:**
```bash
adb logcat | grep -E "(GatewayClient|MainScreen)"
```

**Possible causes:**
- Max retries exceeded (restart app)
- `shouldReconnect` set to false
- Handler not scheduled

### Reconnecting too slowly

**Reduce backoff delays:**
```kotlin
private val baseReconnectDelayMs = 500L  // 500ms instead of 1s
private val maxReconnectDelayMs = 10000L  // 10s instead of 30s
```

### Too many reconnection attempts

**Reduce max attempts:**
```kotlin
private val maxReconnectAttempts = 5  // 5 instead of 10
```

### Memory leaks

**Ensure cleanup:**
- Check `disconnect()` calls `resetReconnectionState()`
- 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)
