Chrome on Android: the History Database
Chrome's History SQLite database on Android: visited URLs, per-visit transitions, downloads and search terms, WebKit timestamps, synced visits and retention.
- Location
- /data/data/com.android.chrome/app_chrome/Default/History
- Proves
- Which pages Chrome loaded, when, how the user got there (typed, link, form, reload), what was searched and what was downloaded where
- Timestamps
- WebKit/Chrome time: microseconds since 1601-01-01 UTC (urls.last_visit_time, visits.visit_time, downloads.start_time and end_time)
- Access
- Full file system (root, exploit or agent); credential-encrypted sandbox, so AFU only. Not in logical extraction, ADB backup or bugreport. Downloaded files themselves are in shared storage (logical).
- Retention
- Chrome expires history older than about 90 days; the user can clear it; Incognito tabs write nothing here
- Collection
- Full file system extraction, adb pull /sdcard/Download (downloaded files only), Google account export (synced history)
Tools
Compare all tools- Browser ForensicsIn browser
- ALEAPPCLI · open source
- HindsightCLI · open source
- PlasoCLI · open source
- sqlite3CLI · open source
- DB Browser for SQLiteGUI · open source
- Cellebrite UFEDPlatform · commercial
- Magnet AXIOMPlatform · commercial
- Oxygen Forensic DetectivePlatform · commercial
- MSAB XRYPlatform · commercial
What it is
Chrome on Android is built from the same Chromium code as the desktop browser and stores its profile in the same SQLite formats. The History database records every URL the browser loaded in a normal (non-Incognito) tab, one row per visit, together with downloads and the search terms typed into the address bar. Because Chrome is the default browser on most Android phones, it is often where a smishing link, a sideloaded APK download or a search for incriminating information shows up.
The same schema is used by Chrome Beta (com.chrome.beta), Dev (com.chrome.dev) and Canary (com.chrome.canary) under their own package names.
Where it lives
| Item | Path | Notes |
|---|---|---|
| History | /data/data/com.android.chrome/app_chrome/Default/History | Plus History-journal (and -wal where present) |
| Other profile files | .../app_chrome/Default/ | Web Data (autofill, search engines), Login Data (saved passwords, values encrypted), Cookies, Top Sites, Favicons, Bookmarks |
| Open tabs | /data/data/com.android.chrome/app_tabs/0/ | tab_state* files with the URLs and titles of open tabs |
| Downloads | /sdcard/Download/ | The downloaded files themselves, in shared storage |
Paths are identical from Android 8 to 16; what changes is the Chrome version, which adds tables over time. Samsung Internet (com.sec.android.app.sbrowser) and other Chromium browsers keep their own, similar databases in their own sandboxes.
What it proves
- That Chrome loaded a URL, and each time it did (
visits), not just the last time (urls). - How each visit happened, from the transition type: a typed URL, a followed link, a form submission, a reload, a redirect.
- What the user searched for with the default search engine (
keyword_search_terms). - What was downloaded, from which page, to which path, how big it was and whether it completed (
downloads,downloads_url_chains).
It does not prove the user read the page, and a visit can be triggered by a redirect or by another app opening a link in Chrome. Visits synced from other devices can also appear (see tips).
Key fields
| Table | Columns | Meaning |
|---|---|---|
urls | id, url, title, visit_count, typed_count, last_visit_time, hidden | One row per distinct URL |
visits | url (→ urls.id), visit_time, from_visit, transition, visit_duration, originator_cache_guid | One row per visit; from_visit chains navigation |
keyword_search_terms | url_id, term, normalized_term | Address-bar searches |
downloads | target_path, tab_url, referrer, start_time, end_time, received_bytes, total_bytes, state, danger_type, mime_type | Download records |
downloads_url_chains | id, chain_index, url | Redirect chain that led to the file |
transition & 0xFF gives the core type: 0 LINK, 1 TYPED, 2 AUTO_BOOKMARK, 3 AUTO_SUBFRAME, 4 MANUAL_SUBFRAME, 5 GENERATED, 6 AUTO_TOPLEVEL, 7 FORM_SUBMIT, 8 RELOAD, 9 KEYWORD, 10 KEYWORD_GENERATED. High bits are qualifiers such as 0x01000000 FORWARD_BACK, 0x02000000 FROM_ADDRESS_BAR, 0x40000000 CLIENT_REDIRECT and 0x80000000 SERVER_REDIRECT.
Timestamps
Chrome stores times as microseconds since 1601-01-01 00:00:00 UTC ("WebKit time"). Subtract 11,644,473,600 seconds to reach the Unix epoch:
SELECT datetime(v.visit_time/1000000 - 11644473600, 'unixepoch') AS visit_utc,
u.url, u.title, v.transition & 0xFF AS core, v.transition,
v.from_visit, v.visit_duration/1000000.0 AS seconds, v.originator_cache_guid
FROM visits v JOIN urls u ON u.id = v.url
ORDER BY v.visit_time;
SELECT datetime(start_time/1000000 - 11644473600, 'unixepoch') AS started_utc,
target_path, tab_url, referrer, total_bytes, state, mime_type
FROM downloads ORDER BY start_time;
Retention
Chrome deletes history older than roughly 90 days on its own. Users can clear all or part of it from the history page or Delete browsing data, which removes rows from urls and visits and, depending on the options chosen, download records (never the downloaded files themselves). Incognito tabs write nothing to History. Recently removed rows may survive in the journal or WAL and in free pages of the file. If the user syncs history to a Google account, it also exists server-side.
Collection
- Full file system extraction (AFU) is required: the profile is in the app's credential-encrypted sandbox. Take the whole
app_chrome/Default/folder andapp_tabs/with every sidecar file. - Logical: you can only pull the downloaded files from
/sdcard/Download/; their names and timestamps are useful but say nothing about the source page. - ADB backup and bugreport do not contain browsing history.
Parsing
- Browser Forensics opens the
Historyfile in the browser: visited URLs, a per-visit timeline with decoded transitions, downloads and search terms. - Hindsight parses a whole Chromium profile folder and builds a timeline across History, cookies, cache and other files.
- ALEAPP has Chrome parsers for Android extractions; Plaso reads the History database as part of a super-timeline.
Investigator tips
- On recent Chrome versions, visits synced from the user's other devices can be stored locally with a non-empty
originator_cache_guid. Check that column before attributing a visit to this handset. - Follow
from_visitbackwards from a malicious download to find the page, SMS or chat link that started the chain; then look for the same URL in SMS or WhatsApp. - A
.apkindownloadsfollowed by a new package with a sideload installer in installed packages is the classic malware installation sequence. - Links opened from other apps usually arrive as LINK visits with no
from_visit; confirm the source app was in the foreground with usagestats. - Values in
Login Dataand cookies are encrypted with keys protected by the Android Keystore; the URLs, usernames and timestamps in them are still readable.