xref: /aosp_15_r20/tools/carrier_settings/java/CarrierProtoUtils.java (revision ff35212d322a3e892605b94fa777c67085d45efd)
1*ff35212dScey /*
2*ff35212dScey  * Copyright (C) 2020 Google LLC
3*ff35212dScey  *
4*ff35212dScey  * Licensed under the Apache License, Version 2.0 (the "License");
5*ff35212dScey  * you may not use this file except in compliance with the License.
6*ff35212dScey  * You may obtain a copy of the License at
7*ff35212dScey  *
8*ff35212dScey  *      http://www.apache.org/licenses/LICENSE-2.0
9*ff35212dScey  *
10*ff35212dScey  * Unless required by applicable law or agreed to in writing, software
11*ff35212dScey  * distributed under the License is distributed on an "AS IS" BASIS,
12*ff35212dScey  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*ff35212dScey  * See the License for the specific language governing permissions and
14*ff35212dScey  * limitations under the License.
15*ff35212dScey  */
16*ff35212dScey package com.google.carrier;
17*ff35212dScey 
18*ff35212dScey import com.google.common.base.Preconditions;
19*ff35212dScey import com.google.carrier.CarrierConfig;
20*ff35212dScey import com.google.carrier.CarrierId;
21*ff35212dScey import com.google.carrier.CarrierMap;
22*ff35212dScey import com.google.carrier.CarrierSettings;
23*ff35212dScey import com.google.carrier.MultiCarrierSettings;
24*ff35212dScey import com.google.carrier.VendorConfigClient;
25*ff35212dScey import com.google.carrier.VendorConfigs;
26*ff35212dScey import java.util.ArrayList;
27*ff35212dScey import java.util.Collections;
28*ff35212dScey import java.util.Comparator;
29*ff35212dScey import java.util.List;
30*ff35212dScey 
31*ff35212dScey /** Utility methods */
32*ff35212dScey public class CarrierProtoUtils {
33*ff35212dScey 
34*ff35212dScey   /**
35*ff35212dScey    * The base of release version.
36*ff35212dScey    *
37*ff35212dScey    * A valid release version must be a multiple of the base.
38*ff35212dScey    * A file version must be smaller than the base - otherwise the version schema is broken.
39*ff35212dScey    */
40*ff35212dScey   public static final long RELEASE_VERSION_BASE = 1000000000L;
41*ff35212dScey 
42*ff35212dScey   /**
43*ff35212dScey    * Bump the version by an offset, to differentiate release/branch.
44*ff35212dScey    *
45*ff35212dScey    * The input version must be smaller than RELEASE_VERSION_BASE, and the offset must be a
46*ff35212dScey    * multiple of RELEASE_VERSION_BASE.
47*ff35212dScey    */
addVersionOffset(long version, long offset)48*ff35212dScey   public static long addVersionOffset(long version, long offset) {
49*ff35212dScey 
50*ff35212dScey     Preconditions.checkArgument(version < RELEASE_VERSION_BASE,
51*ff35212dScey         "OMG, by design the file version should be smaller than %s, but is %s.",
52*ff35212dScey         RELEASE_VERSION_BASE, version);
53*ff35212dScey     Preconditions.checkArgument((offset % RELEASE_VERSION_BASE) == 0,
54*ff35212dScey         "The offset %s is not a multiple of %s",
55*ff35212dScey         offset, RELEASE_VERSION_BASE);
56*ff35212dScey 
57*ff35212dScey     return version + offset;
58*ff35212dScey   }
59*ff35212dScey 
60*ff35212dScey   /**
61*ff35212dScey    * Merge configs fields in {@code patch} into {@code base}; configs sorted by key.
62*ff35212dScey    * See {@link mergeCarrierConfig(CarrierConfig, CarrierConfig.Builder)}.
63*ff35212dScey    */
64*ff35212dScey   public static CarrierConfig mergeCarrierConfig(CarrierConfig base, CarrierConfig patch) {
65*ff35212dScey     Preconditions.checkNotNull(patch);
66*ff35212dScey 
67*ff35212dScey     return mergeCarrierConfig(base, patch.toBuilder());
68*ff35212dScey   }
69*ff35212dScey 
70*ff35212dScey   /**
71*ff35212dScey    * Merge configs fields in {@code patch} into {@code base}; configs sorted by key.
72*ff35212dScey    *
73*ff35212dScey    * <p>Sorting is desired because:
74*ff35212dScey    *
75*ff35212dScey    * <ul>
76*ff35212dScey    *   <li>1. The order doesn't matter for the consumer so sorting will not cause behavior change;
77*ff35212dScey    *   <li>2. The output proto is serilized to text for human review; undeterministic order can
78*ff35212dScey    *       confuse reviewers as they cannot easily tell if it's re-ordering or actual data change.
79*ff35212dScey    * </ul>
80*ff35212dScey    */
81*ff35212dScey   public static CarrierConfig mergeCarrierConfig(CarrierConfig base, CarrierConfig.Builder patch) {
82*ff35212dScey     Preconditions.checkNotNull(base);
83*ff35212dScey     Preconditions.checkNotNull(patch);
84*ff35212dScey 
85*ff35212dScey     CarrierConfig.Builder baseBuilder = base.toBuilder();
86*ff35212dScey 
87*ff35212dScey     // Traverse each config in patch
88*ff35212dScey     for (int i = 0; i < patch.getConfigCount(); i++) {
89*ff35212dScey       CarrierConfig.Config patchConfig = patch.getConfig(i);
90*ff35212dScey 
91*ff35212dScey       // Try to find an config in base with the same key as the config from patch
92*ff35212dScey       int j = 0;
93*ff35212dScey       for (j = 0; j < baseBuilder.getConfigCount(); j++) {
94*ff35212dScey         if (baseBuilder.getConfig(j).getKey().equals(patchConfig.getKey())) {
95*ff35212dScey           break;
96*ff35212dScey         }
97*ff35212dScey       }
98*ff35212dScey 
99*ff35212dScey       // If match found, replace base with patch; otherwise append patch into base.
100*ff35212dScey       if (j < baseBuilder.getConfigCount()) {
101*ff35212dScey         baseBuilder.setConfig(j, patchConfig);
102*ff35212dScey       } else {
103*ff35212dScey         baseBuilder.addConfig(patchConfig);
104*ff35212dScey       }
105*ff35212dScey     }
106*ff35212dScey 
107*ff35212dScey     // Sort configs in baseBuilder by key
108*ff35212dScey     List<CarrierConfig.Config> configs = new ArrayList<>(baseBuilder.getConfigList());
109*ff35212dScey     Collections.sort(configs, Comparator.comparing(CarrierConfig.Config::getKey));
110*ff35212dScey     baseBuilder.clearConfig();
111*ff35212dScey     baseBuilder.addAllConfig(configs);
112*ff35212dScey 
113*ff35212dScey     return baseBuilder.build();
114*ff35212dScey   }
115*ff35212dScey 
116*ff35212dScey   /**
117*ff35212dScey    * Find a carrier's CarrierSettings by canonical_name from a MultiCarrierSettings.
118*ff35212dScey    *
119*ff35212dScey    * <p>Return null if not found.
120*ff35212dScey    */
121*ff35212dScey   public static CarrierSettings findCarrierSettingsByCanonicalName(
122*ff35212dScey       MultiCarrierSettings mcs, String name) {
123*ff35212dScey 
124*ff35212dScey     Preconditions.checkNotNull(mcs);
125*ff35212dScey     Preconditions.checkNotNull(name);
126*ff35212dScey 
127*ff35212dScey     for (int i = 0; i < mcs.getSettingCount(); i++) {
128*ff35212dScey       CarrierSettings cs = mcs.getSetting(i);
129*ff35212dScey       if (cs.getCanonicalName().equals(name)) {
130*ff35212dScey         return cs;
131*ff35212dScey       }
132*ff35212dScey     }
133*ff35212dScey 
134*ff35212dScey     return null;
135*ff35212dScey   }
136*ff35212dScey 
137*ff35212dScey   /** Apply device overly to a carrier setting */
138*ff35212dScey   public static CarrierSettings.Builder applyDeviceOverlayToCarrierSettings(
139*ff35212dScey       MultiCarrierSettings allOverlay, CarrierSettings.Builder base) {
140*ff35212dScey     return applyDeviceOverlayToCarrierSettings(allOverlay, base.build());
141*ff35212dScey   }
142*ff35212dScey 
143*ff35212dScey   /** Apply device overly to a carrier setting */
144*ff35212dScey   public static CarrierSettings.Builder applyDeviceOverlayToCarrierSettings(
145*ff35212dScey       MultiCarrierSettings allOverlay, CarrierSettings base) {
146*ff35212dScey 
147*ff35212dScey     Preconditions.checkNotNull(allOverlay);
148*ff35212dScey     Preconditions.checkNotNull(base);
149*ff35212dScey 
150*ff35212dScey     // Find overlay of the base carrier. If not found, just return base.
151*ff35212dScey     CarrierSettings overlay =
152*ff35212dScey         findCarrierSettingsByCanonicalName(allOverlay, base.getCanonicalName());
153*ff35212dScey     if (overlay == null) {
154*ff35212dScey       return base.toBuilder();
155*ff35212dScey     }
156*ff35212dScey 
157*ff35212dScey     CarrierSettings.Builder resultBuilder = base.toBuilder();
158*ff35212dScey     // Add version number of base settings and overlay, so the result version number
159*ff35212dScey     // monotonically increases.
160*ff35212dScey     resultBuilder.setVersion(base.getVersion() + overlay.getVersion());
161*ff35212dScey     // Merge overlay settings into base settings
162*ff35212dScey     resultBuilder.setConfigs(mergeCarrierConfig(resultBuilder.getConfigs(), overlay.getConfigs()));
163*ff35212dScey     // Replace base apns with overlay apns (if not empty)
164*ff35212dScey     if (overlay.getApns().getApnCount() > 0) {
165*ff35212dScey       resultBuilder.setApns(overlay.getApns());
166*ff35212dScey     }
167*ff35212dScey     // Merge the overlay vendor configuration into base vendor configuration
168*ff35212dScey     // Can be cutomized
169*ff35212dScey     return resultBuilder;
170*ff35212dScey   }
171*ff35212dScey 
172*ff35212dScey   /** Apply device overly to multiple carriers setting */
173*ff35212dScey   public static MultiCarrierSettings applyDeviceOverlayToMultiCarrierSettings(
174*ff35212dScey       MultiCarrierSettings overlay, MultiCarrierSettings base) {
175*ff35212dScey 
176*ff35212dScey     Preconditions.checkNotNull(overlay);
177*ff35212dScey     Preconditions.checkNotNull(base);
178*ff35212dScey 
179*ff35212dScey     MultiCarrierSettings.Builder resultBuilder = base.toBuilder().clearSetting();
180*ff35212dScey     long version = 0L;
181*ff35212dScey 
182*ff35212dScey     for (CarrierSettings cs : base.getSettingList()) {
183*ff35212dScey       // Apply overlay and put overlayed carrier setting back
184*ff35212dScey       CarrierSettings.Builder merged = applyDeviceOverlayToCarrierSettings(overlay, cs);
185*ff35212dScey       resultBuilder.addSetting(merged);
186*ff35212dScey       // The top-level version number is the sum of all version numbers of settings
187*ff35212dScey       version += merged.getVersion();
188*ff35212dScey     }
189*ff35212dScey     resultBuilder.setVersion(version);
190*ff35212dScey 
191*ff35212dScey     return resultBuilder.build();
192*ff35212dScey   }
193*ff35212dScey 
194*ff35212dScey   /**
195*ff35212dScey    * Sort a list of CarrierMap with single CarrierId.
196*ff35212dScey    *
197*ff35212dScey    * <p>Precondition: no duplication in input list
198*ff35212dScey    *
199*ff35212dScey    * <p>Order by:
200*ff35212dScey    *
201*ff35212dScey    * <ul>
202*ff35212dScey    *   <li>mcc_mnc
203*ff35212dScey    *   <li>(for the same mcc_mnc) any mvno_data comes before MVNODATA_NOT_SET (Preconditon: there is
204*ff35212dScey    *       only one entry with MVNODATA_NOT_SET per mcc_mnc otherwise they're duplicated)
205*ff35212dScey    *   <li>(for MVNOs of the same mcc_mnc) mvno_data case + value as string
206*ff35212dScey    * </ul>
207*ff35212dScey    */
208*ff35212dScey   public static void sortCarrierMapEntries(List<CarrierMap> list) {
209*ff35212dScey     final Comparator<CarrierMap> byMccMnc =
210*ff35212dScey         Comparator.comparing(
211*ff35212dScey             (cm) -> {
212*ff35212dScey               return cm.getCarrierId(0).getMccMnc();
213*ff35212dScey             });
214*ff35212dScey     final Comparator<CarrierMap> mvnoFirst =
215*ff35212dScey         Comparator.comparingInt(
216*ff35212dScey             (cm) -> {
217*ff35212dScey               switch (cm.getCarrierId(0).getMvnoDataCase()) {
218*ff35212dScey                 case MVNODATA_NOT_SET:
219*ff35212dScey                   return 1;
220*ff35212dScey                 default:
221*ff35212dScey                   return 0;
222*ff35212dScey               }
223*ff35212dScey             });
224*ff35212dScey     final Comparator<CarrierMap> byMvnoDataCaseValue =
225*ff35212dScey         Comparator.comparing(
226*ff35212dScey             (cm) -> {
227*ff35212dScey               final CarrierId cid = cm.getCarrierId(0);
228*ff35212dScey               switch (cid.getMvnoDataCase()) {
229*ff35212dScey                 case GID1:
230*ff35212dScey                   return "GID1=" + cid.getGid1();
231*ff35212dScey                 case IMSI:
232*ff35212dScey                   return "IMSI=" + cid.getImsi();
233*ff35212dScey                 case SPN:
234*ff35212dScey                   return "SPN=" + cid.getSpn();
235*ff35212dScey                 case MVNODATA_NOT_SET:
236*ff35212dScey                   throw new AssertionError("MNO should not be compared here but in `mvnoFirst`");
237*ff35212dScey               }
238*ff35212dScey               throw new AssertionError("uncaught case " + cid.getMvnoDataCase());
239*ff35212dScey             });
240*ff35212dScey     Collections.sort(list, byMccMnc.thenComparing(mvnoFirst).thenComparing(byMvnoDataCaseValue));
241*ff35212dScey   }
242*ff35212dScey }
243