# Flash Call OTP System — Permissions Guide ## Table of Contents 1. [Android Caller App Permissions](#1-android-caller-app-permissions) 2. [Flutter Receiver App Permissions](#2-flutter-receiver-app-permissions) 3. [Server-Side Security](#3-server-side-security) 4. [iOS-Specific Notes](#4-ios-specific-notes) 5. [Permission Request Flow](#5-permission-request-flow) --- ## 1. Android Caller App Permissions ### CALL_PHONE - **Permission String**: `android.permission.CALL_PHONE` - **Protection Level**: Dangerous - **Required Since**: API 1 - **Purpose**: Required to programmatically place phone calls without user interaction. The app uses `ACTION_CALL` with `Uri.parse("tel:$phone")` to automatically dial the target number and hang up after 2.5 seconds. - **Why Not ACTION_DIAL**: `ACTION_DIAL` only opens the dialer and requires the user to tap the call button. Flash call verification must be completely automatic — no user interaction. - **Android Version Notes**: - API 26-27 (Android 8.0-8.1): Standard dangerous permission, requested at runtime - API 28+ (Android 9+): Works as standard dangerous permission - If denied: The app cannot make flash calls. The service will report "failed" for all call tasks. ### READ_PHONE_STATE - **Permission String**: `android.permission.READ_PHONE_STATE` - **Protection Level**: Dangerous - **Required Since**: API 1 - **Purpose**: Required to read telephony state information including call state, network type, and SIM information. Used to detect when a call is active so the app knows when to hang up. - **Android Version Notes**: - API 26-28 (Android 8-9): Standard dangerous permission - API 29+ (Android 10+): More restricted; the app only needs this for call state detection - If denied: Call state detection may not work, but the 2.5-second timer still hangs up reliably ### SEND_SMS - **Permission String**: `android.permission.SEND_SMS` - **Protection Level**: Dangerous - **Required Since**: API 1 - **Purpose**: Required to send SMS messages containing OTP codes to iOS users. When the backend has an SMS delivery task, the Caller App sends the OTP via SMS using `SmsManager`. - **Why Needed**: iOS devices cannot receive flash calls for automatic OTP extraction. The system falls back to SMS delivery, and the Caller Android device acts as the SMS sender. - **Android Version Notes**: - API 26-30 (Android 8-11): Uses `SmsManager.getDefault()` - API 31+ (Android 12+): Uses `context.getSystemService(SmsManager::class.java)` for per-subscription SMS handling - If denied: SMS tasks will fail. Flash calls for Android devices still work. ### FOREGROUND_SERVICE - **Permission String**: `android.permission.FOREGROUND_SERVICE` - **Protection Level**: Normal - **Required Since**: API 26 (Android 8.0) - **Purpose**: Required to run a foreground service that continuously polls the backend for pending tasks. The service must be a foreground service (not a background service) to avoid being killed by the system. - **Android Version Notes**: - API 26+ (Android 8.0+): Mandatory for any long-running service - Without this: The service would be killed within a few minutes by the system ### FOREGROUND_SERVICE_PHONE_CALL - **Permission String**: `android.permission.FOREGROUND_SERVICE_PHONE_CALL` - **Protection Level**: Normal - **Required Since**: API 34 (Android 14) - **Purpose**: Specifies the foreground service type as "phone call". Required on Android 14+ for services that place phone calls. - **Android Version Notes**: - API 34+ (Android 14): Must be declared in manifest AND specified in `startForeground()` call - API 26-33: Not required, but harmless to declare - Without this on Android 14: The foreground service will crash with SecurityException ### RECEIVE_BOOT_COMPLETED - **Permission String**: `android.permission.RECEIVE_BOOT_COMPLETED` - **Protection Level**: Normal - **Required Since**: API 1 - **Purpose**: Allows the app to receive the `BOOT_COMPLETED` broadcast and automatically restart the CallerService after the device reboots. Without this, the user would have to manually open the app and start the service after every restart. - **Android Version Notes**: - All versions: Works as a normal permission (auto-granted) - Note: Some OEMs (Xiaomi, Huawei, Oppo) restrict auto-start by default. Users must enable "Auto-start" in security settings. ### REQUEST_IGNORE_BATTERY_OPTIMIZATIONS - **Permission String**: `android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS` - **Protection Level**: Normal - **Required Since**: API 23 (Android 6.0) - **Purpose**: Prompts the user to add the app to the battery optimization whitelist. This is critical because Android's Doze mode and app standby can kill the foreground service, preventing the device from receiving OTP tasks. - **How It Works**: The app sends an `ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS` intent with the app's package URI. The system shows a dialog asking the user to allow the app to ignore battery optimization. - **Android Version Notes**: - API 23+ (Android 6.0+): Works as normal permission with system dialog - Some OEMs: May require additional steps (e.g., Xiaomi: Security → Autostart; Samsung: Device Care → Battery → App Power Management) - Without this: The service may be killed after 10-30 minutes of screen-off time ### INTERNET - **Permission String**: `android.permission.INTERNET` - **Protection Level**: Normal - **Required Since**: API 1 - **Purpose**: Required for all network communication — polling the backend API and reporting call/SMS results. - **Note**: Automatically granted on all Android versions. --- ## 2. Flutter Receiver App Permissions ### READ_CALL_LOG (Android Only) - **Permission String**: `android.permission.READ_CALL_LOG` - **Protection Level**: Dangerous - **Required Since**: API 16 - **Purpose**: Required to read the device's call log and detect incoming missed calls from the flash call. The app queries the call log every 1.5 seconds looking for missed calls since the OTP was requested. - **How OTP Extraction Works**: When a flash call comes in, it appears as a missed call in the call log. The app reads the caller's phone number (e.g., +962791234829) and extracts the last 4 digits (4829) as the OTP code. - **Android Version Notes**: - API 16-22 (Android 4.1-5.1): Granted at install time - API 23+ (Android 6.0+): Must be requested at runtime - If denied: Automatic OTP detection will not work. User must manually enter the OTP code via the fallback input. ### READ_PHONE_STATE (Android Only) - **Permission String**: `android.permission.READ_PHONE_STATE` - **Protection Level**: Dangerous - **Required Since**: API 1 - **Purpose**: Companion permission for READ_CALL_LOG. Some Android versions require both permissions to properly access call log data. - **Android Version Notes**: - API 29+ (Android 10+): More restricted; may need to request both READ_CALL_LOG and READ_PHONE_STATE together - If denied: Call log access may fail on some devices ### No Extra Permissions on iOS - iOS does not allow reading call logs or SMS messages programmatically - SMS AutoFill works automatically through `AutofillHints.oneTimeCode` without any special permissions - The only requirement is that the SMS follows Apple's format with `@domain #code` at the end --- ## 3. Server-Side Security ### API Key Authentication - **APP_KEY**: Used by the Flutter Receiver App for `request-otp` and `verify-otp` endpoints - **DEVICE_KEY**: Used by the Android Caller App for `pending-call`, `pending-sms`, `call-done`, `sms-done`, and `register-device` endpoints - **Implementation**: Uses `hash_equals()` for timing-safe comparison to prevent timing attacks - **Key Generation**: Use `openssl rand -hex 32` to generate cryptographically secure keys ### Rate Limiting - **Per-Phone Limit**: Max 3 OTP requests per phone number per 10-minute window - **Per-IP Limit**: Max 30 requests per IP per minute for `request-otp` - **General API**: Max 60 requests per IP per minute across all endpoints - **Implementation**: Redis-based with atomic increment operations and TTL-based expiration ### Input Validation - **Phone Format**: E.164 format validation (e.g., +9627XXXXXXXX) — regex: `/^\+[1-9]\d{6,14}$/` - **OTP Format**: Exactly 4 digits — regex: `/^\d{4}$/` - **Device ID**: Length between 5-50 characters - **SQL Injection**: All queries use PDO prepared statements - **XSS**: JSON-only responses (no HTML rendering) ### Data Protection - OTP codes stored in Redis with 120-second TTL (auto-expire) - OTP codes in MySQL are for audit purposes only - Failed verification attempts tracked in Redis (max 5 attempts) - Request logging masks sensitive fields (app_key, otp, password) - API logs auto-cleaned after 30 days --- ## 4. iOS-Specific Notes ### Why Flash Call Doesn't Work on iOS Apple restricts access to the call log through `CoreTelephony` framework. There is no public API to: - Read incoming/outgoing call history - Get caller ID from recent calls programmatically - Detect missed calls in real-time This is a deliberate privacy decision by Apple. The workaround is to use SMS delivery with AutoFill. ### SMS AutoFill Mechanism iOS can automatically suggest OTP codes from SMS messages in the keyboard suggestion bar. This requires: 1. **Correct SMS format**: The message must include the domain and code at the end: ``` رمز التحقق في Intaleq هو 4829 لا تشاركه مع أحد. @otp.intaleqapp.com #4829 ``` 2. **Associated Domains**: The app must have `otp.intaleqapp.com` as an associated domain in its entitlements (for the `#code` to be recognized) 3. **Text field configuration**: The OTP input must have `autofillHints: [AutofillHints.oneTimeCode]` and `keyboardType: TextInputType.number` ### iOS AutoFill Requirements | Requirement | Details | |-------------|---------| | SMS format | Must end with `@domain #code` | | Associated domain | Configured in Xcode → Signing & Capabilities | | Text field | `AutofillHints.oneTimeCode` + number keyboard | | Real device | AutoFill does not work on simulators | | SMS sender | Must come from a phone number (not alphanumeric) | --- ## 5. Permission Request Flow ### Android Caller App — First Launch ``` App Launched │ ▼ ┌─────────────────────────┐ │ PermissionHelper │ │ .requestAllPermissions()│ └──────────┬──────────────┘ │ ▼ ┌──────────────┐ │ CALL_PHONE │──── Denied ──→ Show rationale dialog │ │ │ │ │──── Granted ──→ Continue └──────┬───────┘ │ ▼ ┌──────────────┐ │READ_PHONE_STATE│── Denied ──→ Show rationale dialog │ │ │ │ │── Granted ──→ Continue └──────┬───────┘ │ ▼ ┌──────────────┐ │ SEND_SMS │──── Denied ──→ Show rationale dialog │ │ │ │ │──── Granted ──→ Continue └──────┬───────┘ │ ▼ ┌──────────────────────────────────┐ │ Battery Optimization Exemption │ │ (Intent to system settings) │ │ │ │ User selects "Don't optimize" │ └──────────────────────────────────┘ │ ▼ Ready for Registration ``` ### Flutter Receiver App — OTP Flow ``` User taps "Send OTP" │ ▼ ┌─────────────────────────┐ │ Platform check │ └──────┬──────────────────┘ │ ├──── Android ──→ Check READ_CALL_LOG permission │ │ │ ├── Granted ──→ Start call log polling │ │ │ ├── Denied ──→ Show rationale: │ │ "نحتاج صلاحية قراءة سجل المكالمات │ │ للتحقق التلقائي دون الحاجة لإدخال أي كود" │ │ │ ├── Permanently Denied ──→ Redirect to Settings │ │ + Show manual OTP input fallback │ │ │ └── Still Denied ──→ Show manual OTP input │ └──── iOS ──→ Skip call log entirely │ Show manual 4-digit input with AutofillHints.oneTimeCode │ iOS keyboard shows SMS code suggestion automatically ``` ### Arabic Permission Explanations | Permission | Arabic Explanation | |-----------|-------------------| | CALL_PHONE | "صلاحية إجراء المكالمات مطلوبة للاتصال التلقائي بأرقام التحقق" | | READ_PHONE_STATE | "صلاحية قراءة حالة الهاتف مطلوبة لإدارة المكالمات" | | SEND_SMS | "صلاحية إرسال الرسائل مطلوبة للتحقق من مستخدمي iOS" | | READ_CALL_LOG | "نحتاج صلاحية قراءة سجل المكالمات للتحقق التلقائي دون الحاجة لإدخال أي كود" | | Battery Optimization | "يجب تعطيل تحسين البطارية لضمان عمل الخدمة بشكل مستمر" | --- ## Appendix: Permission Summary by Component | Permission | Caller App | Receiver App | Type | Min API | |-----------|:----------:|:------------:|------|---------| | CALL_PHONE | ✅ | ❌ | Dangerous | 1 | | READ_PHONE_STATE | ✅ | ✅ | Dangerous | 1 | | SEND_SMS | ✅ | ❌ | Dangerous | 1 | | READ_CALL_LOG | ❌ | ✅ | Dangerous | 16 | | FOREGROUND_SERVICE | ✅ | ❌ | Normal | 26 | | FOREGROUND_SERVICE_PHONE_CALL | ✅ | ❌ | Normal | 34 | | RECEIVE_BOOT_COMPLETED | ✅ | ❌ | Normal | 1 | | REQUEST_IGNORE_BATTERY_OPTIMIZATIONS | ✅ | ❌ | Normal | 23 | | INTERNET | ✅ | ✅ | Normal | 1 |