diff options
| -rw-r--r-- | telephony/java/android/telephony/euicc/EuiccNotification.java | 179 | ||||
| -rw-r--r-- | telephony/java/android/telephony/euicc/EuiccRat.java | 259 |
2 files changed, 438 insertions, 0 deletions
diff --git a/telephony/java/android/telephony/euicc/EuiccNotification.java b/telephony/java/android/telephony/euicc/EuiccNotification.java new file mode 100644 index 000000000000..ef3c1ce8cf3b --- /dev/null +++ b/telephony/java/android/telephony/euicc/EuiccNotification.java @@ -0,0 +1,179 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.telephony.euicc; + +import android.annotation.IntDef; +import android.annotation.Nullable; +import android.os.Parcel; +import android.os.Parcelable; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.Arrays; +import java.util.Objects; + +/** + * This represents a signed notification which is defined in SGP.22. It can be either a profile + * installation result or a notification generated for profile operations (e.g., enabling, + * disabling, or deleting). + * + * @hide + * + * TODO(b/35851809): Make this a @SystemApi. + */ +public class EuiccNotification implements Parcelable { + /** Event */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(flag = true, prefix = { "EVENT_" }, value = { + EVENT_INSTALL, + EVENT_ENABLE, + EVENT_DISABLE, + EVENT_DELETE + }) + public @interface Event {} + + /** A profile is downloaded and installed. */ + public static final int EVENT_INSTALL = 1; + + /** A profile is enabled. */ + public static final int EVENT_ENABLE = 1 << 1; + + /** A profile is disabled. */ + public static final int EVENT_DISABLE = 1 << 2; + + /** A profile is deleted. */ + public static final int EVENT_DELETE = 1 << 3; + + /** Value of the bits of all above events */ + @Event + public static final int ALL_EVENTS = + EVENT_INSTALL | EVENT_ENABLE | EVENT_DISABLE | EVENT_DELETE; + + private final int mSeq; + private final String mTargetAddr; + @Event private final int mEvent; + @Nullable private final byte[] mData; + + /** + * Creates an instance. + * + * @param seq The sequence number of this notification. + * @param targetAddr The target server where to send this notification. + * @param event The event which causes this notification. + * @param data The data which needs to be sent to the target server. This can be null for + * building a list of notification metadata without data. + */ + public EuiccNotification(int seq, String targetAddr, @Event int event, @Nullable byte[] data) { + mSeq = seq; + mTargetAddr = targetAddr; + mEvent = event; + mData = data; + } + + /** @return The sequence number of this notification. */ + public int getSeq() { + return mSeq; + } + + /** @return The target server address where this notification should be sent to. */ + public String getTargetAddr() { + return mTargetAddr; + } + + /** @return The event of this notification. */ + @Event + public int getEvent() { + return mEvent; + } + + /** @return The notification data which needs to be sent to the target server. */ + @Nullable + public byte[] getData() { + return mData; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null || getClass() != obj.getClass()) { + return false; + } + + EuiccNotification that = (EuiccNotification) obj; + return mSeq == that.mSeq + && Objects.equals(mTargetAddr, that.mTargetAddr) + && mEvent == that.mEvent + && Arrays.equals(mData, that.mData); + } + + @Override + public int hashCode() { + int result = 1; + result = 31 * result + mSeq; + result = 31 * result + Objects.hashCode(mTargetAddr); + result = 31 * result + mEvent; + result = 31 * result + Arrays.hashCode(mData); + return result; + } + + @Override + public String toString() { + return "EuiccNotification (seq=" + + mSeq + + ", targetAddr=" + + mTargetAddr + + ", event=" + + mEvent + + ", data=" + + (mData == null ? "null" : "byte[" + mData.length + "]") + + ")"; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mSeq); + dest.writeString(mTargetAddr); + dest.writeInt(mEvent); + dest.writeByteArray(mData); + } + + private EuiccNotification(Parcel source) { + mSeq = source.readInt(); + mTargetAddr = source.readString(); + mEvent = source.readInt(); + mData = source.createByteArray(); + } + + public static final Creator<EuiccNotification> CREATOR = + new Creator<EuiccNotification>() { + @Override + public EuiccNotification createFromParcel(Parcel source) { + return new EuiccNotification(source); + } + + @Override + public EuiccNotification[] newArray(int size) { + return new EuiccNotification[size]; + } + }; +} diff --git a/telephony/java/android/telephony/euicc/EuiccRat.java b/telephony/java/android/telephony/euicc/EuiccRat.java new file mode 100644 index 000000000000..6a56503ac380 --- /dev/null +++ b/telephony/java/android/telephony/euicc/EuiccRat.java @@ -0,0 +1,259 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.telephony.euicc; + +import android.annotation.IntDef; +import android.os.Parcel; +import android.os.Parcelable; +import android.service.carrier.CarrierIdentifier; +import android.service.euicc.EuiccProfileInfo; +import android.text.TextUtils; + +import com.android.internal.annotations.VisibleForTesting; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.Arrays; + +/** + * This represents the RAT (Rules Authorisation Table) stored on eUICC. + * + * @hide + * + * TODO(b/35851809): Make this a @SystemApi. + */ +public final class EuiccRat implements Parcelable { + /** Profile policy rule flags */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(flag = true, prefix = { "POLICY_RULE_FLAG_" }, value = { + POLICY_RULE_FLAG_CONSENT_REQUIRED + }) + public @interface PolicyRuleFlag {} + + /** User consent is required to install the profile. */ + public static final int POLICY_RULE_FLAG_CONSENT_REQUIRED = 1; + + private final int[] mPolicyRules; + private final CarrierIdentifier[][] mCarrierIds; + private final int[] mPolicyRuleFlags; + + /** This is used to build new {@link EuiccRat} instance. */ + public static final class Builder { + private int[] mPolicyRules; + private CarrierIdentifier[][] mCarrierIds; + private int[] mPolicyRuleFlags; + private int mPosition; + + /** + * Creates a new builder. + * + * @param ruleNum The number of authorisation rules in the table. + */ + public Builder(int ruleNum) { + mPolicyRules = new int[ruleNum]; + mCarrierIds = new CarrierIdentifier[ruleNum][]; + mPolicyRuleFlags = new int[ruleNum]; + } + + /** + * Builds the RAT instance. This builder should not be used anymore after this method is + * called, otherwise {@link NullPointerException} will be thrown. + */ + public EuiccRat build() { + if (mPosition != mPolicyRules.length) { + throw new IllegalStateException( + "Not enough rules are added, expected: " + + mPolicyRules.length + + ", added: " + + mPosition); + } + return new EuiccRat(mPolicyRules, mCarrierIds, mPolicyRuleFlags); + } + + /** + * Adds an authorisation rule. + * + * @throws ArrayIndexOutOfBoundsException If the {@code mPosition} is larger than the size + * this table. + */ + public Builder add(int policyRules, CarrierIdentifier[] carrierId, int policyRuleFlags) { + if (mPosition >= mPolicyRules.length) { + throw new ArrayIndexOutOfBoundsException(mPosition); + } + mPolicyRules[mPosition] = policyRules; + mCarrierIds[mPosition] = carrierId; + mPolicyRuleFlags[mPosition] = policyRuleFlags; + mPosition++; + return this; + } + } + + /** + * @param mccRule A 2-character or 3-character string which can be either MCC or MNC. The + * character 'E' is used as a wild char to match any digit. + * @param mcc A 2-character or 3-character string which can be either MCC or MNC. + * @return Whether the {@code mccRule} matches {@code mcc}. + * + * @hide + */ + @VisibleForTesting + public static boolean match(String mccRule, String mcc) { + if (mccRule.length() < mcc.length()) { + return false; + } + for (int i = 0; i < mccRule.length(); i++) { + // 'E' is the wild char to match any digit. + if (mccRule.charAt(i) == 'E' + || (i < mcc.length() && mccRule.charAt(i) == mcc.charAt(i))) { + continue; + } + return false; + } + return true; + } + + private EuiccRat(int[] policyRules, CarrierIdentifier[][] carrierIds, int[] policyRuleFlags) { + mPolicyRules = policyRules; + mCarrierIds = carrierIds; + mPolicyRuleFlags = policyRuleFlags; + } + + /** + * Finds the index of the first authorisation rule matching the given policy and carrier id. If + * the returned index is not negative, the carrier is allowed to apply this policy to its + * profile. + * + * @param policy The policy rule. + * @param carrierId The carrier id. + * @return The index of authorization rule. If no rule is found, -1 will be returned. + */ + public int findIndex(@EuiccProfileInfo.PolicyRule int policy, CarrierIdentifier carrierId) { + for (int i = 0; i < mPolicyRules.length; i++) { + if ((mPolicyRules[i] & policy) == 0) { + continue; + } + CarrierIdentifier[] carrierIds = mCarrierIds[i]; + if (carrierIds == null || carrierIds.length == 0) { + continue; + } + for (int j = 0; j < carrierIds.length; j++) { + CarrierIdentifier ruleCarrierId = carrierIds[j]; + if (!match(ruleCarrierId.getMcc(), carrierId.getMcc()) + || !match(ruleCarrierId.getMnc(), carrierId.getMnc())) { + continue; + } + String gid = ruleCarrierId.getGid1(); + if (!TextUtils.isEmpty(gid) && !gid.equals(carrierId.getGid1())) { + continue; + } + gid = ruleCarrierId.getGid2(); + if (!TextUtils.isEmpty(gid) && !gid.equals(carrierId.getGid2())) { + continue; + } + return i; + } + } + return -1; + } + + /** + * Tests if the entry in the table has the given policy rule flag. + * + * @param index The index of the entry. + * @param flag The policy rule flag to be tested. + * @throws ArrayIndexOutOfBoundsException If the {@code index} is negative or larger than the + * size of this table. + */ + public boolean hasPolicyRuleFlag(int index, @PolicyRuleFlag int flag) { + if (index < 0 || index >= mPolicyRules.length) { + throw new ArrayIndexOutOfBoundsException(index); + } + return (mPolicyRuleFlags[index] & flag) != 0; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeIntArray(mPolicyRules); + for (CarrierIdentifier[] ids : mCarrierIds) { + dest.writeTypedArray(ids, flags); + } + dest.writeIntArray(mPolicyRuleFlags); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null || getClass() != obj.getClass()) { + return false; + } + + EuiccRat that = (EuiccRat) obj; + if (mCarrierIds.length != that.mCarrierIds.length) { + return false; + } + for (int i = 0; i < mCarrierIds.length; i++) { + CarrierIdentifier[] carrierIds = mCarrierIds[i]; + CarrierIdentifier[] thatCarrierIds = that.mCarrierIds[i]; + if (carrierIds != null && thatCarrierIds != null) { + if (carrierIds.length != thatCarrierIds.length) { + return false; + } + for (int j = 0; j < carrierIds.length; j++) { + if (!carrierIds[j].equals(thatCarrierIds[j])) { + return false; + } + } + continue; + } else if (carrierIds == null && thatCarrierIds == null) { + continue; + } + return false; + } + + return Arrays.equals(mPolicyRules, that.mPolicyRules) + && Arrays.equals(mPolicyRuleFlags, that.mPolicyRuleFlags); + } + + private EuiccRat(Parcel source) { + mPolicyRules = source.createIntArray(); + int len = mPolicyRules.length; + mCarrierIds = new CarrierIdentifier[len][]; + for (int i = 0; i < len; i++) { + mCarrierIds[i] = source.createTypedArray(CarrierIdentifier.CREATOR); + } + mPolicyRuleFlags = source.createIntArray(); + } + + public static final Creator<EuiccRat> CREATOR = + new Creator<EuiccRat>() { + @Override + public EuiccRat createFromParcel(Parcel source) { + return new EuiccRat(source); + } + + @Override + public EuiccRat[] newArray(int size) { + return new EuiccRat[size]; + } + }; +} |