Building a Local SMS Sync Tool for Samsung Devices: Bridging Android Chat.db to macOS LaunchAgent Infrastructure
What Was Done
During this development session, I created a local SMS synchronization system that bridges Samsung/Android SMS databases directly to the macOS automation infrastructure, eliminating the need for Twilio credentials in development and testing scenarios. The solution consists of two primary components:
- A Python script (
samsung_sms_sync.py) that reads the Androidchat.dbSQLite database and processes SMS messages locally - A macOS LaunchAgent plist configuration (
com.cb.samsung-sms-sync.plist) that schedules periodic syncs without manual intervention
This approach allows SMS ingestion, filtering, and digest generation from local device backups or ADB connections without external service dependencies.
Technical Architecture and Implementation Details
The Chat Database Structure
Android devices (including Samsung) store SMS messages in a SQLite database typically located at /data/data/com.android.providers.telephony/databases/mmssms.db or accessible via Android Debug Bridge (ADB). The relevant schema includes:
smstable: Contains message body, sender/recipient phone numbers, timestamps, and read statusconversationstable: Aggregates messages by thread, tracking the most recent message and message count per conversationcanonical_addressestable: Maps phone numbers to normalized addresses for thread grouping
The script queries this structure to extract conversations in reverse chronological order by activity, making it trivial to identify hot items in the SMS backlog.
Python Script Design (samsung_sms_sync.py)
The core implementation handles several key responsibilities:
# Pseudo-structure of key functions:
def connect_to_chat_db(db_path):
"""
Opens the chat.db SQLite connection.
Typical path: /var/mobile/Library/SMS/chat.db (macOS)
or via ADB for Android devices
"""
def query_recent_conversations(conn, days=7):
"""
Executes SQL joining sms, conversations, and
canonical_addresses tables.
Returns list of dicts with phone, message_count,
last_message_date, last_message_body
"""
def extract_thread(conn, phone_number, line_limit=50):
"""
Retrieves full conversation thread for a given number.
Orders by timestamp, respects line limit for digest length.
Returns structured conversation with metadata.
"""
def generate_digest(conversations_list):
"""
Synthesizes multi-threaded SMS into human-readable summary.
Extracts action items, payment confirmations, equipment status.
Returns markdown formatted for email delivery.
"""
def send_digest_via_ses(digest_html, recipient_email):
"""
Uses boto3 SES client to deliver digest to inbox.
Assumes AWS_REGION and SES sender identity are pre-configured.
"""
The script avoids hardcoding paths by accepting environment variables:
SMS_DB_PATH=${SMS_DB_PATH:-/var/mobile/Library/SMS/chat.db}
DIGEST_RECIPIENT=${DIGEST_RECIPIENT:-user@example.com}
AWS_REGION=${AWS_REGION:-us-west-2}
macOS LaunchAgent Configuration
The LaunchAgent plist (/Users/cb/Library/LaunchAgents/com.cb.samsung-sms-sync.plist) manages scheduled execution:
Key configuration points:
- Label: com.cb.samsung-sms-sync (unique identifier for launchctl)
- StartInterval: 3600 (runs every hour; adjust as needed)
- StandardOutPath: logs/samsung_sms_sync.out (captures script output)
- StandardErrorPath: logs/samsung_sms_sync.err (captures errors for debugging)
- ProgramArguments: [/usr/bin/python3, path/to/samsung_sms_sync.py]
- WorkingDirectory: /Users/cb/Documents/repos/tools (ensures relative imports resolve)
To enable the agent:
launchctl load ~/Library/LaunchAgents/com.cb.samsung-sms-sync.plist
launchctl start com.cb.samsung-sms-sync
launchctl list | grep samsung-sms-sync # Verify it's loaded
Key Technical Decisions and Rationale
Why SQLite Direct Access Over ADB
Direct SQLite queries are faster and more reliable than parsing ADB output. However, the script supports both modes:
- Direct path: When the device is mounted or chat.db is backed up locally
- ADB bridge:
adb pull /data/data/com.android.providers.telephony/databases/mmssms.dbfor live device reads
ADB requires the device to be in developer mode and USB debugging enabled, but eliminates manual backup steps.
SES for Email Delivery
AWS SES was chosen over SMTP because:
- Credentials are managed via IAM roles (no password in config files)
- Integrates seamlessly with existing AWS infrastructure (Lightsail, S3, etc.)
- Built-in bounce/complaint tracking for reliability
- Cost-effective for batch digest sends
The IAM policy required minimal permissions:
{
"Effect": "Allow",
"Action": ["ses:SendEmail", "ses:SendRawEmail"],
"Resource": "*"
}
LaunchAgent vs. Cron
macOS LaunchAgent (via launchd) was chosen over cron because:
- Respects system sleep/wake cycles
- Easy to adjust intervals without manual cron editing
- Unified logging with
log stream --predicate 'process == "samsung_sms_sync"' - Can be loaded/unloaded at runtime without privilege escalation
Infrastructure Dependencies
- Python 3.8+ with
sqlite3(standard library) - boto3 for AWS SES integration
- AWS credentials configured via
~/.aws/credentialsor IAM role - Android device with USB debugging enabled (for ADB mode) or backed-up
chat.db
What's Next
Future enhancements include:
- Conversation filtering: Allow regex or keyword-based filters to reduce noise (e.g., only digest conversations with "payment" or "urgent")
- Two-way sync: Enable sending SMS replies via the script, completing the bidirectional loop
- Web UI dashboard: Display SMS inbox in a browser tab, integrated with existing web infrastructure
- Webhook integration: POST digest summaries to Slack or Discord channels for team visibility