Summary
The Android phone app keeps two small databases that record when a SIM slot changed state and which network lookups the radio logged. Neither file had a reader in ALEAPP. This contribution adds four artifacts for them, all reporting values as stored.
- UICC Log (SIM Slot Events) reads the
uicclogtable ofuicc_log.dband splits four line formats into index, state, subscription id and card fields. - SIM Subscriptions Seen in UICC Log groups the log by phone index and subscription id and adds the matching
siminforow oftelephony.db. - Telephony Network Lookups (trafficstats) reads the
trafficstatstable ofecholocate_system_network_db, and Cells Seen groups it by cell. - Status: In review. The pull request is open as a draft and no maintainer review has been posted yet.
- 4artifacts added
- 49files changed
- 6,884lines added
- 11synthetic cases
Methodology note
Drafted with AI assistance and reviewed by the author before publication. The pull request was also prepared with AI assistance, as its description states.
The issue
An examiner is asked which SIM cards a phone held and when the slots changed. ALEAPP already reads the siminfo table of telephony.db, which lists the subscriptions the phone stored. That is a state at extraction. A siminfo row can outlive the SIM it describes, and its display name can be edited after the event. It does not say when a slot reported READY, when a card was absent or how many times the state changed.
The phone app also keeps a plain log of those events, uicc_log.db, and on some builds a table of network lookups with a cell id and a signal reading. Both sat in the extraction and in no report. The examiner would see the subscription list and have to open the databases by hand to see the sequence.
The parser did not read these two files; now it does. Because the meaning of many values is not documented, the artifacts keep each value as stored and say what is not known.
Show this figure as a table
| File | Before | With this change |
|---|---|---|
| telephony.db (siminfo) | Read by siminfo.py | siminfo.py; also joined |
| uicc_log.db | No artifact | UICC Log; SIM Subscriptions |
| echolocate_system_network_db | No artifact | Network Lookups; Cells Seen |
| netstats collections | Read by netstats.py | Unchanged |
The essence
Four artifacts, two modules, one rule: report what the file stores and name what is not proven. The SIM log is read line by line and the four formats seen are split into fields, with the raw line kept beside them. The subscription view groups those lines and attaches the stored SIM details by matching a subscription id to a siminfo row, an observed match that is labeled as such. The network lookup table is read by column name, because two column orders exist, and a second view groups it by cell.
Two time rules matter. The SIM log time is a device local time with no zone, so it is shown as stored and not converted. The lookup time is a Unix millisecond value and is shown in UTC.
| Artifact | Source table | Key rule |
|---|---|---|
| UICC Log (SIM Slot Events) | uicclog in uicc_log.db | Four line formats split into fields; time as stored, no UTC; unmatched lines kept as Other |
| SIM Subscriptions Seen in UICC Log | uicclog and siminfo in telephony.db | One row per phone index and subscription id; join by value, an observed match |
| Telephony Network Lookups (trafficstats) | trafficstats in echolocate_system_network_db | Columns read by name; millisecond time in UTC; timeline output off |
| Telephony Network Lookups, Cells Seen | the same rows | Grouped by cell id, technology, network detail and band; RSRP range excludes -999 |
Our methodology
Find the format from the data. No source for these files was found, so the four line formats and the two column orders were read from the databases themselves. The notes say so. They also state what was not established: which component writes each file, and what many values mean.
Recount without our code. On five Samsung Android 16 extractions the SIM log held 123, 223, 387, 423 and 428 rows. Two of them also held the lookup table, with 14,442 and 11,376 rows. An independent script using only sqlite and csv re-derived every artifact row count and every column: all SIM log rows, all lookup rows with all columns, 92 and 48 cell groups whose row sums equal the table, and 3, 1, 1, 1 and 2 subscription groups with their first, last and count values and the stored SIM details. All of it matched.
Look at the output the way a reader would. The repository's output checker flagged a constant Android User column, an empty Carrier Name column and two coincidentally equal columns. These are now named in the notes, and a rerun reports nothing to look at.
Damage the input. Six damaged variants of each of four real database files (truncated at half and at 4 KiB, first page zeroed, header only, empty, random bytes) returned no rows and raised no exception, for both artifacts of the module each time. Unit tests add a missing table, a missing column, timestamps that are not numbers, zero, in seconds or in microseconds, unparsable address text, a second Android user and a table of 60,000 rows.
Build synthetic cases. Eleven synthetic cases with invented values cover both column orders of the lookup table, an older siminfo table lacking several columns, an empty table, a missing telephony.db, two Android users and a data/data layout so that storage views can be re-staged. The run script gave 172 passes against 150 on the base, the difference being 22 new test units, and no failures.
Show this figure as a table
| Panel | Device | Rows |
|---|---|---|
| uicc_log.db | 1 | 123 |
| uicc_log.db | 2 | 223 |
| uicc_log.db | 3 | 387 |
| uicc_log.db | 4 | 423 |
| uicc_log.db | 5 | 428 |
| trafficstats | 1 | 14,442 |
| trafficstats | 2 | 11,376 |
The value
For a case about SIM changes, an examiner can now read the sequence of slot events and see which subscription ids were active, with the stored SIM details beside them. The SIM log is a short trail. Row ids start at 1 on four devices and at 2001 on one, which is consistent with older rows having been removed there, so the table should be treated as a log that can be trimmed. The absence of an event is not evidence that it did not happen.
The network lookup views give a record of cells seen by the radio, with the first and last time and a row count. A cell id on its own does not say where a device was, and nothing is derived from it. The lookup file was present only on the two devices that carried a carrier-branded 'echolocate' application, so it may be specific to builds with that application. Other builds were not tested.
What it does not show. The meaning of the SIM states, the null token, the card fields, the response codes and the -999 signal value is not documented and is reported as stored. The SIM log time is consistent with a local time on two devices but the zone is not proven. Every address item is 32 hexadecimal digits, including failed lookups, so the items are not read as IP addresses. The consumed column is 0 on every row of both devices, so the table does not show data volume, and nothing in it ties a row to a SIM. A SIM state is not by itself proof of who inserted or used a SIM.
What is pending. The pull request is open as a draft with no review yet, and continuous integration had not run when it was written. It lists three open points. There is no sample_data, because none of these files is in the public corpus and a maintainer with a matching image could add counts. There is no Pixel or other-vendor build and no other Android version. The carrier application's own databases and several related files are not covered.
Show this figure as a table
| Event | Index | State | Subscription ID |
|---|---|---|---|
| SIM_STATE_CHANGED | 0 | LOADED | 1 |
| CardState | 0 | CARDSTATE_PRESENT | (none) |
| SIM_STATE_CHANGED | 1 | ABSENT | -1 |
| SlotStatus | (none) | (none) | (none) |
Where this sits in the Lab's research
No Lab report covers this yet; the method above is from the pull request. The approach follows the checks used across this series: an independent recount, damaged and synthetic inputs, and notes that state what is not known. The adjacent merged work on network and telephony sections of bug reports is described in another post.
Sources
| Claim or number | Where it comes from |
|---|---|
| Four artifacts in two modules; tables and fields read | PR body (What this changes); diff, scripts/artifacts/uiccLog.py and telephonyTrafficStats.py |
| 6,884 lines added, 0 removed, 49 files changed, 2 commits; opened 25 October 2026; draft; no reviews or comments | PR metadata (gh pr view, 5 November 2026) |
| SIM log rows 123, 223, 387, 423, 428; lookup rows 14,442 and 11,376 | PR body (checklist: verified on real data) |
| Independent recount; 92 and 48 cell groups; 3, 1, 1, 1 and 2 subscription groups | PR body (checklist) |
| Two older Samsung devices (Android 10 and 14) hold neither file | PR body (checklist) |
| No digit run of seven or more in 1,584 SIM log rows | Diff notes of get_uicc_log (1,584 is the sum of the five row counts) |
| Output checker findings and fixes | PR body (checklist) |
| Six damaged variants of each of four real files; unit tests listed | PR body (checklist) |
| 11 synthetic cases; 15 new unit tests; PASS=172 against 150; 22 new units | PR body (Test record; Anything reviewers should know) |
| Time rules: SIM log time as stored; lookup time in UTC | Diff notes of get_uicc_log and get_telephony_trafficstats |
| Address items are 32 hexadecimal digits; consumed is 0 on every row | PR body (not proven list); diff notes of get_telephony_trafficstats |
| Lookup file present only on devices with a carrier-branded application | Diff notes of get_telephony_trafficstats |
| Row ids start at 1 on four devices and at 2001 on one | Diff notes of get_uicc_log |
| sample_data left out; no Pixel or other vendor; not covered files | PR body (checklist; Anything reviewers should know) |
| No Lab report covers this | Lab report inventory (no paper maps to this pull request) |