xref: /aosp_15_r20/tools/carrier_settings/python/update_carrier_data.py (revision ff35212d322a3e892605b94fa777c67085d45efd)
1*ff35212dScey# Copyright (C) 2020 Google LLC
2*ff35212dScey#
3*ff35212dScey# Licensed under the Apache License, Version 2.0 (the "License");
4*ff35212dScey# you may not use this file except in compliance with the License.
5*ff35212dScey# You may obtain a copy of the License at
6*ff35212dScey#
7*ff35212dScey#      http://www.apache.org/licenses/LICENSE-2.0
8*ff35212dScey#
9*ff35212dScey# Unless required by applicable law or agreed to in writing, software
10*ff35212dScey# distributed under the License is distributed on an "AS IS" BASIS,
11*ff35212dScey# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*ff35212dScey# See the License for the specific language governing permissions and
13*ff35212dScey# limitations under the License.
14*ff35212dScey
15*ff35212dSceyr"""Read a MultiCarrierSettings file and update CarrierSettings data.
16*ff35212dScey
17*ff35212dSceyFor APNs in the input file, they are simply appended to the apn list of the
18*ff35212dSceycorresponding carrier in CarrierSettings data. If a new carrier (identified by
19*ff35212dSceycanonical_name) appears in input, the other_carriers.textpb will be updated.
20*ff35212dScey
21*ff35212dSceyHow to run:
22*ff35212dScey
23*ff35212dSceyupdate_carrier_data.par \
24*ff35212dScey--in_file=/tmp/tmpapns.textpb \
25*ff35212dScey--data_dir=/tmp/carrier/data
26*ff35212dScey"""
27*ff35212dScey
28*ff35212dSceyfrom __future__ import absolute_import
29*ff35212dSceyfrom __future__ import division
30*ff35212dSceyfrom __future__ import print_function
31*ff35212dSceyimport argparse
32*ff35212dSceyimport copy
33*ff35212dSceyimport os
34*ff35212dSceyfrom python import compare
35*ff35212dSceyfrom google.protobuf import text_format
36*ff35212dSceyfrom proto import carrier_list_pb2
37*ff35212dSceyfrom proto import carrier_settings_pb2
38*ff35212dScey
39*ff35212dSceyparser = argparse.ArgumentParser()
40*ff35212dSceyparser.add_argument(
41*ff35212dScey    '--data_dir', default='./data', help='Folder path for CarrierSettings data')
42*ff35212dSceyparser.add_argument(
43*ff35212dScey    '--in_file', default='./tmpapns.textpb', help='Temp APN file')
44*ff35212dSceyFLAGS = parser.parse_args()
45*ff35212dScey
46*ff35212dSceyTIER1_CARRIERS_TEXTPB = os.path.join(FLAGS.data_dir, 'tier1_carriers.textpb')
47*ff35212dSceyOTHER_CARRIERS_TEXTPB = os.path.join(FLAGS.data_dir, 'other_carriers.textpb')
48*ff35212dScey
49*ff35212dScey
50*ff35212dSceydef equals_apn(a, b):
51*ff35212dScey  """Tell if two ApnItem proto are the same."""
52*ff35212dScey  a = compare.NormalizeRepeatedFields(copy.deepcopy(a))
53*ff35212dScey  b = compare.NormalizeRepeatedFields(copy.deepcopy(b))
54*ff35212dScey  # ignore 'name' field
55*ff35212dScey  a.ClearField('name')
56*ff35212dScey  b.ClearField('name')
57*ff35212dScey  return compare.Proto2Equals(a, b)
58*ff35212dScey
59*ff35212dScey
60*ff35212dSceydef find_apn(apn, apns):
61*ff35212dScey  """Tell if apn is in apns."""
62*ff35212dScey  for a in apns:
63*ff35212dScey    if equals_apn(apn, a):
64*ff35212dScey      return True
65*ff35212dScey  return False
66*ff35212dScey
67*ff35212dScey
68*ff35212dSceydef merge_mms_apn(a, b):
69*ff35212dScey  """Try to merge mmsc fields of b into a, if that's the only diff."""
70*ff35212dScey  aa = compare.NormalizeRepeatedFields(copy.deepcopy(a))
71*ff35212dScey  bb = compare.NormalizeRepeatedFields(copy.deepcopy(b))
72*ff35212dScey  # check if any fields other than mms are different
73*ff35212dScey  for field in ['name', 'mmsc_proxy', 'mmsc_proxy_port']:
74*ff35212dScey    aa.ClearField(field)
75*ff35212dScey    bb.ClearField(field)
76*ff35212dScey  if compare.Proto2Equals(aa, bb):
77*ff35212dScey    for field in ['mmsc_proxy', 'mmsc_proxy_port']:
78*ff35212dScey      if b.HasField(field):
79*ff35212dScey        setattr(a, field, getattr(b, field))
80*ff35212dScey
81*ff35212dScey
82*ff35212dSceydef clean_apn(setting):
83*ff35212dScey  """Remove duplicated ApnItems from a CarrierSettings proto.
84*ff35212dScey
85*ff35212dScey  Args:
86*ff35212dScey    setting: a CarrierSettings proto
87*ff35212dScey
88*ff35212dScey  Returns:
89*ff35212dScey    None
90*ff35212dScey  """
91*ff35212dScey  if not setting.HasField('apns') or len(setting.apns.apn) <= 1:
92*ff35212dScey    return
93*ff35212dScey  apns = setting.apns.apn[:]
94*ff35212dScey  cleaned_apns = [a for n, a in enumerate(apns) if not find_apn(a, apns[:n])]
95*ff35212dScey  del setting.apns.apn[:]
96*ff35212dScey  setting.apns.apn.extend(cleaned_apns)
97*ff35212dScey
98*ff35212dScey
99*ff35212dSceydef merge_apns(dest_apns, source_apns):
100*ff35212dScey  """Merge source_apns into dest_apns."""
101*ff35212dScey  for apn in dest_apns:
102*ff35212dScey    for source in source_apns:
103*ff35212dScey      merge_mms_apn(apn, source)
104*ff35212dScey  ret = list(dest_apns)
105*ff35212dScey  for source in source_apns:
106*ff35212dScey    if not find_apn(source, ret):
107*ff35212dScey      ret.append(source)
108*ff35212dScey  return ret
109*ff35212dScey
110*ff35212dScey
111*ff35212dSceydef to_string(cid):
112*ff35212dScey  """Return the string representation of a CarrierId."""
113*ff35212dScey  ret = cid.mcc_mnc
114*ff35212dScey  if cid.HasField('spn'):
115*ff35212dScey    ret += 'SPN=' + cid.spn.upper()
116*ff35212dScey  if cid.HasField('imsi'):
117*ff35212dScey    ret += 'IMSI=' + cid.imsi.upper()
118*ff35212dScey  if cid.HasField('gid1'):
119*ff35212dScey    ret += 'GID1=' + cid.gid1.upper()
120*ff35212dScey  return ret
121*ff35212dScey
122*ff35212dScey
123*ff35212dSceydef to_carrier_id(cid_string):
124*ff35212dScey  """Return the CarrierId from its string representation."""
125*ff35212dScey  cid = carrier_list_pb2.CarrierId()
126*ff35212dScey  if 'SPN=' in cid_string:
127*ff35212dScey    ind = cid_string.find('SPN=')
128*ff35212dScey    cid.mcc_mnc = cid_string[:ind]
129*ff35212dScey    cid.spn = cid_string[ind + len('SPN='):]
130*ff35212dScey  elif 'IMSI=' in cid_string:
131*ff35212dScey    ind = cid_string.find('IMSI=')
132*ff35212dScey    cid.mcc_mnc = cid_string[:ind]
133*ff35212dScey    cid.imsi = cid_string[ind + len('IMSI='):]
134*ff35212dScey  elif 'GID1=' in cid_string:
135*ff35212dScey    ind = cid_string.find('GID1=')
136*ff35212dScey    cid.mcc_mnc = cid_string[:ind]
137*ff35212dScey    cid.gid1 = cid_string[ind + len('GID1='):]
138*ff35212dScey  else:
139*ff35212dScey    cid.mcc_mnc = cid_string
140*ff35212dScey  return cid
141*ff35212dScey
142*ff35212dScey
143*ff35212dSceydef get_input(path):
144*ff35212dScey  """Read input MultiCarrierSettings textpb file.
145*ff35212dScey
146*ff35212dScey  Args:
147*ff35212dScey    path: the path to input MultiCarrierSettings textpb file
148*ff35212dScey
149*ff35212dScey  Returns:
150*ff35212dScey    A MultiCarrierSettings. None when failed.
151*ff35212dScey  """
152*ff35212dScey  mcs = None
153*ff35212dScey  with open(path, 'r', encoding='utf-8') as f:
154*ff35212dScey    mcs = carrier_settings_pb2.MultiCarrierSettings()
155*ff35212dScey    text_format.Merge(f.read(), mcs)
156*ff35212dScey
157*ff35212dScey  return mcs
158*ff35212dScey
159*ff35212dScey
160*ff35212dSceydef get_knowncarriers(files):
161*ff35212dScey  """Create a mapping from mccmnc and possible mvno data to canonical name.
162*ff35212dScey
163*ff35212dScey  Args:
164*ff35212dScey    files: list of paths to carrier list textpb files
165*ff35212dScey
166*ff35212dScey  Returns:
167*ff35212dScey    A dict, key is to_string(carrier_id), value is cname.
168*ff35212dScey  """
169*ff35212dScey  ret = dict()
170*ff35212dScey  for path in files:
171*ff35212dScey    with open(path, 'r', encoding='utf-8') as f:
172*ff35212dScey      carriers = carrier_list_pb2.CarrierList()
173*ff35212dScey      text_format.Merge(f.read(), carriers)
174*ff35212dScey      for carriermap in carriers.entry:
175*ff35212dScey        for cid in carriermap.carrier_id:
176*ff35212dScey          ret[to_string(cid)] = carriermap.canonical_name
177*ff35212dScey
178*ff35212dScey  return ret
179*ff35212dScey
180*ff35212dScey
181*ff35212dSceydef clear_apn_fields_in_default_value(carrier_settings):
182*ff35212dScey
183*ff35212dScey  def clean(apn):
184*ff35212dScey    if apn.HasField('bearer_bitmask') and apn.bearer_bitmask == '0':
185*ff35212dScey      apn.ClearField('bearer_bitmask')
186*ff35212dScey    return apn
187*ff35212dScey
188*ff35212dScey  for apn in carrier_settings.apns.apn:
189*ff35212dScey    clean(apn)
190*ff35212dScey  return carrier_settings
191*ff35212dScey
192*ff35212dScey
193*ff35212dSceydef merge_carrier_settings(patch, carrier_file):
194*ff35212dScey  """Merge a CarrierSettings into a base CarrierSettings in textpb file.
195*ff35212dScey
196*ff35212dScey  This function merge apns only. It assumes that the patch and base have the
197*ff35212dScey  same canonical_name.
198*ff35212dScey
199*ff35212dScey  Args:
200*ff35212dScey    patch: the carrier_settings to be merged
201*ff35212dScey    carrier_file: the path to the base carrier_settings file
202*ff35212dScey  """
203*ff35212dScey  # Load base
204*ff35212dScey  with open(carrier_file, 'r', encoding='utf-8') as f:
205*ff35212dScey    base_setting = text_format.ParseLines(f,
206*ff35212dScey                                          carrier_settings_pb2.CarrierSettings())
207*ff35212dScey
208*ff35212dScey  clean_apn(patch)
209*ff35212dScey  clean_apn(base_setting)
210*ff35212dScey
211*ff35212dScey  # Merge apns
212*ff35212dScey  apns = base_setting.apns.apn[:]
213*ff35212dScey  apns = merge_apns(apns, patch.apns.apn[:])
214*ff35212dScey  del base_setting.apns.apn[:]
215*ff35212dScey  base_setting.apns.apn.extend(apns)
216*ff35212dScey
217*ff35212dScey  # Write back
218*ff35212dScey  with open(carrier_file, 'w', encoding='utf-8') as f:
219*ff35212dScey    text_format.PrintMessage(base_setting, f, as_utf8=True)
220*ff35212dScey
221*ff35212dScey
222*ff35212dSceydef merge_multi_carrier_settings(patch_list, carrier_file):
223*ff35212dScey  """Merge CarrierSettings into a base MultiCarrierSettings in textpb file.
224*ff35212dScey
225*ff35212dScey  This function merge apns only. The base may or may not contains an entry with
226*ff35212dScey  the same canonical_name as the patch.
227*ff35212dScey
228*ff35212dScey  Args:
229*ff35212dScey    patch_list: a list of CarrierSettings to be merged
230*ff35212dScey    carrier_file: the path to the base MultiCarrierSettings file
231*ff35212dScey  """
232*ff35212dScey  # Load base
233*ff35212dScey  with open(carrier_file, 'r', encoding='utf-8') as f:
234*ff35212dScey    base_settings = text_format.ParseLines(
235*ff35212dScey        f, carrier_settings_pb2.MultiCarrierSettings())
236*ff35212dScey
237*ff35212dScey  for patch in patch_list:
238*ff35212dScey    clean_apn(patch)
239*ff35212dScey    # find the (first and only) entry with patch.canonical_name and update it.
240*ff35212dScey    for setting in base_settings.setting:
241*ff35212dScey      if setting.canonical_name == patch.canonical_name:
242*ff35212dScey        clean_apn(setting)
243*ff35212dScey        apns = setting.apns.apn[:]
244*ff35212dScey        apns = merge_apns(apns, patch.apns.apn[:])
245*ff35212dScey        del setting.apns.apn[:]
246*ff35212dScey        setting.apns.apn.extend(apns)
247*ff35212dScey        break
248*ff35212dScey    # Or if no match, append it to base_settings
249*ff35212dScey    else:
250*ff35212dScey      base_settings.setting.extend([patch])
251*ff35212dScey
252*ff35212dScey  # Write back
253*ff35212dScey  with open(carrier_file, 'w', encoding='utf-8') as f:
254*ff35212dScey    text_format.PrintMessage(base_settings, f, as_utf8=True)
255*ff35212dScey
256*ff35212dScey
257*ff35212dSceydef add_new_carriers(cnames, carrier_list_file):
258*ff35212dScey  """Add a new carrier into a CarrierList in textpb file.
259*ff35212dScey
260*ff35212dScey  The carrier_id of the new carrier is induced from the cname, assuming
261*ff35212dScey  that the cname is constructed by to_string.
262*ff35212dScey
263*ff35212dScey  Args:
264*ff35212dScey    cnames: a list of canonical_name of new carriers
265*ff35212dScey    carrier_list_file: the path to the CarrierList textpb file
266*ff35212dScey
267*ff35212dScey  Returns:
268*ff35212dScey    None
269*ff35212dScey  """
270*ff35212dScey  with open(carrier_list_file, 'r', encoding='utf-8') as f:
271*ff35212dScey    carriers = text_format.ParseLines(f, carrier_list_pb2.CarrierList())
272*ff35212dScey
273*ff35212dScey  for cname in cnames:
274*ff35212dScey    # Append the new carrier
275*ff35212dScey    new_carrier = carriers.entry.add()
276*ff35212dScey    new_carrier.canonical_name = cname
277*ff35212dScey    new_carrier.carrier_id.extend([to_carrier_id(cname)])
278*ff35212dScey
279*ff35212dScey  tmp = sorted(carriers.entry, key=lambda c: c.canonical_name)
280*ff35212dScey  del carriers.entry[:]
281*ff35212dScey  carriers.entry.extend(tmp)
282*ff35212dScey
283*ff35212dScey  with open(carrier_list_file, 'w', encoding='utf-8') as f:
284*ff35212dScey    text_format.PrintMessage(carriers, f, as_utf8=True)
285*ff35212dScey
286*ff35212dScey
287*ff35212dSceydef add_apns_for_other_carriers_by_mccmnc(apns, tier1_carriers, other_carriers):
288*ff35212dScey  """Add APNs for carriers in others.textpb that doesn't have APNs, by mccmnc.
289*ff35212dScey
290*ff35212dScey  If a carrier defined as mccmnc + mvno_data doesn't hava APNs, it should use
291*ff35212dScey  APNs from the carrier defined as mccmnc only.
292*ff35212dScey
293*ff35212dScey  Modifies others.textpb file in-place.
294*ff35212dScey
295*ff35212dScey  If a carriersettingstool.no_apn_for_mvno_bool is defined as true for a MVNO,
296*ff35212dScey  the APNs from the corresponding MNO(by MCC/MNC) will not be used.
297*ff35212dScey
298*ff35212dScey  Args:
299*ff35212dScey    apns: a list of CarrierSettings message with APNs only.
300*ff35212dScey    tier1_carriers: parsed tier-1 carriers list; must not contain new carriers.
301*ff35212dScey      A dict, key is to_string(carrier_id), value is cname.
302*ff35212dScey    other_carriers: parsed other carriers list; must not contain new carriers. A
303*ff35212dScey      dict, key is to_string(carrier_id), value is cname.
304*ff35212dScey  """
305*ff35212dScey  # Convert apns from a list to a map, key being the canonical_name
306*ff35212dScey  apns_dict = {
307*ff35212dScey      carrier_settings.canonical_name: carrier_settings
308*ff35212dScey      for carrier_settings in apns
309*ff35212dScey  }
310*ff35212dScey
311*ff35212dScey  others_textpb = '%s/setting/others.textpb' % FLAGS.data_dir
312*ff35212dScey  with open(others_textpb, 'r', encoding='utf-8') as others_textpb_file:
313*ff35212dScey    others = text_format.ParseLines(others_textpb_file,
314*ff35212dScey                                    carrier_settings_pb2.MultiCarrierSettings())
315*ff35212dScey
316*ff35212dScey  for setting in others.setting:
317*ff35212dScey    if not setting.HasField('apns'):
318*ff35212dScey      carrier_id = to_carrier_id(setting.canonical_name)
319*ff35212dScey      if carrier_id.HasField('mvno_data'):
320*ff35212dScey        # in case we don't need MNO APN for this MVNO
321*ff35212dScey        skip_mno_apn = False
322*ff35212dScey        if setting.HasField('configs'):
323*ff35212dScey          for conf in setting.configs.config:
324*ff35212dScey            if conf.key == 'carriersettingstool.no_apn_for_mvno_bool':
325*ff35212dScey              skip_mno_apn = conf.bool_value
326*ff35212dScey              break
327*ff35212dScey        if skip_mno_apn:
328*ff35212dScey          continue
329*ff35212dScey        carrier_id.ClearField('mvno_data')
330*ff35212dScey        carrier_id_str_of_mccmnc = to_string(carrier_id)
331*ff35212dScey        cname_of_mccmnc = tier1_carriers.get(
332*ff35212dScey            carrier_id_str_of_mccmnc) or other_carriers.get(
333*ff35212dScey                carrier_id_str_of_mccmnc)
334*ff35212dScey        if cname_of_mccmnc:
335*ff35212dScey          apn = apns_dict.get(cname_of_mccmnc)
336*ff35212dScey          if apn:
337*ff35212dScey            setting.apns.CopyFrom(apn.apns)
338*ff35212dScey
339*ff35212dScey  sanitise_carrier_config(others.setting)
340*ff35212dScey
341*ff35212dScey  with open(others_textpb, 'w', encoding='utf-8') as others_textpb_file:
342*ff35212dScey    text_format.PrintMessage(others, others_textpb_file, as_utf8=True)
343*ff35212dScey
344*ff35212dSceydef sanitise_carrier_config(setting):
345*ff35212dScey  """Remove temparary carrier config items that's only used for conversion tool"""
346*ff35212dScey  for carrier_setting in setting:
347*ff35212dScey    if carrier_setting.HasField('configs'):
348*ff35212dScey      configs = carrier_setting.configs.config[:]
349*ff35212dScey      del carrier_setting.configs.config[:]
350*ff35212dScey      for config in configs:
351*ff35212dScey        if not config.key.startswith('carriersettingstool.'):
352*ff35212dScey          carrier_setting.configs.config.append(config)
353*ff35212dScey
354*ff35212dSceydef add_carrierconfig_for_new_carriers(cnames, tier1_carriers, other_carriers):
355*ff35212dScey  """Add carrier configs for new (non-tier-1) carriers.
356*ff35212dScey
357*ff35212dScey   For new carriers, ie. carriers existing in APN but not CarrierConfig:
358*ff35212dScey      - for <mccmnc>: copy carrier config of <mcc>.
359*ff35212dScey      - for <mccmnc>(GID1|SPN|IMSI)=<mvnodata>: copy carrier config of <mccmnc>,
360*ff35212dScey      or <mcc>.
361*ff35212dScey
362*ff35212dScey  Modifies others.textpb file in-place.
363*ff35212dScey
364*ff35212dScey  Args:
365*ff35212dScey    cnames: a list of canonical_name of new carriers.
366*ff35212dScey    tier1_carriers: parsed tier-1 carriers list; must not contain new carriers.
367*ff35212dScey      A dict, key is to_string(carrier_id), value is cname.
368*ff35212dScey    other_carriers: parsed other carriers list; must not contain new carriers. A
369*ff35212dScey      dict, key is to_string(carrier_id), value is cname.
370*ff35212dScey  """
371*ff35212dScey  carrier_configs_map = {}
372*ff35212dScey
373*ff35212dScey  others_textpb = '%s/setting/others.textpb' % FLAGS.data_dir
374*ff35212dScey  with open(others_textpb, 'r', encoding='utf-8') as others_textpb_file:
375*ff35212dScey    others = text_format.ParseLines(others_textpb_file,
376*ff35212dScey                                    carrier_settings_pb2.MultiCarrierSettings())
377*ff35212dScey    for setting in others.setting:
378*ff35212dScey      if setting.canonical_name in other_carriers:
379*ff35212dScey        carrier_configs_map[setting.canonical_name] = setting.configs
380*ff35212dScey  for cid_str, cname in tier1_carriers.items():
381*ff35212dScey    tier1_textpb = '%s/setting/%s.textpb' % (FLAGS.data_dir, cname)
382*ff35212dScey    with open(tier1_textpb, 'r', encoding='utf-8') as tier1_textpb_file:
383*ff35212dScey      tier1 = text_format.ParseLines(tier1_textpb_file,
384*ff35212dScey                                     carrier_settings_pb2.CarrierSettings())
385*ff35212dScey      carrier_configs_map[cid_str] = tier1.configs
386*ff35212dScey
387*ff35212dScey  for setting in others.setting:
388*ff35212dScey    if setting.canonical_name in cnames:
389*ff35212dScey      carrier_id = to_carrier_id(setting.canonical_name)
390*ff35212dScey      mccmnc = carrier_id.mcc_mnc
391*ff35212dScey      mcc = mccmnc[:3]
392*ff35212dScey      if mccmnc in carrier_configs_map:
393*ff35212dScey        setting.configs.config.extend(carrier_configs_map[mccmnc].config[:])
394*ff35212dScey      elif mcc in carrier_configs_map:
395*ff35212dScey        setting.configs.config.extend(carrier_configs_map[mcc].config[:])
396*ff35212dScey
397*ff35212dScey  with open(others_textpb, 'w', encoding='utf-8') as others_textpb_file:
398*ff35212dScey    text_format.PrintMessage(others, others_textpb_file, as_utf8=True)
399*ff35212dScey
400*ff35212dScey
401*ff35212dSceydef cleanup_mcc_only_carriers():
402*ff35212dScey  """Removes mcc-only carriers from other_carriers.textpb & others.textpb.
403*ff35212dScey
404*ff35212dScey  Modifies other_carriers.textpb file & others.textpb file in-place.
405*ff35212dScey  """
406*ff35212dScey  mcc_only_carriers = set()
407*ff35212dScey
408*ff35212dScey  with open(
409*ff35212dScey      OTHER_CARRIERS_TEXTPB, 'r',
410*ff35212dScey      encoding='utf-8') as other_carriers_textpb_file:
411*ff35212dScey    other_carriers = text_format.ParseLines(other_carriers_textpb_file,
412*ff35212dScey                                            carrier_list_pb2.CarrierList())
413*ff35212dScey
414*ff35212dScey  other_carriers_entry_with_mccmnc = []
415*ff35212dScey  for carrier in other_carriers.entry:
416*ff35212dScey    for carrier_id in carrier.carrier_id:
417*ff35212dScey      if len(carrier_id.mcc_mnc) == 3:
418*ff35212dScey        mcc_only_carriers.add(carrier.canonical_name)
419*ff35212dScey      else:
420*ff35212dScey        other_carriers_entry_with_mccmnc.append(carrier)
421*ff35212dScey  del other_carriers.entry[:]
422*ff35212dScey  other_carriers.entry.extend(other_carriers_entry_with_mccmnc)
423*ff35212dScey
424*ff35212dScey  # Finish early if no mcc_only_carriers; that means no file modification
425*ff35212dScey  # required.
426*ff35212dScey  if not mcc_only_carriers:
427*ff35212dScey    return
428*ff35212dScey
429*ff35212dScey  with open(
430*ff35212dScey      OTHER_CARRIERS_TEXTPB, 'w',
431*ff35212dScey      encoding='utf-8') as other_carriers_textpb_file:
432*ff35212dScey    text_format.PrintMessage(
433*ff35212dScey        other_carriers, other_carriers_textpb_file, as_utf8=True)
434*ff35212dScey
435*ff35212dScey  others_textpb = os.path.join(FLAGS.data_dir, 'setting', 'others.textpb')
436*ff35212dScey  with open(others_textpb, 'r', encoding='utf-8') as others_textpb_file:
437*ff35212dScey    others = text_format.ParseLines(others_textpb_file,
438*ff35212dScey                                    carrier_settings_pb2.MultiCarrierSettings())
439*ff35212dScey  copy_others_setting = others.setting[:]
440*ff35212dScey  del others.setting[:]
441*ff35212dScey  others.setting.extend([
442*ff35212dScey      setting for setting in copy_others_setting
443*ff35212dScey      if setting.canonical_name not in mcc_only_carriers
444*ff35212dScey  ])
445*ff35212dScey
446*ff35212dScey  with open(others_textpb, 'w', encoding='utf-8') as others_textpb_file:
447*ff35212dScey    text_format.PrintMessage(others, others_textpb_file, as_utf8=True)
448*ff35212dScey
449*ff35212dScey
450*ff35212dSceydef main():
451*ff35212dScey  apns = get_input(FLAGS.in_file).setting
452*ff35212dScey  tier1_carriers = get_knowncarriers([TIER1_CARRIERS_TEXTPB])
453*ff35212dScey  other_carriers = get_knowncarriers([OTHER_CARRIERS_TEXTPB])
454*ff35212dScey  new_carriers = []
455*ff35212dScey
456*ff35212dScey  # Step 1a: merge APNs into CarrierConfigs by canonical name.
457*ff35212dScey  # Also find out "new carriers" existing in APNs but not in CarrierConfigs.
458*ff35212dScey  other_carriers_patch = []
459*ff35212dScey  for carrier_settings in apns:
460*ff35212dScey    carrier_settings = clear_apn_fields_in_default_value(carrier_settings)
461*ff35212dScey
462*ff35212dScey    cname = carrier_settings.canonical_name
463*ff35212dScey    if cname in tier1_carriers.values():
464*ff35212dScey      merge_carrier_settings(carrier_settings,
465*ff35212dScey                             '%s/setting/%s.textpb' % (FLAGS.data_dir, cname))
466*ff35212dScey    else:
467*ff35212dScey      other_carriers_patch.append(carrier_settings)
468*ff35212dScey      if cname not in other_carriers.values():
469*ff35212dScey        new_carriers.append(cname)
470*ff35212dScey
471*ff35212dScey  merge_multi_carrier_settings(other_carriers_patch,
472*ff35212dScey                               '%s/setting/others.textpb' % FLAGS.data_dir)
473*ff35212dScey
474*ff35212dScey  # Step 1b: populate carrier configs for new carriers.
475*ff35212dScey  add_carrierconfig_for_new_carriers(new_carriers, tier1_carriers,
476*ff35212dScey                                     other_carriers)
477*ff35212dScey
478*ff35212dScey  # Step 2: merge new carriers into non-tier1 carrier list.
479*ff35212dScey  add_new_carriers(new_carriers, OTHER_CARRIERS_TEXTPB)
480*ff35212dScey  # Update other_carriers map
481*ff35212dScey  other_carriers = get_knowncarriers([OTHER_CARRIERS_TEXTPB])
482*ff35212dScey
483*ff35212dScey  # Step 3: merge APNs into CarrierConfigs by mccmnc: for a carrier defined
484*ff35212dScey  # as mccmnc + gid/spn/imsi, if it doesn't have any APNs, it should use APNs
485*ff35212dScey  # from carrier defined as mccmnc only.
486*ff35212dScey  # Only handle non-tier1 carriers, as tier1 carriers are assumed to be better
487*ff35212dScey  # maintained and are already having APNs defined.
488*ff35212dScey  add_apns_for_other_carriers_by_mccmnc(apns, tier1_carriers, other_carriers)
489*ff35212dScey
490*ff35212dScey  # Step 4: clean up mcc-only carriers; they're used in step 3 but should not
491*ff35212dScey  # be in final carrier settings to avoid confusing CarrierSettings app.
492*ff35212dScey  cleanup_mcc_only_carriers()
493*ff35212dScey
494*ff35212dScey
495*ff35212dSceyif __name__ == '__main__':
496*ff35212dScey  main()
497