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 package android.telephony.euicc;
17 
18 import android.annotation.IntDef;
19 import android.annotation.Nullable;
20 import android.annotation.SystemApi;
21 import android.os.Parcel;
22 import android.os.Parcelable;
23 import android.service.carrier.CarrierIdentifier;
24 import android.service.euicc.EuiccProfileInfo;
25 import android.text.TextUtils;
26 
27 import com.android.internal.annotations.VisibleForTesting;
28 
29 import java.lang.annotation.Retention;
30 import java.lang.annotation.RetentionPolicy;
31 import java.util.Arrays;
32 import java.util.List;
33 
34 /**
35  * This represents the RAT (Rules Authorisation Table) stored on eUICC.
36  * @hide
37  */
38 @SystemApi
39 public final class EuiccRulesAuthTable implements Parcelable {
40     /** Profile policy rule flags */
41     @Retention(RetentionPolicy.SOURCE)
42     @IntDef(flag = true, prefix = { "POLICY_RULE_FLAG_" }, value = {
43             POLICY_RULE_FLAG_CONSENT_REQUIRED
44     })
45     /** @hide */
46     public @interface PolicyRuleFlag {}
47 
48     /** User consent is required to install the profile. */
49     public static final int POLICY_RULE_FLAG_CONSENT_REQUIRED = 1;
50 
51     private final int[] mPolicyRules;
52     private final CarrierIdentifier[][] mCarrierIds;
53     private final int[] mPolicyRuleFlags;
54 
55     /** This is used to build new {@link EuiccRulesAuthTable} instance. */
56     public static final class Builder {
57         private int[] mPolicyRules;
58         private CarrierIdentifier[][] mCarrierIds;
59         private int[] mPolicyRuleFlags;
60         private int mPosition;
61 
62         /**
63          * Creates a new builder.
64          *
65          * @param ruleNum The number of authorisation rules in the table.
66          */
Builder(int ruleNum)67         public Builder(int ruleNum) {
68             mPolicyRules = new int[ruleNum];
69             mCarrierIds = new CarrierIdentifier[ruleNum][];
70             mPolicyRuleFlags = new int[ruleNum];
71         }
72 
73         /**
74          * Builds the RAT instance. This builder should not be used anymore after this method is
75          * called, otherwise {@link NullPointerException} will be thrown.
76          */
build()77         public EuiccRulesAuthTable build() {
78             if (mPosition != mPolicyRules.length) {
79                 throw new IllegalStateException(
80                         "Not enough rules are added, expected: "
81                                 + mPolicyRules.length
82                                 + ", added: "
83                                 + mPosition);
84             }
85             return new EuiccRulesAuthTable(mPolicyRules, mCarrierIds, mPolicyRuleFlags);
86         }
87 
88         /**
89          * Adds an authorisation rule.
90          *
91          * @throws ArrayIndexOutOfBoundsException If the {@code mPosition} is larger than the size
92          *     this table.
93          */
add(int policyRules, List<CarrierIdentifier> carrierId, int policyRuleFlags)94         public Builder add(int policyRules, List<CarrierIdentifier> carrierId, int policyRuleFlags) {
95             if (mPosition >= mPolicyRules.length) {
96                 throw new ArrayIndexOutOfBoundsException(mPosition);
97             }
98             mPolicyRules[mPosition] = policyRules;
99             if (carrierId != null && carrierId.size() > 0) {
100                 mCarrierIds[mPosition] = carrierId.toArray(new CarrierIdentifier[carrierId.size()]);
101             }
102             mPolicyRuleFlags[mPosition] = policyRuleFlags;
103             mPosition++;
104             return this;
105         }
106     }
107 
108     /**
109      * @param mccRule A 2-character or 3-character string which can be either MCC or MNC. The
110      *     character 'E' is used as a wild char to match any digit.
111      * @param mcc A 2-character or 3-character string which can be either MCC or MNC.
112      * @return Whether the {@code mccRule} matches {@code mcc}.
113      *
114      * @hide
115      */
116     @VisibleForTesting
match(String mccRule, String mcc)117     public static boolean match(String mccRule, String mcc) {
118         if (mccRule.length() < mcc.length()) {
119             return false;
120         }
121         for (int i = 0; i < mccRule.length(); i++) {
122             // 'E' is the wild char to match any digit.
123             if (mccRule.charAt(i) == 'E'
124                     || (i < mcc.length() && mccRule.charAt(i) == mcc.charAt(i))) {
125                 continue;
126             }
127             return false;
128         }
129         return true;
130     }
131 
EuiccRulesAuthTable(int[] policyRules, CarrierIdentifier[][] carrierIds, int[] policyRuleFlags)132     private EuiccRulesAuthTable(int[] policyRules, CarrierIdentifier[][] carrierIds,
133             int[] policyRuleFlags) {
134         mPolicyRules = policyRules;
135         mCarrierIds = carrierIds;
136         mPolicyRuleFlags = policyRuleFlags;
137     }
138 
139     /**
140      * Finds the index of the first authorisation rule matching the given policy and carrier id. If
141      * the returned index is not negative, the carrier is allowed to apply this policy to its
142      * profile.
143      *
144      * @param policy The policy rule.
145      * @param carrierId The carrier id.
146      * @return The index of authorization rule. If no rule is found, -1 will be returned.
147      */
findIndex(@uiccProfileInfo.PolicyRule int policy, CarrierIdentifier carrierId)148     public int findIndex(@EuiccProfileInfo.PolicyRule int policy, CarrierIdentifier carrierId) {
149         for (int i = 0; i < mPolicyRules.length; i++) {
150             if ((mPolicyRules[i] & policy) == 0) {
151                 continue;
152             }
153             CarrierIdentifier[] carrierIds = mCarrierIds[i];
154             if (carrierIds == null || carrierIds.length == 0) {
155                 continue;
156             }
157             for (int j = 0; j < carrierIds.length; j++) {
158                 CarrierIdentifier ruleCarrierId = carrierIds[j];
159                 if (!match(ruleCarrierId.getMcc(), carrierId.getMcc())
160                         || !match(ruleCarrierId.getMnc(), carrierId.getMnc())) {
161                     continue;
162                 }
163                 String gid = ruleCarrierId.getGid1();
164                 if (!TextUtils.isEmpty(gid) && !gid.equals(carrierId.getGid1())) {
165                     continue;
166                 }
167                 gid = ruleCarrierId.getGid2();
168                 if (!TextUtils.isEmpty(gid) && !gid.equals(carrierId.getGid2())) {
169                     continue;
170                 }
171                 return i;
172             }
173         }
174         return -1;
175     }
176 
177     /**
178      * Tests if the entry in the table has the given policy rule flag.
179      *
180      * @param index The index of the entry.
181      * @param flag The policy rule flag to be tested.
182      * @throws ArrayIndexOutOfBoundsException If the {@code index} is negative or larger than the
183      *     size of this table.
184      */
hasPolicyRuleFlag(int index, @PolicyRuleFlag int flag)185     public boolean hasPolicyRuleFlag(int index, @PolicyRuleFlag int flag) {
186         if (index < 0 || index >= mPolicyRules.length) {
187             throw new ArrayIndexOutOfBoundsException(index);
188         }
189         return (mPolicyRuleFlags[index] & flag) != 0;
190     }
191 
192     @Override
describeContents()193     public int describeContents() {
194         return 0;
195     }
196 
197     @Override
writeToParcel(Parcel dest, int flags)198     public void writeToParcel(Parcel dest, int flags) {
199         dest.writeIntArray(mPolicyRules);
200         for (CarrierIdentifier[] ids : mCarrierIds) {
201             dest.writeTypedArray(ids, flags);
202         }
203         dest.writeIntArray(mPolicyRuleFlags);
204     }
205 
206     @Override
equals(@ullable Object obj)207     public boolean equals(@Nullable Object obj) {
208         if (this == obj) {
209             return true;
210         }
211         if (obj == null || getClass() != obj.getClass()) {
212             return false;
213         }
214 
215         EuiccRulesAuthTable that = (EuiccRulesAuthTable) obj;
216         if (mCarrierIds.length != that.mCarrierIds.length) {
217             return false;
218         }
219         for (int i = 0; i < mCarrierIds.length; i++) {
220             CarrierIdentifier[] carrierIds = mCarrierIds[i];
221             CarrierIdentifier[] thatCarrierIds = that.mCarrierIds[i];
222             if (carrierIds != null && thatCarrierIds != null) {
223                 if (carrierIds.length != thatCarrierIds.length) {
224                     return false;
225                 }
226                 for (int j = 0; j < carrierIds.length; j++) {
227                     if (!carrierIds[j].equals(thatCarrierIds[j])) {
228                         return false;
229                     }
230                 }
231                 continue;
232             } else if (carrierIds == null && thatCarrierIds == null) {
233                 continue;
234             }
235             return false;
236         }
237 
238         return Arrays.equals(mPolicyRules, that.mPolicyRules)
239                 && Arrays.equals(mPolicyRuleFlags, that.mPolicyRuleFlags);
240     }
241 
EuiccRulesAuthTable(Parcel source)242     private EuiccRulesAuthTable(Parcel source) {
243         mPolicyRules = source.createIntArray();
244         int len = mPolicyRules.length;
245         mCarrierIds = new CarrierIdentifier[len][];
246         for (int i = 0; i < len; i++) {
247             mCarrierIds[i] = source.createTypedArray(CarrierIdentifier.CREATOR);
248         }
249         mPolicyRuleFlags = source.createIntArray();
250     }
251 
252     public static final @android.annotation.NonNull Creator<EuiccRulesAuthTable> CREATOR =
253             new Creator<EuiccRulesAuthTable>() {
254                 @Override
255                 public EuiccRulesAuthTable createFromParcel(Parcel source) {
256                     return new EuiccRulesAuthTable(source);
257                 }
258 
259                 @Override
260                 public EuiccRulesAuthTable[] newArray(int size) {
261                     return new EuiccRulesAuthTable[size];
262                 }
263             };
264 }
265