Building a Local SMS Sync Tool for Samsung Devices Without Cloud Credentials

Over the past development session, I encountered a common problem in ops engineering: needing to read SMS messages from a business line without maintaining Twilio credentials in every environment. The solution involved building a local SMS sync daemon that bridges Android devices to macOS, eliminating dependency on cloud SMS infrastructure for routine message ingestion.

The Problem Statement

The existing SMS infrastructure relied on Twilio for inbox reads and message delivery. However, maintaining API credentials across development machines, CI/CD pipelines, and local tooling introduces security friction and operational overhead. Additionally, a business SMS line (the Jada business account at +6199867344) needed to be accessible locally without requiring Twilio SID/auth token pairs in every `.secrets/repos.env` file.

The constraint was clear: build a solution that works "without Twilio" by leveraging the Android device itself as the source of truth for SMS data.

Architecture: Local Android-to-Mac Bridge

Rather than pulling from cloud APIs, the approach uses Android Debug Bridge (ADB) to query the device's native SMS database directly. This follows the principle of "source-of-truth locality"—the SMS records live on the device; we simply transport them to the development environment for processing.

File Structure

  • /Users/cb/Documents/repos/tools/samsung_sms_sync.py — Main sync script that queries device SMS via ADB
  • /Users/cb/Library/LaunchAgents/com.cb.samsung-sms-sync.plist — macOS LaunchAgent config for daemon scheduling
  • /Users/cb/Documents/repos/.secrets/repos.env — Environment variables (does NOT require Twilio creds for local sync)

Dependencies Installed

brew install android-platform-tools

This provides the adb binary, which communicates with Samsung devices via USB. The tool queries the device's /data/data/com.android.providers.telephony/databases/ directory for SMS records.

Technical Implementation Details

Samsung SMS Sync Script

The script (samsung_sms_sync.py) handles three core operations:

  1. Device Detection: Uses adb devices to verify the Samsung device is connected and authorized.
  2. SMS Database Query: Executes adb shell commands to query the device's SMS content provider, extracting phone numbers, message bodies, timestamps, and read status.
  3. Local Persistence: Stores extracted messages in a local SQLite database or JSON export file for downstream processing.
  4. Digest Generation: Parses threads by phone number and generates markdown summaries that can be emailed or logged.

Key function responsibilities:

  • detect_device() — Confirms ADB connection and device authorization status
  • query_sms_database(phone_filter=None) — Runs ADB queries against the device's SMS content provider; optional filter for specific phone numbers
  • parse_thread(phone_number) — Extracts all messages for a given phone number, sorts by timestamp, and structures as conversation objects
  • generate_digest(threads) — Converts thread objects into human-readable markdown with timestamps, sender, and message content
  • send_digest_email(digest, recipient) — Uses SES to mail the digest (credentials pulled from `repos.env`)

LaunchAgent Scheduling

The plist file (com.cb.samsung-sms-sync.plist) configures macOS to run the sync script on a scheduled interval. This ensures SMS records are regularly imported without manual intervention.

<key>StartInterval</key>
<integer>3600</integer>

This example runs the sync every 3600 seconds (1 hour). The agent logs output to a local file for debugging:

<key>StandardOutPath</key>
<string>/var/log/samsung-sms-sync.log</string>
<key>StandardErrorPath</key>
<string>/var/log/samsung-sms-sync-error.log</string>

Key Design Decisions

Why ADB Instead of Twilio?

Twilio requires active account credentials and incurs per-message costs. ADB queries are free—they're local filesystem reads. For a business running multiple SMS lines, eliminating credential management complexity and per-API-call overhead justifies the USB dependency.

Why Local Persistence?

Storing messages in a local SQLite database (or JSON export) serves two purposes: (1) deduplication—we can check for new messages since the last sync run, and (2) offline access—if the device disconnects, recent message threads remain queryable.

Why Email Digests?

Rather than forcing users to parse raw SMS exports, the script generates conversation digests grouped by phone number. These are emailed via SES (AWS Simple Email Service), keeping the notification in the user's existing inbox workflow. SES credentials are pulled from repos.env, which is standard for AWS integrations.

Why LaunchAgent Over Cron?

LaunchAgent respects macOS security frameworks (Gatekeeper, TCC) and integrates with the system's unified scheduling. It also gracefully handles user logout/login cycles and provides better logging integration.

Infrastructure & Environment Configuration

The script requires minimal configuration:

# In ~/.secrets/repos.env
AWS_REGION=us-west-2
AWS_SES_SENDER=ops@yourdomain.com
SMS_DIGEST_RECIPIENT=user@yourdomain.com

AWS credentials (for SES) should be in the standard `~/.aws/credentials` file or environment variables. This decouples SMS read operations (which run locally) from email dispatch (which requires AWS).

Operational Workflow

  1. Engineer connects Samsung device via USB and authorizes ADB debugging.
  2. LaunchAgent triggers sync script on schedule (e.g., hourly).
  3. Script queries device SMS database using adb shell content query commands.
  4. New messages are parsed and deduplicated against local database.
  5. Digest is generated and emailed to configured recipient.
  6. Logs are written to `/var/log/samsung-sms-sync*.log` for monitoring.

What's Next

  • Multi-Device Support: Extend the script to handle multiple Samsung devices connected via ADB, with per-device sync status tracking.
  • Message Filtering: Add command-line flags to filter digests by date range, phone number, or keyword patterns.
  • Slack Integration: Route digests to Slack channels instead of (or in addition to) email for team visibility.
  • Outbound SMS: Build a companion script that uses the device as an SMS gateway for sending messages, eliminating Twilio dependency entirely for small-volume operations.