uicc_files: supplement optional files missing from pySim's model

Add a supplement to the cardless registry generator for the 13 files the
Services editor's FS check references but pySim does not emit (optional,
service-gated): EF.LRPLMNSI/IAL/IPD/3GPPPSDATAOFF/TVCONFIG, DF.MExE +
EF.MExE-ST/ORPK/ARPK/TPRPK, DF.SoLSA + EF.SAI/EF.SLL, DF.BCAST,
DF.ACDC + EF.ACDC_LIST, DF.TV, and DF.A2X + EF.AST/EF.A2X_CONFIG (under
DF.TELECOM).  Emitted with distinct (name, path) so same-FID files in
other DFs (EF.SAI vs EF.PBR, EF.AST) stay separate; ADF.USIM rows are
mirrored to ADF.USIM-NON-IMSI.  490 -> 527 entries.

Regenerated the asset; test_uicc_files.py pins the 20 new paths, the
FID-collision cases and (name, root) uniqueness.  1091 Python tests.
This commit is contained in:
catarrh committed 2026-10-26 00:01:23 +03:00
1 parent 12ff552df8
commit a1892094f4
3 files changed
+414 -1

No files matched your search

+297 -1
View File
@@ -1,5 +1,5 @@
{
"generated": "2026-09-23",
"generated": "2026-10-25",
"files": [
{
"path": "MF/2F00",
@@ -161,6 +161,30 @@
"kind": "ef",
"root": "MF"
},
{
"path": "MF/7F10/5F3F",
"name": "DF.A2X",
"sym": "MF/DF.TELECOM/DF.A2X",
"fid": "5F3F",
"kind": "df",
"root": "MF"
},
{
"path": "MF/7F10/5F3F/4F01",
"name": "EF.AST",
"sym": "MF/DF.TELECOM/DF.A2X/EF.AST",
"fid": "4F01",
"kind": "ef",
"root": "MF"
},
{
"path": "MF/7F10/5F3F/4F02",
"name": "EF.A2X_CONFIG",
"sym": "MF/DF.TELECOM/DF.A2X/EF.A2X_CONFIG",
"fid": "4F02",
"kind": "ef",
"root": "MF"
},
{
"path": "MF/7F10/6F3A",
"name": "EF.ADN",
@@ -1030,6 +1054,46 @@
"kind": "ef",
"root": "ADF.USIM"
},
{
"path": "ADF.USIM/5F3C",
"name": "DF.MExE",
"sym": "ADF.USIM/DF.MExE",
"fid": "5F3C",
"kind": "df",
"root": "ADF.USIM"
},
{
"path": "ADF.USIM/5F3C/4F40",
"name": "EF.MExE-ST",
"sym": "ADF.USIM/DF.MExE/EF.MExE-ST",
"fid": "4F40",
"kind": "ef",
"root": "ADF.USIM"
},
{
"path": "ADF.USIM/5F3C/4F41",
"name": "EF.ORPK",
"sym": "ADF.USIM/DF.MExE/EF.ORPK",
"fid": "4F41",
"kind": "ef",
"root": "ADF.USIM"
},
{
"path": "ADF.USIM/5F3C/4F42",
"name": "EF.ARPK",
"sym": "ADF.USIM/DF.MExE/EF.ARPK",
"fid": "4F42",
"kind": "ef",
"root": "ADF.USIM"
},
{
"path": "ADF.USIM/5F3C/4F43",
"name": "EF.TPRPK",
"sym": "ADF.USIM/DF.MExE/EF.TPRPK",
"fid": "4F43",
"kind": "ef",
"root": "ADF.USIM"
},
{
"path": "ADF.USIM/5F40",
"name": "DF.WLAN",
@@ -1182,6 +1246,38 @@
"kind": "ef",
"root": "ADF.USIM"
},
{
"path": "ADF.USIM/5F70",
"name": "DF.SoLSA",
"sym": "ADF.USIM/DF.SoLSA",
"fid": "5F70",
"kind": "df",
"root": "ADF.USIM"
},
{
"path": "ADF.USIM/5F70/4F30",
"name": "EF.SAI",
"sym": "ADF.USIM/DF.SoLSA/EF.SAI",
"fid": "4F30",
"kind": "ef",
"root": "ADF.USIM"
},
{
"path": "ADF.USIM/5F70/4F31",
"name": "EF.SLL",
"sym": "ADF.USIM/DF.SoLSA/EF.SLL",
"fid": "4F31",
"kind": "ef",
"root": "ADF.USIM"
},
{
"path": "ADF.USIM/5F80",
"name": "DF.BCAST",
"sym": "ADF.USIM/DF.BCAST",
"fid": "5F80",
"kind": "df",
"root": "ADF.USIM"
},
{
"path": "ADF.USIM/5F90",
"name": "DF.ProSe",
@@ -1302,6 +1398,30 @@
"kind": "ef",
"root": "ADF.USIM"
},
{
"path": "ADF.USIM/5FA0",
"name": "DF.ACDC",
"sym": "ADF.USIM/DF.ACDC",
"fid": "5FA0",
"kind": "df",
"root": "ADF.USIM"
},
{
"path": "ADF.USIM/5FA0/4F01",
"name": "EF.ACDC_LIST",
"sym": "ADF.USIM/DF.ACDC/EF.ACDC_LIST",
"fid": "4F01",
"kind": "ef",
"root": "ADF.USIM"
},
{
"path": "ADF.USIM/5FB0",
"name": "DF.TV",
"sym": "ADF.USIM/DF.TV",
"fid": "5FB0",
"kind": "df",
"root": "ADF.USIM"
},
{
"path": "ADF.USIM/5FC0",
"name": "DF.5GS",
@@ -2262,6 +2382,14 @@
"kind": "ef",
"root": "ADF.USIM"
},
{
"path": "ADF.USIM/6FDC",
"name": "EF.LRPLMNSI",
"sym": "ADF.USIM/EF.LRPLMNSI",
"fid": "6FDC",
"kind": "ef",
"root": "ADF.USIM"
},
{
"path": "ADF.USIM/6FDD",
"name": "EF.NAFKCA",
@@ -2358,6 +2486,14 @@
"kind": "ef",
"root": "ADF.USIM"
},
{
"path": "ADF.USIM/6FF0",
"name": "EF.IAL",
"sym": "ADF.USIM/EF.IAL",
"fid": "6FF0",
"kind": "ef",
"root": "ADF.USIM"
},
{
"path": "ADF.USIM/6FF1",
"name": "EF.IPS",
@@ -2366,6 +2502,14 @@
"kind": "ef",
"root": "ADF.USIM"
},
{
"path": "ADF.USIM/6FF2",
"name": "EF.IPD",
"sym": "ADF.USIM/EF.IPD",
"fid": "6FF2",
"kind": "ef",
"root": "ADF.USIM"
},
{
"path": "ADF.USIM/6FF3",
"name": "EF.ePDGId",
@@ -2414,6 +2558,22 @@
"kind": "ef",
"root": "ADF.USIM"
},
{
"path": "ADF.USIM/6FF9",
"name": "EF.3GPPPSDATAOFF",
"sym": "ADF.USIM/EF.3GPPPSDATAOFF",
"fid": "6FF9",
"kind": "ef",
"root": "ADF.USIM"
},
{
"path": "ADF.USIM/6FFB",
"name": "EF.TVCONFIG",
"sym": "ADF.USIM/EF.TVCONFIG",
"fid": "6FFB",
"kind": "ef",
"root": "ADF.USIM"
},
{
"path": "ADF.USIM/6FFC",
"name": "EF.XCAPConfigData",
@@ -2527,6 +2687,46 @@
"kind": "ef",
"root": "ADF.USIM-NON-IMSI"
},
{
"path": "ADF.USIM-NON-IMSI/5F3C",
"name": "DF.MExE",
"sym": "ADF.USIM-non-IMSI/DF.MExE",
"fid": "5F3C",
"kind": "df",
"root": "ADF.USIM-NON-IMSI"
},
{
"path": "ADF.USIM-NON-IMSI/5F3C/4F40",
"name": "EF.MExE-ST",
"sym": "ADF.USIM-non-IMSI/DF.MExE/EF.MExE-ST",
"fid": "4F40",
"kind": "ef",
"root": "ADF.USIM-NON-IMSI"
},
{
"path": "ADF.USIM-NON-IMSI/5F3C/4F41",
"name": "EF.ORPK",
"sym": "ADF.USIM-non-IMSI/DF.MExE/EF.ORPK",
"fid": "4F41",
"kind": "ef",
"root": "ADF.USIM-NON-IMSI"
},
{
"path": "ADF.USIM-NON-IMSI/5F3C/4F42",
"name": "EF.ARPK",
"sym": "ADF.USIM-non-IMSI/DF.MExE/EF.ARPK",
"fid": "4F42",
"kind": "ef",
"root": "ADF.USIM-NON-IMSI"
},
{
"path": "ADF.USIM-NON-IMSI/5F3C/4F43",
"name": "EF.TPRPK",
"sym": "ADF.USIM-non-IMSI/DF.MExE/EF.TPRPK",
"fid": "4F43",
"kind": "ef",
"root": "ADF.USIM-NON-IMSI"
},
{
"path": "ADF.USIM-NON-IMSI/5F40",
"name": "DF.WLAN",
@@ -2679,6 +2879,38 @@
"kind": "ef",
"root": "ADF.USIM-NON-IMSI"
},
{
"path": "ADF.USIM-NON-IMSI/5F70",
"name": "DF.SoLSA",
"sym": "ADF.USIM-non-IMSI/DF.SoLSA",
"fid": "5F70",
"kind": "df",
"root": "ADF.USIM-NON-IMSI"
},
{
"path": "ADF.USIM-NON-IMSI/5F70/4F30",
"name": "EF.SAI",
"sym": "ADF.USIM-non-IMSI/DF.SoLSA/EF.SAI",
"fid": "4F30",
"kind": "ef",
"root": "ADF.USIM-NON-IMSI"
},
{
"path": "ADF.USIM-NON-IMSI/5F70/4F31",
"name": "EF.SLL",
"sym": "ADF.USIM-non-IMSI/DF.SoLSA/EF.SLL",
"fid": "4F31",
"kind": "ef",
"root": "ADF.USIM-NON-IMSI"
},
{
"path": "ADF.USIM-NON-IMSI/5F80",
"name": "DF.BCAST",
"sym": "ADF.USIM-non-IMSI/DF.BCAST",
"fid": "5F80",
"kind": "df",
"root": "ADF.USIM-NON-IMSI"
},
{
"path": "ADF.USIM-NON-IMSI/5F90",
"name": "DF.ProSe",
@@ -2799,6 +3031,30 @@
"kind": "ef",
"root": "ADF.USIM-NON-IMSI"
},
{
"path": "ADF.USIM-NON-IMSI/5FA0",
"name": "DF.ACDC",
"sym": "ADF.USIM-non-IMSI/DF.ACDC",
"fid": "5FA0",
"kind": "df",
"root": "ADF.USIM-NON-IMSI"
},
{
"path": "ADF.USIM-NON-IMSI/5FA0/4F01",
"name": "EF.ACDC_LIST",
"sym": "ADF.USIM-non-IMSI/DF.ACDC/EF.ACDC_LIST",
"fid": "4F01",
"kind": "ef",
"root": "ADF.USIM-NON-IMSI"
},
{
"path": "ADF.USIM-NON-IMSI/5FB0",
"name": "DF.TV",
"sym": "ADF.USIM-non-IMSI/DF.TV",
"fid": "5FB0",
"kind": "df",
"root": "ADF.USIM-NON-IMSI"
},
{
"path": "ADF.USIM-NON-IMSI/5FC0",
"name": "DF.5GS",
@@ -3751,6 +4007,14 @@
"kind": "ef",
"root": "ADF.USIM-NON-IMSI"
},
{
"path": "ADF.USIM-NON-IMSI/6FDC",
"name": "EF.LRPLMNSI",
"sym": "ADF.USIM-non-IMSI/EF.LRPLMNSI",
"fid": "6FDC",
"kind": "ef",
"root": "ADF.USIM-NON-IMSI"
},
{
"path": "ADF.USIM-NON-IMSI/6FDD",
"name": "EF.NAFKCA",
@@ -3847,6 +4111,14 @@
"kind": "ef",
"root": "ADF.USIM-NON-IMSI"
},
{
"path": "ADF.USIM-NON-IMSI/6FF0",
"name": "EF.IAL",
"sym": "ADF.USIM-non-IMSI/EF.IAL",
"fid": "6FF0",
"kind": "ef",
"root": "ADF.USIM-NON-IMSI"
},
{
"path": "ADF.USIM-NON-IMSI/6FF1",
"name": "EF.IPS",
@@ -3855,6 +4127,14 @@
"kind": "ef",
"root": "ADF.USIM-NON-IMSI"
},
{
"path": "ADF.USIM-NON-IMSI/6FF2",
"name": "EF.IPD",
"sym": "ADF.USIM-non-IMSI/EF.IPD",
"fid": "6FF2",
"kind": "ef",
"root": "ADF.USIM-NON-IMSI"
},
{
"path": "ADF.USIM-NON-IMSI/6FF3",
"name": "EF.ePDGId",
@@ -3903,6 +4183,22 @@
"kind": "ef",
"root": "ADF.USIM-NON-IMSI"
},
{
"path": "ADF.USIM-NON-IMSI/6FF9",
"name": "EF.3GPPPSDATAOFF",
"sym": "ADF.USIM-non-IMSI/EF.3GPPPSDATAOFF",
"fid": "6FF9",
"kind": "ef",
"root": "ADF.USIM-NON-IMSI"
},
{
"path": "ADF.USIM-NON-IMSI/6FFB",
"name": "EF.TVCONFIG",
"sym": "ADF.USIM-non-IMSI/EF.TVCONFIG",
"fid": "6FFB",
"kind": "ef",
"root": "ADF.USIM-NON-IMSI"
},
{
"path": "ADF.USIM-NON-IMSI/6FFC",
"name": "EF.XCAPConfigData",
+63
View File
@@ -11,6 +11,11 @@ classes - the same model pySim assembles at equip time:
It ships as ``frontend/uicc_files.json`` so the SIM/USIM RFM SELECT picker has
a file list offline (no card, no server); when a card is equipped the picker
merges the live tree over it and hides probed-absent files.
The optional / service-gated files pySim's model does not carry (the SoLSA,
MExE, BCAST, ACDC, TV and A2X DFs, EF.LRPLMNSI, EF.IAL, EF.IPD, EF.TVCONFIG,
EF.3GPPPSDATAOFF) are appended from ``SUPPLEMENT`` with their spec locations -
the decoded-editing filesystem check resolves a file by its (name, root), so a
same-FID file under another DF must stay a distinct entry.
``tests/test_uicc_files.py`` regenerates the list and fails when pySim adds or
renames files, so the asset cannot silently drift.
@@ -55,6 +60,62 @@ def _entry(fid_path, sym_path, name, fid, kind, root, aid=None):
return out
# Files the pySim model does not carry (optional / service-gated), with their
# spec locations: TS 31.102 4.2.86 (EF.LRPLMNSI), 4.2.100 (EF.IAL), 4.2.102
# (EF.IPD), 4.2.108 (EF.TVCONFIG), 4.2.109 (EF.3GPPPSDATAOFF), 4.4.1
# (DF.SoLSA), 4.4.4 (DF.MExE), 4.4.7 (DF.BCAST - its files are OMA BCAST, so
# the DF is listed alone), 4.4.9 (DF.ACDC), 4.4.10 (DF.TV - EF.TVUSD is a
# variable 4Fxx, omitted) and 4.6.6 (DF.A2X); the FID map is
# docs/uicc/UICC_FILES.md 4.13. Entries are (root, parent FID chain, fid,
# name); a DF is a 'DF.' name and precedes its children so the symbolic chain
# can be resolved.
SUPPLEMENT = (
('ADF.USIM', (), '6FDC', 'EF.LRPLMNSI'),
('ADF.USIM', (), '6FF0', 'EF.IAL'),
('ADF.USIM', (), '6FF2', 'EF.IPD'),
('ADF.USIM', (), '6FF9', 'EF.3GPPPSDATAOFF'),
('ADF.USIM', (), '6FFB', 'EF.TVCONFIG'),
('ADF.USIM', (), '5F3C', 'DF.MExE'),
('ADF.USIM', ('5F3C',), '4F40', 'EF.MExE-ST'),
('ADF.USIM', ('5F3C',), '4F41', 'EF.ORPK'),
('ADF.USIM', ('5F3C',), '4F42', 'EF.ARPK'),
('ADF.USIM', ('5F3C',), '4F43', 'EF.TPRPK'),
('ADF.USIM', (), '5F70', 'DF.SoLSA'),
('ADF.USIM', ('5F70',), '4F30', 'EF.SAI'),
('ADF.USIM', ('5F70',), '4F31', 'EF.SLL'),
('ADF.USIM', (), '5F80', 'DF.BCAST'),
('ADF.USIM', (), '5FA0', 'DF.ACDC'),
('ADF.USIM', ('5FA0',), '4F01', 'EF.ACDC_LIST'),
('ADF.USIM', (), '5FB0', 'DF.TV'),
('MF', ('7F10',), '5F3F', 'DF.A2X'),
('MF', ('7F10', '5F3F'), '4F01', 'EF.AST'),
('MF', ('7F10', '5F3F'), '4F02', 'EF.A2X_CONFIG'),
)
# The non-IMSI USIM application repeats the ADF.USIM files under its own root
# (the walk emits it as a separate ADF with the same file set).
USIM_ROOT = 'ADF.USIM'
USIM_MIRROR = 'ADF.USIM-NON-IMSI'
def _add_supplement(out, seen, sym_by_path):
"""Append SUPPLEMENT, mirroring the ADF.USIM files under the non-IMSI root."""
for root, parents, fid, name in SUPPLEMENT:
roots = [root]
if root == USIM_ROOT and USIM_MIRROR in seen:
roots.append(USIM_MIRROR)
for target in roots:
key = '/'.join((target,) + tuple(parents) + (fid,)).upper()
if key in seen:
continue
parent_key = '/'.join((target,) + tuple(parents)).upper()
sym = '%s/%s' % (sym_by_path.get(parent_key, parent_key), name)
seen.add(key)
out.append(_entry(key, sym, name, fid,
'df' if name.startswith('DF.') else 'ef', target))
sym_by_path[key] = sym
def _walk(node, fid_path, sym_path, root, out, seen):
"""Depth-first walk of a DF/ADF subtree (children is a fid -> CardFile dict)."""
for fid, child in sorted(node.children.items()):
@@ -116,6 +177,8 @@ def build():
seen.add(key)
out.append(_entry(key, adf.name, adf.name, '', 'adf', adf.name, app.aid))
_walk(adf, key, adf.name, adf.name, out, seen)
# The optional / service-gated files pySim's model does not carry.
_add_supplement(out, seen, {e['path']: e['sym'] for e in out})
# Stable order: MF first, then the ADFs alphabetically; DFs before their EFs
# is implicit in the walk, paths sort consistently inside a root.
out.sort(key=lambda e: (0 if e['root'] == 'MF' else 1, e['root'], e['path']))
+54
View File
@@ -72,6 +72,60 @@ class TestUiccFilesAsset(unittest.TestCase):
self.assertRegex(f['fid'], r'^[0-9A-F]{4}$')
self.assertTrue(f['name'].startswith(('EF.', 'DF.')))
def test_optional_files_missing_from_the_pysim_model(self):
"""The service-gated files pySim does not model (the SUPPLEMENT)."""
files = _load()['files']
by_path = {f['path']: f for f in files}
expected = {
# TS 31.102 4.2.86 / 4.2.100 / 4.2.102 / 4.2.108 / 4.2.109
'ADF.USIM/6FDC': 'EF.LRPLMNSI',
'ADF.USIM/6FF0': 'EF.IAL',
'ADF.USIM/6FF2': 'EF.IPD',
'ADF.USIM/6FF9': 'EF.3GPPPSDATAOFF',
'ADF.USIM/6FFB': 'EF.TVCONFIG',
# 4.4.4 DF.MExE
'ADF.USIM/5F3C': 'DF.MExE',
'ADF.USIM/5F3C/4F40': 'EF.MExE-ST',
'ADF.USIM/5F3C/4F41': 'EF.ORPK',
'ADF.USIM/5F3C/4F42': 'EF.ARPK',
'ADF.USIM/5F3C/4F43': 'EF.TPRPK',
# 4.4.1 DF.SoLSA
'ADF.USIM/5F70': 'DF.SoLSA',
'ADF.USIM/5F70/4F30': 'EF.SAI',
'ADF.USIM/5F70/4F31': 'EF.SLL',
# 4.4.7 / 4.4.9 / 4.4.10
'ADF.USIM/5F80': 'DF.BCAST',
'ADF.USIM/5FA0': 'DF.ACDC',
'ADF.USIM/5FA0/4F01': 'EF.ACDC_LIST',
'ADF.USIM/5FB0': 'DF.TV',
# 4.6.6 DF.A2X (DF.TELECOM)
'MF/7F10/5F3F': 'DF.A2X',
'MF/7F10/5F3F/4F01': 'EF.AST',
'MF/7F10/5F3F/4F02': 'EF.A2X_CONFIG',
}
for path, name in expected.items():
self.assertIn(path, by_path)
self.assertEqual(by_path[path]['name'], name)
self.assertEqual(by_path[path]['root'], path.split('/')[0])
self.assertEqual(by_path[path]['kind'],
'df' if name.startswith('DF.') else 'ef')
# The same FID under another DF stays a distinct entry (name, path).
self.assertEqual(by_path['ADF.USIM/5F3A/4F30']['name'], 'EF.PBR')
self.assertEqual(by_path['MF/7F10/5F3A/4F30']['name'], 'EF.PBR')
# The ADF.USIM files are mirrored under the non-IMSI USIM root.
for path, name in expected.items():
if not path.startswith('ADF.USIM/'):
continue
mirror = 'ADF.USIM-NON-IMSI' + path[len('ADF.USIM'):]
self.assertIn(mirror, by_path)
self.assertEqual(by_path[mirror]['name'], name)
self.assertEqual(by_path[mirror]['root'], 'ADF.USIM-NON-IMSI')
# (name, root) resolves each SUPPLEMENT file to exactly one path.
for path, name in expected.items():
root = path.split('/')[0]
self.assertEqual([f['path'] for f in files
if f['name'] == name and f['root'] == root], [path])
if __name__ == '__main__':
unittest.main()