326 lines
12 KiB
Markdown
326 lines
12 KiB
Markdown
# Textpipe SMS API Gateway - Implementation Plan
|
|
|
|
## Overview
|
|
|
|
Build an Android SMS API Gateway with an embedded HTTP server (Ktor), dual SIM support, local message database, and background service.
|
|
|
|
---
|
|
|
|
## Architecture
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────────────────┐
|
|
│ MainActivity │
|
|
│ (Compose UI + Permissions) │
|
|
└─────────────────────────────────────────────────────────────────┘
|
|
│
|
|
▼
|
|
┌─────────────────────────────────────────────────────────────────┐
|
|
│ TextpipeService │
|
|
│ (Foreground Service + Lifecycle) │
|
|
│ ┌──────────────────────────────────────────────────────────┐ │
|
|
│ │ TextpipeServer (Ktor CIO) │ │
|
|
│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────────┐ │ │
|
|
│ │ │ /api/status │ │/api/sms/send│ │/api/sms/messages│ │ │
|
|
│ │ └─────────────┘ └─────────────┘ └─────────────────┘ │ │
|
|
│ └──────────────────────────────────────────────────────────┘ │
|
|
└─────────────────────────────────────────────────────────────────┘
|
|
│ │ │
|
|
▼ ▼ ▼
|
|
┌─────────────┐ ┌─────────────┐ ┌─────────────────┐
|
|
│ SimManager │ │ SmsSender │ │ SmsRepository │
|
|
│ (API Keys) │ │ (SmsManager)│ │ (Room + Send) │
|
|
└─────────────┘ └─────────────┘ └─────────────────┘
|
|
│ │
|
|
▼ ▼
|
|
┌─────────────────────────────────┐
|
|
│ AppDatabase (Room) │
|
|
│ sms_messages table │
|
|
└─────────────────────────────────┘
|
|
```
|
|
|
|
---
|
|
|
|
## Package Structure
|
|
|
|
```
|
|
sh.sar.textpipe/
|
|
├── MainActivity.kt (modify)
|
|
├── TextpipeApplication.kt (create)
|
|
├── data/
|
|
│ ├── db/
|
|
│ │ ├── AppDatabase.kt
|
|
│ │ ├── SmsMessageDao.kt
|
|
│ │ └── SmsMessageEntity.kt
|
|
│ ├── model/
|
|
│ │ └── ApiModels.kt
|
|
│ └── repository/
|
|
│ └── SmsRepository.kt
|
|
├── server/
|
|
│ ├── TextpipeServer.kt
|
|
│ ├── auth/
|
|
│ │ └── ApiKeyAuth.kt
|
|
│ └── routes/
|
|
│ ├── SmsRoutes.kt
|
|
│ └── StatusRoutes.kt
|
|
├── service/
|
|
│ ├── TextpipeService.kt
|
|
│ ├── BootReceiver.kt
|
|
│ ├── SmsReceiver.kt
|
|
│ └── SmsSender.kt
|
|
├── sim/
|
|
│ └── SimManager.kt
|
|
├── root/
|
|
│ └── RootManager.kt
|
|
└── ui/
|
|
├── MainScreen.kt
|
|
└── MainViewModel.kt
|
|
```
|
|
|
|
---
|
|
|
|
## API Endpoints
|
|
|
|
### Authentication
|
|
- API key in header: `Authorization: Bearer <key>` or `X-API-Key: <key>`
|
|
- Each SIM slot has its own unique API key (auto-generated, persistent)
|
|
- API key determines which SIM sends the message
|
|
|
|
### Endpoints
|
|
|
|
| Method | Path | Auth | Description |
|
|
|--------|------|------|-------------|
|
|
| GET | `/api/status` | No | Server health, SIM info, uptime |
|
|
| POST | `/api/sms/send` | Yes | Send SMS |
|
|
| GET | `/api/sms/messages` | Yes | List messages (optional `?type=sent\|received`) |
|
|
| GET | `/api/sms/status/{id}` | Yes | Get specific message status |
|
|
|
|
### Request/Response Examples
|
|
|
|
**POST /api/sms/send**
|
|
```json
|
|
// Request
|
|
{ "to": "+1234567890", "text": "Hello World" }
|
|
|
|
// Response
|
|
{ "id": 1, "status": "pending", "simSlot": 0 }
|
|
```
|
|
|
|
**GET /api/sms/messages**
|
|
```json
|
|
[
|
|
{
|
|
"id": 1,
|
|
"type": "sent",
|
|
"address": "+1234567890",
|
|
"text": "Hello World",
|
|
"simSlot": 0,
|
|
"status": "delivered",
|
|
"timestamp": 1707148800000,
|
|
"deliveryTimestamp": 1707148805000
|
|
}
|
|
]
|
|
```
|
|
|
|
**GET /api/status**
|
|
```json
|
|
{
|
|
"running": true,
|
|
"port": 8080,
|
|
"uptime": 3600000,
|
|
"sims": [
|
|
{ "slot": 0, "carrier": "Carrier1", "number": "+1...", "hasApiKey": true },
|
|
{ "slot": 1, "carrier": "Carrier2", "number": "+1...", "hasApiKey": true }
|
|
]
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Dependencies to Add
|
|
|
|
### libs.versions.toml additions
|
|
|
|
```toml
|
|
[versions]
|
|
ktor = "3.0.3"
|
|
room = "2.6.1"
|
|
kotlinxSerialization = "1.7.3"
|
|
datastorePreferences = "1.1.1"
|
|
ksp = "2.0.21-1.0.28"
|
|
coroutines = "1.9.0"
|
|
|
|
[libraries]
|
|
ktor-server-core = { group = "io.ktor", name = "ktor-server-core", version.ref = "ktor" }
|
|
ktor-server-cio = { group = "io.ktor", name = "ktor-server-cio", version.ref = "ktor" }
|
|
ktor-server-content-negotiation = { group = "io.ktor", name = "ktor-server-content-negotiation", version.ref = "ktor" }
|
|
ktor-serialization-kotlinx-json = { group = "io.ktor", name = "ktor-serialization-kotlinx-json", version.ref = "ktor" }
|
|
ktor-server-status-pages = { group = "io.ktor", name = "ktor-server-status-pages", version.ref = "ktor" }
|
|
ktor-server-cors = { group = "io.ktor", name = "ktor-server-cors", version.ref = "ktor" }
|
|
room-runtime = { group = "androidx.room", name = "room-runtime", version.ref = "room" }
|
|
room-ktx = { group = "androidx.room", name = "room-ktx", version.ref = "room" }
|
|
room-compiler = { group = "androidx.room", name = "room-compiler", version.ref = "room" }
|
|
kotlinx-serialization-json = { group = "org.jetbrains.kotlinx", name = "kotlinx-serialization-json", version.ref = "kotlinxSerialization" }
|
|
datastore-preferences = { group = "androidx.datastore", name = "datastore-preferences", version.ref = "datastorePreferences" }
|
|
kotlinx-coroutines-android = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-android", version.ref = "coroutines" }
|
|
|
|
[plugins]
|
|
kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }
|
|
ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" }
|
|
```
|
|
|
|
---
|
|
|
|
## Permissions Required
|
|
|
|
```xml
|
|
<!-- SMS -->
|
|
<uses-permission android:name="android.permission.SEND_SMS" />
|
|
<uses-permission android:name="android.permission.RECEIVE_SMS" />
|
|
<uses-permission android:name="android.permission.READ_SMS" />
|
|
|
|
<!-- Phone/SIM -->
|
|
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
|
|
<uses-permission android:name="android.permission.READ_PHONE_NUMBERS" />
|
|
|
|
<!-- Network -->
|
|
<uses-permission android:name="android.permission.INTERNET" />
|
|
|
|
<!-- Background Service -->
|
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE" />
|
|
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
|
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
|
|
|
<!-- Battery -->
|
|
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
|
|
|
|
<!-- Notifications -->
|
|
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
|
```
|
|
|
|
---
|
|
|
|
## Implementation Order
|
|
|
|
### Phase 0: Build Configuration
|
|
1. `gradle/libs.versions.toml` - Add all new dependencies
|
|
2. `build.gradle.kts` (root) - Add KSP and serialization plugins
|
|
3. `app/build.gradle.kts` - Add dependencies and plugins
|
|
4. `AndroidManifest.xml` - Add permissions, services, receivers
|
|
|
|
### Phase 1: Data Layer
|
|
5. `data/model/ApiModels.kt` - Request/response data classes
|
|
6. `data/db/SmsMessageEntity.kt` - Room entity
|
|
7. `data/db/SmsMessageDao.kt` - Database operations
|
|
8. `data/db/AppDatabase.kt` - Room database
|
|
|
|
### Phase 2: Core Managers
|
|
9. `sim/SimManager.kt` - SIM detection, API key management
|
|
10. `root/RootManager.kt` - Root access and iptables port redirect
|
|
|
|
### Phase 3: SMS Operations
|
|
11. `service/SmsSender.kt` - Send SMS with delivery tracking
|
|
12. `data/repository/SmsRepository.kt` - Combine database + sending
|
|
13. `service/SmsReceiver.kt` - Receive incoming SMS
|
|
|
|
### Phase 4: HTTP Server
|
|
14. `server/auth/ApiKeyAuth.kt` - API key validation
|
|
15. `server/routes/StatusRoutes.kt` - Health check endpoint
|
|
16. `server/routes/SmsRoutes.kt` - SMS API endpoints
|
|
17. `server/TextpipeServer.kt` - Ktor server configuration
|
|
|
|
### Phase 5: Service Layer
|
|
18. `service/TextpipeService.kt` - Foreground service
|
|
19. `service/BootReceiver.kt` - Auto-start on boot
|
|
20. `TextpipeApplication.kt` - Application class
|
|
|
|
### Phase 6: UI Layer
|
|
21. `ui/MainViewModel.kt` - UI state management
|
|
22. `ui/MainScreen.kt` - Compose UI
|
|
23. `MainActivity.kt` - Update with permissions and new UI
|
|
|
|
---
|
|
|
|
## Key Technical Decisions
|
|
|
|
### HTTP Server
|
|
- **Ktor CIO engine** (not Netty) - lighter weight for Android
|
|
- Server runs on configurable port (default 8080)
|
|
- For ports 80/443: Use iptables NAT redirect via `su` (don't run JVM as root)
|
|
|
|
### Dual SIM Support
|
|
- `SubscriptionManager.getActiveSubscriptionInfoList()` for SIM detection
|
|
- `SmsManager.createForSubscriptionId(subscriptionId)` for sending
|
|
- API key maps to subscription ID, not slot index
|
|
|
|
### SMS Delivery Tracking
|
|
- PendingIntents for sent confirmation and delivery reports
|
|
- Update database status: pending → sent → delivered/failed
|
|
- Multipart SMS via `sendMultipartTextMessage()`
|
|
|
|
### Background Persistence
|
|
- Foreground service with persistent notification
|
|
- `FOREGROUND_SERVICE_TYPE_SPECIAL_USE` for API 34+
|
|
- Boot receiver for auto-start
|
|
|
|
### API Key Storage
|
|
- SharedPreferences (not DataStore) for synchronous reads
|
|
- Key format: UUID, stored per SIM slot
|
|
- Generated on first run, persistent across app updates
|
|
|
|
---
|
|
|
|
## Files to Create/Modify (22 total)
|
|
|
|
| # | File | Action |
|
|
|---|------|--------|
|
|
| 1 | `gradle/libs.versions.toml` | Modify |
|
|
| 2 | `build.gradle.kts` (root) | Modify |
|
|
| 3 | `app/build.gradle.kts` | Modify |
|
|
| 4 | `app/src/main/AndroidManifest.xml` | Modify |
|
|
| 5 | `data/model/ApiModels.kt` | Create |
|
|
| 6 | `data/db/SmsMessageEntity.kt` | Create |
|
|
| 7 | `data/db/SmsMessageDao.kt` | Create |
|
|
| 8 | `data/db/AppDatabase.kt` | Create |
|
|
| 9 | `sim/SimManager.kt` | Create |
|
|
| 10 | `root/RootManager.kt` | Create |
|
|
| 11 | `service/SmsSender.kt` | Create |
|
|
| 12 | `data/repository/SmsRepository.kt` | Create |
|
|
| 13 | `service/SmsReceiver.kt` | Create |
|
|
| 14 | `server/auth/ApiKeyAuth.kt` | Create |
|
|
| 15 | `server/routes/StatusRoutes.kt` | Create |
|
|
| 16 | `server/routes/SmsRoutes.kt` | Create |
|
|
| 17 | `server/TextpipeServer.kt` | Create |
|
|
| 18 | `service/TextpipeService.kt` | Create |
|
|
| 19 | `service/BootReceiver.kt` | Create |
|
|
| 20 | `TextpipeApplication.kt` | Create |
|
|
| 21 | `ui/MainViewModel.kt` | Create |
|
|
| 22 | `ui/MainScreen.kt` | Create |
|
|
| 23 | `MainActivity.kt` | Modify |
|
|
|
|
---
|
|
|
|
## UI Features (Basic for now)
|
|
|
|
- Server status indicator (Running/Stopped)
|
|
- Port configuration input
|
|
- Start/Stop toggle button
|
|
- SIM cards list with:
|
|
- Carrier name
|
|
- Phone number (if available)
|
|
- API key with copy button
|
|
- Root port redirect toggle (if root available)
|
|
- Battery optimization exemption button
|
|
|
|
---
|
|
|
|
## Out of Scope (Deferred)
|
|
|
|
- WebUI for viewing messages in browser
|
|
- Rate limiting
|
|
- IP whitelisting
|
|
- HTTPS/TLS (beyond port redirect)
|
|
- Message scheduling
|
|
- Webhook callbacks
|