Files
flash-call-otp/docs/PERMISSIONS.md
2026-05-23 16:17:20 +03:00

14 KiB

Flash Call OTP System — Permissions Guide

Table of Contents

  1. Android Caller App Permissions
  2. Flutter Receiver App Permissions
  3. Server-Side Security
  4. iOS-Specific Notes
  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