Open Source / ALEAPP #1787

Reading SIM slot logs and network lookups

Two files of the Android phone app record SIM slot events and network lookups, and neither had a reader. This pull request adds artifacts that report them as stored and name what is not known.

  • Daniel Addai, SectraTEK Lab
  • In review
  • CC BY 4.0

View pull request #1787 on GitHub (opens in a new tab)

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 uicclog table of uicc_log.db and 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 siminfo row of telephony.db.
  • Telephony Network Lookups (trafficstats) reads the trafficstats table of echolocate_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.

Which telephony files a report readsA table of four files with what reads each before and after the change. telephony.db was read by siminfo.py alone and is now also joined to the SIM log. uicc_log.db and echolocate_system_network_db had no artifact and now have two each. The netstats collections are unchanged.FileBefore this changeWith this changetelephony.db (siminfo)com.android.providers.telephonyRead by siminfo.pysiminfo.py; also joineduicc_log.dbcom.android.phone/databasesNo artifactUICC Log; SIM Subscriptionsecholocate_system_network_dbcom.android.phone/databasesNo artifactNetwork Lookups; Cells Seennetstats collectionsbinary filesRead by netstats.pyUnchanged
Figure 1Two files of the Android phone app had no reader. The SIM log is also joined to the subscription rows that siminfo.py already reads. Source: PR body (What this changes; no overlap with existing artifacts) (diagram).
Show this figure as a table
Figure 1 as a table
FileBeforeWith this change
telephony.db (siminfo)Read by siminfo.pysiminfo.py; also joined
uicc_log.dbNo artifactUICC Log; SIM Subscriptions
echolocate_system_network_dbNo artifactNetwork Lookups; Cells Seen
netstats collectionsRead by netstats.pyUnchanged

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.

Artifacts added by the pull request
ArtifactSource tableKey rule
UICC Log (SIM Slot Events)uicclog in uicc_log.dbFour line formats split into fields; time as stored, no UTC; unmatched lines kept as Other
SIM Subscriptions Seen in UICC Loguicclog and siminfo in telephony.dbOne row per phone index and subscription id; join by value, an observed match
Telephony Network Lookups (trafficstats)trafficstats in echolocate_system_network_dbColumns read by name; millisecond time in UTC; timeline output off
Telephony Network Lookups, Cells Seenthe same rowsGrouped 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.

Pipeline of the four telephony artifactsFour flows, one per artifact. The SIM slot log is parsed line by line and written with its stored time. SIM state lines with a subscription id are grouped and joined to the siminfo table of telephony.db. The trafficstats table is read by column name with its millisecond time shown in UTC, then grouped by cell id, technology, network detail and band.uiccLog.py: get_uicc_loguiccLog.py: get_uicc_sim_historytelephonyTrafficStats.py: get_telephony_trafficstatstelephonyTrafficStats.py: get_telephony_cell_summaryuicc_log.dbtable uicclog_id, time, logParse the text4 line formats;others kept rawUICC Log(SIM Slot Events)time as storedSIM_STATE_CHANGEDlines with asubscription id >= 0Group by indexand subscription idfirst, last, countsJoin telephony.dbsiminfo._id =subscription idSIM SubscriptionsSeen in UICC Logdetails as storedecholocate_system_network_dbtable trafficstatsRead by column namems timestamp to UTCvalues as storedTelephony NetworkLookups(trafficstats)The same rowsper Android userGroup by cell id,technology, detail,bandFirst, last, rows,RSRP rangewithout -999Telephony NetworkLookups,Cells Seen
Figure 2Each flow starts at a real table and ends at one report. The SIM log time has no zone in the file and is not converted. The network lookup time is a Unix millisecond value and is shown in UTC. Source: diff, scripts/artifacts/uiccLog.py and scripts/artifacts/telephonyTrafficStats.py (diagram).
Rows read from the two telephony filesTwo bar charts with their own linear scales. The SIM log held 123, 223, 387, 423 and 428 rows on five devices, on a scale of zero to 500. The trafficstats table held 14,442 and 11,376 rows on two devices, on a scale of zero to 15,000.uicc_log.db: rows on five devices0100200300400500Device 1123Device 2223Device 3387Device 4423Device 5428echolocate_system_network_db: trafficstats rows on two devices05,00010,00015,000Device 114,442Device 211,376Each device label is local to its panel and does not link a device in one panel to a device in the other.
Figure 3Row counts on the tested devices. For every artifact the row count and every column were re-derived with an independent script and matched. Source: PR body (checklist: verified on real data, counts only; no synthetic values).
Show this figure as a table
Figure 3 as a table
PanelDeviceRows
uicc_log.db1123
uicc_log.db2223
uicc_log.db3387
uicc_log.db4423
uicc_log.db5428
trafficstats114,442
trafficstats211,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.

What the two SIM views showTwo small report tables with invented values. The first lists SIM slot events with the stored time, event name, index, state, subscription id and card fields. The second lists one row per index and subscription id with first and last stored time and counts of READY and LOADED lines.UICC Log (SIM Slot Events), synthetic exampleLog Time (as stored)EventIndexStateSubscription IDCard fields2026-02-03 08:10:12SIM_STATE_CHANGED0LOADED12026-02-03 08:10:12CardState0CARDSTATE_PRESENTapps 1, port 02026-02-03 08:12:40SIM_STATE_CHANGED1ABSENT-12026-02-03 08:12:41SlotStatuslayout textThe time is a device local time with no zone, shown as stored. Raw Log (not shown) holds the full line.SIM Subscriptions Seen in UICC Log, synthetic exampleIndexSubscription IDFirst SeenLast SeenREADY LinesLOADED Lines012026-02-03 08:10:122026-02-09 17:44:3125122026-02-04 09:02:102026-02-09 17:44:3513Display name, carrier text, ICCID, IMSI and number follow from the siminfo row of telephony.db, as stored.
Figure 4Synthetic example with invented values. The time is not converted to UTC because the file does not record a zone. Source: diff, scripts/artifacts/uiccLog.py (column names; synthetic example).
Show this figure as a table
Figure 4, first view, as a table
EventIndexStateSubscription ID
SIM_STATE_CHANGED0LOADED1
CardState0CARDSTATE_PRESENT(none)
SIM_STATE_CHANGED1ABSENT-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

Sources for the numbers and claims on this page
Claim or numberWhere it comes from
Four artifacts in two modules; tables and fields readPR 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 commentsPR metadata (gh pr view, 5 November 2026)
SIM log rows 123, 223, 387, 423, 428; lookup rows 14,442 and 11,376PR body (checklist: verified on real data)
Independent recount; 92 and 48 cell groups; 3, 1, 1, 1 and 2 subscription groupsPR body (checklist)
Two older Samsung devices (Android 10 and 14) hold neither filePR body (checklist)
No digit run of seven or more in 1,584 SIM log rowsDiff notes of get_uicc_log (1,584 is the sum of the five row counts)
Output checker findings and fixesPR body (checklist)
Six damaged variants of each of four real files; unit tests listedPR body (checklist)
11 synthetic cases; 15 new unit tests; PASS=172 against 150; 22 new unitsPR body (Test record; Anything reviewers should know)
Time rules: SIM log time as stored; lookup time in UTCDiff notes of get_uicc_log and get_telephony_trafficstats
Address items are 32 hexadecimal digits; consumed is 0 on every rowPR body (not proven list); diff notes of get_telephony_trafficstats
Lookup file present only on devices with a carrier-branded applicationDiff notes of get_telephony_trafficstats
Row ids start at 1 on four devices and at 2001 on oneDiff notes of get_uicc_log
sample_data left out; no Pixel or other vendor; not covered filesPR body (checklist; Anything reviewers should know)
No Lab report covers thisLab report inventory (no paper maps to this pull request)