Clone: git clone https://github.com/moses-y/android-sms-gateway
The Problem
Many teams need to send and receive SMS programmatically without depending on a third‑party cloud service or incurring carrier‑level latency. On‑premises or local‑network deployments are required for privacy‑sensitive or offline environments, but most off‑the‑shelf solutions either require internet access or lack a simple API surface.
What This Does
SMS Gateway for Android™ turns a smartphone into a local SMS gateway.
- Core API lives under
app/src/main/assets/api/– the web UI (index.html) and Swagger assets expose endpoints for sending messages, retrieving inbox state, and configuring the gateway. - Local‑server mode runs a Kotlin HTTP server inside the app (
modules/localserver/WebService.kt,modules/localserver/routes/*). It listens on the device’s IP and services/messages,/auth,/webhooks, etc. - Cloud‑fallback is optional: when direct device access isn’t possible, the same API can be routed through a cloud‑hosted instance (the repo references this pattern in the README).
- Message handling includes multipart partitioning, status tracking, and webhook delivery for incoming SMS/MMS (
modules/messages/MessagesService.kt,modules/webhooks/EventsReceiver.kt,modules/receiver/MmsReceiver.kt). - Security is provided via JWT auth scopes (
modules/localserver/auth/JwtService.kt,modules/localserver/auth/ScopeAuthorization.kt) and configurable encryption (modules/encryption/EncryptionSettings.kt).
The codebase is organised into ~300 Kotlin source files under app/, with Gradle‑managed dependencies and a Gradle wrapper (gradlew).
How It Is Wired
Execution starts at the Android entry point app/src/main/java/me/capcom/smsgateway/MainActivity.kt, which bootstraps the App.kt module. From there:
- Local server startup –
modules/localserver/WebService.ktcreates an embedded Ktor‑style server (indicated by the routes defined inAuthRoutes.kt,MessagesRoutes.kt, etc.). The server binds to0.0.0.0:8080(configurable) and begins accepting HTTP requests. - SMS receive – the
modules/receiver/package registers aBroadcastReceiver(MmsReceiver.kt) that forwards incoming messages to the in‑memory store (modules/receiver/data/InboxMessage.kt). A background worker (SendMessagesWorker.kt) persists outgoing requests to the database (AppDatabase.kt). - API routing – inbound HTTP calls hit the route handlers (
MessagesRoutes.kt,WebhooksRoutes.kt), which interact with the data layer (MessagesRepository.kt,MessagesDao.kt) and the entity model (Message.kt,MessageRecipient.kt). - Outbound sending –
modules/messages/MessagesService.ktexposes asendMessagefunction that writes aStoredSendRequestto the DB and triggers theSendMessagesWorkerworker.
The internal import graph contains 2 modules with 0 circular dependencies and no import edges, meaning the system is largely flat; however, the three “high cognitive‑load” files (WebService.kt, MessagesService.kt, EventsReceiver.kt) each have indentation depth up to 9, making control flow hard to follow.
How To Use It
Setup
- Build the APK:
./gradlew assembleDebug(Gradle wrapper present at the repo root). - Install on an Android 5.0+ device.
- Enable “Unknown sources” and grant the required permissions (
android.permission.SEND_SMS,android.permission.RECEIVE_SMS,android.permission.ACCESS_NETWORK_STATE) – seeapp/src/main/AndroidManifest.xml.
Configuration
- Edit
app/src/main/assets/api/swagger.jsonto expose or hide endpoints. - Adjust local‑server bind address and port in
modules/localserver/LocalServerSettings.kt. - Set JWT secret/key in
modules/localserver/auth/JwtService.kt(the file contains aSECRET_KEYconstant; replace with a strong value for production).
Running it
- After installation, open the built‑in UI at
http://<device‑ip>:8080(or the custom port). The Swagger UI (index.html) lists all available operations. - To send a message via CLI or external tool, POST JSON to
/api/messageswith fieldsrecipientandbody. The gateway will store the request and dispatch it through the cellular modem. - Incoming SMS trigger the webhook defined in
modules/webhooks/WebHooksService.kt; configure the target URL inmodules/webhooks/WebhooksSettings.kt.
Real‑World Use
A field‑engineering team can deploy this app on a rugged Android device located in a remote site without internet. The device’s local API allows a central monitoring system to poll GET /api/messages for status updates and receive webhook POSTs when a new SMS arrives, eliminating the need for a carrier‑hosted gateway. Because the stack runs entirely on‑device, latency is minimal and data never leaves the local network unless the admin opts into the cloud‑fallback mode.
Code Health & Issues
- Deep nesting – 41 files exhibit indentation depth 9 (e.g.,
WebService.kt,MessagesService.kt,EventsReceiver.kt). Flatten with early returns/guard clauses. - Duplicated code – 66 repeated 6‑line blocks appear across 30 files including
MessagesDao.kt,MessagesService.kt,EncryptionSettings.kt,LogsSettings.kt. Extract shared helpers to DRY the logic. - High branching density – 4 files (
Converters.kt,InboxMessage.kt,LocalIPProvider.kt) have 18 branch points over 41 lines. Decompose decision logic into a strategy/table‑driven dispatch. - SDLC observations
- Pin third‑party GitHub Actions to a commit SHA –
.github/workflowsuse@v3tags (peaceiris/actions-gh-pages,softprops/action-gh-release). Replace with 40‑character SHAs to avoid secret leakage from tag moves. - Declare least‑privilege GITHUB_TOKEN – 2 workflows (
discord-release-notify.yml, etc.) have nopermissionsblock; addcontents: readat the top and widen per‑job as needed. - Expand the test suite – only 5 test files exist against 189 source files (coverage ≈ 2.6 %). Add tests for the highest fan‑in modules first.
- Set job timeouts – 4 workflows (
build-apk.yml, etc.) lacktimeout-minutes; add realistic bounds (e.g.,timeout-minutes: 60) to prevent runaway jobs.
The Bottom Line
This repo delivers a functional, on‑device SMS gateway with a clean API surface and optional cloud fallback, making it suitable for offline or privacy‑critical deployments. The codebase is sizable (≈300 Kotlin files) but suffers from deep nesting, duplicated logic, and sparse test coverage. The CI hygiene issues (untagged Actions, missing permissions, absent timeouts) are straightforward to remediate. It’s a good fit for teams that need a self‑hosted SMS pipe without external dependencies, provided they invest in flattening the control flow and expanding the test net.