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:

  1. Local server startup – modules/localserver/WebService.kt creates an embedded Ktor‑style server (indicated by the routes defined in AuthRoutes.kt, MessagesRoutes.kt, etc.). The server binds to 0.0.0.0:8080 (configurable) and begins accepting HTTP requests.
  2. SMS receive – the modules/receiver/ package registers a BroadcastReceiver (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).
  3. 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).
  4. Outbound sending – modules/messages/MessagesService.kt exposes a sendMessage function that writes a StoredSendRequest to the DB and triggers the SendMessagesWorker worker.

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) – see app/src/main/AndroidManifest.xml.

Configuration

  • Edit app/src/main/assets/api/swagger.json to 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 a SECRET_KEY constant; 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/messages with fields recipient and body. 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 in modules/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/workflows use @v3 tags (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 no permissions block; add contents: read at 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.) lack timeout-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.