pySim-shell: Create + use per-RuntimeLchan SimCardCommands
This new approach will "fork" separate SimCardCommands instances
for each RuntimeLchan. Higher-layer code should now always use the
RuntimeLchan.scc rather than the RuntimeState.card._scc in order to
make sure commands use the correct logical channel.
Change-Id: I13e2e871f2afc2460d9fd1cd566de42267c7d389
Related: OS#6230
diff --git a/pySim/ara_m.py b/pySim/ara_m.py
index 8144381..b4ac747 100644
--- a/pySim/ara_m.py
+++ b/pySim/ara_m.py
@@ -306,13 +306,13 @@
def do_aram_get_all(self, opts):
"""GET DATA [All] on the ARA-M Applet"""
- res_do = ADF_ARAM.get_all(self._cmd.card._scc._tp)
+ res_do = ADF_ARAM.get_all(self._cmd.lchan.scc._tp)
if res_do:
self._cmd.poutput_json(res_do.to_dict())
def do_aram_get_config(self, opts):
"""Perform GET DATA [Config] on the ARA-M Applet: Tell it our version and retrieve its version."""
- res_do = ADF_ARAM.get_config(self._cmd.card._scc._tp)
+ res_do = ADF_ARAM.get_config(self._cmd.lchan.scc._tp)
if res_do:
self._cmd.poutput_json(res_do.to_dict())
@@ -373,14 +373,14 @@
d = [{'ref_ar_do': [{'ref_do': ref_do_content}, {'ar_do': ar_do_content}]}]
csrado = CommandStoreRefArDO()
csrado.from_dict(d)
- res_do = ADF_ARAM.store_data(self._cmd.card._scc._tp, csrado)
+ res_do = ADF_ARAM.store_data(self._cmd.lchan.scc._tp, csrado)
if res_do:
self._cmd.poutput_json(res_do.to_dict())
def do_aram_delete_all(self, opts):
"""Perform STORE DATA [Command-Delete[all]] to delete all access rules."""
deldo = CommandDelete()
- res_do = ADF_ARAM.store_data(self._cmd.card._scc._tp, deldo)
+ res_do = ADF_ARAM.store_data(self._cmd.lchan.scc._tp, deldo)
if res_do:
self._cmd.poutput_json(res_do.to_dict())
diff --git a/pySim/commands.py b/pySim/commands.py
index 3edee98..336cf0c 100644
--- a/pySim/commands.py
+++ b/pySim/commands.py
@@ -74,6 +74,7 @@
"""Fork a per-lchan specific SimCardCommands instance off the current instance."""
ret = SimCardCommands(transport = self._tp, lchan_nr = lchan_nr)
ret.cla_byte = self._cla_byte
+ ret.sel_ctrl = self.sel_ctrl
return ret
@property
diff --git a/pySim/global_platform.py b/pySim/global_platform.py
index ea8b70d..bfa2adf 100644
--- a/pySim/global_platform.py
+++ b/pySim/global_platform.py
@@ -238,7 +238,7 @@
self._cmd.poutput('Unknown data object "%s", available options: %s' % (tlv_cls_name,
do_names))
return
- (data, sw) = self._cmd.card._scc.get_data(cla=0x80, tag=tlv_cls.tag)
+ (data, sw) = self._cmd.lchan.scc.get_data(cla=0x80, tag=tlv_cls.tag)
ie = tlv_cls()
ie.from_tlv(h2b(data))
self._cmd.poutput_json(ie.to_dict())
diff --git a/pySim/runtime.py b/pySim/runtime.py
index 88de69e..27c2ef1 100644
--- a/pySim/runtime.py
+++ b/pySim/runtime.py
@@ -167,6 +167,7 @@
self.selected_adf = None
self.selected_file_fcp = None
self.selected_file_fcp_hex = None
+ self.scc = self.rs.card._scc.fork_lchan(lchan_nr)
def add_lchan(self, lchan_nr: int) -> 'RuntimeLchan':
"""Add a new logical channel from the current logical channel. Just affects
@@ -246,7 +247,7 @@
"Cannot select unknown file by name %s, only hexadecimal 4 digit FID is allowed" % fid)
try:
- (data, sw) = self.rs.card._scc.select_file(fid)
+ (data, sw) = self.scc.select_file(fid)
except SwMatchError as swm:
k = self.interpret_sw(swm.sw_actual)
if not k:
@@ -301,7 +302,7 @@
(data, sw) = self.rs.card.select_adf_by_aid(p.aid)
self.selected_adf = p
else:
- (data, sw) = self.rs.card._scc.select_file(p.fid)
+ (data, sw) = self.scc.select_file(p.fid)
self.selected_file = p
except SwMatchError as swm:
self._select_post(cmd_app)
@@ -344,7 +345,7 @@
if isinstance(f, CardADF):
(data, sw) = self.rs.card.select_adf_by_aid(f.aid)
else:
- (data, sw) = self.rs.card._scc.select_file(f.fid)
+ (data, sw) = self.scc.select_file(f.fid)
self.selected_file = f
except SwMatchError as swm:
k = self.interpret_sw(swm.sw_actual)
@@ -364,7 +365,7 @@
def status(self):
"""Request STATUS (current selected file FCP) from card."""
- (data, sw) = self.rs.card._scc.status()
+ (data, sw) = self.scc.status()
return self.selected_file.decode_select_response(data)
def get_file_for_selectable(self, name: str):
@@ -375,7 +376,7 @@
"""Request ACTIVATE FILE of specified file."""
sels = self.selected_file.get_selectables()
f = sels[name]
- data, sw = self.rs.card._scc.activate_file(f.fid)
+ data, sw = self.scc.activate_file(f.fid)
return data, sw
def read_binary(self, length: int = None, offset: int = 0):
@@ -389,7 +390,7 @@
"""
if not isinstance(self.selected_file, TransparentEF):
raise TypeError("Only works with TransparentEF")
- return self.rs.card._scc.read_binary(self.selected_file.fid, length, offset)
+ return self.scc.read_binary(self.selected_file.fid, length, offset)
def read_binary_dec(self) -> Tuple[dict, str]:
"""Read [part of] a transparent EF binary data and decode it.
@@ -413,7 +414,7 @@
"""
if not isinstance(self.selected_file, TransparentEF):
raise TypeError("Only works with TransparentEF")
- return self.rs.card._scc.update_binary(self.selected_file.fid, data_hex, offset, conserve=self.rs.conserve_write)
+ return self.scc.update_binary(self.selected_file.fid, data_hex, offset, conserve=self.rs.conserve_write)
def update_binary_dec(self, data: dict):
"""Update transparent EF from abstract data. Encodes the data to binary and
@@ -436,7 +437,7 @@
if not isinstance(self.selected_file, LinFixedEF):
raise TypeError("Only works with Linear Fixed EF")
# returns a string of hex nibbles
- return self.rs.card._scc.read_record(self.selected_file.fid, rec_nr)
+ return self.scc.read_record(self.selected_file.fid, rec_nr)
def read_record_dec(self, rec_nr: int = 0) -> Tuple[dict, str]:
"""Read a record and decode it to abstract data.
@@ -458,7 +459,7 @@
"""
if not isinstance(self.selected_file, LinFixedEF):
raise TypeError("Only works with Linear Fixed EF")
- return self.rs.card._scc.update_record(self.selected_file.fid, rec_nr, data_hex,
+ return self.scc.update_record(self.selected_file.fid, rec_nr, data_hex,
conserve=self.rs.conserve_write,
leftpad=self.selected_file.leftpad)
@@ -484,7 +485,7 @@
if not isinstance(self.selected_file, BerTlvEF):
raise TypeError("Only works with BER-TLV EF")
# returns a string of hex nibbles
- return self.rs.card._scc.retrieve_data(self.selected_file.fid, tag)
+ return self.scc.retrieve_data(self.selected_file.fid, tag)
def retrieve_tags(self):
"""Retrieve tags available on BER-TLV EF.
@@ -494,7 +495,7 @@
"""
if not isinstance(self.selected_file, BerTlvEF):
raise TypeError("Only works with BER-TLV EF")
- data, sw = self.rs.card._scc.retrieve_data(self.selected_file.fid, 0x5c)
+ data, sw = self.scc.retrieve_data(self.selected_file.fid, 0x5c)
tag, length, value, remainder = bertlv_parse_one(h2b(data))
return list(value)
@@ -507,7 +508,7 @@
"""
if not isinstance(self.selected_file, BerTlvEF):
raise TypeError("Only works with BER-TLV EF")
- return self.rs.card._scc.set_data(self.selected_file.fid, tag, data_hex, conserve=self.rs.conserve_write)
+ return self.scc.set_data(self.selected_file.fid, tag, data_hex, conserve=self.rs.conserve_write)
def unregister_cmds(self, cmd_app=None):
"""Unregister all file specific commands."""
diff --git a/pySim/ts_102_222.py b/pySim/ts_102_222.py
index a4ea453..372d67f 100644
--- a/pySim/ts_102_222.py
+++ b/pySim/ts_102_222.py
@@ -49,7 +49,7 @@
self._cmd.perror("Refusing to permanently delete the file, please read the help text.")
return
f = self._cmd.lchan.get_file_for_selectable(opts.NAME)
- (data, sw) = self._cmd.card._scc.delete_file(f.fid)
+ (data, sw) = self._cmd.lchan.scc.delete_file(f.fid)
def complete_delete_file(self, text, line, begidx, endidx) -> List[str]:
"""Command Line tab completion for DELETE FILE"""
@@ -70,7 +70,7 @@
self._cmd.perror("Refusing to terminate the file, please read the help text.")
return
f = self._cmd.lchan.get_file_for_selectable(opts.NAME)
- (data, sw) = self._cmd.card._scc.terminate_df(f.fid)
+ (data, sw) = self._cmd.lchan.scc.terminate_df(f.fid)
def complete_terminate_df(self, text, line, begidx, endidx) -> List[str]:
"""Command Line tab completion for TERMINATE DF"""
@@ -86,7 +86,7 @@
self._cmd.perror("Refusing to terminate the file, please read the help text.")
return
f = self._cmd.lchan.get_file_for_selectable(opts.NAME)
- (data, sw) = self._cmd.card._scc.terminate_ef(f.fid)
+ (data, sw) = self._cmd.lchan.scc.terminate_ef(f.fid)
def complete_terminate_ef(self, text, line, begidx, endidx) -> List[str]:
"""Command Line tab completion for TERMINATE EF"""
@@ -104,7 +104,7 @@
if not opts.force_terminate_card:
self._cmd.perror("Refusing to permanently terminate the card, please read the help text.")
return
- (data, sw) = self._cmd.card._scc.terminate_card_usage()
+ (data, sw) = self._cmd.lchan.scc.terminate_card_usage()
create_parser = argparse.ArgumentParser()
create_parser.add_argument('FILE_ID', type=str, help='File Identifier as 4-character hex string')
@@ -149,7 +149,7 @@
ShortFileIdentifier(decoded=opts.short_file_id),
]
fcp = FcpTemplate(children=ies)
- (data, sw) = self._cmd.card._scc.create_file(b2h(fcp.to_tlv()))
+ (data, sw) = self._cmd.lchan.scc.create_file(b2h(fcp.to_tlv()))
# the newly-created file is automatically selected but our runtime state knows nothing of it
self._cmd.lchan.select_file(self._cmd.lchan.selected_file)
@@ -200,7 +200,7 @@
}
ies.append(ProprietaryInformation(children=[ToolkitAccessConditions(decoded=toolkit_ac)]))
fcp = FcpTemplate(children=ies)
- (data, sw) = self._cmd.card._scc.create_file(b2h(fcp.to_tlv()))
+ (data, sw) = self._cmd.lchan.scc.create_file(b2h(fcp.to_tlv()))
# the newly-created file is automatically selected but our runtime state knows nothing of it
self._cmd.lchan.select_file(self._cmd.lchan.selected_file)
@@ -217,7 +217,7 @@
ies = [FileIdentifier(decoded=f.fid),
FileSize(decoded=opts.file_size)]
fcp = FcpTemplate(children=ies)
- (data, sw) = self._cmd.card._scc.resize_file(b2h(fcp.to_tlv()))
+ (data, sw) = self._cmd.lchan.scc.resize_file(b2h(fcp.to_tlv()))
# the resized file is automatically selected but our runtime state knows nothing of it
self._cmd.lchan.select_file(self._cmd.lchan.selected_file)
diff --git a/pySim/ts_31_102.py b/pySim/ts_31_102.py
index 54ff4c8..cd0d99c 100644
--- a/pySim/ts_31_102.py
+++ b/pySim/ts_31_102.py
@@ -1512,7 +1512,7 @@
@cmd2.with_argparser(authenticate_parser)
def do_authenticate(self, opts):
"""Perform Authentication and Key Agreement (AKA)."""
- (data, sw) = self._cmd.card._scc.authenticate(opts.rand, opts.autn)
+ (data, sw) = self._cmd.lchan.scc.authenticate(opts.rand, opts.autn)
self._cmd.poutput_json(data)
term_prof_parser = argparse.ArgumentParser()
@@ -1526,7 +1526,7 @@
in the context of SIM Toolkit, Proactive SIM and OTA. You
must specify a hex-string with the encoded terminal profile
you want to send to the card."""
- (data, sw) = self._cmd.card._scc.terminal_profile(opts.PROFILE)
+ (data, sw) = self._cmd.lchan.scc.terminal_profile(opts.PROFILE)
self._cmd.poutput('SW: %s, data: %s' % (sw, data))
envelope_parser = argparse.ArgumentParser()
@@ -1538,7 +1538,7 @@
variety of information is communicated from the terminal
(modem/phone) to the card, particularly in the context of
SIM Toolkit, Proactive SIM and OTA."""
- (data, sw) = self._cmd.card._scc.envelope(opts.PAYLOAD)
+ (data, sw) = self._cmd.lchan.scc.envelope(opts.PAYLOAD)
self._cmd.poutput('SW: %s, data: %s' % (sw, data))
envelope_sms_parser = argparse.ArgumentParser()
@@ -1556,7 +1556,7 @@
dev_ids = DeviceIdentities(
decoded={'source_dev_id': 'network', 'dest_dev_id': 'uicc'})
sms_dl = SMSPPDownload(children=[dev_ids, tpdu_ie])
- (data, sw) = self._cmd.card._scc.envelope(b2h(sms_dl.to_tlv()))
+ (data, sw) = self._cmd.lchan.scc.envelope(b2h(sms_dl.to_tlv()))
self._cmd.poutput('SW: %s, data: %s' % (sw, data))
get_id_parser = argparse.ArgumentParser()
@@ -1570,7 +1570,7 @@
context = 0x01 # SUCI
if opts.nswo_context:
context = 0x02 # SUCI 5G NSWO
- (data, sw) = self._cmd.card._scc.get_identity(context)
+ (data, sw) = self._cmd.lchan.scc.get_identity(context)
do = SUCI_TlvDataObject()
do.from_tlv(h2b(data))
do_d = do.to_dict()
diff --git a/pySim/ts_51_011.py b/pySim/ts_51_011.py
index 14db686..7413098 100644
--- a/pySim/ts_51_011.py
+++ b/pySim/ts_51_011.py
@@ -1001,7 +1001,7 @@
@cmd2.with_argparser(authenticate_parser)
def do_authenticate(self, opts):
"""Perform GSM Authentication."""
- (data, sw) = self._cmd.card._scc.run_gsm(opts.rand)
+ (data, sw) = self._cmd.lchan.scc.run_gsm(opts.rand)
self._cmd.poutput_json(data)