diff --git a/frontend/index.html b/frontend/index.html
index f087bf5..1c76954 100644
--- a/frontend/index.html
+++ b/frontend/index.html
@@ -1413,7 +1413,7 @@
// ===== Version =====
// Single source of truth for the PWA version: shown in the header and used
// by the server version check in pysimConnect().
-const SIMPLE_VERSION = '2.7.16';
+const SIMPLE_VERSION = '2.7.17';
document.getElementById('app-version').textContent = 'v' + SIMPLE_VERSION;
// ===== Tab switching =====
@@ -8303,15 +8303,26 @@ function pysimProactiveSeqChanged(seq) {
_pysimProactiveSeq = seq;
return true;
}
+// TS 102 223 9.4 command types (values equal the proactive command template
+// tags); the log falls back to 'Cmd 0xNN' for anything not listed.
const CMD_NAMES = {
- '03': 'POLL INTERVAL', '05': 'SET UP EVENT LIST',
- '13': 'SEND SHORT MESSAGE', '20': 'PLAY TONE',
- '21': 'DISPLAY TEXT', '22': 'GET INKEY', '23': 'GET INPUT',
- '24': 'SELECT ITEM', '25': 'SET UP MENU',
- '26': 'PROVIDE LOCAL INFORMATION', '27': 'TIMER MANAGEMENT',
- '15': 'LAUNCH BROWSER', '70': 'ACTIVATE',
- '40': 'OPEN CHANNEL', '41': 'CLOSE CHANNEL',
- '42': 'RECEIVE DATA', '43': 'SEND DATA', '44': 'GET CHANNEL STATUS',
+ '01': 'REFRESH', '02': 'MORE TIME', '03': 'POLL INTERVAL', '04': 'POLLING OFF',
+ '05': 'SET UP EVENT LIST', '10': 'SET UP CALL', '11': 'SEND SS', '12': 'SEND USSD',
+ '13': 'SEND SHORT MESSAGE', '14': 'SEND DTMF', '15': 'LAUNCH BROWSER',
+ '16': 'GEOGRAPHICAL LOCATION REQUEST',
+ '20': 'PLAY TONE', '21': 'DISPLAY TEXT', '22': 'GET INKEY', '23': 'GET INPUT',
+ '24': 'SELECT ITEM', '25': 'SET UP MENU', '26': 'PROVIDE LOCAL INFORMATION',
+ '27': 'TIMER MANAGEMENT', '28': 'SET UP IDLE MODE TEXT',
+ '30': 'PERFORM CARD APDU', '31': 'POWER ON CARD', '32': 'POWER OFF CARD',
+ '33': 'GET READER STATUS', '34': 'RUN AT COMMAND', '35': 'LANGUAGE NOTIFICATION',
+ '40': 'OPEN CHANNEL', '41': 'CLOSE CHANNEL', '42': 'RECEIVE DATA', '43': 'SEND DATA',
+ '44': 'GET CHANNEL STATUS', '45': 'SERVICE SEARCH', '46': 'GET SERVICE INFORMATION',
+ '47': 'DECLARE SERVICE', '50': 'SET FRAMES', '51': 'GET FRAMES STATUS',
+ '60': 'RETRIEVE MULTIMEDIA MESSAGE', '61': 'SUBMIT MULTIMEDIA MESSAGE',
+ '62': 'DISPLAY MULTIMEDIA MESSAGE', '70': 'ACTIVATE',
+ '71': 'CONTACTLESS STATE CHANGED', '72': 'COMMAND CONTAINER',
+ '73': 'ENCAPSULATED SESSION CONTROL', '79': 'LSI COMMAND',
+ '81': 'End of the proactive UICC session',
};
const CMD_QUALIFIER_SHORT = {
diff --git a/frontend/sw.js b/frontend/sw.js
index 4c0a54d..c82e49d 100644
--- a/frontend/sw.js
+++ b/frontend/sw.js
@@ -1,4 +1,4 @@
-const CACHE = 'simple-v219';
+const CACHE = 'simple-v220';
const URLS = [
'index.html',
'help.html',
diff --git a/frontend/tests/event_forms.test.js b/frontend/tests/event_forms.test.js
index 03e5e3a..5c375af 100644
--- a/frontend/tests/event_forms.test.js
+++ b/frontend/tests/event_forms.test.js
@@ -35,7 +35,10 @@ eval(extractBlock('const REJECTION_CAUSES = [', 'const EVENT_FORMS = {').replace
eval(extractBlock('const EVENT_FORMS = {', 'const PLI_QUALIFIERS = [').replace(/^const /gm, 'var '));
test('CMD_NAMES decodes timer management and the BIP commands', () => {
+ assert.strictEqual(CMD_NAMES['01'], 'REFRESH');
+ assert.strictEqual(CMD_NAMES['10'], 'SET UP CALL');
assert.strictEqual(CMD_NAMES['27'], 'TIMER MANAGEMENT');
+ assert.strictEqual(CMD_NAMES['28'], 'SET UP IDLE MODE TEXT');
assert.strictEqual(CMD_NAMES['40'], 'OPEN CHANNEL');
assert.strictEqual(CMD_NAMES['41'], 'CLOSE CHANNEL');
assert.strictEqual(CMD_NAMES['42'], 'RECEIVE DATA');
diff --git a/pyproject.toml b/pyproject.toml
index 1b77bbd..302e31b 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "pysim-simple-server"
-version = "2.7.16"
+version = "2.7.17"
description = "HTTP REST server wrapping pysim for the SIMple PWA"
requires-python = ">=3.8"
# pysim is a git-only dependency installed explicitly by setup.bat/setup.sh.
diff --git a/pysim_simple_server/server.py b/pysim_simple_server/server.py
index b6d07fb..bf9f8df 100644
--- a/pysim_simple_server/server.py
+++ b/pysim_simple_server/server.py
@@ -26,7 +26,7 @@ from osmocom.tlv import BER_TLV_IE
from osmocom.utils import rpad
-VERSION = '2.7.16'
+VERSION = '2.7.17'
MAX_ENVELOPE_SEGMENTS = 5 # max SMS segments for outgoing C-APDU in ENVELOPE
@@ -1086,15 +1086,29 @@ _PROACTIVE_LOG = []
_PROACTIVE_SESSION_START = None
_PROACTIVE_ENTRY_ID = 0
+# TS 102 223 §9.4 command types (the values are also the proactive command
+# template tags). Unknown values fall back to 'UNKNOWN' in the log.
PROACTIVE_TYPE_NAMES = {
- 0x03: 'POLL INTERVAL', 0x05: 'SET UP EVENT LIST',
- 0x13: 'SEND SHORT MESSAGE', 0x20: 'PLAY TONE',
+ 0x01: 'REFRESH', 0x02: 'MORE TIME', 0x03: 'POLL INTERVAL',
+ 0x04: 'POLLING OFF', 0x05: 'SET UP EVENT LIST', 0x10: 'SET UP CALL',
+ 0x11: 'SEND SS', 0x12: 'SEND USSD', 0x13: 'SEND SHORT MESSAGE',
+ 0x14: 'SEND DTMF', 0x15: 'LAUNCH BROWSER',
+ 0x16: 'GEOGRAPHICAL LOCATION REQUEST', 0x20: 'PLAY TONE',
0x21: 'DISPLAY TEXT', 0x22: 'GET INKEY', 0x23: 'GET INPUT',
0x24: 'SELECT ITEM', 0x25: 'SET UP MENU',
0x26: 'PROVIDE LOCAL INFORMATION', 0x27: 'TIMER MANAGEMENT',
- 0x15: 'LAUNCH BROWSER', 0x70: 'ACTIVATE',
- 0x40: 'OPEN CHANNEL', 0x41: 'CLOSE CHANNEL',
- 0x42: 'RECEIVE DATA', 0x43: 'SEND DATA', 0x44: 'GET CHANNEL STATUS',
+ 0x28: 'SET UP IDLE MODE TEXT', 0x30: 'PERFORM CARD APDU',
+ 0x31: 'POWER ON CARD', 0x32: 'POWER OFF CARD', 0x33: 'GET READER STATUS',
+ 0x34: 'RUN AT COMMAND', 0x35: 'LANGUAGE NOTIFICATION',
+ 0x40: 'OPEN CHANNEL', 0x41: 'CLOSE CHANNEL', 0x42: 'RECEIVE DATA',
+ 0x43: 'SEND DATA', 0x44: 'GET CHANNEL STATUS', 0x45: 'SERVICE SEARCH',
+ 0x46: 'GET SERVICE INFORMATION', 0x47: 'DECLARE SERVICE',
+ 0x50: 'SET FRAMES', 0x51: 'GET FRAMES STATUS',
+ 0x60: 'RETRIEVE MULTIMEDIA MESSAGE', 0x61: 'SUBMIT MULTIMEDIA MESSAGE',
+ 0x62: 'DISPLAY MULTIMEDIA MESSAGE', 0x70: 'ACTIVATE',
+ 0x71: 'CONTACTLESS STATE CHANGED', 0x72: 'COMMAND CONTAINER',
+ 0x73: 'ENCAPSULATED SESSION CONTROL', 0x79: 'LSI COMMAND',
+ 0x81: 'End of the proactive UICC session',
}
PLI_QUALIFIER_NAMES = {
diff --git a/tests/test_proactive_names.py b/tests/test_proactive_names.py
new file mode 100644
index 0000000..caf99c1
--- /dev/null
+++ b/tests/test_proactive_names.py
@@ -0,0 +1,38 @@
+# coding=utf-8
+"""Tests for the proactive command type names (TS 102 223 §9.4).
+
+The live card issues REFRESH (0x01) right after a Location status event; a
+missing entry makes the proactive log show the generic 'Cmd 0x01' fallback.
+"""
+
+import unittest
+
+from pysim_simple_server.server import PROACTIVE_TYPE_NAMES
+
+
+class ProactiveTypeNamesTests(unittest.TestCase):
+ def test_refresh_is_named(self):
+ self.assertEqual(PROACTIVE_TYPE_NAMES[0x01], 'REFRESH')
+
+ def test_common_types_match_the_spec(self):
+ for value, name in [
+ (0x02, 'MORE TIME'), (0x03, 'POLL INTERVAL'), (0x04, 'POLLING OFF'),
+ (0x05, 'SET UP EVENT LIST'), (0x10, 'SET UP CALL'),
+ (0x11, 'SEND SS'), (0x12, 'SEND USSD'),
+ (0x13, 'SEND SHORT MESSAGE'), (0x14, 'SEND DTMF'),
+ (0x15, 'LAUNCH BROWSER'), (0x16, 'GEOGRAPHICAL LOCATION REQUEST'),
+ (0x21, 'DISPLAY TEXT'), (0x24, 'SELECT ITEM'), (0x25, 'SET UP MENU'),
+ (0x26, 'PROVIDE LOCAL INFORMATION'), (0x27, 'TIMER MANAGEMENT'),
+ (0x28, 'SET UP IDLE MODE TEXT'), (0x40, 'OPEN CHANNEL'),
+ (0x44, 'GET CHANNEL STATUS'), (0x70, 'ACTIVATE'),
+ ]:
+ self.assertEqual(PROACTIVE_TYPE_NAMES[value], name, hex(value))
+
+ def test_names_are_nonempty_strings(self):
+ for value, name in PROACTIVE_TYPE_NAMES.items():
+ self.assertIsInstance(value, int)
+ self.assertTrue(isinstance(name, str) and name, hex(value))
+
+
+if __name__ == '__main__':
+ unittest.main()