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
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()
|