You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

261 lines
7.7 KiB

# Copyright (C) 2020 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
r"""Read APN conf xml file and output an textpb.
How to run:
update_apn.par --apn_file=./apns-full-conf.xml \
--data_dir=./data --out_file=/tmpapns.textpb
"""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
import argparse
import collections
from xml.dom import minidom
from google.protobuf import text_format
import carrier_list_pb2
import carrier_settings_pb2
parser = argparse.ArgumentParser()
parser.add_argument(
'--apn_file', default='./apns-full-conf.xml', help='Path to APN xml file')
parser.add_argument(
'--data_dir', default='./data', help='Folder path for CarrierSettings data')
parser.add_argument(
'--out_file', default='./tmpapns.textpb', help='Temp APN file')
FLAGS = parser.parse_args()
CARRIER_LISTS = ['tier1_carriers.textpb', 'other_carriers.textpb']
def to_string(cid):
"""Return a string for CarrierId."""
ret = cid.mcc_mnc
if cid.HasField('spn'):
ret += 'SPN=' + cid.spn.upper()
if cid.HasField('imsi'):
ret += 'IMSI=' + cid.imsi.upper()
if cid.HasField('gid1'):
ret += 'GID1=' + cid.gid1.upper()
return ret
def get_cname(cid, known_carriers):
"""Return a canonical name based on cid and known_carriers.
If found a match in known_carriers, return it. Otherwise generate a new one
by concating the values.
Args:
cid: proto of CarrierId
known_carriers: mapping from mccmnc and possible mvno data to canonical name
Returns:
string for canonical name, like verizon_us or 27402
"""
name = to_string(cid)
if name in known_carriers:
return known_carriers[name]
else:
return name
def get_knowncarriers(files):
"""Create a mapping from mccmnc and possible mvno data to canonical name.
Args:
files: list of paths to carrier list textpb files
Returns:
A dict, key is to_string(carrier_id), value is cname.
"""
ret = dict()
for path in files:
with open(path, 'r', encoding='utf-8') as f:
carriers = text_format.Parse(f.read(), carrier_list_pb2.CarrierList())
for carriermap in carriers.entry:
# could print error if already exist
for cid in carriermap.carrier_id:
ret[to_string(cid)] = carriermap.canonical_name
return ret
def gen_cid(apn_node):
"""Generate carrier id proto from APN node.
Args:
apn_node: DOM node from getElementsByTag
Returns:
CarrierId proto
"""
ret = carrier_list_pb2.CarrierId()
ret.mcc_mnc = (apn_node.getAttribute('mcc') + apn_node.getAttribute('mnc'))
mvno_type = apn_node.getAttribute('mvno_type')
mvno_data = apn_node.getAttribute('mvno_match_data')
if mvno_type.lower() == 'spn':
ret.spn = mvno_data
if mvno_type.lower() == 'imsi':
ret.imsi = mvno_data
# in apn xml, gid means gid1, and no gid2
if mvno_type.lower() == 'gid':
ret.gid1 = mvno_data
return ret
APN_TYPE_MAP = {
'*': carrier_settings_pb2.ApnItem.ALL,
'default': carrier_settings_pb2.ApnItem.DEFAULT,
'internet': carrier_settings_pb2.ApnItem.DEFAULT,
'vzw800': carrier_settings_pb2.ApnItem.DEFAULT,
'mms': carrier_settings_pb2.ApnItem.MMS,
'sup': carrier_settings_pb2.ApnItem.SUPL,
'supl': carrier_settings_pb2.ApnItem.SUPL,
'agps': carrier_settings_pb2.ApnItem.SUPL,
'pam': carrier_settings_pb2.ApnItem.DUN,
'dun': carrier_settings_pb2.ApnItem.DUN,
'hipri': carrier_settings_pb2.ApnItem.HIPRI,
'ota': carrier_settings_pb2.ApnItem.FOTA,
'fota': carrier_settings_pb2.ApnItem.FOTA,
'admin': carrier_settings_pb2.ApnItem.FOTA,
'ims': carrier_settings_pb2.ApnItem.IMS,
'cbs': carrier_settings_pb2.ApnItem.CBS,
'ia': carrier_settings_pb2.ApnItem.IA,
'emergency': carrier_settings_pb2.ApnItem.EMERGENCY,
'xcap': carrier_settings_pb2.ApnItem.XCAP,
'ut': carrier_settings_pb2.ApnItem.UT,
'rcs': carrier_settings_pb2.ApnItem.RCS,
}
def map_apntype(typestr):
"""Map from APN type string to list of ApnType enums.
Args:
typestr: APN type string in apn conf xml, comma separated
Returns:
List of ApnType values in ApnItem proto.
"""
typelist = [apn.strip().lower() for apn in typestr.split(',')]
return list(set([APN_TYPE_MAP[t] for t in typelist if t]))
APN_PROTOCOL_MAP = {
'ip': carrier_settings_pb2.ApnItem.IP,
'ipv4': carrier_settings_pb2.ApnItem.IP,
'ipv6': carrier_settings_pb2.ApnItem.IPV6,
'ipv4v6': carrier_settings_pb2.ApnItem.IPV4V6,
'ppp': carrier_settings_pb2.ApnItem.PPP
}
BOOL_MAP = {'true': True, 'false': False, '1': True, '0': False}
APN_SKIPXLAT_MAP = {
-1: carrier_settings_pb2.ApnItem.SKIP_464XLAT_DEFAULT,
0: carrier_settings_pb2.ApnItem.SKIP_464XLAT_DISABLE,
1: carrier_settings_pb2.ApnItem.SKIP_464XLAT_ENABLE
}
# not include already handeld string keys like mcc, protocol
APN_STRING_KEYS = [
'bearer_bitmask', 'server', 'proxy', 'port', 'user', 'password', 'mmsc',
'mmsc_proxy', 'mmsc_proxy_port'
]
# keys that are different between apn.xml and apn.proto
APN_REMAP_KEYS = {
'mmsproxy': 'mmsc_proxy',
'mmsport': 'mmsc_proxy_port'
}
APN_INT_KEYS = [
'authtype', 'mtu', 'profile_id', 'max_conns', 'wait_time', 'max_conns_time'
]
APN_BOOL_KEYS = [
'modem_cognitive', 'user_visible', 'user_editable'
]
def gen_apnitem(node):
"""Create ApnItem proto based on APN node from xml file.
Args:
node: xml dom node from apn conf xml file.
Returns:
An ApnItem proto.
"""
apn = carrier_settings_pb2.ApnItem()
apn.name = node.getAttribute('carrier')
apn.value = node.getAttribute('apn')
apn.type.extend(map_apntype(node.getAttribute('type')))
for key in ['protocol', 'roaming_protocol']:
if node.hasAttribute(key):
setattr(apn, key, APN_PROTOCOL_MAP[node.getAttribute(key).lower()])
for key in node.attributes.keys():
# Treat bearer as bearer_bitmask if no bearer_bitmask specified
if key == 'bearer' and not node.hasAttribute('bearer_bitmask'):
setattr(apn, 'bearer_bitmask', node.getAttribute(key))
continue
if key == 'skip_464xlat':
setattr(apn, key, APN_SKIPXLAT_MAP[int(node.getAttribute(key))])
continue
if key in APN_STRING_KEYS:
setattr(apn, key, node.getAttribute(key))
continue
if key in APN_REMAP_KEYS:
setattr(apn, APN_REMAP_KEYS[key], node.getAttribute(key))
continue
if key in APN_INT_KEYS:
setattr(apn, key, int(node.getAttribute(key)))
continue
if key in APN_BOOL_KEYS:
setattr(apn, key, BOOL_MAP[node.getAttribute(key).lower()])
continue
return apn
def main():
known = get_knowncarriers([FLAGS.data_dir + '/' + f for f in CARRIER_LISTS])
with open(FLAGS.apn_file, 'r', encoding='utf-8') as apnfile:
dom = minidom.parse(apnfile)
apn_map = collections.defaultdict(list)
for apn_node in dom.getElementsByTagName('apn'):
cname = get_cname(gen_cid(apn_node), known)
apn = gen_apnitem(apn_node)
apn_map[cname].append(apn)
mcs = carrier_settings_pb2.MultiCarrierSettings()
for c in apn_map:
carriersettings = mcs.setting.add()
carriersettings.canonical_name = c
carriersettings.apns.apn.extend(apn_map[c])
with open(FLAGS.out_file, 'w', encoding='utf-8') as apnout:
apnout.write(text_format.MessageToString(mcs, as_utf8=True))
if __name__ == '__main__':
main()