• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright (C) 2020 Google LLC
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7#      http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15r"""Read APN conf xml file and output an textpb.
16
17How to run:
18
19update_apn.par --apn_file=./apns-full-conf.xml \
20--data_dir=./data --out_file=/tmpapns.textpb
21"""
22
23from __future__ import absolute_import
24from __future__ import division
25from __future__ import print_function
26import argparse
27import collections
28from xml.dom import minidom
29from google.protobuf import text_format
30
31import carrier_list_pb2
32import carrier_settings_pb2
33
34parser = argparse.ArgumentParser()
35parser.add_argument(
36    '--apn_file', default='./apns-full-conf.xml', help='Path to APN xml file')
37parser.add_argument(
38    '--data_dir', default='./data', help='Folder path for CarrierSettings data')
39parser.add_argument(
40    '--out_file', default='./tmpapns.textpb', help='Temp APN file')
41FLAGS = parser.parse_args()
42
43CARRIER_LISTS = ['tier1_carriers.textpb', 'other_carriers.textpb']
44
45
46def to_string(cid):
47  """Return a string for CarrierId."""
48  ret = cid.mcc_mnc
49  if cid.HasField('spn'):
50    ret += 'SPN=' + cid.spn.upper()
51  if cid.HasField('imsi'):
52    ret += 'IMSI=' + cid.imsi.upper()
53  if cid.HasField('gid1'):
54    ret += 'GID1=' + cid.gid1.upper()
55  return ret
56
57
58def get_cname(cid, known_carriers):
59  """Return a canonical name based on cid and known_carriers.
60
61  If found a match in known_carriers, return it. Otherwise generate a new one
62  by concating the values.
63
64  Args:
65    cid: proto of CarrierId
66    known_carriers: mapping from mccmnc and possible mvno data to canonical name
67
68  Returns:
69    string for canonical name, like verizon_us or 27402
70  """
71  name = to_string(cid)
72  if name in known_carriers:
73    return known_carriers[name]
74  else:
75    return name
76
77
78def get_knowncarriers(files):
79  """Create a mapping from mccmnc and possible mvno data to canonical name.
80
81  Args:
82    files: list of paths to carrier list textpb files
83
84  Returns:
85    A dict, key is to_string(carrier_id), value is cname.
86  """
87  ret = dict()
88  for path in files:
89    with open(path, 'r', encoding='utf-8') as f:
90      carriers = text_format.Parse(f.read(), carrier_list_pb2.CarrierList())
91      for carriermap in carriers.entry:
92        # could print error if already exist
93        for cid in carriermap.carrier_id:
94          ret[to_string(cid)] = carriermap.canonical_name
95
96  return ret
97
98
99def gen_cid(apn_node):
100  """Generate carrier id proto from APN node.
101
102  Args:
103    apn_node: DOM node from getElementsByTag
104
105  Returns:
106    CarrierId proto
107  """
108  ret = carrier_list_pb2.CarrierId()
109  ret.mcc_mnc = (apn_node.getAttribute('mcc') + apn_node.getAttribute('mnc'))
110  mvno_type = apn_node.getAttribute('mvno_type')
111  mvno_data = apn_node.getAttribute('mvno_match_data')
112  if mvno_type.lower() == 'spn':
113    ret.spn = mvno_data
114  if mvno_type.lower() == 'imsi':
115    ret.imsi = mvno_data
116  # in apn xml, gid means gid1, and no gid2
117  if mvno_type.lower() == 'gid':
118    ret.gid1 = mvno_data
119  return ret
120
121
122APN_TYPE_MAP = {
123    '*': carrier_settings_pb2.ApnItem.ALL,
124    'default': carrier_settings_pb2.ApnItem.DEFAULT,
125    'internet': carrier_settings_pb2.ApnItem.DEFAULT,
126    'vzw800': carrier_settings_pb2.ApnItem.DEFAULT,
127    'mms': carrier_settings_pb2.ApnItem.MMS,
128    'sup': carrier_settings_pb2.ApnItem.SUPL,
129    'supl': carrier_settings_pb2.ApnItem.SUPL,
130    'agps': carrier_settings_pb2.ApnItem.SUPL,
131    'pam': carrier_settings_pb2.ApnItem.DUN,
132    'dun': carrier_settings_pb2.ApnItem.DUN,
133    'hipri': carrier_settings_pb2.ApnItem.HIPRI,
134    'ota': carrier_settings_pb2.ApnItem.FOTA,
135    'fota': carrier_settings_pb2.ApnItem.FOTA,
136    'admin': carrier_settings_pb2.ApnItem.FOTA,
137    'ims': carrier_settings_pb2.ApnItem.IMS,
138    'cbs': carrier_settings_pb2.ApnItem.CBS,
139    'ia': carrier_settings_pb2.ApnItem.IA,
140    'emergency': carrier_settings_pb2.ApnItem.EMERGENCY,
141    'xcap': carrier_settings_pb2.ApnItem.XCAP,
142    'ut': carrier_settings_pb2.ApnItem.UT,
143    'rcs': carrier_settings_pb2.ApnItem.RCS,
144}
145
146
147def map_apntype(typestr):
148  """Map from APN type string to list of ApnType enums.
149
150  Args:
151    typestr: APN type string in apn conf xml, comma separated
152  Returns:
153    List of ApnType values in ApnItem proto.
154  """
155  typelist = [apn.strip().lower() for apn in typestr.split(',')]
156  return list(set([APN_TYPE_MAP[t] for t in typelist if t]))
157
158
159APN_PROTOCOL_MAP = {
160    'ip': carrier_settings_pb2.ApnItem.IP,
161    'ipv4': carrier_settings_pb2.ApnItem.IP,
162    'ipv6': carrier_settings_pb2.ApnItem.IPV6,
163    'ipv4v6': carrier_settings_pb2.ApnItem.IPV4V6,
164    'ppp': carrier_settings_pb2.ApnItem.PPP
165}
166
167BOOL_MAP = {'true': True, 'false': False, '1': True, '0': False}
168
169APN_SKIPXLAT_MAP = {
170    -1: carrier_settings_pb2.ApnItem.SKIP_464XLAT_DEFAULT,
171    0: carrier_settings_pb2.ApnItem.SKIP_464XLAT_DISABLE,
172    1: carrier_settings_pb2.ApnItem.SKIP_464XLAT_ENABLE
173}
174
175# not include already handeld string keys like mcc, protocol
176APN_STRING_KEYS = [
177    'bearer_bitmask', 'server', 'proxy', 'port', 'user', 'password', 'mmsc',
178    'mmsc_proxy', 'mmsc_proxy_port'
179]
180
181# keys that are different between apn.xml and apn.proto
182APN_REMAP_KEYS = {
183    'mmsproxy': 'mmsc_proxy',
184    'mmsport': 'mmsc_proxy_port'
185}
186
187APN_INT_KEYS = [
188    'authtype', 'mtu', 'profile_id', 'max_conns', 'wait_time', 'max_conns_time'
189]
190
191APN_BOOL_KEYS = [
192    'modem_cognitive', 'user_visible', 'user_editable'
193]
194
195
196def gen_apnitem(node):
197  """Create ApnItem proto based on APN node from xml file.
198
199  Args:
200    node: xml dom node from apn conf xml file.
201
202  Returns:
203    An ApnItem proto.
204  """
205  apn = carrier_settings_pb2.ApnItem()
206  apn.name = node.getAttribute('carrier')
207  apn.value = node.getAttribute('apn')
208  apn.type.extend(map_apntype(node.getAttribute('type')))
209  for key in ['protocol', 'roaming_protocol']:
210    if node.hasAttribute(key):
211      setattr(apn, key, APN_PROTOCOL_MAP[node.getAttribute(key).lower()])
212
213  for key in node.attributes.keys():
214    # Treat bearer as bearer_bitmask if no bearer_bitmask specified
215    if key == 'bearer' and not node.hasAttribute('bearer_bitmask'):
216      setattr(apn, 'bearer_bitmask', node.getAttribute(key))
217      continue
218    if key == 'skip_464xlat':
219      setattr(apn, key, APN_SKIPXLAT_MAP[int(node.getAttribute(key))])
220      continue
221    if key in APN_STRING_KEYS:
222      setattr(apn, key, node.getAttribute(key))
223      continue
224    if key in APN_REMAP_KEYS:
225      setattr(apn, APN_REMAP_KEYS[key], node.getAttribute(key))
226      continue
227    if key in APN_INT_KEYS:
228      setattr(apn, key, int(node.getAttribute(key)))
229      continue
230    if key in APN_BOOL_KEYS:
231      setattr(apn, key, BOOL_MAP[node.getAttribute(key).lower()])
232      continue
233
234  return apn
235
236
237def main():
238  known = get_knowncarriers([FLAGS.data_dir + '/' + f for f in CARRIER_LISTS])
239
240  with open(FLAGS.apn_file, 'r', encoding='utf-8') as apnfile:
241    dom = minidom.parse(apnfile)
242
243  apn_map = collections.defaultdict(list)
244  for apn_node in dom.getElementsByTagName('apn'):
245    cname = get_cname(gen_cid(apn_node), known)
246    apn = gen_apnitem(apn_node)
247    apn_map[cname].append(apn)
248
249  mcs = carrier_settings_pb2.MultiCarrierSettings()
250  for c in apn_map:
251    carriersettings = mcs.setting.add()
252    carriersettings.canonical_name = c
253    carriersettings.apns.apn.extend(apn_map[c])
254
255  with open(FLAGS.out_file, 'w', encoding='utf-8') as apnout:
256    apnout.write(text_format.MessageToString(mcs, as_utf8=True))
257
258
259if __name__ == '__main__':
260  main()
261