v3.25.1: the run-ownership fix for the proactive auto-handling

A card that re-queues a command immediately after each accepted TR
(live 2026-10-11: the EP BAP card re-issued the same PROVIDE LOCAL
INFORMATION 1100 times) turned the background poll's FETCH/TR chain
into an unbounded loop that held _CARD_LOCK forever - every card
operation, a test run's start included, blocked behind it.

The chain now yields:
- _handle_proactive_chain aborts before the next FETCH while a test run
  owns the card (_TEST_RUNNING) - the pending command stays for the
  script - and takes max_rounds; the background poll passes
  _POLL_CHAIN_MAX = 10, so a chatty card cannot hold the lock.
- _do_status_poll re-arms on its run skip (a heartbeat that never
  fetches); _timer_expired and the BIP Data-available event wait during
  a run; _bip_flush_channel_events stays quiet too.
- _sim_resume_poll() resumes polling at every run end - the two worker
  bodies, their exception paths and the spawn failure.

Also the TR command-details qualifier echo (TS 102 223 6.8.1: the
object shall be identical to the command's): _build_tr,
testscript.build_tr and the interactive _menu_send_response echoed a
hardcoded 00 - harmless for the loop's qual-00 PLI, wrong for any
other qualifier.  The menu pendings now carry cmd_qual (parsed in the
on_fetch handler).

Tests: the chain bound/abort (a fake card re-queueing a command), the
poll/timer guards, the resume wiring, the qualifier echo in both
builders.  883 frontend + 865 Python; sw cache simple-v425.
This commit is contained in:
2026-10-11 12:23:12 +03:00
parent 87931afc41
commit af00932aca
10 changed files with 4743 additions and 32 deletions
+7 -3
View File
@@ -700,15 +700,19 @@ def _encode_text(text, dcs):
return text.encode('gsm03.38')
def build_tr(cmd_num, cmd_type, dev_dst, dev_src, respond, answer_tlvs=None):
def build_tr(cmd_num, cmd_type, dev_dst, dev_src, respond, answer_tlvs=None,
cmd_qual=None):
"""Flat COMPREHENSION-TLV TERMINAL RESPONSE payload (TS 102 223 6.8) in the
6.8.0 object order: command details, device identities, Result, then the
command-specific objects (the terminal's own `answer_tlvs` - the PLI data
of PROVIDE LOCAL INFORMATION, the echoed Duration of POLL INTERVAL - the
Text string for GET INKEY/GET INPUT, the Item identifier for SELECT ITEM)
and any extra raw TLVs. Matches the interactive menu TR layout; the
runner passes the terminal objects the script did not override."""
out = bytearray([0x81, 0x03, cmd_num & 0xFF, cmd_type & 0xFF, 0x00])
runner passes the terminal objects the script did not override. The
command details echo the command's qualifier (TS 102 223 6.8.1: the
object shall be identical to the command's)."""
out = bytearray([0x81, 0x03, cmd_num & 0xFF, cmd_type & 0xFF,
(cmd_qual or 0) & 0xFF])
out += bytes([0x82, 0x02, dev_dst & 0xFF, dev_src & 0xFF])
result = int(respond.get('result', 0))
add_info = int(respond.get('additional_info', 0) or 0)