diff options
| author | 2021-09-13 10:19:12 +0000 | |
|---|---|---|
| committer | 2021-11-12 10:11:22 +0000 | |
| commit | 177c4a4d025b729bc6d08eb21e7c26312fa180f9 (patch) | |
| tree | 4b3fe5e3a22c66b6ee5f29a561122c348dc1f0d2 | |
| parent | cff20397e7e257b8bc493b795b3e717b9563a4fd (diff) | |
[MEP] eSIM API refactor to support MEP
Newly added UiccPortInfo and UiccSlotMapping classes and introduced
UiccPortInfo as a port list in UiccCardInfo and UiccSlotInfo,
Deprecated some constructors and updated callers.
Test: build
Bug: 159354974
Change-Id: I71e4e1ef649b7301d126dde23d53783761de940f
17 files changed, 894 insertions, 87 deletions
diff --git a/core/api/current.txt b/core/api/current.txt index 912842c6e9b4..88e108987735 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -42602,6 +42602,7 @@ package android.telephony { method @Deprecated public int getMnc(); method @Nullable public String getMncString(); method public String getNumber(); + method public int getPortIndex(); method public int getSimSlotIndex(); method public int getSubscriptionId(); method public int getSubscriptionType(); @@ -43145,14 +43146,28 @@ package android.telephony { method public int describeContents(); method public int getCardId(); method @Nullable public String getEid(); - method @Nullable public String getIccId(); - method public int getSlotIndex(); + method @Deprecated @Nullable public String getIccId(); + method public int getPhysicalSlotIndex(); + method @NonNull public java.util.Collection<android.telephony.UiccPortInfo> getPorts(); + method @Deprecated public int getSlotIndex(); method public boolean isEuicc(); + method public boolean isMultipleEnabledProfilesSupported(); method public boolean isRemovable(); method public void writeToParcel(android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.telephony.UiccCardInfo> CREATOR; } + public final class UiccPortInfo implements android.os.Parcelable { + method public int describeContents(); + method @Nullable public String getIccId(); + method @IntRange(from=0) public int getLogicalSlotIndex(); + method @IntRange(from=0) public int getPortIndex(); + method public boolean isActive(); + method public void writeToParcel(@Nullable android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.telephony.UiccPortInfo> CREATOR; + field public static final String ICCID_REDACTED = "FFFFFFFFFFFFFFFFFFFF"; + } + public abstract class VisualVoicemailService extends android.app.Service { ctor public VisualVoicemailService(); method public android.os.IBinder onBind(android.content.Intent); @@ -43453,6 +43468,7 @@ package android.telephony.euicc { method @Nullable public String getEid(); method @Nullable public android.telephony.euicc.EuiccInfo getEuiccInfo(); method public boolean isEnabled(); + method public boolean isSimPortAvailable(int); method public void startResolutionActivity(android.app.Activity, int, android.content.Intent, android.app.PendingIntent) throws android.content.IntentSender.SendIntentException; method @RequiresPermission("android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS") public void switchToSubscription(int, android.app.PendingIntent); method @RequiresPermission("android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS") public void updateSubscriptionNickname(int, @Nullable String, @NonNull android.app.PendingIntent); diff --git a/core/api/system-current.txt b/core/api/system-current.txt index f92c36d488ad..c2a7aa5c671f 100755 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -12256,6 +12256,7 @@ package android.telephony { method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setSimPowerState(int, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>); method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setSimPowerStateForSlot(int, int); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setSimPowerStateForSlot(int, int, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>); + method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setSimSlotMapping(@NonNull java.util.Collection<android.telephony.UiccSlotMapping>); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setSystemSelectionChannels(@NonNull java.util.List<android.telephony.RadioAccessSpecifier>, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setSystemSelectionChannels(@NonNull java.util.List<android.telephony.RadioAccessSpecifier>); method @Deprecated public void setVisualVoicemailEnabled(android.telecom.PhoneAccountHandle, boolean); @@ -12267,7 +12268,7 @@ package android.telephony { method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int[] supplyPinReportResult(String); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean supplyPuk(String, String); method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int[] supplyPukReportResult(String, String); - method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean switchSlots(int[]); + method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean switchSlots(int[]); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void toggleRadioOnOff(); method @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public void updateOtaEmergencyNumberDbFilePath(@NonNull android.os.ParcelFileDescriptor); method public void updateServiceLocation(); @@ -12437,10 +12438,11 @@ package android.telephony { method public int describeContents(); method public String getCardId(); method public int getCardStateInfo(); - method public boolean getIsActive(); + method @Deprecated public boolean getIsActive(); method public boolean getIsEuicc(); method public boolean getIsExtendedApduSupported(); - method public int getLogicalSlotIdx(); + method @Deprecated public int getLogicalSlotIdx(); + method @NonNull public java.util.Collection<android.telephony.UiccPortInfo> getPorts(); method public boolean isRemovable(); method public void writeToParcel(android.os.Parcel, int); field public static final int CARD_STATE_INFO_ABSENT = 1; // 0x1 @@ -12450,6 +12452,15 @@ package android.telephony { field @NonNull public static final android.os.Parcelable.Creator<android.telephony.UiccSlotInfo> CREATOR; } + public final class UiccSlotMapping implements android.os.Parcelable { + method public int describeContents(); + method @IntRange(from=0) public int getLogicalSlotIndex(); + method @IntRange(from=0) public int getPhysicalSlotIndex(); + method @IntRange(from=0) public int getPortIndex(); + method public void writeToParcel(@Nullable android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.telephony.UiccSlotMapping> CREATOR; + } + public abstract class VisualVoicemailService extends android.app.Service { method public static final void sendVisualVoicemailSms(android.content.Context, android.telecom.PhoneAccountHandle, String, short, String, android.app.PendingIntent); method public static final void setSmsFilterSettings(android.content.Context, android.telecom.PhoneAccountHandle, android.telephony.VisualVoicemailSmsFilterSettings); @@ -12741,7 +12752,8 @@ package android.telephony.euicc { method public void authenticateServer(String, String, byte[], byte[], byte[], byte[], java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<byte[]>); method public void cancelSession(String, byte[], @android.telephony.euicc.EuiccCardManager.CancelReason int, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<byte[]>); method public void deleteProfile(String, String, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<java.lang.Void>); - method public void disableProfile(String, String, boolean, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<java.lang.Void>); + method @Deprecated public void disableProfile(String, String, boolean, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<java.lang.Void>); + method public void disableProfile(@Nullable String, @Nullable String, int, boolean, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.euicc.EuiccCardManager.ResultCallback<java.lang.Void>); method public void listNotifications(String, @android.telephony.euicc.EuiccNotification.Event int, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<android.telephony.euicc.EuiccNotification[]>); method public void loadBoundProfilePackage(String, byte[], java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<byte[]>); method public void prepareDownload(String, @Nullable byte[], byte[], byte[], byte[], java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<byte[]>); @@ -12759,7 +12771,8 @@ package android.telephony.euicc { method public void retrieveNotificationList(String, @android.telephony.euicc.EuiccNotification.Event int, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<android.telephony.euicc.EuiccNotification[]>); method public void setDefaultSmdpAddress(String, String, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<java.lang.Void>); method public void setNickname(String, String, String, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<java.lang.Void>); - method public void switchToProfile(String, String, boolean, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<android.service.euicc.EuiccProfileInfo>); + method @Deprecated public void switchToProfile(String, String, boolean, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<android.service.euicc.EuiccProfileInfo>); + method public void switchToProfile(@Nullable String, @Nullable String, int, boolean, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.euicc.EuiccCardManager.ResultCallback<android.service.euicc.EuiccProfileInfo>); field public static final int CANCEL_REASON_END_USER_REJECTED = 0; // 0x0 field public static final int CANCEL_REASON_POSTPONED = 1; // 0x1 field public static final int CANCEL_REASON_PPR_NOT_ALLOWED = 3; // 0x3 diff --git a/telephony/java/android/telephony/SubscriptionInfo.java b/telephony/java/android/telephony/SubscriptionInfo.java index 63a7acfb5142..d6d6775cb40f 100644 --- a/telephony/java/android/telephony/SubscriptionInfo.java +++ b/telephony/java/android/telephony/SubscriptionInfo.java @@ -222,6 +222,11 @@ public class SubscriptionInfo implements Parcelable { private boolean mAreUiccApplicationsEnabled = true; /** + * The port index of the Uicc card. + */ + private final int mPortIndex; + + /** * Public copy constructor. * @hide */ @@ -274,6 +279,22 @@ public class SubscriptionInfo implements Parcelable { int carrierId, int profileClass, int subType, @Nullable String groupOwner, @Nullable UiccAccessRule[] carrierConfigAccessRules, boolean areUiccApplicationsEnabled) { + this(id, iccId, simSlotIndex, displayName, carrierName, nameSource, iconTint, number, + roaming, icon, mcc, mnc, countryIso, isEmbedded, nativeAccessRules, cardString, + cardId, isOpportunistic, groupUUID, isGroupDisabled, carrierId, profileClass, + subType, groupOwner, carrierConfigAccessRules, areUiccApplicationsEnabled, 0); + } + /** + * @hide + */ + public SubscriptionInfo(int id, String iccId, int simSlotIndex, CharSequence displayName, + CharSequence carrierName, int nameSource, int iconTint, String number, int roaming, + Bitmap icon, String mcc, String mnc, String countryIso, boolean isEmbedded, + @Nullable UiccAccessRule[] nativeAccessRules, String cardString, int cardId, + boolean isOpportunistic, @Nullable String groupUUID, boolean isGroupDisabled, + int carrierId, int profileClass, int subType, @Nullable String groupOwner, + @Nullable UiccAccessRule[] carrierConfigAccessRules, + boolean areUiccApplicationsEnabled, int portIndex) { this.mId = id; this.mIccId = iccId; this.mSimSlotIndex = simSlotIndex; @@ -300,8 +321,8 @@ public class SubscriptionInfo implements Parcelable { this.mGroupOwner = groupOwner; this.mCarrierConfigAccessRules = carrierConfigAccessRules; this.mAreUiccApplicationsEnabled = areUiccApplicationsEnabled; + this.mPortIndex = portIndex; } - /** * @return the subscription ID. */ @@ -737,6 +758,14 @@ public class SubscriptionInfo implements Parcelable { public int getCardId() { return this.mCardId; } + /** + * Returns the port index of the SIM card which contains the subscription. + * + * @return the portIndex + */ + public int getPortIndex() { + return this.mPortIndex; + } /** * Set whether the subscription's group is disabled. @@ -783,6 +812,7 @@ public class SubscriptionInfo implements Parcelable { UiccAccessRule[] nativeAccessRules = source.createTypedArray(UiccAccessRule.CREATOR); String cardString = source.readString(); int cardId = source.readInt(); + int portId = source.readInt(); boolean isOpportunistic = source.readBoolean(); String groupUUID = source.readString(); boolean isGroupDisabled = source.readBoolean(); @@ -800,7 +830,7 @@ public class SubscriptionInfo implements Parcelable { carrierName, nameSource, iconTint, number, dataRoaming, /* icon= */ null, mcc, mnc, countryIso, isEmbedded, nativeAccessRules, cardString, cardId, isOpportunistic, groupUUID, isGroupDisabled, carrierid, profileClass, subType, - groupOwner, carrierConfigAccessRules, areUiccApplicationsEnabled); + groupOwner, carrierConfigAccessRules, areUiccApplicationsEnabled, portId); info.setAssociatedPlmns(ehplmns, hplmns); return info; } @@ -830,6 +860,7 @@ public class SubscriptionInfo implements Parcelable { dest.writeTypedArray(mNativeAccessRules, flags); dest.writeString(mCardString); dest.writeInt(mCardId); + dest.writeInt(mPortIndex); dest.writeBoolean(mIsOpportunistic); dest.writeString(mGroupUUID == null ? null : mGroupUUID.toString()); dest.writeBoolean(mIsGroupDisabled); @@ -876,6 +907,7 @@ public class SubscriptionInfo implements Parcelable { + " mnc=" + mMnc + " countryIso=" + mCountryIso + " isEmbedded=" + mIsEmbedded + " nativeAccessRules=" + Arrays.toString(mNativeAccessRules) + " cardString=" + cardStringToPrint + " cardId=" + mCardId + + " portIndex=" + mPortIndex + " isOpportunistic=" + mIsOpportunistic + " groupUUID=" + mGroupUUID + " isGroupDisabled=" + mIsGroupDisabled + " profileClass=" + mProfileClass @@ -892,7 +924,7 @@ public class SubscriptionInfo implements Parcelable { return Objects.hash(mId, mSimSlotIndex, mNameSource, mIconTint, mDataRoaming, mIsEmbedded, mIsOpportunistic, mGroupUUID, mIccId, mNumber, mMcc, mMnc, mCountryIso, mCardString, mCardId, mDisplayName, mCarrierName, mNativeAccessRules, mIsGroupDisabled, - mCarrierId, mProfileClass, mGroupOwner, mAreUiccApplicationsEnabled); + mCarrierId, mProfileClass, mGroupOwner, mAreUiccApplicationsEnabled, mPortIndex); } @Override @@ -925,6 +957,7 @@ public class SubscriptionInfo implements Parcelable { && Objects.equals(mCountryIso, toCompare.mCountryIso) && Objects.equals(mCardString, toCompare.mCardString) && Objects.equals(mCardId, toCompare.mCardId) + && mPortIndex == toCompare.mPortIndex && Objects.equals(mGroupOwner, toCompare.mGroupOwner) && TextUtils.equals(mDisplayName, toCompare.mDisplayName) && TextUtils.equals(mCarrierName, toCompare.mCarrierName) diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index 9c52220c4bb4..5f2c7de91fb8 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -131,6 +131,8 @@ import java.lang.annotation.RetentionPolicy; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; @@ -144,6 +146,7 @@ import java.util.function.Consumer; import java.util.stream.Collectors; import java.util.stream.IntStream; + /** * Provides access to information about the telephony services on * the device. Applications can use the methods in this class to @@ -3931,8 +3934,8 @@ public class TelephonyManager { * <p> * If the caller has carrier priviliges on any active subscription, then they have permission to * get simple information like the card ID ({@link UiccCardInfo#getCardId()}), whether the card - * is an eUICC ({@link UiccCardInfo#isEuicc()}), and the slot index where the card is inserted - * ({@link UiccCardInfo#getSlotIndex()}). + * is an eUICC ({@link UiccCardInfo#isEuicc()}), and the physical slot index where the card is + * inserted ({@link UiccCardInfo#getPhysicalSlotIndex()}. * <p> * To get private information such as the EID ({@link UiccCardInfo#getEid()}) or ICCID * ({@link UiccCardInfo#getIccId()}), the caller must have carrier priviliges on that specific @@ -3976,7 +3979,7 @@ public class TelephonyManager { if (telephony == null) { return null; } - return telephony.getUiccSlotsInfo(); + return telephony.getUiccSlotsInfo(mContext.getOpPackageName()); } catch (RemoteException e) { return null; } @@ -4009,8 +4012,13 @@ public class TelephonyManager { * size should be same as {@link #getUiccSlotsInfo()}. * @return boolean Return true if the switch succeeds, false if the switch fails. * @hide + * @deprecated {@link #setSimSlotMapping(Collection, Executor, Consumer)} */ + // TODO: once integrating the HAL changes we can convert int[] to List<UiccSlotMapping> and + // converge API's in ITelephony.aidl and PhoneInterfaceManager + @SystemApi + @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean switchSlots(int[] physicalSlots) { try { @@ -4025,6 +4033,109 @@ public class TelephonyManager { } /** + * @param slotMapping Logical to physical slot and port mapping. + * @return {@code true} if slotMapping is valid. + * @return {@code false} if slotMapping is invalid. + * + * slotMapping is invalid if there are different entries (physical slot + port) mapping to the + * same logical slot or if there are same {physical slot + port} mapping to the different + * logical slot + * @hide + */ + private static boolean isSlotMappingValid(@NonNull Collection<UiccSlotMapping> slotMapping) { + // Grouping the collection by logicalSlotIndex, finding different entries mapping to the + // same logical slot + Map<Integer, List<UiccSlotMapping>> slotMappingInfo = slotMapping.stream().collect( + Collectors.groupingBy(UiccSlotMapping::getLogicalSlotIndex)); + for (Map.Entry<Integer, List<UiccSlotMapping>> entry : slotMappingInfo.entrySet()) { + List<UiccSlotMapping> logicalSlotMap = entry.getValue(); + if (logicalSlotMap.size() > 1) { + // duplicate logicalSlotIndex found + return false; + } + } + + // Grouping the collection by physical slot and port, finding same entries mapping to the + // different logical slot + Map<List<Integer>, List<UiccSlotMapping>> slotMapInfos = slotMapping.stream().collect( + Collectors.groupingBy( + slot -> Arrays.asList(slot.getPhysicalSlotIndex(), slot.getPortIndex()))); + for (Map.Entry<List<Integer>, List<UiccSlotMapping>> entry : slotMapInfos.entrySet()) { + List<UiccSlotMapping> portAndPhysicalSlotList = entry.getValue(); + if (portAndPhysicalSlotList.size() > 1) { + // duplicate pair of portIndex and physicalSlotIndex found + return false; + } + } + return true; + } + /** + * Maps the logical slots to physical slots and ports. Mapping is specified from + * {@link UiccSlotMapping} which consist of both physical slot index and port index. + * Logical slot is the slot that is seen by modem. Physical slot is the actual physical slot. + * Port index is the index (enumerated value) for the associated port available on the SIM. + * Each physical slot can have multiple ports if multi-enabled profile(MEP) is supported. + * + * Example: no. of logical slots 1 and physical slots 2 do not support MEP, each physical slot + * has one port: + * The only logical slot (index 0) can be mapped to first physical slot (value 0), port(index + * 0) or + * second physical slot(value 1), port (index 0), while the other physical slot remains unmapped + * and inactive. + * slotMapping[0] = UiccSlotMapping{0 //logical slot, 0 //physical slot//, 0 //port//} + * slotMapping[0] = UiccSlotMapping{1 // logical slot, 1 //physical slot//, 0 //port//} + * + * Example no. of logical slots 2 and physical slots 2 supports MEP with 2 ports available: + * Each logical slot must be mapped to a port (physical slot and port combination). + * First logical slot (index 0) can be mapped to physical slot 1 and the second logical slot + * can be mapped to either port from physical slot 2. + * + * slotMapping[0] = UiccSlotMapping{0, 0, 0} and slotMapping[1] = UiccSlotMapping{1, 0, 0} or + * slotMapping[0] = UiccSlotMapping{0, 0, 0} and slotMapping[1] = UiccSlotMapping{1, 1, 1} + * + * or the other way around, the second logical slot(index 1) can be mapped to physical slot 1 + * and the first logical slot can be mapped to either port from physical slot 2. + * + * slotMapping[1] = UiccSlotMapping{0, 0, 0} and slotMapping[0] = UiccSlotMapping{1, 0, 0} or + * slotMapping[1] = UiccSlotMapping{0, 0, 0} and slotMapping[0] = UiccSlotMapping{1, 1, 1} + * + * another possible mapping is each logical slot maps to each port of physical slot 2 and there + * is no active logical modem mapped to physical slot 1. + * + * slotMapping[0] = UiccSlotMapping{1, 0, 0} and slotMapping[1] = UiccSlotMapping{1, 1, 1} or + * slotMapping[0] = UiccSlotMapping{1, 1, 1} and slotMapping[1] = UiccSlotMapping{1, 0, 0} + * + * @param slotMapping Logical to physical slot and port mapping. + * @throws IllegalStateException if telephony service is null or slot mapping was sent when the + * radio in middle of a silent restart or other invalid states to handle the command + * @throws IllegalArgumentException if the caller passes in an invalid collection of + * UiccSlotMapping like duplicate data, etc + * + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + public void setSimSlotMapping(@NonNull Collection<UiccSlotMapping> slotMapping) { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + if (isSlotMappingValid(slotMapping)) { + boolean result = telephony.setSimSlotMapping(new ArrayList(slotMapping)); + if (!result) { + throw new IllegalStateException("setSimSlotMapping has failed"); + } + } else { + throw new IllegalArgumentException("Duplicate UiccSlotMapping data found"); + } + } else { + throw new IllegalStateException("telephony service is null."); + } + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + } + + /** * Get the mapping from logical slots to physical slots. The key of the map is the logical slot * id and the value is the physical slots id mapped to this logical slot id. * @@ -4041,7 +4152,7 @@ public class TelephonyManager { try { ITelephony telephony = getITelephony(); if (telephony != null) { - int[] slotMappingArray = telephony.getSlotsMapping(); + int[] slotMappingArray = telephony.getSlotsMapping(mContext.getOpPackageName()); for (int i = 0; i < slotMappingArray.length; i++) { slotMapping.put(i, slotMappingArray[i]); } diff --git a/telephony/java/android/telephony/UiccCardInfo.java b/telephony/java/android/telephony/UiccCardInfo.java index b438920362ff..7dfe450fdb1a 100644 --- a/telephony/java/android/telephony/UiccCardInfo.java +++ b/telephony/java/android/telephony/UiccCardInfo.java @@ -20,21 +20,27 @@ import android.annotation.Nullable; import android.os.Parcel; import android.os.Parcelable; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; import java.util.Objects; /** * The UiccCardInfo represents information about a currently inserted UICC or embedded eUICC. */ public final class UiccCardInfo implements Parcelable { - private final boolean mIsEuicc; private final int mCardId; private final String mEid; private final String mIccId; - private final int mSlotIndex; + private final int mPhysicalSlotIndex; private final boolean mIsRemovable; + private final boolean mIsMultipleEnabledProfilesSupported; + private final List<UiccPortInfo> mPortList; + private boolean mIccIdAccessRestricted = false; - public static final @android.annotation.NonNull Creator<UiccCardInfo> CREATOR = new Creator<UiccCardInfo>() { + public static final @NonNull Creator<UiccCardInfo> CREATOR = new Creator<UiccCardInfo>() { @Override public UiccCardInfo createFromParcel(Parcel in) { return new UiccCardInfo(in); @@ -47,22 +53,29 @@ public final class UiccCardInfo implements Parcelable { }; private UiccCardInfo(Parcel in) { - mIsEuicc = in.readByte() != 0; + mIsEuicc = in.readBoolean(); mCardId = in.readInt(); - mEid = in.readString(); - mIccId = in.readString(); - mSlotIndex = in.readInt(); - mIsRemovable = in.readByte() != 0; + mEid = in.readString8(); + mIccId = in.readString8(); + mPhysicalSlotIndex = in.readInt(); + mIsRemovable = in.readBoolean(); + mIsMultipleEnabledProfilesSupported = in.readBoolean(); + mPortList = new ArrayList<UiccPortInfo>(); + in.readTypedList(mPortList, UiccPortInfo.CREATOR); + mIccIdAccessRestricted = in.readBoolean(); } @Override public void writeToParcel(Parcel dest, int flags) { - dest.writeByte((byte) (mIsEuicc ? 1 : 0)); + dest.writeBoolean(mIsEuicc); dest.writeInt(mCardId); - dest.writeString(mEid); - dest.writeString(mIccId); - dest.writeInt(mSlotIndex); - dest.writeByte((byte) (mIsRemovable ? 1 : 0)); + dest.writeString8(mEid); + dest.writeString8(mIccId); + dest.writeInt(mPhysicalSlotIndex); + dest.writeBoolean(mIsRemovable); + dest.writeBoolean(mIsMultipleEnabledProfilesSupported); + dest.writeTypedList(mPortList, flags); + dest.writeBoolean(mIccIdAccessRestricted); } @Override @@ -71,20 +84,34 @@ public final class UiccCardInfo implements Parcelable { } /** + * Construct a UiccCardInfo. + * + * @param isEuicc is a flag to check is eUICC or not + * @param cardId is unique ID used to identify a UiccCard. + * @param eid is unique eUICC Identifier + * @param physicalSlotIndex is unique index referring to a physical SIM slot. + * @param isRemovable is a flag to check is removable or embedded + * @param isMultipleEnabledProfilesSupported is a flag to check is MEP enabled or not + * @param portList has the information regarding port, ICCID and its active status + * * @hide */ - public UiccCardInfo(boolean isEuicc, int cardId, String eid, String iccId, int slotIndex, - boolean isRemovable) { + public UiccCardInfo(boolean isEuicc, int cardId, String eid, int physicalSlotIndex, + boolean isRemovable, boolean isMultipleEnabledProfilesSupported, + @NonNull List<UiccPortInfo> portList) { this.mIsEuicc = isEuicc; this.mCardId = cardId; this.mEid = eid; - this.mIccId = iccId; - this.mSlotIndex = slotIndex; + this.mIccId = null; + this.mPhysicalSlotIndex = physicalSlotIndex; this.mIsRemovable = isRemovable; + this.mIsMultipleEnabledProfilesSupported = isMultipleEnabledProfilesSupported; + this.mPortList = portList; } /** * Return whether the UICC is an eUICC. + * * @return true if the UICC is an eUICC. */ public boolean isEuicc() { @@ -119,40 +146,83 @@ public final class UiccCardInfo implements Parcelable { * <p> * Note that this field may be omitted if the caller does not have the correct permissions * (see {@link TelephonyManager#getUiccCardsInfo()}). + * + * @deprecated with support for MEP(multiple enabled profile), a SIM card can have more than one + * ICCID active at the same time.Instead use {@link UiccPortInfo#getIccId()} to retrieve ICCID. + * To find {@link UiccPortInfo} use {@link UiccCardInfo#getPorts()} + * + * @throws UnsupportedOperationException if the calling app's target SDK is T and beyond. */ @Nullable + @Deprecated public String getIccId() { - return mIccId; + if (mIccIdAccessRestricted) { + throw new UnsupportedOperationException("getIccId from UiccPortInfo"); + } + //always return ICCID from first port. + return getPorts().stream().findFirst().get().getIccId(); } /** * Gets the slot index for the slot that the UICC is currently inserted in. + * + * @deprecated use {@link #getPhysicalSlotIndex()} */ + @Deprecated public int getSlotIndex() { - return mSlotIndex; + return mPhysicalSlotIndex; } /** - * Returns a copy of the UiccCardinfo with the EID and ICCID set to null. These values are - * generally private and require carrier privileges to view. - * - * @hide + * Gets the physical slot index for the slot that the UICC is currently inserted in. */ - @NonNull - public UiccCardInfo getUnprivileged() { - return new UiccCardInfo(mIsEuicc, mCardId, null, null, mSlotIndex, mIsRemovable); + public int getPhysicalSlotIndex() { + return mPhysicalSlotIndex; } /** * Return whether the UICC or eUICC is removable. * <p> * UICCs are generally removable, but eUICCs may be removable or built in to the device. + * * @return true if the UICC or eUICC is removable */ public boolean isRemovable() { return mIsRemovable; } + /* + * Whether the UICC card supports multiple enable profile(MEP) + * UICCs are generally MEP disabled, there can be only one active profile on the physical + * sim card. + * + * @return {@code true} if the eUICC is supporting multiple enabled profile(MEP). + */ + public boolean isMultipleEnabledProfilesSupported() { + return mIsMultipleEnabledProfilesSupported; + } + + /** + * Get information regarding port, ICCID and its active status. + * + * @return Collection of {@link UiccPortInfo} + */ + public @NonNull Collection<UiccPortInfo> getPorts() { + return Collections.unmodifiableList(mPortList); + } + + /** + * if the flag is set to {@code true} the calling app is not allowed to access deprecated + * {@link #getIccId()} + * @param iccIdAccessRestricted is the flag to check if app is allowed to access ICCID + * + * @hide + */ + public void setIccIdAccessRestricted(boolean iccIdAccessRestricted) { + this.mIccIdAccessRestricted = iccIdAccessRestricted; + } + + @Override public boolean equals(Object obj) { if (this == obj) { @@ -167,13 +237,16 @@ public final class UiccCardInfo implements Parcelable { && (mCardId == that.mCardId) && (Objects.equals(mEid, that.mEid)) && (Objects.equals(mIccId, that.mIccId)) - && (mSlotIndex == that.mSlotIndex) - && (mIsRemovable == that.mIsRemovable)); + && (mPhysicalSlotIndex == that.mPhysicalSlotIndex) + && (mIsRemovable == that.mIsRemovable) + && (mIsMultipleEnabledProfilesSupported == that.mIsMultipleEnabledProfilesSupported) + && (Objects.equals(mPortList, that.mPortList))); } @Override public int hashCode() { - return Objects.hash(mIsEuicc, mCardId, mEid, mIccId, mSlotIndex, mIsRemovable); + return Objects.hash(mIsEuicc, mCardId, mEid, mIccId, mPhysicalSlotIndex, mIsRemovable, + mIsMultipleEnabledProfilesSupported, mPortList); } @Override @@ -185,11 +258,17 @@ public final class UiccCardInfo implements Parcelable { + ", mEid=" + mEid + ", mIccId=" - + mIccId - + ", mSlotIndex=" - + mSlotIndex + + SubscriptionInfo.givePrintableIccid(mIccId) + + ", mPhysicalSlotIndex=" + + mPhysicalSlotIndex + ", mIsRemovable=" + mIsRemovable + + ", mIsMultipleEnabledProfilesSupported=" + + mIsMultipleEnabledProfilesSupported + + ", mPortList=" + + mPortList + + ", mIccIdAccessRestricted=" + + mIccIdAccessRestricted + ")"; } -} +}
\ No newline at end of file diff --git a/telephony/java/android/telephony/UiccPortInfo.aidl b/telephony/java/android/telephony/UiccPortInfo.aidl new file mode 100644 index 000000000000..7fff4ba5c651 --- /dev/null +++ b/telephony/java/android/telephony/UiccPortInfo.aidl @@ -0,0 +1,19 @@ +/* + * 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; + +parcelable UiccPortInfo; diff --git a/telephony/java/android/telephony/UiccPortInfo.java b/telephony/java/android/telephony/UiccPortInfo.java new file mode 100644 index 000000000000..d1838c0b91f4 --- /dev/null +++ b/telephony/java/android/telephony/UiccPortInfo.java @@ -0,0 +1,173 @@ +/* + * Copyright (C) 2021 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; + +import android.annotation.IntRange; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.Objects; + +/** + * UiccPortInfo class represents information about a single port contained on {@link UiccCardInfo}. + * Per GSMA SGP.22 V3.0, a port is a logical entity to which an active UICC profile can be bound on + * a UICC card. If UICC supports 2 ports, then the port index is numbered 0,1. + * Each port index is unique within an UICC, but not necessarily unique across UICC’s. + * For UICC's does not support MEP(Multi-enabled profile), just return the default port index 0. + */ +public final class UiccPortInfo implements Parcelable{ + private final String mIccId; + private final int mPortIndex; + private final int mLogicalSlotIndex; + private final boolean mIsActive; + + /** + * A redacted String if caller does not have permission to read ICCID. + */ + public static final String ICCID_REDACTED = "FFFFFFFFFFFFFFFFFFFF"; + + public static final @NonNull Creator<UiccPortInfo> CREATOR = + new Creator<UiccPortInfo>() { + @Override + public UiccPortInfo createFromParcel(Parcel in) { + return new UiccPortInfo(in); + } + @Override + public UiccPortInfo[] newArray(int size) { + return new UiccPortInfo[size]; + } + }; + + private UiccPortInfo(Parcel in) { + mIccId = in.readString8(); + mPortIndex = in.readInt(); + mLogicalSlotIndex = in.readInt(); + mIsActive = in.readBoolean(); + } + + @Override + public void writeToParcel(@Nullable Parcel dest, int flags) { + dest.writeString8(mIccId); + dest.writeInt(mPortIndex); + dest.writeInt(mLogicalSlotIndex); + dest.writeBoolean(mIsActive); + } + + @Override + public int describeContents() { + return 0; + } + + /** + * Construct a UiccPortInfo. + * + * @param iccId The ICCID of the profile. + * @param portIndex The port index is an enumeration of the ports available on the UICC. + * @param logicalSlotIndex is unique index referring to a logical SIM slot. + * @param isActive is flag to check if port was tied to a modem stack. + * + * @hide + */ + public UiccPortInfo(String iccId, int portIndex, int logicalSlotIndex, boolean isActive) { + this.mIccId = iccId; + this.mPortIndex = portIndex; + this.mLogicalSlotIndex = logicalSlotIndex; + this.mIsActive = isActive; + } + + /** + * Get the ICCID of the profile associated with this port. + * If this port is not {@link #isActive()}, returns {@code null}. + * If the caller does not have access to the ICCID for this port, it will be redacted and + * {@link #ICCID_REDACTED} will be returned. + */ + public @Nullable String getIccId() { + return mIccId; + } + + /** + * The port index is an enumeration of the ports available on the UICC. + * Example: if eUICC1 supports 2 ports, then the port index is numbered 0,1. + * Each port index is unique within an UICC, but not necessarily unique across UICC’s. + * For UICC's does not support MEP(Multi-enabled profile), just return the default port index 0. + */ + @IntRange(from = 0) + public int getPortIndex() { + return mPortIndex; + } + + /** + * @return {@code true} if port was tied to a modem stack. + */ + public boolean isActive() { + return mIsActive; + } + + /** + * Gets logical slot index for the slot that the UICC is currently attached. + * Logical slot index or ID: unique index referring to a logical SIM slot. + * Logical slot IDs start at 0 and go up depending on the number of supported active slots on + * a device. + * For example, a dual-SIM device typically has slot 0 and slot 1. + * If a device has multiple physical slots but only supports one active slot, + * it will have only the logical slot ID 0. + * + * @return the logical slot index for UICC port, if there is no logical slot index it returns + * {@link SubscriptionManager#INVALID_SIM_SLOT_INDEX} + */ + @IntRange(from = 0) + public int getLogicalSlotIndex() { + return mLogicalSlotIndex; + } + + @Override + public boolean equals(@Nullable Object obj) { + if (this == obj) { + return true; + } + if (obj == null || getClass() != obj.getClass()) { + return false; + } + + UiccPortInfo that = (UiccPortInfo) obj; + return (Objects.equals(mIccId, that.mIccId)) + && (mPortIndex == that.mPortIndex) + && (mLogicalSlotIndex == that.mLogicalSlotIndex) + && (mIsActive == that.mIsActive); + } + + @Override + public int hashCode() { + return Objects.hash(mIccId, mPortIndex, mLogicalSlotIndex, mIsActive); + } + + @NonNull + @Override + public String toString() { + return "UiccPortInfo (isActive=" + + mIsActive + + ", iccId=" + + SubscriptionInfo.givePrintableIccid(mIccId) + + ", portIndex=" + + mPortIndex + + ", mLogicalSlotIndex=" + + mLogicalSlotIndex + + ")"; + } +} diff --git a/telephony/java/android/telephony/UiccSlotInfo.java b/telephony/java/android/telephony/UiccSlotInfo.java index a0e949aa7fc5..2b1c8c863eb6 100644 --- a/telephony/java/android/telephony/UiccSlotInfo.java +++ b/telephony/java/android/telephony/UiccSlotInfo.java @@ -24,6 +24,10 @@ import android.os.Parcelable; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; import java.util.Objects; /** @@ -64,8 +68,10 @@ public class UiccSlotInfo implements Parcelable { private final int mLogicalSlotIdx; private final boolean mIsExtendedApduSupported; private final boolean mIsRemovable; + private final List<UiccPortInfo> mPortList; + private boolean mLogicalSlotAccessRestricted = false; - public static final @android.annotation.NonNull Creator<UiccSlotInfo> CREATOR = new Creator<UiccSlotInfo>() { + public static final @NonNull Creator<UiccSlotInfo> CREATOR = new Creator<UiccSlotInfo>() { @Override public UiccSlotInfo createFromParcel(Parcel in) { return new UiccSlotInfo(in); @@ -78,24 +84,29 @@ public class UiccSlotInfo implements Parcelable { }; private UiccSlotInfo(Parcel in) { - mIsActive = in.readByte() != 0; - mIsEuicc = in.readByte() != 0; - mCardId = in.readString(); + mIsActive = in.readBoolean(); + mIsEuicc = in.readBoolean(); + mCardId = in.readString8(); mCardStateInfo = in.readInt(); mLogicalSlotIdx = in.readInt(); - mIsExtendedApduSupported = in.readByte() != 0; - mIsRemovable = in.readByte() != 0; + mIsExtendedApduSupported = in.readBoolean(); + mIsRemovable = in.readBoolean(); + mPortList = new ArrayList<UiccPortInfo>(); + in.readTypedList(mPortList, UiccPortInfo.CREATOR); + mLogicalSlotAccessRestricted = in.readBoolean(); } @Override public void writeToParcel(Parcel dest, int flags) { - dest.writeByte((byte) (mIsActive ? 1 : 0)); - dest.writeByte((byte) (mIsEuicc ? 1 : 0)); - dest.writeString(mCardId); + dest.writeBoolean(mIsActive); + dest.writeBoolean(mIsEuicc); + dest.writeString8(mCardId); dest.writeInt(mCardStateInfo); dest.writeInt(mLogicalSlotIdx); - dest.writeByte((byte) (mIsExtendedApduSupported ? 1 : 0)); - dest.writeByte((byte) (mIsRemovable ? 1 : 0)); + dest.writeBoolean(mIsExtendedApduSupported); + dest.writeBoolean(mIsRemovable); + dest.writeTypedList(mPortList, flags); + dest.writeBoolean(mLogicalSlotAccessRestricted); } @Override @@ -117,25 +128,42 @@ public class UiccSlotInfo implements Parcelable { this.mLogicalSlotIdx = logicalSlotIdx; this.mIsExtendedApduSupported = isExtendedApduSupported; this.mIsRemovable = false; + this.mPortList = null; } /** + * Construct a UiccSlotInfo. * @hide */ - public UiccSlotInfo(boolean isActive, boolean isEuicc, String cardId, - @CardStateInfo int cardStateInfo, int logicalSlotIdx, boolean isExtendedApduSupported, - boolean isRemovable) { - this.mIsActive = isActive; + public UiccSlotInfo(boolean isEuicc, String cardId, + @CardStateInfo int cardStateInfo, boolean isExtendedApduSupported, + boolean isRemovable, @NonNull List<UiccPortInfo> portList) { + this.mIsActive = portList.get(0).isActive(); this.mIsEuicc = isEuicc; this.mCardId = cardId; this.mCardStateInfo = cardStateInfo; - this.mLogicalSlotIdx = logicalSlotIdx; + this.mLogicalSlotIdx = portList.get(0).getLogicalSlotIndex(); this.mIsExtendedApduSupported = isExtendedApduSupported; this.mIsRemovable = isRemovable; + this.mPortList = portList; } + /** + * @deprecated There is no longer isActive state for each slot because ports belonging + * to the physical slot could have different states + * we instead use {@link UiccPortInfo#isActive()} + * To get UiccPortInfo use {@link UiccSlotInfo#getPorts()} + * + * @return {@code true} if status is active. + * @throws UnsupportedOperationException if the calling app's target SDK is T and beyond. + */ + @Deprecated public boolean getIsActive() { - return mIsActive; + if (mLogicalSlotAccessRestricted) { + throw new UnsupportedOperationException("get port status from UiccPortInfo"); + } + //always return status from first port. + return getPorts().stream().findFirst().get().isActive(); } public boolean getIsEuicc() { @@ -159,8 +187,21 @@ public class UiccSlotInfo implements Parcelable { return mCardStateInfo; } + /** + * @deprecated There is no longer getLogicalSlotIndex + * There is no longer getLogicalSlotIdx as each port belonging to this physical slot could have + * different logical slot index. Use {@link UiccPortInfo#getLogicalSlotIndex()} instead + * + * @throws UnsupportedOperationException if the calling app's target SDK is T and beyond. + */ + @Deprecated public int getLogicalSlotIdx() { - return mLogicalSlotIdx; + if (mLogicalSlotAccessRestricted) { + throw new UnsupportedOperationException("get logical slot index from UiccPortInfo"); + } + //always return logical slot index from first port. + //portList always have at least one element. + return getPorts().stream().findFirst().get().getLogicalSlotIndex(); } /** @@ -170,16 +211,37 @@ public class UiccSlotInfo implements Parcelable { return mIsExtendedApduSupported; } - /** + /** * Return whether the UICC slot is for a removable UICC. * <p> * UICCs are generally removable, but eUICCs may be removable or built in to the device. + * * @return true if the slot is for removable UICCs */ public boolean isRemovable() { return mIsRemovable; } + /** + * Get Information regarding port, iccid and its active status. + * + * @return Collection of {@link UiccPortInfo} + */ + public @NonNull Collection<UiccPortInfo> getPorts() { + return Collections.unmodifiableList(mPortList); + } + + /** + * Set the flag to check compatibility of the calling app's target SDK is T and beyond. + * + * @param logicalSlotAccessRestricted is the flag to check compatibility. + * + * @hide + */ + public void setLogicalSlotAccessRestricted(boolean logicalSlotAccessRestricted) { + this.mLogicalSlotAccessRestricted = logicalSlotAccessRestricted; + } + @Override public boolean equals(@Nullable Object obj) { if (this == obj) { @@ -196,20 +258,14 @@ public class UiccSlotInfo implements Parcelable { && (mCardStateInfo == that.mCardStateInfo) && (mLogicalSlotIdx == that.mLogicalSlotIdx) && (mIsExtendedApduSupported == that.mIsExtendedApduSupported) - && (mIsRemovable == that.mIsRemovable); + && (mIsRemovable == that.mIsRemovable) + && (Objects.equals(mPortList, that.mPortList)); } @Override public int hashCode() { - int result = 1; - result = 31 * result + (mIsActive ? 1 : 0); - result = 31 * result + (mIsEuicc ? 1 : 0); - result = 31 * result + Objects.hashCode(mCardId); - result = 31 * result + mCardStateInfo; - result = 31 * result + mLogicalSlotIdx; - result = 31 * result + (mIsExtendedApduSupported ? 1 : 0); - result = 31 * result + (mIsRemovable ? 1 : 0); - return result; + return Objects.hash(mIsActive, mIsEuicc, mCardId, mCardStateInfo, mLogicalSlotIdx, + mIsExtendedApduSupported, mIsRemovable, mPortList); } @NonNull @@ -229,6 +285,10 @@ public class UiccSlotInfo implements Parcelable { + mIsExtendedApduSupported + ", mIsRemovable=" + mIsRemovable + + ", mPortList=" + + mPortList + + ", mLogicalSlotAccessRestricted=" + + mLogicalSlotAccessRestricted + ")"; } } diff --git a/telephony/java/android/telephony/UiccSlotMapping.aidl b/telephony/java/android/telephony/UiccSlotMapping.aidl new file mode 100644 index 000000000000..3b1949958935 --- /dev/null +++ b/telephony/java/android/telephony/UiccSlotMapping.aidl @@ -0,0 +1,19 @@ +/* + * 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; + +parcelable UiccSlotMapping; diff --git a/telephony/java/android/telephony/UiccSlotMapping.java b/telephony/java/android/telephony/UiccSlotMapping.java new file mode 100644 index 000000000000..87e7acdc792d --- /dev/null +++ b/telephony/java/android/telephony/UiccSlotMapping.java @@ -0,0 +1,168 @@ +/* + * Copyright (C) 2021 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; + +import android.annotation.IntRange; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SystemApi; +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.Objects; + +/** + * <p>Provides information for a SIM slot mapping, which establishes a unique mapping between a + * logical SIM slot and a physical SIM slot and port index. A logical SIM slot represents a + * potentially active SIM slot, where a physical SIM slot and port index represent a hardware SIM + * slot and port (capable of having an active profile) which can be mapped to a logical sim slot. + * <p>It contains the following parameters: + * <ul> + * <li>Port index: unique index referring to a port belonging to the physical SIM slot. + * If the SIM does not support multiple enabled profiles, the port index is default index 0.</li> + * <li>Physical slot index: unique index referring to a physical SIM slot. Physical slot IDs start + * at 0 and go up depending on the number of physical slots on the device. + * This differs from the number of logical slots a device has, which corresponds to the number of + * active slots a device is capable of using. For example, a device which switches between dual-SIM + * and single-SIM mode may always have two physical slots, but in single-SIM mode it will have only + * one logical slot.</li> + * <li>Logical slot index: unique index referring to a logical SIM slot, Logical slot IDs start at 0 + * and go up depending on the number of supported active slots on a device. + * For example, a dual-SIM device typically has slot 0 and slot 1. If a device has multiple physical + * slots but only supports one active slot, it will have only the logical slot ID 0</li> + * </ul> + * + * <p> This configurations tells a specific logical slot is mapped to a port from an actual physical + * sim slot @see <a href="https://developer.android.com/guide/topics/connectivity/telecom/telephony-ids">the Android Developer Site</a> + * for more information. + * @hide + */ +@SystemApi +public final class UiccSlotMapping implements Parcelable { + private final int mPortIndex; + private final int mPhysicalSlotIndex; + private final int mLogicalSlotIndex; + + public static final @NonNull Creator<UiccSlotMapping> CREATOR = + new Creator<UiccSlotMapping>() { + @Override + public UiccSlotMapping createFromParcel(Parcel in) { + return new UiccSlotMapping(in); + } + + @Override + public UiccSlotMapping[] newArray(int size) { + return new UiccSlotMapping[size]; + } + }; + + private UiccSlotMapping(Parcel in) { + mPortIndex = in.readInt(); + mPhysicalSlotIndex = in.readInt(); + mLogicalSlotIndex = in.readInt(); + } + + @Override + public void writeToParcel(@Nullable Parcel dest, int flags) { + dest.writeInt(mPortIndex); + dest.writeInt(mPhysicalSlotIndex); + dest.writeInt(mLogicalSlotIndex); + } + + @Override + public int describeContents() { + return 0; + } + + /** + * + * @param portIndex The port index is an enumeration of the ports available on the UICC. + * @param physicalSlotIndex is unique index referring to a physical SIM slot. + * @param logicalSlotIndex is unique index referring to a logical SIM slot. + * + * @hide + */ + public UiccSlotMapping(int portIndex, int physicalSlotIndex, int logicalSlotIndex) { + this.mPortIndex = portIndex; + this.mPhysicalSlotIndex = physicalSlotIndex; + this.mLogicalSlotIndex = logicalSlotIndex; + } + + /** + * Port index is the unique index referring to a port belonging to the physical SIM slot. + * If the SIM does not support multiple enabled profiles, the port index is default index 0. + * + * @return port index. + */ + @IntRange(from = 0) + public int getPortIndex() { + return mPortIndex; + } + + /** + * Gets the physical slot index for the slot that the UICC is currently inserted in. + * + * @return physical slot index which is the index of actual physical UICC slot. + */ + @IntRange(from = 0) + public int getPhysicalSlotIndex() { + return mPhysicalSlotIndex; + } + + /** + * Gets logical slot index for the slot that the UICC is currently attached. + * Logical slot index is the unique index referring to a logical slot(logical modem stack). + * + * @return logical slot index; + */ + @IntRange(from = 0) + public int getLogicalSlotIndex() { + return mLogicalSlotIndex; + } + + @Override + public boolean equals(@Nullable Object obj) { + if (this == obj) { + return true; + } + if (obj == null || getClass() != obj.getClass()) { + return false; + } + + UiccSlotMapping that = (UiccSlotMapping) obj; + return (mPortIndex == that.mPortIndex) + && (mPhysicalSlotIndex == that.mPhysicalSlotIndex) + && (mLogicalSlotIndex == that.mLogicalSlotIndex); + } + + @Override + public int hashCode() { + return Objects.hash(mPortIndex, mPhysicalSlotIndex, mLogicalSlotIndex); + } + + @NonNull + @Override + public String toString() { + return "UiccSlotMapping (mPortIndex=" + + mPortIndex + + ", mPhysicalSlotIndex=" + + mPhysicalSlotIndex + + ", mLogicalSlotIndex=" + + mLogicalSlotIndex + + ")"; + } +} diff --git a/telephony/java/android/telephony/euicc/EuiccCardManager.java b/telephony/java/android/telephony/euicc/EuiccCardManager.java index e1aec0a593b4..ab35d77c0b4d 100644 --- a/telephony/java/android/telephony/euicc/EuiccCardManager.java +++ b/telephony/java/android/telephony/euicc/EuiccCardManager.java @@ -17,6 +17,7 @@ package android.telephony.euicc; import android.annotation.CallbackExecutor; import android.annotation.IntDef; +import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; import android.content.Context; @@ -24,6 +25,7 @@ import android.os.Binder; import android.os.RemoteException; import android.service.euicc.EuiccProfileInfo; import android.telephony.TelephonyFrameworkInitializer; +import android.telephony.TelephonyManager; import android.util.Log; import com.android.internal.telephony.euicc.IAuthenticateServerCallback; @@ -122,7 +124,6 @@ public class EuiccCardManager { /** Result code indicating the caller is not the active LPA. */ public static final int RESULT_CALLER_NOT_ALLOWED = -3; - /** * Callback to receive the result of an eUICC card API. * @@ -220,12 +221,48 @@ public class EuiccCardManager { * @param refresh Whether sending the REFRESH command to modem. * @param executor The executor through which the callback should be invoked. * @param callback The callback to get the result code. + * + * @deprecated instead use {@link #disableProfile(String, String, int, boolean, Executor, + * ResultCallback)} */ + @Deprecated public void disableProfile(String cardId, String iccid, boolean refresh, @CallbackExecutor Executor executor, ResultCallback<Void> callback) { try { getIEuiccCardController().disableProfile(mContext.getOpPackageName(), cardId, iccid, - refresh, new IDisableProfileCallback.Stub() { + TelephonyManager.DEFAULT_PORT_INDEX, refresh, + new IDisableProfileCallback.Stub() { + @Override + public void onComplete(int resultCode) { + final long token = Binder.clearCallingIdentity(); + try { + executor.execute(() -> callback.onComplete(resultCode, null)); + } finally { + Binder.restoreCallingIdentity(token); + } + } + }); + } catch (RemoteException e) { + Log.e(TAG, "Error calling disableProfile", e); + throw e.rethrowFromSystemServer(); + } + } + /** + * Disables the profile of the given ICCID. + * + * @param cardId The Id of the eUICC. + * @param iccid The iccid of the profile. + * @param portIndex the Port index is the unique index referring to a port. + * @param refresh Whether sending the REFRESH command to modem. + * @param executor The executor through which the callback should be invoked. + * @param callback The callback to get the result code. + */ + public void disableProfile(@Nullable String cardId, @Nullable String iccid, int portIndex, + boolean refresh, @NonNull @CallbackExecutor Executor executor, + @NonNull ResultCallback<Void> callback) { + try { + getIEuiccCardController().disableProfile(mContext.getOpPackageName(), cardId, iccid, + portIndex, refresh, new IDisableProfileCallback.Stub() { @Override public void onComplete(int resultCode) { final long token = Binder.clearCallingIdentity(); @@ -251,12 +288,51 @@ public class EuiccCardManager { * @param refresh Whether sending the REFRESH command to modem. * @param executor The executor through which the callback should be invoked. * @param callback The callback to get the result code and the EuiccProfileInfo enabled. + * + * @deprecated instead use {@link #switchToProfile(String, String, int, boolean, Executor, + * ResultCallback)} */ + @Deprecated public void switchToProfile(String cardId, String iccid, boolean refresh, @CallbackExecutor Executor executor, ResultCallback<EuiccProfileInfo> callback) { try { getIEuiccCardController().switchToProfile(mContext.getOpPackageName(), cardId, iccid, - refresh, new ISwitchToProfileCallback.Stub() { + TelephonyManager.DEFAULT_PORT_INDEX, refresh, + new ISwitchToProfileCallback.Stub() { + @Override + public void onComplete(int resultCode, EuiccProfileInfo profile) { + final long token = Binder.clearCallingIdentity(); + try { + executor.execute(() -> callback.onComplete(resultCode, profile)); + } finally { + Binder.restoreCallingIdentity(token); + } + } + }); + } catch (RemoteException e) { + Log.e(TAG, "Error calling switchToProfile", e); + throw e.rethrowFromSystemServer(); + } + } + + /** + * Switches from the current profile to another profile. The current profile will be disabled + * and the specified profile will be enabled. Here portIndex specifies on which port the + * profile is to be enabled. + * + * @param cardId The Id of the eUICC. + * @param iccid The iccid of the profile to switch to. + * @param portIndex The Port index is the unique index referring to a port. + * @param refresh Whether sending the REFRESH command to modem. + * @param executor The executor through which the callback should be invoked. + * @param callback The callback to get the result code and the EuiccProfileInfo enabled. + */ + public void switchToProfile(@Nullable String cardId, @Nullable String iccid, int portIndex, + boolean refresh, @NonNull @CallbackExecutor Executor executor, + @NonNull ResultCallback<EuiccProfileInfo> callback) { + try { + getIEuiccCardController().switchToProfile(mContext.getOpPackageName(), cardId, iccid, + portIndex, refresh, new ISwitchToProfileCallback.Stub() { @Override public void onComplete(int resultCode, EuiccProfileInfo profile) { final long token = Binder.clearCallingIdentity(); diff --git a/telephony/java/android/telephony/euicc/EuiccManager.java b/telephony/java/android/telephony/euicc/EuiccManager.java index 2edb564cc950..45022a6e4d8c 100644 --- a/telephony/java/android/telephony/euicc/EuiccManager.java +++ b/telephony/java/android/telephony/euicc/EuiccManager.java @@ -1418,4 +1418,22 @@ public class EuiccManager { .getEuiccControllerService() .get()); } + + /** + * Returns whether the passing portIndex is available. + * A port is available if it has no profiles enabled on it or calling app has carrier privilege + * over the profile installed on the selected port. + * Always returns false if the cardId is a physical card. + * + * @param portIndex is an enumeration of the ports available on the UICC. + * @return {@code true} if port is available + */ + public boolean isSimPortAvailable(int portIndex) { + try { + return getIEuiccController().isSimPortAvailable(mCardId, portIndex, + mContext.getOpPackageName()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } } diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl index d99fb85e2fd7..37cb9fb9ef98 100644 --- a/telephony/java/com/android/internal/telephony/ITelephony.aidl +++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl @@ -76,6 +76,7 @@ import java.util.Map; import android.telephony.UiccCardInfo; import android.telephony.UiccSlotInfo; +import android.telephony.UiccSlotMapping; /** * Interface used to interact with the phone. Mostly this is used by the @@ -1733,17 +1734,35 @@ interface ITelephony { * @return UiccSlotInfo array. * @hide */ - UiccSlotInfo[] getUiccSlotsInfo(); + UiccSlotInfo[] getUiccSlotsInfo(String callingPackage); /** * Map logicalSlot to physicalSlot, and activate the physicalSlot if it is inactive. * @param physicalSlots Index i in the array representing physical slot for phone i. The array * size should be same as getPhoneCount(). + * @deprecated Use {@link #setSimSlotMapping(in List<UiccSlotMapping> slotMapping)} instead. * @return boolean Return true if the switch succeeds, false if the switch fails. */ boolean switchSlots(in int[] physicalSlots); /** + * Maps the logical slots to the SlotPortMapping which consist of both physical slot index and + * port index. Logical slot is the slot that is seen by modem. Physical slot is the actual + * physical slot. Port index is the index (enumerated value) for the associated port available + * on the SIM. Each physical slot can have multiple ports which enables multi-enabled profile + * (MEP). If eUICC physical slot supports 2 ports, then the port index is numbered 0,1 and if + * eUICC2 supports 4 ports then the port index is numbered 0,1,2,3. Each portId is unique within + * a UICC physical slot but not necessarily unique across UICC’s. SEP(Single enabled profile) + * eUICC and non-eUICC will only have port Index 0. + * + * Logical slots that are already mapped to the requested SlotPortMapping are not impacted. + * @param slotMapping Index i in the list representing slot mapping for phone i. + * + * @return {@code true} if the switch succeeds, {@code false} if the switch fails. + */ + boolean setSimSlotMapping(in List<UiccSlotMapping> slotMapping); + + /** * Returns whether mobile data roaming is enabled on the subscription with id {@code subId}. * * @param subId the subscription id @@ -2121,7 +2140,7 @@ interface ITelephony { /** * Get the mapping from logical slots to physical slots. */ - int[] getSlotsMapping(); + int[] getSlotsMapping(String callingPackage); /** * Get the IRadio HAL Version encoded as 100 * MAJOR_VERSION + MINOR_VERSION or -1 if unknown diff --git a/telephony/java/com/android/internal/telephony/PhoneConstants.java b/telephony/java/com/android/internal/telephony/PhoneConstants.java index 3a99f0e010c6..f6502466e25e 100644 --- a/telephony/java/com/android/internal/telephony/PhoneConstants.java +++ b/telephony/java/com/android/internal/telephony/PhoneConstants.java @@ -171,6 +171,8 @@ public class PhoneConstants { public static final String SLOT_KEY = "slot"; + public static final String PORT_KEY = "port"; + // FIXME: This is used to pass a subId via intents, we need to look at its usage, which is // FIXME: extensive, and see if this should be an array of all active subId's or ...? /** diff --git a/telephony/java/com/android/internal/telephony/euicc/IEuiccCardController.aidl b/telephony/java/com/android/internal/telephony/euicc/IEuiccCardController.aidl index e33f44c7b7d9..c717c092cbed 100644 --- a/telephony/java/com/android/internal/telephony/euicc/IEuiccCardController.aidl +++ b/telephony/java/com/android/internal/telephony/euicc/IEuiccCardController.aidl @@ -45,10 +45,10 @@ interface IEuiccCardController { in IGetAllProfilesCallback callback); oneway void getProfile(String callingPackage, String cardId, String iccid, in IGetProfileCallback callback); - oneway void disableProfile(String callingPackage, String cardId, String iccid, boolean refresh, - in IDisableProfileCallback callback); - oneway void switchToProfile(String callingPackage, String cardId, String iccid, boolean refresh, - in ISwitchToProfileCallback callback); + oneway void disableProfile(String callingPackage, String cardId, String iccid, int portIndex, + boolean refresh, in IDisableProfileCallback callback); + oneway void switchToProfile(String callingPackage, String cardId, String iccid, int portIndex, + boolean refresh, in ISwitchToProfileCallback callback); oneway void setNickname(String callingPackage, String cardId, String iccid, String nickname, in ISetNicknameCallback callback); oneway void deleteProfile(String callingPackage, String cardId, String iccid, diff --git a/telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl b/telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl index 35e8a12e898a..944ce3486ca6 100644 --- a/telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl +++ b/telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl @@ -51,4 +51,5 @@ interface IEuiccController { void setSupportedCountries(boolean isSupported, in List<String> countriesList); List<String> getSupportedCountries(boolean isSupported); boolean isSupportedCountry(String countryIso); + boolean isSimPortAvailable(int cardId, int portIndex, String callingPackage); } diff --git a/telephony/java/com/android/internal/telephony/uicc/IccUtils.java b/telephony/java/com/android/internal/telephony/uicc/IccUtils.java index 5b44dba49929..21c3f760771c 100644 --- a/telephony/java/com/android/internal/telephony/uicc/IccUtils.java +++ b/telephony/java/com/android/internal/telephony/uicc/IccUtils.java @@ -23,6 +23,7 @@ import android.content.res.Resources.NotFoundException; import android.graphics.Bitmap; import android.graphics.Color; import android.os.Build; +import android.telephony.UiccPortInfo; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.telephony.GsmAlphabet; @@ -44,8 +45,7 @@ public class IccUtils { static final int FPLMN_BYTE_SIZE = 3; // ICCID used for tests by some OEMs - // TODO(b/159354974): Replace the constant here with UiccPortInfo.ICCID_REDACTED once ready - private static final String TEST_ICCID = "FFFFFFFFFFFFFFFFFFFF"; + public static final String TEST_ICCID = UiccPortInfo.ICCID_REDACTED; // A table mapping from a number to a hex character for fast encoding hex strings. private static final char[] HEX_CHARS = { |