SMS and MMS on Android: mmssms.db
The Android telephony provider database holding SMS and MMS: tables, timestamps in ms and seconds, MMS parts, RCS caveats and deleted records.
- Location
- /data/user_de/0/com.android.providers.telephony/databases/mmssms.db
- Proves
- Which SMS and MMS were received, sent, drafted or failed, with whom, when, on which SIM, and which app wrote each SMS
- Timestamps
- sms.date and sms.date_sent in Unix milliseconds (UTC); pdu.date and pdu.date_sent in Unix seconds
- Access
- Full file system (root, exploit or agent); DE storage, so readable in BFU by a privileged extraction. Logical: provider view only through an agent app with SMS permission. Not in ADB backup or bugreport.
- Retention
- Until the user or the messaging app deletes the thread, or factory reset; deleted rows may survive in freelist pages and the WAL or journal
- Collection
- Full file system extraction, Agent-based logical extraction, SMS Backup & Restore XML (user export)
Tools
Compare all tools- Messaging ForensicsIn browser
- ALEAPPCLI · open source
- AndrillerGUI · open source
- AutopsyGUI · open source
- sqlite3CLI · open source
- DB Browser for SQLiteGUI · open source
- Cellebrite UFEDPlatform · commercial
- Magnet AXIOMPlatform · commercial
- Oxygen Forensic DetectivePlatform · commercial
- MSAB XRYPlatform · commercial
- Messaging Forensics: Android SMS: reads the sms table of mmssms.db (not MMS parts) and SMS Backup & Restore XML, which also carries MMS and calls. WhatsApp: needs a decrypted msgstore.db, not a .crypt14/.crypt15 backup. Telegram and Signal support covers their desktop apps only.
What it is
mmssms.db is the SQLite database behind Android's telephony content provider (com.android.providers.telephony). Whatever app the user picks as the default SMS app (Google Messages, Samsung Messages or another), classic SMS and MMS are stored here, and every app with SMS permission reads them through content://sms and content://mms. It is one of the first databases to open in almost any case involving a phone number.
Two things it does not hold: RCS chat messages, which Google Messages and OEM messaging apps keep in their own databases, and messages of third-party apps such as WhatsApp.
Where it lives
| Item | Path | Notes |
|---|---|---|
| Database | /data/user_de/0/com.android.providers.telephony/databases/mmssms.db | Device-encrypted storage since Android 7, because the provider is direct-boot aware. Older write-ups and some tools cite /data/data/com.android.providers.telephony/databases/; search the extraction for both |
| Sidecars | mmssms.db-wal, mmssms.db-shm or mmssms.db-journal | Collect them with the database |
| MMS attachments | /data/user_de/0/com.android.providers.telephony/app_parts/PART_<number> | Referenced by part._data |
| Secondary users | /data/user_de/<user>/com.android.providers.telephony/ | One provider per user profile |
| RCS (Google Messages) | /data/data/com.google.android.apps.messaging/databases/bugle_db | Credential-encrypted; holds RCS and a copy of SMS/MMS threads as the app sees them |
Paths are the same from Android 8 to 16 on AOSP-based builds. OEMs add columns (Samsung adds several vendor fields) but keep the core tables. On a multi-SIM phone, one database covers all SIMs, distinguished by sub_id.
What it proves
- That an SMS or MMS with a given body, address and direction exists in the device's message store, and in which folder (inbox, sent, draft, outbox, failed, queued).
- When the device received it (
date) and, for received SMS, when the sender's side stamped it (date_sent). - Which subscription (SIM) handled it (
sub_id) and which app inserted the row (creator, the package name). - Read and seen state, and whether the thread was locked or archived.
It does not prove that the user read or wrote a message: read flips when the thread is opened by any app, and a message in sent could have been sent by malware with SMS permission (check creator).
Key fields
| Table | Column | Meaning |
|---|---|---|
sms | address | Other party's number as received or dialled |
sms | type | 1 inbox, 2 sent, 3 draft, 4 outbox, 5 failed, 6 queued |
sms | date / date_sent | Device receive or send time / sender or SMSC time (0 for sent messages) |
sms | read, seen, status, locked | Read flag, notification seen, delivery status, protected from deletion |
sms | sub_id, creator, service_center | SIM subscription, writing package, SMSC number |
sms | thread_id, body | Conversation and text |
pdu | msg_box, m_type, date | MMS folder (1 inbox, 2 sent, 3 drafts, 4 outbox), PDU type, time in seconds |
addr | msg_id, address, type | MMS addresses: 137 from, 151 to, 130 cc, 129 bcc |
part | mid, ct, _data, text | MMS part: content type, file path in app_parts, inline text |
threads / canonical_addresses | recipient_ids / address | Conversation list and the numbers behind it |
Timestamps
SMS times are Unix epoch milliseconds; MMS times in pdu are Unix epoch seconds. Mixing them up puts MMS in January 1970 or SMS in the year 55000. All are UTC.
SELECT _id, thread_id, address,
datetime(date/1000, 'unixepoch') AS device_time_utc,
datetime(NULLIF(date_sent,0)/1000, 'unixepoch') AS sender_time_utc,
CASE type WHEN 1 THEN 'inbox' WHEN 2 THEN 'sent' WHEN 3 THEN 'draft'
WHEN 4 THEN 'outbox' WHEN 5 THEN 'failed' WHEN 6 THEN 'queued' END AS box,
read, sub_id, creator, body
FROM sms ORDER BY date;
SELECT p._id, datetime(p.date, 'unixepoch') AS utc, p.msg_box,
(SELECT group_concat(address) FROM addr WHERE msg_id = p._id AND type = 137) AS sender,
pt.ct, pt._data, pt.text
FROM pdu p LEFT JOIN part pt ON pt.mid = p._id
ORDER BY p.date;
A large gap between date and date_sent on a received SMS usually means the phone was off or out of coverage when the message was sent.
Retention
Messages stay until deleted by the user, by the messaging app (for example an "auto-delete old messages" setting) or by a factory reset. Deleting a thread removes rows from sms, pdu, part and the search index, and the app_parts files. The database is SQLite, so recently deleted rows often survive in WAL frames, rollback journals, freelist pages or unallocated space inside the file until they are overwritten or the database is vacuumed.
Collection
- Full file system extraction: the only way to get the database with its sidecars and MMS part files. Because the provider lives in device-encrypted storage, a privileged extraction can reach it even in BFU, unlike most app data (see file-based encryption).
- Agent-based logical extraction: commercial and open-source agents read
content://smsandcontent://mmswith SMS permission. You get the provider's current view, with no deleted records. - User export: SMS Backup & Restore produces
sms-<date>.xmlandcalls-<date>.xmlfiles that include MMS and call history. - Not available: ADB backup on current versions, bugreport.
Parsing
- ALEAPP, Andriller and Autopsy parse
mmssms.dbinto SMS and MMS reports; commercial suites add carving of deleted records. - Messaging Forensics opens
mmssms.db(thesmstable, WAL applied) and SMS Backup & Restore XML in the browser and puts them on one timeline with other chat sources. - For MMS, query
pdu,addrandpartyourself (above) and hash theapp_partsfiles you export. - To look for deleted rows, compare a copy opened with the WAL against the main file alone, and inspect freelist pages with a SQLite carving tool.
Investigator tips
- On a phone using Google Messages with RCS enabled,
mmssms.dbcan look empty for contacts who also use RCS: openbugle_dbbefore concluding there were no messages. creatoris the quickest way to spot SMS written by something other than the default SMS app, for example a banking trojan or a backup-restore tool. Cross-check that package in installed packages.- Match
addressagainst contacts throughphone_lookupor normalised numbers, and against the call log to build a per-person communication timeline. - Short codes and alphanumeric senders (bank names, delivery services) are common smishing lures: pivot from the URL in the body to Chrome history.
- A restored backup inserts old messages with new
_idvalues in a burst: many rows with ascending_idbut olddatevalues point to a restore, not to live traffic.