Databases and settings¶
The unit keeps its persistent state in ~29 SQLite databases, and the application reaches them through two DBUS servers rather than by opening files itself. There are two copies of every database, and the difference between them is the thing that most often makes an edit appear to do nothing.
How these findings were obtained
Read-only analysis: the application image (the C_BCM_DB_MANAGER_SERVER /
C_BCM_UP_SERVER interfaces and their query strings), the updater
(upgrade.out/UpgPlugin.out), and one SPYSTORE dump of the live /USER_DATA tree.
The structure of most databases is therefore not known — the tables below name only
what has actually been established, and mark the rest. Inspect a database you own with
the dbschema tool to fill the gaps.
The seed copy and the live copy¶
| where | who owns it | |
|---|---|---|
| seed | Data_base/sqlite/*.sqlite inside <module>/system.bin |
us — it is part of the media partition the tools rebuild |
| live | /USER_DATA/user_data/sqlite/ |
the car — written as the unit is used |
system.bin is a gzip'd tar the unit extracts to a read-only /SYSTEM/; the seeds sit in
its Data_base/sqlite/ directory. Editing a seed changes nothing on the unit (confirmed on
hardware): the application reads its settings from USER_DATA, not from /SYSTEM/. That is
why the source scheduler and the AUX boot work
cannot be steered by editing the seed.
The live directory is /USER_DATA/user_data/sqlite/, whose parent
C_FS_STORAGE_CTRL_PATH::GetUserDataDir resolves as /USER_DATA/user_data/ (read). A
SPYSTORE dump (executed, 2026-09-14) held 14 databases there, each with a .inf CRC
sidecar; the same tree holds Audio/ (Tuner.dat/Radio.dat presets), Nav/, TTS/ and
T2BF/.
Two things about those live files are established from that dump (read):
up_common.sqliteandup_user.sqliteare stored gzip'd (1f8b …), which is how the unit keeps them on disk — the boot log'sgzUnixRead.nav_dest.sqlite, by contrast, is a plain SQLite file. Which other stores are compressed is not known.- The
.infsidecar beside each database is exactlyCRC32: <signed decimal>and is (re)generated byC_UPGRADE::ManageSQLiteFiles(read; see Flashing).
connectivity.sqlite — the paired phones — is not under sqlite/: the boot log shows it is
imported from the system partition, so it is out of reach of a USER_DATA backup.
tools/dbschema.py reads a gzip'd store transparently, so both forms are inspectable.
The live copy is the car's, not yours to overwrite
USER_DATA holds paired phones, navigation destinations and presets. Replacing a database
there overwrites state the car owns, which reflashing does not put back. A build manifest
with a user_data payload is refused by build_package.py unless it also sets
accept_data_loss: true. Say so plainly before shipping one.
How the application reaches them¶
The application does not open a database path directly. It asks a DBUS service and receives a path (read):
C_BCM_DB_MANAGER_SERVER—get_database_path,save_database,database_corruption_detected. It is the registry front end and the corruption watch.C_BCM_UP_SERVER— theup_*(user profile) settings stores.db_manager.sqlite— the registry itself: the list of every database and its persistence policy. Its own tables and columns are not known.
The shared UP_Keys table¶
The up_* stores share one generic, typed key/value table (read):
Type selects which value column is meaningful — an integer, a float, a string or a blob — and
Idx distinguishes repeated keys. A row is addressed as Section.Name; the settings keys read
for the AUX work are supervisor.Last_Source, supervisor.Last_Source_Priority and
supervisor.Src_Radio_SchedPos (read, from the disassembly and a settings dump).
The unit's own dump — CMMUPKeys::ShowStatus, reported as 271 keys — prints each row as
<Section>.Name.<Idx> <type> : <value> (read):
supervisor.Last_Source.0 <int> : 1
supervisor.Last_Source_Priority.0 <int> : 10
supervisor.Src_Radio_SchedPos.0 <int> : 7
This is also where per-source audio defaults such as AUX / Vol_aux live (read). The two
trailing columns in the schema above — reset_factory_enabled and modified — come from the
synthetic up_common.sqlite that tests/media_helpers.py builds; the unit's own
CREATE TABLE has not been captured, so its full column set is not known.
The databases, by group¶
The inventory is Architecture §6. The third column names only the tables that have actually been established; not known means exactly that.
| database | group | tables established |
|---|---|---|
config.sqlite |
configuration | not known |
config_options.sqlite |
configuration | not known — never opened |
versions.sqlite |
configuration | not known |
version_history.sqlite |
configuration | not known |
db_manager.sqlite |
configuration | not known — the database registry |
upgrade.sqlite |
configuration | not known |
cheatcodes.sqlite |
configuration | cheatcodes (read) |
RadioLogo.sqlite |
configuration | not known |
tuner.sqlite |
configuration | not known |
media_catalog.sqlite |
configuration | not known |
media_cdc_catalog.sqlite |
configuration | not known |
media_jkb_catalog.sqlite |
configuration | not known |
nav_poi.sqlite |
configuration | ZARAssociation (read) |
up_common.sqlite |
user / runtime | UP_Keys (read) |
up_config.sqlite |
user / runtime | UP_Keys (inferred — up_* family); never opened |
up_user.sqlite |
user / runtime | UP_Keys (inferred — up_* family) |
up_user_hmi.sqlite |
user / runtime | UP_Keys (inferred — up_* family); never opened |
agenda.sqlite |
user / runtime | not known |
KeyboardHistory.sqlite |
user / runtime | not known |
Pictures.sqlite |
user / runtime | not known |
browser.sqlite |
user / runtime | not known |
connectivity.sqlite |
user / runtime | not known — imported from the system partition |
Trip.sqlite |
user / runtime | not known |
navigation.sqlite |
user / runtime | not known |
nav_dest.sqlite |
user / runtime | not known — navigation destinations |
diagnosis.sqlite |
user / runtime | not known |
diag_zi.sqlite |
user / runtime | not known — copied into a SPYTAKE collect |
db_alerts.sqlite |
user / runtime | not known |
desktopServices.sqlite |
desktop | current_menu, current_carrousel, persistent_menu (names read; columns not known) |
Two of those tables are worth a sentence:
cheatcodesis the one schema the project has already written down in full — see Cheatcodes and the spy system. The codes are data, not compiled in.nav_poi.sqliteships aZARAssociationtable; the "which POI subcategories are danger zones" membership is a join against it, so it is editable data (read; see What is reachable).
The four that have not been opened¶
These databases have not been opened at all (read):
up_config.sqlite, up_user_hmi.sqlite, desktopServices.sqlite, config_options.sqlite.
Feature flags hide in exactly such places, so these are the first candidates for a database
that changes behaviour without a patch. desktopServices.sqlite is a partial exception: the
menu tree is known to come from its current_menu / current_carrousel tables and it also
carries persistent_menu, but the columns of those tables are not known.
Inspecting your own¶
tools/dbschema.py dumps the schema — tables, columns, types, NOT NULL,
defaults, primary keys, indexes and foreign keys — of a database, or of every
Data_base/sqlite/*.sqlite inside a media partition you own:
uv run tools/dbschema.py schema /USER_DATA/user_data/sqlite/nav_dest.sqlite
uv run tools/dbschema.py from-package SMEG_PLUS_UPG/NAV/system.bin
uv run tools/dbschema.py from-package SMEG_PLUS_UPG/NAV --json
It reads only, copies the bytes to a throwaway location first, and never ships vendor data.
Point it at a system.bin from your own package, or at a database recovered from your own unit
(a SPYSTORE backup, say) — not at anything from someone else's.
Not known¶
- The table structure of every database except the few named above.
- Whether the seed and live copies of a database always share a schema.
- The exact meaning of each
UP_Keys.Typevalue (inferred: it selects the value column). - Which live stores are gzip'd — observed for
up_commonandup_useronly. - What
db_manager.sqliterecords, beyond being the registry.