1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.telephony;
18 
19 import android.annotation.IntDef;
20 import android.annotation.NonNull;
21 import android.annotation.SystemApi;
22 import android.os.Parcel;
23 import android.os.Parcelable;
24 import android.service.carrier.CarrierIdentifier;
25 
26 import java.lang.annotation.Retention;
27 import java.lang.annotation.RetentionPolicy;
28 import java.util.ArrayList;
29 import java.util.List;
30 import java.util.Locale;
31 import java.util.Objects;
32 
33 /**
34  * Contains the list of carrier restrictions.
35  * Allowed list: it indicates the list of carriers that are allowed.
36  * Excluded list: it indicates the list of carriers that are excluded.
37  * Default carrier restriction: it indicates the default behavior and the priority between the two
38  * lists:
39  *  - not allowed: the device only allows usage of carriers that are present in the allowed list
40  *    and not present in the excluded list. This implies that if a carrier is not present in either
41  *    list, it is not allowed.
42  *  - allowed: the device allows all carriers, except those present in the excluded list and not
43  *    present in the allowed list. This implies that if a carrier is not present in either list,
44  *    it is allowed.
45  * MultiSim policy: it indicates the behavior in case of devices with two or more SIM cards.
46  *  - MULTISIM_POLICY_NONE: the same configuration is applied to all SIM slots independently. This
47  *    is the default value if none is set.
48  *  - MULTISIM_POLICY_ONE_VALID_SIM_MUST_BE_PRESENT: it indicates that any SIM card can be used
49  *    as far as one SIM card matching the configuration is present in the device.
50  *
51  * Both lists support the character '?' as wild character. For example, an entry indicating
52  * MCC=310 and MNC=??? will match all networks with MCC=310.
53  *
54  * Example 1: Allowed list contains MCC and MNC of operator A. Excluded list contains operator B,
55  *            which has same MCC and MNC, but also GID1 value. The priority allowed list is set
56  *            to true. Only SIM cards of operator A are allowed, but not those of B or any other
57  *            operator.
58  * Example 2: Allowed list contains MCC and MNC of operator A. Excluded list contains an entry
59  *            with same MCC, and '???' as MNC. The priority allowed list is set to false.
60  *            SIM cards of operator A and all SIM cards with a different MCC value are allowed.
61  *            SIM cards of operators with same MCC value and different MNC are not allowed.
62  * @hide
63  */
64 @SystemApi
65 public final class CarrierRestrictionRules implements Parcelable {
66     /**
67      * The device only allows usage of carriers that are present in the allowed list and not
68      * present in the excluded list. This implies that if a carrier is not present in either list,
69      * it is not allowed.
70      */
71     public static final int CARRIER_RESTRICTION_DEFAULT_NOT_ALLOWED = 0;
72 
73     /**
74      * The device allows all carriers, except those present in the excluded list and not present
75      * in the allowed list. This implies that if a carrier is not present in either list, it is
76      * allowed.
77      */
78     public static final int CARRIER_RESTRICTION_DEFAULT_ALLOWED = 1;
79 
80     /** The same configuration is applied to all SIM slots independently. */
81     public static final int MULTISIM_POLICY_NONE = 0;
82 
83     /** Any SIM card can be used as far as one SIM card matching the configuration is present. */
84     public static final int MULTISIM_POLICY_ONE_VALID_SIM_MUST_BE_PRESENT = 1;
85 
86     /** @hide */
87     @Retention(RetentionPolicy.SOURCE)
88     @IntDef(prefix = "MULTISIM_POLICY_",
89             value = {MULTISIM_POLICY_NONE, MULTISIM_POLICY_ONE_VALID_SIM_MUST_BE_PRESENT})
90     public @interface MultiSimPolicy {}
91 
92     /** @hide */
93     @Retention(RetentionPolicy.SOURCE)
94     @IntDef(prefix = "CARRIER_RESTRICTION_DEFAULT_",
95             value = {CARRIER_RESTRICTION_DEFAULT_NOT_ALLOWED, CARRIER_RESTRICTION_DEFAULT_ALLOWED})
96     public @interface CarrierRestrictionDefault {}
97 
98     /* Wild character for comparison */
99     private static final char WILD_CHARACTER = '?';
100 
101     private List<CarrierIdentifier> mAllowedCarriers;
102     private List<CarrierIdentifier> mExcludedCarriers;
103     @CarrierRestrictionDefault
104     private int mCarrierRestrictionDefault;
105     @MultiSimPolicy
106     private int mMultiSimPolicy;
107     @TelephonyManager.CarrierRestrictionStatus
108     private int mCarrierRestrictionStatus;
109 
CarrierRestrictionRules()110     private CarrierRestrictionRules() {
111         mAllowedCarriers = new ArrayList<CarrierIdentifier>();
112         mExcludedCarriers = new ArrayList<CarrierIdentifier>();
113         mCarrierRestrictionDefault = CARRIER_RESTRICTION_DEFAULT_NOT_ALLOWED;
114         mMultiSimPolicy = MULTISIM_POLICY_NONE;
115         mCarrierRestrictionStatus = TelephonyManager.CARRIER_RESTRICTION_STATUS_UNKNOWN;
116     }
117 
CarrierRestrictionRules(Parcel in)118     private CarrierRestrictionRules(Parcel in) {
119         mAllowedCarriers = new ArrayList<CarrierIdentifier>();
120         mExcludedCarriers = new ArrayList<CarrierIdentifier>();
121 
122         in.readTypedList(mAllowedCarriers, CarrierIdentifier.CREATOR);
123         in.readTypedList(mExcludedCarriers, CarrierIdentifier.CREATOR);
124         mCarrierRestrictionDefault = in.readInt();
125         mMultiSimPolicy = in.readInt();
126         mCarrierRestrictionStatus = in.readInt();
127     }
128 
129     /**
130      * Creates a new builder for this class
131      * @hide
132      */
newBuilder()133     public static Builder newBuilder() {
134         return new Builder();
135     }
136 
137     /**
138      * Indicates if all carriers are allowed
139      */
isAllCarriersAllowed()140     public boolean isAllCarriersAllowed() {
141         return (mAllowedCarriers.isEmpty() && mExcludedCarriers.isEmpty()
142                 && mCarrierRestrictionDefault == CARRIER_RESTRICTION_DEFAULT_ALLOWED);
143     }
144 
145     /**
146      * Retrieves list of allowed carriers
147      *
148      * @return the list of allowed carriers
149      */
getAllowedCarriers()150     public @NonNull List<CarrierIdentifier> getAllowedCarriers() {
151         return mAllowedCarriers;
152     }
153 
154     /**
155      * Retrieves list of excluded carriers
156      *
157      * @return the list of excluded carriers
158      */
getExcludedCarriers()159     public @NonNull List<CarrierIdentifier> getExcludedCarriers() {
160         return mExcludedCarriers;
161     }
162 
163     /**
164      * Retrieves the default behavior of carrier restrictions
165      */
getDefaultCarrierRestriction()166     public @CarrierRestrictionDefault int getDefaultCarrierRestriction() {
167         return mCarrierRestrictionDefault;
168     }
169 
170     /**
171      * @return The policy used for multi-SIM devices
172      */
getMultiSimPolicy()173     public @MultiSimPolicy int getMultiSimPolicy() {
174         return mMultiSimPolicy;
175     }
176 
177     /**
178      * Tests an array of carriers with the carrier restriction configuration. The list of carrier
179      * ids passed as argument does not need to be the same as currently present in the device.
180      *
181      * @param carrierIds list of {@link CarrierIdentifier}, one for each SIM slot on the device
182      * @return a list of boolean with the same size as input, indicating if each
183      * {@link CarrierIdentifier} is allowed or not.
184      */
areCarrierIdentifiersAllowed( @onNull List<CarrierIdentifier> carrierIds)185     public @NonNull List<Boolean> areCarrierIdentifiersAllowed(
186             @NonNull List<CarrierIdentifier> carrierIds) {
187         ArrayList<Boolean> result = new ArrayList<>(carrierIds.size());
188 
189         // First calculate the result for each slot independently
190         for (int i = 0; i < carrierIds.size(); i++) {
191             boolean inAllowedList = isCarrierIdInList(carrierIds.get(i), mAllowedCarriers);
192             boolean inExcludedList = isCarrierIdInList(carrierIds.get(i), mExcludedCarriers);
193             if (mCarrierRestrictionDefault == CARRIER_RESTRICTION_DEFAULT_NOT_ALLOWED) {
194                 result.add((inAllowedList && !inExcludedList) ? true : false);
195             } else {
196                 result.add((inExcludedList && !inAllowedList) ? false : true);
197             }
198         }
199         // Apply the multi-slot policy, if needed.
200         if (mMultiSimPolicy == MULTISIM_POLICY_ONE_VALID_SIM_MUST_BE_PRESENT) {
201             for (boolean b : result) {
202                 if (b) {
203                     result.replaceAll(x -> true);
204                     break;
205                 }
206             }
207         }
208         return result;
209     }
210 
211     /**
212      * Indicates if a certain carrier {@code id} is present inside a {@code list}
213      *
214      * @return true if the carrier {@code id} is present, false otherwise
215      */
isCarrierIdInList(CarrierIdentifier id, List<CarrierIdentifier> list)216     private static boolean isCarrierIdInList(CarrierIdentifier id, List<CarrierIdentifier> list) {
217         for (CarrierIdentifier listItem : list) {
218             // Compare MCC and MNC
219             if (!patternMatch(id.getMcc(), listItem.getMcc())
220                     || !patternMatch(id.getMnc(), listItem.getMnc())) {
221                 continue;
222             }
223 
224             // Compare SPN. Comparison is on the complete strings, case insensitive and with wild
225             // characters.
226             String listItemValue = convertNullToEmpty(listItem.getSpn());
227             String idValue = convertNullToEmpty(id.getSpn());
228             if (!listItemValue.isEmpty()) {
229                 if (!patternMatch(idValue, listItemValue)) {
230                     continue;
231                 }
232             }
233 
234             // The IMSI of the configuration can be shorter than actual IMSI in the SIM card.
235             listItemValue = convertNullToEmpty(listItem.getImsi());
236             idValue = convertNullToEmpty(id.getImsi());
237             if (!patternMatch(
238                     idValue.substring(0, Math.min(idValue.length(), listItemValue.length())),
239                     listItemValue)) {
240                 continue;
241             }
242 
243             // The GID1 of the configuration can be shorter than actual GID1 in the SIM card.
244             listItemValue = convertNullToEmpty(listItem.getGid1());
245             idValue = convertNullToEmpty(id.getGid1());
246             if (!patternMatch(
247                     idValue.substring(0, Math.min(idValue.length(), listItemValue.length())),
248                     listItemValue)) {
249                 continue;
250             }
251 
252             // The GID2 of the configuration can be shorter than actual GID2 in the SIM card.
253             listItemValue = convertNullToEmpty(listItem.getGid2());
254             idValue = convertNullToEmpty(id.getGid2());
255             if (!patternMatch(
256                     idValue.substring(0, Math.min(idValue.length(), listItemValue.length())),
257                     listItemValue)) {
258                 continue;
259             }
260 
261             // Valid match was found in the list
262             return true;
263         }
264         return false;
265     }
266 
convertNullToEmpty(String value)267     private static String convertNullToEmpty(String value) {
268         return Objects.toString(value, "");
269     }
270 
271     /**
272      * Performs a case insensitive string comparison against a given pattern. The character '?'
273      * is used in the pattern as wild character in the comparison. The string must have the same
274      * length as the pattern.
275      *
276      * @param str string to match
277      * @param pattern string containing the pattern
278      * @return true in case of match, false otherwise
279      */
patternMatch(String str, String pattern)280     private static boolean patternMatch(String str, String pattern) {
281         if (str.length() != pattern.length()) {
282             return false;
283         }
284         String lowerCaseStr = str.toLowerCase(Locale.ROOT);
285         String lowerCasePattern = pattern.toLowerCase(Locale.ROOT);
286 
287         for (int i = 0; i < lowerCasePattern.length(); i++) {
288             if (lowerCasePattern.charAt(i) != lowerCaseStr.charAt(i)
289                     && lowerCasePattern.charAt(i) != WILD_CHARACTER) {
290                 return false;
291             }
292         }
293         return true;
294     }
295 
296     /** @hide */
getCarrierRestrictionStatus()297     public int getCarrierRestrictionStatus() {
298         return mCarrierRestrictionStatus;
299     }
300 
301     /**
302      * {@link Parcelable#writeToParcel}
303      */
304     @Override
writeToParcel(Parcel out, int flags)305     public void writeToParcel(Parcel out, int flags) {
306         out.writeTypedList(mAllowedCarriers);
307         out.writeTypedList(mExcludedCarriers);
308         out.writeInt(mCarrierRestrictionDefault);
309         out.writeInt(mMultiSimPolicy);
310         out.writeInt(mCarrierRestrictionStatus);
311     }
312 
313     /**
314      * {@link Parcelable#describeContents}
315      */
316     @Override
describeContents()317     public int describeContents() {
318         return 0;
319     }
320 
321     /**
322      * {@link Parcelable.Creator}
323      */
324     public static final @android.annotation.NonNull Creator<CarrierRestrictionRules> CREATOR =
325             new Creator<CarrierRestrictionRules>() {
326         @Override
327         public CarrierRestrictionRules createFromParcel(Parcel in) {
328             return new CarrierRestrictionRules(in);
329         }
330 
331         @Override
332         public CarrierRestrictionRules[] newArray(int size) {
333             return new CarrierRestrictionRules[size];
334         }
335     };
336 
337     @NonNull
338     @Override
toString()339     public String toString() {
340         return "CarrierRestrictionRules(allowed:" + mAllowedCarriers + ", excluded:"
341                 + mExcludedCarriers + ", default:" + mCarrierRestrictionDefault
342                 + ", multisim policy:" + mMultiSimPolicy + ")";
343     }
344 
345     /**
346      * Builder for a {@link CarrierRestrictionRules}.
347      */
348     public static final class Builder {
349         private final CarrierRestrictionRules mRules;
350 
Builder()351         public Builder() {
352             mRules = new CarrierRestrictionRules();
353         }
354 
355         /** build command */
build()356         public @NonNull CarrierRestrictionRules build() {
357             return mRules;
358         }
359 
360         /**
361          * Indicate that all carriers are allowed.
362          */
setAllCarriersAllowed()363         public @NonNull Builder setAllCarriersAllowed() {
364             mRules.mAllowedCarriers.clear();
365             mRules.mExcludedCarriers.clear();
366             mRules.mCarrierRestrictionDefault = CARRIER_RESTRICTION_DEFAULT_ALLOWED;
367             return this;
368         }
369 
370         /**
371          * Set list of allowed carriers.
372          *
373          * @param allowedCarriers list of allowed carriers
374          */
setAllowedCarriers( @onNull List<CarrierIdentifier> allowedCarriers)375         public @NonNull Builder setAllowedCarriers(
376                 @NonNull List<CarrierIdentifier> allowedCarriers) {
377             mRules.mAllowedCarriers = new ArrayList<CarrierIdentifier>(allowedCarriers);
378             return this;
379         }
380 
381         /**
382          * Set list of excluded carriers.
383          *
384          * @param excludedCarriers list of excluded carriers
385          */
setExcludedCarriers( @onNull List<CarrierIdentifier> excludedCarriers)386         public @NonNull Builder setExcludedCarriers(
387                 @NonNull List<CarrierIdentifier> excludedCarriers) {
388             mRules.mExcludedCarriers = new ArrayList<CarrierIdentifier>(excludedCarriers);
389             return this;
390         }
391 
392         /**
393          * Set the default behavior of the carrier restrictions
394          *
395          * @param carrierRestrictionDefault prioritized carrier list
396          */
setDefaultCarrierRestriction( @arrierRestrictionDefault int carrierRestrictionDefault)397         public @NonNull Builder setDefaultCarrierRestriction(
398                 @CarrierRestrictionDefault int carrierRestrictionDefault) {
399             mRules.mCarrierRestrictionDefault = carrierRestrictionDefault;
400             return this;
401         }
402 
403         /**
404          * Set the policy to be used for multi-SIM devices
405          *
406          * @param multiSimPolicy multi SIM policy
407          */
setMultiSimPolicy(@ultiSimPolicy int multiSimPolicy)408         public @NonNull Builder setMultiSimPolicy(@MultiSimPolicy int multiSimPolicy) {
409             mRules.mMultiSimPolicy = multiSimPolicy;
410             return this;
411         }
412 
413         /**
414          * Set the device's carrier restriction status
415          *
416          * @param carrierRestrictionStatus device restriction status
417          * @hide
418          */
419         public @NonNull
setCarrierRestrictionStatus(int carrierRestrictionStatus)420         Builder setCarrierRestrictionStatus(int carrierRestrictionStatus) {
421             mRules.mCarrierRestrictionStatus = carrierRestrictionStatus;
422             return this;
423         }
424     }
425 }
426