fix: keep pySim's command-set bookkeeping in sync in the ES10 selections
After a profile switch the shell was unusable: every file select failed
with 'Attribute already exists: do_decode_hex (ShellCommands)' and even
equipping did not help - only a server restart recovered it.
pySim's equip() unregisters only the command sets of the file that is
selected at that moment, then re-registers everything while selecting
MF/EF.ICCID and MF with the app as cmd_app. esim._select_isdr() selected
the ISD-R ADF without cmd_app, so MF's sets (ShellCommands, ...) stayed
registered while the selection moved to the ADF; the equip after the
profile switch then died re-registering them (cmd2 raises
CommandSetRegistrationError), leaving the app broken.
- _select_isdr() passes the shell app as cmd_app, so the old file's sets
are unregistered and the ADF's (none) registered.
- _restore() passes it to soft_reset() too, so the MF re-selection updates
the bookkeeping on both the success and the failure path.
- _esim_reinit() probes select('MF', app) after the equip and reports
reinitialized: false on a registration error (a card-level select
failure, e.g. no active profile, is tolerated).
- tests assert the cmd_app plumbing on the ISD-R select and the restore
(profiles, chip and both switch paths).
This commit is contained in:
+19
-6
@@ -33,9 +33,11 @@ class FakeLchan:
|
||||
def __init__(self):
|
||||
self.scc = SimpleNamespace(name='lchan-scc')
|
||||
self.selected = None
|
||||
self.cmd_app = None
|
||||
|
||||
def select_file(self, app):
|
||||
def select_file(self, app, cmd_app=None):
|
||||
self.selected = app
|
||||
self.cmd_app = cmd_app
|
||||
|
||||
|
||||
def make_app(profile='Consumer eUICC (SGP.22)', isdr=True):
|
||||
@@ -46,13 +48,16 @@ def make_app(profile='Consumer eUICC (SGP.22)', isdr=True):
|
||||
rs = SimpleNamespace(profile=profile,
|
||||
mf=SimpleNamespace(applications=apps),
|
||||
lchan=[lchan],
|
||||
resets=0)
|
||||
resets=0,
|
||||
soft_reset_cmd_app=None)
|
||||
|
||||
def soft_reset():
|
||||
def soft_reset(cmd_app=None):
|
||||
rs.resets += 1
|
||||
rs.soft_reset_cmd_app = cmd_app
|
||||
|
||||
rs.soft_reset = soft_reset
|
||||
return SimpleNamespace(rs=rs), lchan
|
||||
app = SimpleNamespace(rs=rs)
|
||||
return app, lchan
|
||||
|
||||
|
||||
def profile_info(iccid, aid, state=None, nickname=None, cls=None, owner=None,
|
||||
@@ -128,9 +133,12 @@ class EsimTests(unittest.TestCase):
|
||||
self.assertEqual(out['profiles'][1]['state'], 'disabled')
|
||||
self.assertIsNone(out['profiles'][1]['icon'])
|
||||
self.assertIsNone(out['profiles'][1]['icon_size'])
|
||||
# ISD-R was selected and the previous selection restored
|
||||
# ISD-R was selected (with the shell app, so pySim's command-set
|
||||
# bookkeeping follows) and the previous selection restored the same way
|
||||
self.assertEqual(lchan.selected.name, 'ISD-R')
|
||||
self.assertIs(lchan.cmd_app, app)
|
||||
self.assertEqual(app.rs.resets, 1)
|
||||
self.assertIs(app.rs.soft_reset_cmd_app, app)
|
||||
# the request asks for every ProfileInfo tag
|
||||
self.assertIn('9F70', self.calls[0].to_tlv().hex().upper())
|
||||
|
||||
@@ -251,9 +259,12 @@ class EsimTests(unittest.TestCase):
|
||||
self.assertEqual(out['result'], 'ok')
|
||||
self.assertFalse(out['refresh_seen'])
|
||||
self.assertEqual(len(sent), 1)
|
||||
# the ISD-R was selected for the command and the selection restored
|
||||
# the ISD-R was selected for the command (with the shell app) and the
|
||||
# selection restored the same way
|
||||
self.assertEqual(lchan.selected.name, 'ISD-R')
|
||||
self.assertIs(lchan.cmd_app, app)
|
||||
self.assertEqual(app.rs.resets, 1)
|
||||
self.assertIs(app.rs.soft_reset_cmd_app, app)
|
||||
|
||||
def test_switch_profile_treats_91xx_as_ok_and_runs_the_chain(self):
|
||||
app, lchan = make_app()
|
||||
@@ -268,7 +279,9 @@ class EsimTests(unittest.TestCase):
|
||||
self.assertTrue(out['refresh_seen'])
|
||||
self.assertEqual(chain, ['9111'])
|
||||
self.assertEqual(lchan.selected.name, 'ISD-R')
|
||||
self.assertIs(lchan.cmd_app, app)
|
||||
self.assertEqual(app.rs.resets, 1)
|
||||
self.assertIs(app.rs.soft_reset_cmd_app, app)
|
||||
|
||||
def test_switch_profile_chain_failure_keeps_the_accepted_switch(self):
|
||||
app, _ = make_app()
|
||||
|
||||
Reference in New Issue
Block a user