# UCall — Production Architecture Blueprint & Technical Specification

**Document Version:** 2.1 (Implementation-Grade)  
**Target Platforms:** Android & iOS (Flutter)  
**Reference Specification:** [PRD v1.2](file:///d:/UCall/App/Doc/prd.md) & Design Collage (`Ucall Mobile Calling App UI Collage.png`)

---

## 1. System Boundaries & Dual-Channel Architecture

UCall provides unified access to **two distinct calling flows** and **two distinct messaging flows**. The platform boundaries are strictly enforced to prevent platform policy violations and architectural debt.

```
                                  ┌───────────────────────────────┐
                                  │       UCall Flutter App       │
                                  └───────┬───────────────┬───────┘
                                          │               │
                      ┌───────────────────▼───┐       ┌───▼───────────────────┐
                      │     Calling Layer     │       │    Messaging Layer    │
                      └───┬───────────────┬───┘       └───┬───────────────┬───┘
                          │               │               │               │
                  ┌───────▼───────┐ ┌─────▼───────┐ ┌─────▼───────┐ ┌─────▼───────┐
                  │Native Cellular│ │ In-App VoIP │ │ Carrier SMS │ │ Internet    │
                  │  (OS Handoff) │ │(VoIP Engine)│ │ (OS Handoff)│ │    Chat     │
                  └───────┬───────┘ └─────┬───────┘ └─────┬───────┘ └─────┬───────┘
                          │               │               │               │
                          ▼               ▼               ▼               ▼
                  System Dialer     CallKit / APNs  System SMS      WebSocket /
                  via `tel:` URI    WebRTC / Twilio via `sms:` URI  REST + Push
                  (OS Managed UI)   (UCall Dark UI) (OS Managed UI) (UCall Chat UI)
```

### Boundary & Scope Rules

1. **Native Cellular Calls (`tel:` Handoff):**
   - UCall dispatches the normalized destination to the operating system using the standard `tel:` URI.
   - **Boundary:** The native phone app controls the call screen, audio routing, and cellular termination. UCall does **not** simulate or control cellular in-call screens.
   - **Call History:** iOS sandboxing prohibits reading native cellular logs. Android restricts `READ_CALL_LOG` to default phone handlers. UCall only creates a local *Outbound Cellular Attempt* log upon launching `tel:`. Unified cellular history is explicitly out of scope.

2. **In-App VoIP Calls (Custom Engine):**
   - Authorized internet calls between UCall accounts (or PSTN outbound if supported by backend provider).
   - Managed entirely within the app via the **custom dark active-call screen**, driven by live SDK events.

3. **Carrier SMS (`sms:` Handoff):**
   - Dispatches recipient and message to the native system composer via platform-specific URI formats.
   - **Boundary:** Sending, delivery, and history remain entirely inside Apple Messages or Android Messages. UCall does **not** request restricted SMS permissions and does **not** assume default SMS app status. UCall never reports SMS messages as "delivered."

4. **UCall Internet Chat (Real-time Messaging):**
   - Bi-directional messaging via WebSockets, supported by REST history endpoints.
   - Full support for authoritatively validated delivery states (`pending → sent → delivered → read → failed`), attachment uploads, and push notifications.

---

## 2. Platform Permissions Matrix

UCall utilizes modern contextual permissions and platform pickers to avoid broad permission warnings and Play Store / App Store rejections.

| Capability | iOS Key / Framework | Android Permission | Strategy |
|---|---|---|---|
| **Microphone** | `NSMicrophoneUsageDescription` | `RECORD_AUDIO` | Requested contextually immediately prior to the first VoIP call. |
| **Contacts** | `NSContactsUsageDescription` | `READ_CONTACTS`, `WRITE_CONTACTS` | Requested on onboarding or when first entering the Contacts tab. |
| **Notifications** | User Notification Center | `POST_NOTIFICATIONS` (Android 13+) | Requested for incoming VoIP ringing and new message alerts. |
| **Photo / Media Picker** | `PHPickerViewController` | Android Photo Picker | **Zero permissions required.** Uses system pickers (`image_picker` with platform picker mode). |
| **Carrier SMS** | System Composer | System Composer | **Zero permissions required.** Standard URL scheme launch. |
| **Cellular Calls** | System Dialer | System Dialer | **Zero permissions required.** Standard `tel:` launch. |

---

## 3. VoIP Calling Engine & Native Lifecycle

### Provider-Specific Session Contract (`POST /api/v1/calls/session`)

Caller identity is derived strictly from the authenticated JWT session on the backend; client-supplied caller identity is rejected. The backend returns a discriminated union payload matching the chosen calling stack:

#### A. Twilio Voice Session
```json
{
  "sessionId": "sess_tw_89f02a1b",
  "provider": "twilio_voice",
  "identity": "usr_arun_491",
  "accessToken": "eyJhbGciOiJSUzI1NiIs...",
  "twimlAppSid": "APxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
  "destination": "+919876543210",
  "expiresAt": "2026-10-23T11:00:00Z"
}
```

#### B. WebRTC Session
```json
{
  "sessionId": "sess_rtc_77a91c3d",
  "provider": "webrtc",
  "channelId": "chan_call_498102_991823",
  "signalingUrl": "wss://signaling.ucall.app/v1/ws?token=...",
  "iceServers": [
    { "urls": ["stun:stun.l.google.com:19302"] },
    { "urls": ["turn:turn.ucall.app:3478"], "username": "usr_99", "credential": "pwd" }
  ],
  "destination": "+919876543210",
  "expiresAt": "2026-10-23T11:00:00Z"
}
```

#### C. LiveKit Session
```json
{
  "sessionId": "sess_lk_11f44e5a",
  "provider": "livekit",
  "serverUrl": "wss://livekit.ucall.app",
  "roomName": "call_498102_991823",
  "token": "eyJhbGciOiJ...",
  "destination": "+919876543210",
  "expiresAt": "2026-10-23T11:00:00Z"
}
```

---

### Complete Native Incoming & Outgoing Flow

```mermaid
sequenceDiagram
    autonumber
    actor Caller as Remote Caller
    participant Backend as UCall Backend
    participant APNS as Apple APNs (VoIP) / FCM
    participant Native as iOS CallKit / Android Telecom
    participant App as Flutter VoIP Service
    participant Screen as Active Call Screen

    Caller->>Backend: Initiate VoIP Call
    Backend->>APNS: Dispatch High-Priority VoIP Push
    APNS->>Native: Deliver Push Payload
    Note over Native: iOS: reportNewIncomingCall() to CXProvider immediately<br/>Android: Telecom ConnectionService / Foreground Service
    Native-->>Native: Display System Ringing Screen
    Native->>App: User taps "Accept / Answer"
    App->>Backend: Acknowledge & Claim Session
    App->>App: Activate AudioSession & Connect Provider SDK
    App->>Screen: Transition to Navy Dark Screen (Connected)
```

### Complete `CallService` Contract (`call_service.dart`)

```dart
enum CallMode { cellular, voip }

enum CallStatus {
  idle,
  incomingRinging,
  initiating,
  remoteRinging,
  connecting,
  connected,
  reconnecting,
  onHold,
  ending,
  ended,
  busy,
  rejected,
  failed,
}

enum CallRejectReason { busy, declined, timeout, unsupported }
enum AudioRoute { earpiece, speaker, bluetooth, wired }

abstract interface class CallService {
  /// Outgoing native cellular handoff via tel:
  Future<void> startCellularCall(String phoneNumber);

  /// Outgoing in-app VoIP call
  Future<CallSession> startVoipCall({required String destination});

  /// Incoming call operations
  Future<void> answerCall(String callId);
  Future<void> rejectCall(String callId, {CallRejectReason reason = CallRejectReason.declined});
  Future<void> handleCallTimeout(String callId);

  /// In-call controls (Feature-flagged by provider capability)
  Future<void> endVoipCall(String callId);
  Future<void> setMuted(bool muted);
  Future<void> setSpeakerphone(bool enabled);
  Future<void> setHold(bool onHold);
  Future<void> sendDtmf(String digits);
  Future<void> setAudioRoute(AudioRoute route);

  /// State & Streams
  Stream<CallSessionState> get callSessionStream;
  CallSessionState get currentSession;
}
```

---

## 4. Internet Chat Engine & Message Delivery Semantics

### Authoritative Delivery Receipts Lifecycle

Push notifications are delivery alerts, **not** proof of message receipt. Delivery receipts require active client acknowledgment:

```
[Pending]   ──► Local client optimistic enqueue with temporary client UUID.
     │
[Sent]      ──► Server receives, validates, deduplicates, persists, and assigns authoritative messageId.
     │
[Delivered] ──► Recipient's active client receives the message payload and returns a delivery ACK to server.
     │
[Read]      ──► Recipient client reports message viewed in chat viewport (double blue check ✓✓).
```

### Idempotency & Message Deduplication
Every message generated by the Flutter client includes a `clientMessageId` (UUIDv4). If a temporary network interruption occurs during transmission, retrying the operation with the same `clientMessageId` ensures the backend acknowledges the existing record rather than creating a duplicate message.

### WebSocket Resilience & State Recovery
1. **Lifecycle Suspension:** When the app enters the background, non-essential WebSocket traffic is suspended to conserve battery and avoid OS kill events.
2. **Foreground Reconnection & Missed-Event Sync:** Upon returning to the foreground, the WebSocket connection is re-established with exponential backoff and jitter ($\pm 20\%$). The client immediately triggers:
   ```http
   GET /api/v1/conversations/{conversationId}/messages/sync?sinceMessageId=msg_last_seen
   ```
   This synchronizes all missed messages, read receipts, and delivery updates occurred during background or offline state.

---

## 5. Resilient Networking & Safe Retry Interceptors

Automatic retries are strictly restricted to **idempotent HTTP methods** (`GET`, `HEAD`, `OPTIONS`, `PUT`, `DELETE`). Non-idempotent operations (`POST`) are never retried automatically without an `Idempotency-Key` header.

```dart
class NetworkClient {
  late final Dio dio;

  NetworkClient(SecureTokenStorage tokenStorage) {
    dio = Dio(BaseOptions(
      baseUrl: AppConfig.apiBaseUrl,
      connectTimeout: const Duration(seconds: 10),
      receiveTimeout: const Duration(seconds: 15),
      headers: {'Accept': 'application/json'},
    ));

    // QueuedInterceptor prevents multiple concurrent 401s from issuing duplicate token refreshes
    dio.interceptors.add(QueuedAuthInterceptor(tokenStorage, dio));

    // Safe Retry Interceptor
    dio.interceptors.add(
      RetryInterceptor(
        dio: dio,
        retries: 3,
        retryDelays: const [Duration(seconds: 1), Duration(seconds: 2), Duration(seconds: 4)],
        retryEvaluator: (error) {
          final isNetworkError = error.type == DioExceptionType.connectionTimeout ||
                                 error.type == DioExceptionType.connectionError;
          final requestMethod = error.requestOptions.method.toUpperCase();
          final isIdempotentMethod = const ['GET', 'HEAD', 'OPTIONS', 'PUT', 'DELETE'].contains(requestMethod);
          final hasIdempotencyKey = error.requestOptions.headers.containsKey('Idempotency-Key');

          return isNetworkError && (isIdempotentMethod || hasIdempotencyKey);
        },
      ),
    );
  }
}
```

---

## 6. Platform-Safe SMS Handoff

To prevent malformed URI errors and missing handler crashes across devices:

```dart
class SystemComposerSmsService implements SmsService {
  @override
  Future<bool> openSmsComposer({
    required String recipient,
    String? initialText,
  }) async {
    final cleanNumber = recipient.replaceAll(RegExp(r'[^\d+]'), '');
    
    // Platform-specific parameter formatting
    final uriString = Platform.isIOS
        ? 'sms:$cleanNumber${initialText != null ? '&body=${Uri.encodeComponent(initialText)}' : ''}'
        : 'sms:$cleanNumber${initialText != null ? '?body=${Uri.encodeComponent(initialText)}' : ''}';

    final uri = Uri.parse(uriString);

    if (await canLaunchUrl(uri)) {
      return await launchUrl(uri, mode: LaunchMode.externalApplication);
    } else {
      // Graceful fallback: Device has no SMS application (e.g. WiFi-only tablet)
      return false;
    }
  }
}
```

---

## 7. Local Storage Encryption & Security Lifecycle

```
┌────────────────────────────────────────────────────────┐
│                   Secure Storage                       │
│      (iOS Keychain / Android EncryptedKeystore)        │
│  - JWT Access Token & Refresh Token                    │
│  - 256-bit AES Master Database Key                     │
└──────────────────────────┬─────────────────────────────┘
                           │ Decrypts on launch
┌──────────────────────────▼─────────────────────────────┐
│                 Encrypted Local Cache                  │
│       (Hive.openBox(..., encryptionCipher: AES))       │
│  - Cached Conversations & Messages                     │
│  - Cached Contacts & Favorites                         │
│  - Outbound Cellular Call Log Attempts                 │
└────────────────────────────────────────────────────────┘
```

- **Encryption Implementation:** Encrypted boxes are opened via `Hive.openBox('messages_box', encryptionCipher: HiveAesCipher(masterKey))`.
- **Data Purge on Logout:** All encrypted Hive boxes are physically deleted from disk via `Hive.deleteBoxFromDisk()`, and tokens and keys in `FlutterSecureStorage` are wiped.

---

## 8. Design System Tokens (PRD v1.2 Match)

```dart
import 'package:flutter/material.dart';

abstract final class AppColors {
  // Brand Colors
  static const Color primaryNavy     = Color(0xFF061A3D); // Active call & Splash background
  static const Color primaryBlue     = Color(0xFF0866E8); // Action buttons & sent chat bubbles
  static const Color deepBlue        = Color(0xFF00358F); // Gradient secondary stop
  static const Color cyanAccent      = Color(0xFF00CFC8); // Cyan wave artwork & online badges

  // Actions & States
  static const Color successGreen    = Color(0xFF21C878); // Call button & online dots
  static const Color errorRed        = Color(0xFFFF4545); // End call button & backspace
  static const Color warningAmber    = Color(0xFFF59E0B);

  // Surfaces & Backgrounds
  static const Color background      = Color(0xFFF7FAFE); // App scaffold background
  static const Color surface         = Color(0xFFFFFFFF); // Card & list tile surfaces
  static const Color surfaceMuted    = Color(0xFFF1F5F9); // Search bar & received bubbles
  static const Color activeCallBg    = Color(0xFF061A3D); // Navy dark VoIP call screen

  // Typography
  static const Color textPrimary     = Color(0xFF071A3D); // Headings & contact titles
  static const Color textSecondary   = Color(0xFF697B99); // Subtitles, timestamps, legends
  static const Color border          = Color(0xFFE3EAF4); // Card outlines & thin dividers
  
  static const LinearGradient primaryGradient = LinearGradient(
    colors: [primaryBlue, deepBlue],
    begin: Alignment.topLeft,
    end: Alignment.bottomRight,
  );
}
```

---

## 9. Implementation Readiness Matrix

| Component | Status | Next Immediate Action |
|---|---|---|
| **Design Tokens & Theme** | Ready to Build | Create `app_colors.dart` and `app_theme.dart`. |
| **Splash & Onboarding UI** | Ready to Build | Build PageView slider and contextual permission screens. |
| **Dialer & 4-Tab Navigation** | Ready to Build | Implement `StatefulShellRoute` with 3x4 dialpad and wallet badge. |
| **Contacts & Recents UI** | Ready to Build | Build with A-Z fast scroller using mock repository. |
| **Messages & 1-on-1 Chat UI** | Ready to Build | Build chat feed, custom bubbles, and composer with mock data. |
| **Navy Active Call Screen UI** | Ready to Build | Build full-screen call UI driven by `MockVoipService`. |
| **Native Cellular & SMS Handoff** | Ready to Prototype | Implement `tel:` launcher and platform-safe `sms:` service. |
| **Production VoIP Integration** | Integration Blocked | **Confirm backend provider (Twilio vs WebRTC vs LiveKit)**. |
| **Real-Time WebSocket Chat** | Integration Blocked | **Confirm backend WebSocket protocol & message schemas**. |

---

## 10. The Decisive Next Step

The UI and native handoffs are completely unblocked and can be built immediately against mock repositories without waiting for backend readiness.

To unblock the production communication integrations:
> **Pivotal Question for Backend Team:** Has your backend developer already implemented a VoIP provider (e.g., Twilio Voice, LiveKit, or WebRTC signaling) and a WebSocket chat service, or does the existing backend currently provide only REST APIs?
