Home | History | Annotate | Download | only in euicc
      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.SystemApi;
     20 import android.os.Parcel;
     21 import android.os.Parcelable;
     22 import android.service.carrier.CarrierIdentifier;
     23 import android.service.euicc.EuiccProfileInfo;
     24 import android.text.TextUtils;
     25 
     26 import com.android.internal.annotations.VisibleForTesting;
     27 
     28 import java.lang.annotation.Retention;
     29 import java.lang.annotation.RetentionPolicy;
     30 import java.util.Arrays;
     31 import java.util.ArrayList;
     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          */
     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          */
     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          */
     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
    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 
    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      */
    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      */
    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
    193     public int describeContents() {
    194         return 0;
    195     }
    196 
    197     @Override
    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
    207     public boolean equals(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 
    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 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