bip/smpp2sim: TERMINAL PROFILE that matches what we do

pySim-smpp2sim sends "ff" * 32, that byte list tells every card the
terminal has a display, a keypad, a second card slot, a radio it can
query for location and NMR, five BIP bearers and six transport modes and
a toaster and a dog according to TS 102 223 5.2

We only have the twelfth byte and one bit of the seventeenth and no dog.

A card issues annoying weird things like PROVIDE LOCAL INFORMATION or
UDP only because the profile said so, so stop pretending we know what any
of that is.

Annex T table T.1 lists what a Connected Entity (a CAT client that is not
the modem) may announce. Announce that and the SMS-PP download and
SEND SHORT MESSAGE bits the OTA path needs, only the bearer is obviously
made up, it's the host TCP stack and 5.2 closest match is GPRS.

We can still extend and change all of this, but for now something that
works and constrains what the card asks for and is therefore actually
reproducible is important.

Change-Id: I91a60760fc3ad816b7385da8b26ec7330b462abd
This commit is contained in:
Eric Wild
2026-09-23 17:51:04 +02:00
parent d671cee649
commit 7e8f711ec2
3 changed files with 78 additions and 7 deletions
+39 -1
View File
@@ -30,7 +30,7 @@ from pySim.cat import (ProactiveCommand, CommandDetails, DeviceIdentities,
OtherAddress, ChannelData, ChannelDataLength, ChannelStatus,
Result)
from pySim.bip import Proact
from pySim.bip import Proact, ProactChannels, terminal_profile
class _EchoServer:
@@ -329,6 +329,44 @@ class OpenChannelRefusalTest(unittest.TestCase):
self.assertEqual(self.proact.channels.channels, {}) # channel given back
class TerminalProfileTest(unittest.TestCase):
"""TS 102 223 5.2, one bit per CAT facility"""
def setUp(self):
self.profile = terminal_profile()
def byte(self, n):
return self.profile[n - 1] # 1-based, as 5.2 numbers them
def test_announced(self):
self.assertEqual(len(self.profile), 32)
self.assertEqual(self.byte(1), 0x13) # profile download, SMS-PP download b2+b5
self.assertEqual(self.byte(4), 0x02) # SEND SHORT MESSAGE
self.assertEqual(self.byte(5) & 0x01, 0x01) # SET UP EVENT LIST
self.assertEqual(self.byte(6), 0x0c) # events: data available, channel status
self.assertEqual(self.byte(12), 0x1f) # OPEN/CLOSE CHANNEL, RECEIVE/SEND DATA, STATUS
self.assertEqual(self.byte(13) >> 5, ProactChannels.MAX_CHANNELS)
self.assertEqual(self.byte(14), 0x60) # class ND, class NK
self.assertEqual(self.byte(17), 0x01) # TCP, UICC client mode, remote
def test_not_announced(self):
self.assertEqual(self.byte(3) & 0x60, 0) # POLL INTERVAL, POLLING OFF
self.assertEqual(self.byte(4) & 0xc0, 0) # PROVIDE LOCAL INFORMATION, NMR
self.assertEqual(self.byte(12) & 0xe0, 0) # SERVICE SEARCH/INFORMATION, DECLARE SERVICE
self.assertEqual(self.byte(14) & 0x1f, 0) # no characters down the display
for n in (7, 9, 10, 11, 15, 16, 18): # class "a", class "d", display, ESN/IMEISV
self.assertEqual(self.byte(n), 0)
def test_channel_count(self):
self.assertEqual(terminal_profile(3)[12] >> 5, 3)
with self.assertRaises(ValueError): # 8.56: 1 to 7
terminal_profile(8)
if __name__ == "__main__":
unittest.main()
class BipSinkTest(unittest.TestCase):
"""Both sinks are optional, a driver with no SMS path at all must not crash and burn
with a card that sends one, and one that has one must get the PDU."""