As the documentation strings say: The size should be a *tuple*. In reality we so far passed a set. The problem with the set is that ordering is not guaranteed, and hence we cannot assume the first and second item have meaning (minimum vs. default size). While at it, use a type annotation to catch such bugs easily. Change-Id: I553616f8c6c4aaa8f635b3d7d94e8e8f49ed5a56
291 lines
11 KiB
Python
291 lines
11 KiB
Python
# -*- coding: utf-8 -*-
|
|
|
|
# without this, pylint will fail when inner classes are used
|
|
# within the 'nested' kwarg of our TlvMeta metaclass on python 3.7 :(
|
|
# pylint: disable=undefined-variable
|
|
|
|
"""
|
|
DF_PHONEBOOK, DF_MULTIMEDIA, DF_MCS as specified in 3GPP TS 31.102 V16.6.0
|
|
Needs to be a separate python module to avoid cyclic imports
|
|
"""
|
|
|
|
#
|
|
# Copyright (C) 2022 Harald Welte <laforge@osmocom.org>
|
|
#
|
|
# This program is free software: you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation, either version 2 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
#
|
|
|
|
from pySim.tlv import *
|
|
from pySim.filesystem import *
|
|
from pySim.construct import *
|
|
from construct import Optional as COptional
|
|
from construct import *
|
|
|
|
# TS 31.102 Section 4.2.8
|
|
class EF_UServiceTable(TransparentEF):
|
|
def __init__(self, fid, sfid, name, desc, size, table, **kwargs):
|
|
super().__init__(fid=fid, sfid=sfid, name=name, desc=desc, size=size, **kwargs)
|
|
self.table = table
|
|
|
|
@staticmethod
|
|
def _bit_byte_offset_for_service(service: int) -> Tuple[int, int]:
|
|
i = service - 1
|
|
byte_offset = i//8
|
|
bit_offset = (i % 8)
|
|
return (byte_offset, bit_offset)
|
|
|
|
def _decode_bin(self, in_bin):
|
|
ret = {}
|
|
for i in range(0, len(in_bin)):
|
|
byte = in_bin[i]
|
|
for bitno in range(0, 8):
|
|
service_nr = i * 8 + bitno + 1
|
|
ret[service_nr] = {
|
|
'activated': True if byte & (1 << bitno) else False
|
|
}
|
|
if service_nr in self.table:
|
|
ret[service_nr]['description'] = self.table[service_nr]
|
|
return ret
|
|
|
|
def _encode_bin(self, in_json):
|
|
# compute the required binary size
|
|
bin_len = 0
|
|
for srv in in_json.keys():
|
|
service_nr = int(srv)
|
|
(byte_offset, bit_offset) = EF_UServiceTable._bit_byte_offset_for_service(
|
|
service_nr)
|
|
if byte_offset >= bin_len:
|
|
bin_len = byte_offset+1
|
|
# encode the actual data
|
|
out = bytearray(b'\x00' * bin_len)
|
|
for srv in in_json.keys():
|
|
service_nr = int(srv)
|
|
(byte_offset, bit_offset) = EF_UServiceTable._bit_byte_offset_for_service(
|
|
service_nr)
|
|
if in_json[srv]['activated'] == True:
|
|
bit = 1
|
|
else:
|
|
bit = 0
|
|
out[byte_offset] |= (bit) << bit_offset
|
|
return out
|
|
|
|
def get_active_services(self, cmd):
|
|
# obtain list of currently active services
|
|
(service_data, sw) = cmd.lchan.read_binary_dec()
|
|
active_services = []
|
|
for s in service_data.keys():
|
|
if service_data[s]['activated']:
|
|
active_services.append(s)
|
|
return active_services
|
|
|
|
def ust_service_check(self, cmd):
|
|
"""Check consistency between services of this file and files present/activated"""
|
|
num_problems = 0
|
|
# obtain list of currently active services
|
|
active_services = self.get_active_services(cmd)
|
|
# iterate over all the service-constraints we know of
|
|
files_by_service = self.parent.files_by_service
|
|
try:
|
|
for s in sorted(files_by_service.keys()):
|
|
active_str = 'active' if s in active_services else 'inactive'
|
|
cmd.poutput("Checking service No %u (%s)" % (s, active_str))
|
|
for f in files_by_service[s]:
|
|
should_exist = f.should_exist_for_services(active_services)
|
|
try:
|
|
cmd.lchan.select_file(f)
|
|
sw = None
|
|
exists = True
|
|
except SwMatchError as e:
|
|
sw = str(e)
|
|
exists = False
|
|
if exists != should_exist:
|
|
num_problems += 1
|
|
if exists:
|
|
cmd.perror(" ERROR: File %s is selectable but should not!" % f)
|
|
else:
|
|
cmd.perror(" ERROR: File %s is not selectable (%s) but should!" % (f, sw))
|
|
finally:
|
|
# re-select the EF.UST
|
|
cmd.lchan.select_file(self)
|
|
return num_problems
|
|
|
|
|
|
|
|
# TS 31.102 Section 4.4.2.1
|
|
class EF_PBR(LinFixedEF):
|
|
def __init__(self, fid='4F30', name='EF.PBR', desc='Phone Book Reference', **kwargs):
|
|
super().__init__(fid, name=name, desc=desc, **kwargs)
|
|
#self._tlv = FIXME
|
|
|
|
# TS 31.102 Section 4.4.2.12.2
|
|
class EF_PSC(TransparentEF):
|
|
_construct = Struct('synce_counter'/Int32ub)
|
|
def __init__(self, fid='4F22', name='EF.PSC', desc='Phone Book Synchronization Counter', **kwargs):
|
|
super().__init__(fid, name=name, desc=desc, **kwargs)
|
|
#self._tlv = FIXME
|
|
|
|
# TS 31.102 Section 4.4.2.12.3
|
|
class EF_CC(TransparentEF):
|
|
_construct = Struct('change_counter'/Int16ub)
|
|
def __init__(self, fid='4F23', name='EF.CC', desc='Change Counter', **kwargs):
|
|
super().__init__(fid, name=name, desc=desc, **kwargs)
|
|
|
|
# TS 31.102 Section 4.4.2.12.4
|
|
class EF_PUID(TransparentEF):
|
|
_construct = Struct('previous_uid'/Int16ub)
|
|
def __init__(self, fid='4F24', name='EF.PUID', desc='Previous Unique Identifer', **kwargs):
|
|
super().__init__(fid, name=name, desc=desc, **kwargs)
|
|
|
|
# TS 31.102 Section 4.4.2
|
|
class DF_PHONEBOOK(CardDF):
|
|
def __init__(self, fid='5F3A', name='DF.PHONEBOOK', desc='Phonebook', **kwargs):
|
|
super().__init__(fid=fid, name=name, desc=desc, **kwargs)
|
|
files = [
|
|
EF_PBR(),
|
|
EF_PSC(),
|
|
EF_CC(),
|
|
EF_PUID(),
|
|
# FIXME: Those 4Fxx entries with unspecified FID...
|
|
]
|
|
self.add_files(files)
|
|
|
|
|
|
|
|
# TS 31.102 Section 4.6.3.1
|
|
class EF_MML(BerTlvEF):
|
|
def __init__(self, fid='4F47', name='EF.MML', desc='Multimedia Messages List', **kwargs):
|
|
super().__init__(fid, name=name, desc=desc, **kwargs)
|
|
|
|
# TS 31.102 Section 4.6.3.2
|
|
class EF_MMDF(BerTlvEF):
|
|
def __init__(self, fid='4F48', name='EF.MMDF', desc='Multimedia Messages Data File', **kwargs):
|
|
super().__init__(fid, name=name, desc=desc, **kwargs)
|
|
|
|
class DF_MULTIMEDIA(CardDF):
|
|
def __init__(self, fid='5F3B', name='DF.MULTIMEDIA', desc='Multimedia', **kwargs):
|
|
super().__init__(fid=fid, name=name, desc=desc, **kwargs)
|
|
files = [
|
|
EF_MML(),
|
|
EF_MMDF(),
|
|
]
|
|
self.add_files(files)
|
|
|
|
|
|
# TS 31.102 Section 4.6.4.1
|
|
EF_MST_map = {
|
|
1: 'MCPTT UE configuration data',
|
|
2: 'MCPTT User profile data',
|
|
3: 'MCS Group configuration data',
|
|
4: 'MCPTT Service configuration data',
|
|
5: 'MCS UE initial configuration data',
|
|
6: 'MCData UE configuration data',
|
|
7: 'MCData user profile data',
|
|
8: 'MCData service configuration data',
|
|
9: 'MCVideo UE configuration data',
|
|
10: 'MCVideo user profile data',
|
|
11: 'MCVideo service configuration data',
|
|
}
|
|
|
|
# TS 31.102 Section 4.6.4.2
|
|
class EF_MCS_CONFIG(BerTlvEF):
|
|
class McpttUeConfigurationData(BER_TLV_IE, tag=0x80):
|
|
pass
|
|
class McpttUserProfileData(BER_TLV_IE, tag=0x81):
|
|
pass
|
|
class McsGroupConfigurationData(BER_TLV_IE, tag=0x82):
|
|
pass
|
|
class McpttServiceConfigurationData(BER_TLV_IE, tag=0x83):
|
|
pass
|
|
class McsUeInitialConfigurationData(BER_TLV_IE, tag=0x84):
|
|
pass
|
|
class McdataUeConfigurationData(BER_TLV_IE, tag=0x85):
|
|
pass
|
|
class McdataUserProfileData(BER_TLV_IE, tag=0x86):
|
|
pass
|
|
class McdataServiceConfigurationData(BER_TLV_IE, tag=0x87):
|
|
pass
|
|
class McvideoUeConfigurationData(BER_TLV_IE, tag=0x88):
|
|
pass
|
|
class McvideoUserProfileData(BER_TLV_IE, tag=0x89):
|
|
pass
|
|
class McvideoServiceConfigurationData(BER_TLV_IE, tag=0x8a):
|
|
pass
|
|
class McsConfigDataCollection(TLV_IE_Collection, nested=[McpttUeConfigurationData,
|
|
McpttUserProfileData, McsGroupConfigurationData,
|
|
McpttServiceConfigurationData, McsUeInitialConfigurationData,
|
|
McdataUeConfigurationData, McdataUserProfileData,
|
|
McdataServiceConfigurationData, McvideoUeConfigurationData,
|
|
McvideoUserProfileData, McvideoServiceConfigurationData]):
|
|
pass
|
|
def __init__(self, fid='4F02', sfid=0x02, name='EF.MCS_CONFIG', desc='MCS configuration data', **kwargs):
|
|
super().__init__(fid=fid, sfid=sfid, name=name, desc=desc, **kwargs)
|
|
self._tlv = EF_MCS_CONFIG.McsConfigDataCollection
|
|
|
|
# TS 31.102 Section 4.6.4.1
|
|
class EF_MST(EF_UServiceTable):
|
|
def __init__(self, fid='4F01', sfid=0x01, name='EF.MST', desc='MCS Service Table', size=(2,2),
|
|
table=EF_MST_map, **kwargs):
|
|
super().__init__(fid=fid, sfid=sfid, name=name, desc=desc, size=size, table=table)
|
|
|
|
class DF_MCS(CardDF):
|
|
def __init__(self, fid='5F3D', name='DF.MCS', desc='Mission Critical Services', **kwargs):
|
|
super().__init__(fid=fid, name=name, desc=desc, **kwargs)
|
|
files = [
|
|
EF_MST(),
|
|
EF_MCS_CONFIG(),
|
|
]
|
|
self.add_files(files)
|
|
|
|
|
|
# TS 31.102 Section 4.6.5.2
|
|
EF_VST_map = {
|
|
1: 'MCPTT UE configuration data',
|
|
2: 'MCPTT User profile data',
|
|
3: 'MCS Group configuration data',
|
|
4: 'MCPTT Service configuration data',
|
|
5: 'MCS UE initial configuration data',
|
|
6: 'MCData UE configuration data',
|
|
7: 'MCData user profile data',
|
|
8: 'MCData service configuration data',
|
|
9: 'MCVideo UE configuration data',
|
|
10: 'MCVideo user profile data',
|
|
11: 'MCVideo service configuration data',
|
|
}
|
|
|
|
# TS 31.102 Section 4.6.5.2
|
|
class EF_VST(EF_UServiceTable):
|
|
def __init__(self, fid='4F01', sfid=0x01, name='EF.VST', desc='V2X Service Table', size=(2,2),
|
|
table=EF_VST_map, **kwargs):
|
|
super().__init__(fid=fid, sfid=sfid, name=name, desc=desc, size=size, table=table)
|
|
|
|
# TS 31.102 Section 4.6.5.3
|
|
class EF_V2X_CONFIG(BerTlvEF):
|
|
class V2xConfigurationData(BER_TLV_IE, tag=0x80):
|
|
pass
|
|
class V2xConfigDataCollection(TLV_IE_Collection, nested=[V2xConfigurationData]):
|
|
pass
|
|
def __init__(self, fid='4F02', sfid=0x02, name='EF.V2X_CONFIG', desc='V2X configuration data', **kwargs):
|
|
super().__init__(fid=fid, sfid=sfid, name=name, desc=desc, **kwargs)
|
|
self._tlv = EF_V2X_CONFIG.V2xConfigDataCollection
|
|
|
|
# TS 31.102 Section 4.6.5
|
|
class DF_V2X(CardDF):
|
|
def __init__(self, fid='5F3E', name='DF.V2X', desc='Vehicle to X', **kwargs):
|
|
super().__init__(fid=fid, name=name, desc=desc, **kwargs)
|
|
files = [
|
|
EF_VST(),
|
|
EF_V2X_CONFIG(),
|
|
]
|
|
self.add_files(files)
|