/*
 * Copyright (C) 2014 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 static android.text.TextUtils.formatSimple;

import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;
import android.graphics.Rect;
import android.graphics.Typeface;
import android.os.Parcel;
import android.os.ParcelUuid;
import android.os.Parcelable;
import android.telephony.SubscriptionManager.ProfileClass;
import android.telephony.SubscriptionManager.SimDisplayNameSource;
import android.telephony.SubscriptionManager.SubscriptionType;
import android.telephony.SubscriptionManager.TransferStatus;
import android.telephony.SubscriptionManager.UsageSetting;
import android.text.TextUtils;
import android.util.DisplayMetrics;
import android.util.Log;

import com.android.internal.telephony.flags.Flags;
import com.android.internal.telephony.util.TelephonyUtils;
import com.android.telephony.Rlog;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Set;

/**
 * A Parcelable class for Subscription Information.
 */
public class SubscriptionInfo implements Parcelable {
    /**
     * Size of text to render on the icon.
     */
    private static final int TEXT_SIZE = 16;

    /**
     * Subscription Identifier, this is a device unique number
     * and not an index into an array
     */
    private final int mId;

    /**
     * The ICCID of the SIM that is associated with this subscription, empty if unknown.
     */
    @NonNull
    private final String mIccId;

    /**
     * The index of the SIM slot that currently contains the subscription and not necessarily unique
     * and maybe {@link SubscriptionManager#INVALID_SIM_SLOT_INDEX} if unknown or the subscription
     * is inactive.
     */
    private final int mSimSlotIndex;

    /**
     * The name displayed to the user that identifies this subscription. This name is used
     * in Settings page and can be renamed by the user.
     */
    @NonNull
    private final CharSequence mDisplayName;

    /**
     * The name displayed to the user that identifies subscription provider name. This name is the
     * SPN displayed in status bar and many other places. Can't be renamed by the user.
     */
    @NonNull
    private final CharSequence mCarrierName;

    /**
     * The source of the {@link #mDisplayName}.
     */
    @SimDisplayNameSource
    private final int mDisplayNameSource;

    /**
     * The color to be used for tinting the icon when displaying to the user.
     */
    private final int mIconTint;

    /**
     * The number presented to the user identify this subscription.
     */
    @NonNull
    private final String mNumber;

    /**
     * Whether user enables data roaming for this subscription or not. Either
     * {@link SubscriptionManager#DATA_ROAMING_ENABLE} or
     * {@link SubscriptionManager#DATA_ROAMING_DISABLE}.
     */
    private final int mDataRoaming;

    /**
     * Mobile Country Code.
     */
    @Nullable
    private final String mMcc;

    /**
     * Mobile Network Code.
     */
    @Nullable
    private final String mMnc;

    /**
     * EHPLMNs associated with the subscription.
     */
    @NonNull
    private final String[] mEhplmns;

    /**
     * HPLMNs associated with the subscription.
     */
    @NonNull
    private final String[] mHplmns;

    /**
     * Whether the subscription is from eSIM.
     */
    private final boolean mIsEmbedded;

    /**
     * The string ID of the SIM card. It is the ICCID of the active profile for a UICC card and the
     * EID for an eUICC card.
     */
    @NonNull
    private final String mCardString;

    /**
     * The access rules for this subscription, if it is embedded and defines any. This does not
     * include access rules for non-embedded subscriptions.
     */
    @Nullable
    private final UiccAccessRule[] mNativeAccessRules;

    /**
     * The carrier certificates for this subscription that are saved in carrier configs.
     * This does not include access rules from the Uicc, whether embedded or non-embedded.
     */
    @Nullable
    private final UiccAccessRule[] mCarrierConfigAccessRules;

    /**
     * Whether the subscription is opportunistic.
     */
    private final boolean mIsOpportunistic;

    /**
     * A UUID assigned to the subscription group. {@code null} if not assigned.
     *
     * @see SubscriptionManager#createSubscriptionGroup(List)
     */
    @Nullable
    private final ParcelUuid mGroupUuid;

    /**
     * ISO Country code for the subscription's provider.
     */
    @NonNull
    private final String mCountryIso;

    /**
     * The subscription carrier id.
     *
     * @see TelephonyManager#getSimCarrierId()
     */
    private final int mCarrierId;

    /**
     * The profile class populated from the profile metadata if present. Otherwise,
     * the profile class defaults to {@link SubscriptionManager#PROFILE_CLASS_UNSET} if there is no
     * profile metadata or the subscription is not on an eUICC ({@link #isEmbedded} returns
     * {@code false}).
     */
    @ProfileClass
    private final int mProfileClass;

    /**
     * Type of the subscription.
     */
    @SubscriptionType
    private final int mType;

    /**
     * A package name that specifies who created the group. Empty if not available.
     */
    @NonNull
    private final String mGroupOwner;

    /**
     * Whether uicc applications are configured to enable or disable.
     * By default it's true.
     */
    private final boolean mAreUiccApplicationsEnabled;

    /**
     * The port index of the Uicc card.
     */
    private final int mPortIndex;

    /**
     * Subscription's preferred usage setting.
     */
    @UsageSetting
    private final int mUsageSetting;

    /**
     * Subscription's transfer status
     */
    private final int mTransferStatus;

    // Below are the fields that do not exist in the database.

    /**
     * SIM icon bitmap cache.
     */
    @Nullable
    private Bitmap mIconBitmap;

    /**
     * The card ID of the SIM card. This maps uniquely to {@link #mCardString}.
     */
    private final int mCardId;

    /**
     * Whether group of the subscription is disabled. This is only useful if it's a grouped
     * opportunistic subscription. In this case, if all primary (non-opportunistic) subscriptions
     * in the group are deactivated (unplugged pSIM or deactivated eSIM profile), we should disable
     * this opportunistic subscription.
     */
    private final boolean mIsGroupDisabled;

    /**
     * Whether this subscription is used for communicating with non-terrestrial networks.
     */
    private final boolean mIsOnlyNonTerrestrialNetwork;

    /**
     * The service capabilities (in the form of bitmask combination) the subscription supports.
     */
    private final int mServiceCapabilities;

    /**
     * @hide
     *
     * @deprecated Use {@link SubscriptionInfo.Builder}.
     */
    // TODO: Clean up after external usages moved to builder model.
    @Deprecated
    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) {
        this(id, iccId, simSlotIndex, displayName, carrierName, nameSource, iconTint, number,
                roaming, icon, mcc, mnc, countryIso, isEmbedded, nativeAccessRules, cardString, -1,
                false, null, false, TelephonyManager.UNKNOWN_CARRIER_ID,
                SubscriptionManager.PROFILE_CLASS_UNSET,
                SubscriptionManager.SUBSCRIPTION_TYPE_LOCAL_SIM, null, null, true);
    }

    /**
     * @hide
     *
     * @deprecated Use {@link SubscriptionInfo.Builder}.
     */
    // TODO: Clean up after external usages moved to builder model.
    @Deprecated
    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,
            boolean isOpportunistic, @Nullable String groupUUID, int carrierId, int profileClass) {
        this(id, iccId, simSlotIndex, displayName, carrierName, nameSource, iconTint, number,
                roaming, icon, mcc, mnc, countryIso, isEmbedded, nativeAccessRules, cardString, -1,
                isOpportunistic, groupUUID, false, carrierId, profileClass,
                SubscriptionManager.SUBSCRIPTION_TYPE_LOCAL_SIM, null, null, true);
    }

    /**
     * @hide
     *
     * @deprecated Use {@link SubscriptionInfo.Builder}.
     */
    // TODO: Clean up after external usages moved to builder model.
    @Deprecated
    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) {
        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
     *
     * @deprecated Use {@link SubscriptionInfo.Builder}.
     */
    // TODO: Clean up after external usages moved to builder model.
    @Deprecated
    public SubscriptionInfo(int id, String iccId, int simSlotIndex, CharSequence displayName,
            CharSequence carrierName, int displayNameSource, 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(id, iccId, simSlotIndex, displayName, carrierName, displayNameSource, iconTint, number,
                roaming, icon, mcc, mnc, countryIso, isEmbedded, nativeAccessRules, cardString,
                cardId, isOpportunistic, groupUUID, isGroupDisabled, carrierId, profileClass,
                subType, groupOwner, carrierConfigAccessRules, areUiccApplicationsEnabled,
                portIndex, SubscriptionManager.USAGE_SETTING_DEFAULT);
    }

    /**
     * @hide
     *
     * @deprecated Use {@link SubscriptionInfo.Builder}.
     */
    // TODO: Clean up after external usages moved to builder model.
    @Deprecated
    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, @UsageSetting int usageSetting) {
        this.mId = id;
        this.mIccId = iccId;
        this.mSimSlotIndex = simSlotIndex;
        this.mDisplayName =  displayName;
        this.mCarrierName = carrierName;
        this.mDisplayNameSource = nameSource;
        this.mIconTint = iconTint;
        this.mNumber = number;
        this.mDataRoaming = roaming;
        this.mIconBitmap = icon;
        this.mMcc = TextUtils.emptyIfNull(mcc);
        this.mMnc = TextUtils.emptyIfNull(mnc);
        this.mHplmns = null;
        this.mEhplmns = null;
        this.mCountryIso = TextUtils.emptyIfNull(countryIso);
        this.mIsEmbedded = isEmbedded;
        this.mNativeAccessRules = nativeAccessRules;
        this.mCardString = TextUtils.emptyIfNull(cardString);
        this.mCardId = cardId;
        this.mIsOpportunistic = isOpportunistic;
        this.mGroupUuid = groupUuid == null ? null : ParcelUuid.fromString(groupUuid);
        this.mIsGroupDisabled = isGroupDisabled;
        this.mCarrierId = carrierId;
        this.mProfileClass = profileClass;
        this.mType = subType;
        this.mGroupOwner = TextUtils.emptyIfNull(groupOwner);
        this.mCarrierConfigAccessRules = carrierConfigAccessRules;
        this.mAreUiccApplicationsEnabled = areUiccApplicationsEnabled;
        this.mPortIndex = portIndex;
        this.mUsageSetting = usageSetting;
        this.mIsOnlyNonTerrestrialNetwork = false;
        this.mServiceCapabilities = 0;
        this.mTransferStatus = 0;
    }

    /**
     * Constructor from builder.
     *
     * @param builder Builder of {@link SubscriptionInfo}.
     */
    private SubscriptionInfo(@NonNull Builder builder) {
        this.mId = builder.mId;
        this.mIccId = builder.mIccId;
        this.mSimSlotIndex = builder.mSimSlotIndex;
        this.mDisplayName = builder.mDisplayName;
        this.mCarrierName = builder.mCarrierName;
        this.mDisplayNameSource = builder.mDisplayNameSource;
        this.mIconTint = builder.mIconTint;
        this.mNumber = builder.mNumber;
        this.mDataRoaming = builder.mDataRoaming;
        this.mIconBitmap = builder.mIconBitmap;
        this.mMcc = builder.mMcc;
        this.mMnc = builder.mMnc;
        this.mEhplmns = builder.mEhplmns;
        this.mHplmns = builder.mHplmns;
        this.mCountryIso = builder.mCountryIso;
        this.mIsEmbedded = builder.mIsEmbedded;
        this.mNativeAccessRules = builder.mNativeAccessRules;
        this.mCardString = builder.mCardString;
        this.mCardId = builder.mCardId;
        this.mIsOpportunistic = builder.mIsOpportunistic;
        this.mGroupUuid = builder.mGroupUuid;
        this.mIsGroupDisabled = builder.mIsGroupDisabled;
        this.mCarrierId = builder.mCarrierId;
        this.mProfileClass = builder.mProfileClass;
        this.mType = builder.mType;
        this.mGroupOwner = builder.mGroupOwner;
        this.mCarrierConfigAccessRules = builder.mCarrierConfigAccessRules;
        this.mAreUiccApplicationsEnabled = builder.mAreUiccApplicationsEnabled;
        this.mPortIndex = builder.mPortIndex;
        this.mUsageSetting = builder.mUsageSetting;
        this.mIsOnlyNonTerrestrialNetwork = builder.mIsOnlyNonTerrestrialNetwork;
        this.mServiceCapabilities = builder.mServiceCapabilities;
        this.mTransferStatus = builder.mTransferStatus;
    }

    /**
     * @return The subscription ID.
     */
    public int getSubscriptionId() {
        return mId;
    }

    /**
     * Returns the ICC ID.
     *
     * Starting with API level 29 Security Patch 2021-04-05, returns the ICC ID if the calling app
     * has been granted the READ_PRIVILEGED_PHONE_STATE permission, has carrier privileges (see
     * {@link TelephonyManager#hasCarrierPrivileges}), or is a device owner or profile owner that
     * has been granted the READ_PHONE_STATE permission. The profile owner is an app that owns a
     * managed profile on the device; for more details see <a
     * href="https://developer.android.com/work/managed-profiles">Work profiles</a>. Profile
     * owner access is deprecated and will be removed in a future release.
     *
     * @return the ICC ID, or an empty string if one of these requirements is not met
     */
    public String getIccId() {
        return mIccId;
    }

    /**
     * @return The index of the SIM slot that currently contains the subscription and not
     * necessarily unique and maybe {@link SubscriptionManager#INVALID_SIM_SLOT_INDEX} if unknown or
     * the subscription is inactive.
     */
    public int getSimSlotIndex() {
        return mSimSlotIndex;
    }

    /**
     * @return The carrier id of this subscription carrier.
     *
     * @see TelephonyManager#getSimCarrierId()
     */
    public int getCarrierId() {
        return mCarrierId;
    }

    /**
     * @return The name displayed to the user that identifies this subscription. This name is
     * used in Settings page and can be renamed by the user.
     *
     * @see #getCarrierName()
     */
    public CharSequence getDisplayName() {
        return mDisplayName;
    }

    /**
     * @return The name displayed to the user that identifies subscription provider name. This name
     * is the SPN displayed in status bar and many other places. Can't be renamed by the user.
     *
     * @see #getDisplayName()
     */
    public CharSequence getCarrierName() {
        return mCarrierName;
    }

    /**
     * @return The source of the {@link #getDisplayName()}.
     *
     * @hide
     */
    @SimDisplayNameSource
    public int getDisplayNameSource() {
        return mDisplayNameSource;
    }

    /**
     * Creates and returns an icon {@code Bitmap} to represent this {@code SubscriptionInfo} in a
     * user interface.
     *
     * @param context A {@code Context} to get the {@code DisplayMetrics}s from.
     *
     * @return A bitmap icon for this {@code SubscriptionInfo}.
     */
    public Bitmap createIconBitmap(Context context) {
        if (mIconBitmap == null) {
            mIconBitmap = BitmapFactory.decodeResource(context.getResources(),
                    com.android.internal.R.drawable.ic_sim_card_multi_24px_clr);
        }
        int width = mIconBitmap.getWidth();
        int height = mIconBitmap.getHeight();
        DisplayMetrics metrics = context.getResources().getDisplayMetrics();

        // Create a new bitmap of the same size because it will be modified.
        Bitmap workingBitmap = Bitmap.createBitmap(metrics, width, height, mIconBitmap.getConfig());

        Canvas canvas = new Canvas(workingBitmap);
        Paint paint = new Paint();

        // Tint the icon with the color.
        paint.setColorFilter(new PorterDuffColorFilter(mIconTint, PorterDuff.Mode.SRC_ATOP));
        canvas.drawBitmap(mIconBitmap, 0, 0, paint);
        paint.setColorFilter(null);

        // Write the sim slot index.
        paint.setAntiAlias(true);
        paint.setTypeface(Typeface.create("sans-serif", Typeface.NORMAL));
        paint.setColor(Color.WHITE);
        // Set text size scaled by density
        paint.setTextSize(TEXT_SIZE * metrics.density);
        // Convert sim slot index to localized string
        final String index = formatSimple("%d", mSimSlotIndex + 1);
        final Rect textBound = new Rect();
        paint.getTextBounds(index, 0, 1, textBound);
        final float xOffset = (width / 2.f) - textBound.centerX();
        final float yOffset = (height / 2.f) - textBound.centerY();
        canvas.drawText(index, xOffset, yOffset, paint);

        return workingBitmap;
    }

    /**
     * A highlight color to use in displaying information about this {@code PhoneAccount}.
     *
     * @return A hexadecimal color value.
     */
    public int getIconTint() {
        return mIconTint;
    }

    /**
     * Returns the number of this subscription.
     *
     * Starting with API level 30, returns the number of this subscription if the calling app meets
     * at least one of the following requirements:
     * <ul>
     *     <li>If the calling app's target SDK is API level 29 or lower and the app has been granted
     *     the READ_PHONE_STATE permission.
     *     <li>If the calling app has been granted any of READ_PRIVILEGED_PHONE_STATE,
     *     READ_PHONE_NUMBERS, or READ_SMS.
     *     <li>If the calling app has carrier privileges (see {@link
     *     TelephonyManager#hasCarrierPrivileges}).
     *     <li>If the calling app is the default SMS role holder.
     * </ul>
     *
     * @return the number of this subscription, or an empty string if none of the requirements
     * are met.
     * @deprecated use {@link SubscriptionManager#getPhoneNumber(int)} instead, which takes a
     *             {@link #getSubscriptionId() subscription ID}.
     */
    @Deprecated
    public String getNumber() {
        return mNumber;
    }

    /**
     * Whether user enables data roaming for this subscription or not. Either
     * {@link SubscriptionManager#DATA_ROAMING_ENABLE} or
     * {@link SubscriptionManager#DATA_ROAMING_DISABLE}.
     */
    public int getDataRoaming() {
        return mDataRoaming;
    }

    /**
     * @return The mobile country code.
     *
     * @deprecated Use {@link #getMccString()} instead.
     */
    @Deprecated
    public int getMcc() {
        try {
            return mMcc == null ? 0 : Integer.parseInt(mMcc);
        } catch (NumberFormatException e) {
            Log.w(SubscriptionInfo.class.getSimpleName(), "MCC string is not a number");
            return 0;
        }
    }

    /**
     * @return The mobile network code.
     *
     * @deprecated Use {@link #getMncString()} instead.
     */
    @Deprecated
    public int getMnc() {
        try {
            return mMnc == null ? 0 : Integer.parseInt(mMnc);
        } catch (NumberFormatException e) {
            Log.w(SubscriptionInfo.class.getSimpleName(), "MNC string is not a number");
            return 0;
        }
    }

    /**
     * @return The mobile country code.
     */
    @Nullable
    public String getMccString() {
        return mMcc;
    }

    /**
     * @return The mobile network code.
     */
    @Nullable
    public String getMncString() {
        return mMnc;
    }

    /**
     * @return The ISO country code. Empty if not available.
     */
    public String getCountryIso() {
        return mCountryIso;
    }

    /**
     * @return {@code true} if the subscription is from eSIM.
     */
    public boolean isEmbedded() {
        return mIsEmbedded;
    }

    /**
     * An opportunistic subscription connects to a network that is
     * limited in functionality and / or coverage.
     *
     * @return Whether subscription is opportunistic.
     */
    public boolean isOpportunistic() {
        return mIsOpportunistic;
    }

    /**
     * @return {@code true} if the subscription is from the actively used SIM.
     *
     * @hide
     */
    public boolean isActive() {
        return mSimSlotIndex >= 0 || mType == SubscriptionManager.SUBSCRIPTION_TYPE_REMOTE_SIM;
    }

    /**
     * Used in scenarios where different subscriptions are bundled as a group.
     * It's typically a primary and an opportunistic subscription. (see {@link #isOpportunistic()})
     * Such that those subscriptions will have some affiliated behaviors such as opportunistic
     * subscription may be invisible to the user.
     *
     * @return Group UUID a String of group UUID if it belongs to a group. Otherwise
     * {@code null}.
     */
    @Nullable
    public ParcelUuid getGroupUuid() {
        return mGroupUuid;
    }

    /**
     * @hide
     */
    @NonNull
    public List<String> getEhplmns() {
        return Collections.unmodifiableList(mEhplmns == null
                ? Collections.emptyList() : Arrays.asList(mEhplmns));
    }

    /**
     * @hide
     */
    @NonNull
    public List<String> getHplmns() {
        return Collections.unmodifiableList(mHplmns == null
                ? Collections.emptyList() : Arrays.asList(mHplmns));
    }

    /**
     * @return The owner package of group the subscription belongs to.
     *
     * @hide
     */
    @NonNull
    public String getGroupOwner() {
        return mGroupOwner;
    }

    /**
     * @return The profile class populated from the profile metadata if present. Otherwise,
     * the profile class defaults to {@link SubscriptionManager#PROFILE_CLASS_UNSET} if there is no
     * profile metadata or the subscription is not on an eUICC ({@link #isEmbedded} return
     * {@code false}).
     *
     * @hide
     */
    @SystemApi
    @ProfileClass
    public int getProfileClass() {
        return mProfileClass;
    }

    /**
     * This method returns the type of a subscription. It can be
     * {@link SubscriptionManager#SUBSCRIPTION_TYPE_LOCAL_SIM} or
     * {@link SubscriptionManager#SUBSCRIPTION_TYPE_REMOTE_SIM}.
     *
     * @return The type of the subscription.
     */
    @SubscriptionType
    public int getSubscriptionType() {
        return mType;
    }

    /**
     * Checks whether the app with the given context is authorized to manage this subscription
     * according to its metadata. Only supported for embedded subscriptions (if {@link #isEmbedded}
     * returns true).
     *
     * @param context Context of the application to check.
     * @return Whether the app is authorized to manage this subscription per its metadata.
     * @hide
     * @deprecated - Do not use.
     */
    @Deprecated
    public boolean canManageSubscription(Context context) {
        return canManageSubscription(context, context.getPackageName());
    }

    /**
     * Checks whether the given app is authorized to manage this subscription according to its
     * metadata. Only supported for embedded subscriptions (if {@link #isEmbedded} returns true).
     *
     * @param context Any context.
     * @param packageName Package name of the app to check.
     * @return whether the app is authorized to manage this subscription per its metadata.
     * @hide
     * @deprecated - Do not use.
     */
    @Deprecated
    public boolean canManageSubscription(Context context, String packageName) {
        List<UiccAccessRule> allAccessRules = getAccessRules();
        if (allAccessRules == null) {
            return false;
        }
        PackageManager packageManager = context.getPackageManager();
        PackageInfo packageInfo;
        try {
            packageInfo = packageManager.getPackageInfo(packageName,
                PackageManager.GET_SIGNING_CERTIFICATES);
        } catch (PackageManager.NameNotFoundException e) {
            Log.d("SubscriptionInfo", "canManageSubscription: Unknown package: " + packageName, e);
            return false;
        }
        for (UiccAccessRule rule : allAccessRules) {
            if (rule.getCarrierPrivilegeStatus(packageInfo)
                    == TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) {
                return true;
            }
        }
        return false;
    }

    /**
     * @return The {@link UiccAccessRule}s that are stored in Uicc, dictating who is authorized to
     * manage this subscription.
     *
     * @hide
     */
    @SystemApi
    @Nullable
    public List<UiccAccessRule> getAccessRules() {
        List<UiccAccessRule> merged = new ArrayList<>();
        if (mNativeAccessRules != null) {
            merged.addAll(Arrays.asList(mNativeAccessRules));
        }
        if (mCarrierConfigAccessRules != null) {
            merged.addAll(Arrays.asList(mCarrierConfigAccessRules));
        }
        return merged.isEmpty() ? null : Collections.unmodifiableList(merged);
    }

    /**
     * Returns the card string of the SIM card which contains the subscription.
     *
     * Starting with API level 29 Security Patch 2021-04-05, returns the card string if the calling
     * app has been granted the READ_PRIVILEGED_PHONE_STATE permission, has carrier privileges (see
     * {@link TelephonyManager#hasCarrierPrivileges}), or is a device owner or profile owner that
     * has been granted the READ_PHONE_STATE permission. The profile owner is an app that owns a
     * managed profile on the device; for more details see <a
     * href="https://developer.android.com/work/managed-profiles">Work profiles</a>. Profile
     * owner access is deprecated and will be removed in a future release.
     *
     * @return The card string of the SIM card which contains the subscription or an empty string
     * if these requirements are not met. The card string is the ICCID for UICCs or the EID for
     * eUICCs.
     *
     * @hide
     */
    @NonNull
    public String getCardString() {
        return mCardString;
    }

    /**
     * @return The card ID of the SIM card which contains the subscription.
     *
     * @see UiccCardInfo#getCardId().
     */
    public int getCardId() {
        return mCardId;
    }
    /**
     * @return The port index of the SIM card which contains the subscription.
     */
    public int getPortIndex() {
        return mPortIndex;
    }

    /**
     * @return {@code true} if the group of the subscription is disabled. This is only useful if
     * it's a grouped opportunistic subscription. In this case, if all primary (non-opportunistic)
     * subscriptions in the group are deactivated (unplugged pSIM or deactivated eSIM profile), we
     * should disable this opportunistic subscription.
     *
     * @hide
     */
    @SystemApi
    public boolean isGroupDisabled() {
        return mIsGroupDisabled;
    }

    /**
     * @return {@code true} if Uicc applications are set to be enabled or disabled.
     * @hide
     */
    @SystemApi
    public boolean areUiccApplicationsEnabled() {
        return mAreUiccApplicationsEnabled;
    }

    /**
     * Get the usage setting for this subscription.
     *
     * @return The usage setting used for this subscription.
     */
    @UsageSetting
    public int getUsageSetting() {
        return mUsageSetting;
    }

    /**
     * Check if the subscription is exclusively for non-terrestrial networks.
     *
     * @return {@code true} if it is a non-terrestrial network subscription, {@code false}
     * otherwise.
     */
    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
    public boolean isOnlyNonTerrestrialNetwork() {
        return mIsOnlyNonTerrestrialNetwork;
    }

    // TODO(b/316183370): replace @code with @link in javadoc after feature is released
    /**
     * Retrieves the service capabilities for the current subscription.
     *
     * <p>These capabilities are hint to system components and applications, allowing them to
     * enhance user experience. For instance, a Dialer application can inform the user that the
     * current subscription is incapable of making voice calls if the voice service is not
     * available.
     *
     * <p>Correct usage of these service capabilities must also consider the device's overall
     * service capabilities. For example, even if the subscription supports voice calls, a voice
     * call might not be feasible on a device that only supports data services. To determine the
     * device's capabilities for voice and SMS services, refer to
     * {@code TelephonyManager#isDeviceVoiceCapable()} and
     * {@code TelephonyManager#isDeviceSmsCapable()}.
     *
     * <p>Emergency service availability may not directly correlate with the subscription or
     * device's general service capabilities. In some cases, emergency calls might be possible
     * even if the subscription or device does not typically support voice services.
     *
     * @return A set of integer representing the subscription's service capabilities,
     * defined by {@code SubscriptionManager#SERVICE_CAPABILITY_VOICE},
     * {@code SubscriptionManager#SERVICE_CAPABILITY_SMS}
     * and {@code SubscriptionManager#SERVICE_CAPABILITY_DATA}.
     *
     * @see TelephonyManager#isDeviceVoiceCapable()
     * @see TelephonyManager#isDeviceSmsCapable()
     * @see CarrierConfigManager#KEY_CELLULAR_SERVICE_CAPABILITIES_INT_ARRAY
     * @see SubscriptionManager#SERVICE_CAPABILITY_VOICE
     * @see SubscriptionManager#SERVICE_CAPABILITY_SMS
     * @see SubscriptionManager#SERVICE_CAPABILITY_DATA
     */
    @NonNull
    @FlaggedApi(Flags.FLAG_DATA_ONLY_CELLULAR_SERVICE)
    public @SubscriptionManager.ServiceCapability Set<Integer> getServiceCapabilities() {
        return SubscriptionManager.getServiceCapabilitiesSet(mServiceCapabilities);
    }

    /**
     * Get the transfer status for this subscription.
     *
     * @return The transfer status for this subscription.
     *
     * @hide
     */
    @FlaggedApi(Flags.FLAG_SUPPORT_PSIM_TO_ESIM_CONVERSION)
    @SystemApi
    public @TransferStatus int getTransferStatus() {
        return mTransferStatus;
    }

    @NonNull
    public static final Parcelable.Creator<SubscriptionInfo> CREATOR =
            new Parcelable.Creator<SubscriptionInfo>() {
        @Override
        public SubscriptionInfo createFromParcel(Parcel source) {
            return new Builder()
                    .setId(source.readInt())
                    .setIccId(source.readString())
                    .setSimSlotIndex(source.readInt())
                    .setDisplayName(TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source))
                    .setCarrierName(TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source))
                    .setDisplayNameSource(source.readInt())
                    .setIconTint(source.readInt())
                    .setNumber(source.readString())
                    .setDataRoaming(source.readInt())
                    .setMcc(source.readString())
                    .setMnc(source.readString())
                    .setCountryIso(source.readString())
                    .setEmbedded(source.readBoolean())
                    .setNativeAccessRules(source.createTypedArray(UiccAccessRule.CREATOR))
                    .setCardString(source.readString())
                    .setCardId(source.readInt())
                    .setPortIndex(source.readInt())
                    .setOpportunistic(source.readBoolean())
                    .setGroupUuid(source.readString8())
                    .setGroupDisabled(source.readBoolean())
                    .setCarrierId(source.readInt())
                    .setProfileClass(source.readInt())
                    .setType(source.readInt())
                    .setEhplmns(source.createStringArray())
                    .setHplmns(source.createStringArray())
                    .setGroupOwner(source.readString())
                    .setCarrierConfigAccessRules(source.createTypedArray(
                            UiccAccessRule.CREATOR))
                    .setUiccApplicationsEnabled(source.readBoolean())
                    .setUsageSetting(source.readInt())
                    .setOnlyNonTerrestrialNetwork(source.readBoolean())
                    .setServiceCapabilities(
                            SubscriptionManager.getServiceCapabilitiesSet(source.readInt()))
                    .setTransferStatus(source.readInt())
                    .build();
        }

        @Override
        public SubscriptionInfo[] newArray(int size) {
            return new SubscriptionInfo[size];
        }
    };

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(mId);
        dest.writeString(mIccId);
        dest.writeInt(mSimSlotIndex);
        TextUtils.writeToParcel(mDisplayName, dest, 0);
        TextUtils.writeToParcel(mCarrierName, dest, 0);
        dest.writeInt(mDisplayNameSource);
        dest.writeInt(mIconTint);
        dest.writeString(mNumber);
        dest.writeInt(mDataRoaming);
        dest.writeString(mMcc);
        dest.writeString(mMnc);
        dest.writeString(mCountryIso);
        // Do not write mIconBitmap since it should be lazily loaded on first usage
        dest.writeBoolean(mIsEmbedded);
        dest.writeTypedArray(mNativeAccessRules, flags);
        dest.writeString(mCardString);
        dest.writeInt(mCardId);
        dest.writeInt(mPortIndex);
        dest.writeBoolean(mIsOpportunistic);
        dest.writeString8(mGroupUuid == null ? null : mGroupUuid.toString());
        dest.writeBoolean(mIsGroupDisabled);
        dest.writeInt(mCarrierId);
        dest.writeInt(mProfileClass);
        dest.writeInt(mType);
        dest.writeStringArray(mEhplmns);
        dest.writeStringArray(mHplmns);
        dest.writeString(mGroupOwner);
        dest.writeTypedArray(mCarrierConfigAccessRules, flags);
        dest.writeBoolean(mAreUiccApplicationsEnabled);
        dest.writeInt(mUsageSetting);
        dest.writeBoolean(mIsOnlyNonTerrestrialNetwork);
        dest.writeInt(mServiceCapabilities);
        dest.writeInt(mTransferStatus);
    }

    @Override
    public int describeContents() {
        return 0;
    }

    /**
     * Get stripped PII information from the id.
     *
     * @param id The raw id (e.g. ICCID, IMSI, etc...).
     * @return The stripped string.
     *
     * @hide
     */
    @Nullable
    public static String getPrintableId(@Nullable String id) {
        String idToPrint = null;
        if (id != null) {
            if (id.length() > 9 && !TelephonyUtils.IS_DEBUGGABLE) {
                idToPrint = id.substring(0, 9) + Rlog.pii(false, id.substring(9));
            } else {
                idToPrint = id;
            }
        }
        return idToPrint;
    }

    @Override
    public String toString() {
        String iccIdToPrint = getPrintableId(mIccId);
        String cardStringToPrint = getPrintableId(mCardString);
        return "[SubscriptionInfo: id=" + mId
                + " iccId=" + iccIdToPrint
                + " simSlotIndex=" + mSimSlotIndex
                + " portIndex=" + mPortIndex
                + " isEmbedded=" + mIsEmbedded
                + " carrierId=" + mCarrierId
                + " displayName=" + mDisplayName
                + " carrierName=" + mCarrierName
                + " isOpportunistic=" + mIsOpportunistic
                + " groupUuid=" + mGroupUuid
                + " groupOwner=" + mGroupOwner
                + " isGroupDisabled=" + mIsGroupDisabled
                + " displayNameSource="
                + SubscriptionManager.displayNameSourceToString(mDisplayNameSource)
                + " iconTint=" + mIconTint
                + " number=" + Rlog.pii(TelephonyUtils.IS_DEBUGGABLE, mNumber)
                + " dataRoaming=" + mDataRoaming
                + " mcc=" + mMcc
                + " mnc=" + mMnc
                + " ehplmns=" + Arrays.toString(mEhplmns)
                + " hplmns=" + Arrays.toString(mHplmns)
                + " cardString=" + cardStringToPrint
                + " cardId=" + mCardId
                + " nativeAccessRules=" + Arrays.toString(mNativeAccessRules)
                + " carrierConfigAccessRules=" + Arrays.toString(mCarrierConfigAccessRules)
                + " countryIso=" + mCountryIso
                + " profileClass=" + mProfileClass
                + " mType=" + SubscriptionManager.subscriptionTypeToString(mType)
                + " areUiccApplicationsEnabled=" + mAreUiccApplicationsEnabled
                + " usageSetting=" + SubscriptionManager.usageSettingToString(mUsageSetting)
                + " isOnlyNonTerrestrialNetwork=" + mIsOnlyNonTerrestrialNetwork
                + " serviceCapabilities=" + SubscriptionManager.getServiceCapabilitiesSet(
                mServiceCapabilities).toString()
                + " transferStatus=" + mTransferStatus
                + "]";
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        SubscriptionInfo that = (SubscriptionInfo) o;
        return mId == that.mId && mSimSlotIndex == that.mSimSlotIndex
                && mDisplayNameSource == that.mDisplayNameSource && mIconTint == that.mIconTint
                && mDataRoaming == that.mDataRoaming && mIsEmbedded == that.mIsEmbedded
                && mIsOpportunistic == that.mIsOpportunistic && mCarrierId == that.mCarrierId
                && mProfileClass == that.mProfileClass && mType == that.mType
                && mAreUiccApplicationsEnabled == that.mAreUiccApplicationsEnabled
                && mPortIndex == that.mPortIndex && mUsageSetting == that.mUsageSetting
                && mCardId == that.mCardId && mIsGroupDisabled == that.mIsGroupDisabled
                && mIccId.equals(that.mIccId) && mDisplayName.equals(that.mDisplayName)
                && mCarrierName.equals(that.mCarrierName) && mNumber.equals(that.mNumber)
                && Objects.equals(mMcc, that.mMcc) && Objects.equals(mMnc,
                that.mMnc) && Arrays.equals(mEhplmns, that.mEhplmns)
                && Arrays.equals(mHplmns, that.mHplmns) && mCardString.equals(
                that.mCardString) && Arrays.equals(mNativeAccessRules,
                that.mNativeAccessRules) && Arrays.equals(mCarrierConfigAccessRules,
                that.mCarrierConfigAccessRules) && Objects.equals(mGroupUuid, that.mGroupUuid)
                && mCountryIso.equals(that.mCountryIso) && mGroupOwner.equals(that.mGroupOwner)
                && mIsOnlyNonTerrestrialNetwork == that.mIsOnlyNonTerrestrialNetwork
                && mServiceCapabilities == that.mServiceCapabilities
                && mTransferStatus == that.mTransferStatus;
    }

    @Override
    public int hashCode() {
        int result = Objects.hash(mId, mIccId, mSimSlotIndex, mDisplayName, mCarrierName,
                mDisplayNameSource, mIconTint, mNumber, mDataRoaming, mMcc, mMnc, mIsEmbedded,
                mCardString, mIsOpportunistic, mGroupUuid, mCountryIso, mCarrierId, mProfileClass,
                mType, mGroupOwner, mAreUiccApplicationsEnabled, mPortIndex, mUsageSetting, mCardId,
                mIsGroupDisabled, mIsOnlyNonTerrestrialNetwork, mServiceCapabilities,
                mTransferStatus);
        result = 31 * result + Arrays.hashCode(mEhplmns);
        result = 31 * result + Arrays.hashCode(mHplmns);
        result = 31 * result + Arrays.hashCode(mNativeAccessRules);
        result = 31 * result + Arrays.hashCode(mCarrierConfigAccessRules);
        return result;
    }

    /**
     * The builder class of {@link SubscriptionInfo}.
     *
     * @hide
     */
    public static class Builder {
        /**
         * The subscription id.
         */
        private int mId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;

        /**
         * The ICCID of the SIM that is associated with this subscription, empty if unknown.
         */
        @NonNull
        private String mIccId = "";

        /**
         * The index of the SIM slot that currently contains the subscription and not necessarily
         * unique and maybe {@link SubscriptionManager#INVALID_SIM_SLOT_INDEX} if unknown or the
         * subscription is inactive.
         */
        private int mSimSlotIndex = SubscriptionManager.INVALID_SIM_SLOT_INDEX;

        /**
         * The name displayed to the user that identifies this subscription. This name is used
         * in Settings page and can be renamed by the user.
         */
        @NonNull
        private CharSequence mDisplayName = "";

        /**
         * The name displayed to the user that identifies subscription provider name. This name
         * is the SPN displayed in status bar and many other places. Can't be renamed by the user.
         */
        @NonNull
        private CharSequence mCarrierName = "";

        /**
         * The source of the display name.
         */
        @SimDisplayNameSource
        private int mDisplayNameSource = SubscriptionManager.NAME_SOURCE_UNKNOWN;

        /**
         * The color to be used for tinting the icon when displaying to the user.
         */
        private int mIconTint = 0;

        /**
         * The number presented to the user identify this subscription.
         */
        @NonNull
        private String mNumber = "";

        /**
         * Whether user enables data roaming for this subscription or not. Either
         * {@link SubscriptionManager#DATA_ROAMING_ENABLE} or
         * {@link SubscriptionManager#DATA_ROAMING_DISABLE}.
         */
        private int mDataRoaming = SubscriptionManager.DATA_ROAMING_DISABLE;

        /**
         * SIM icon bitmap cache.
         */
        @Nullable
        private Bitmap mIconBitmap = null;

        /**
         * The mobile country code.
         */
        @Nullable
        private String mMcc = null;

        /**
         * The mobile network code.
         */
        @Nullable
        private String mMnc = null;

        /**
         * EHPLMNs associated with the subscription.
         */
        @NonNull
        private String[] mEhplmns = new String[0];

        /**
         * HPLMNs associated with the subscription.
         */
        @NonNull
        private String[] mHplmns = new String[0];

        /**
         * The ISO Country code for the subscription's provider.
         */
        @NonNull
        private String mCountryIso = "";

        /**
         * Whether the subscription is from eSIM.
         */
        private boolean mIsEmbedded = false;

        /**
         * The native access rules for this subscription, if it is embedded and defines any. This
         * does not include access rules for non-embedded subscriptions.
         */
        @Nullable
        private UiccAccessRule[] mNativeAccessRules = null;

        /**
         * The card string of the SIM card.
         */
        @NonNull
        private String mCardString = "";

        /**
         * The card ID of the SIM card which contains the subscription.
         */
        private int mCardId = TelephonyManager.UNINITIALIZED_CARD_ID;

        /**
         * Whether the subscription is opportunistic or not.
         */
        private boolean mIsOpportunistic = false;

        /**
         * The group UUID of the subscription group.
         */
        @Nullable
        private ParcelUuid mGroupUuid = null;

        /**
         * Whether group of the subscription is disabled. This is only useful if it's a grouped
         * opportunistic subscription. In this case, if all primary (non-opportunistic)
         * subscriptions in the group are deactivated (unplugged pSIM or deactivated eSIM profile),
         * we should disable this opportunistic subscription.
         */
        private boolean mIsGroupDisabled = false;

        /**
         * The carrier id.
         *
         * @see TelephonyManager#getSimCarrierId()
         */
        private int mCarrierId = TelephonyManager.UNKNOWN_CARRIER_ID;

        /**
         * The profile class populated from the profile metadata if present. Otherwise, the profile
         * class defaults to {@link SubscriptionManager#PROFILE_CLASS_UNSET} if there is no profile
         * metadata or the subscription is not on an eUICC ({@link #isEmbedded} returns
         * {@code false}).
         */
        @ProfileClass
        private int mProfileClass = SubscriptionManager.PROFILE_CLASS_UNSET;

        /**
         * The subscription type.
         */
        @SubscriptionType
        private int mType = SubscriptionManager.SUBSCRIPTION_TYPE_LOCAL_SIM;

        /**
         * The owner package of group the subscription belongs to.
         */
        @NonNull
        private String mGroupOwner = "";

        /**
         * The carrier certificates for this subscription that are saved in carrier configs.
         * This does not include access rules from the Uicc, whether embedded or non-embedded.
         */
        @Nullable
        private UiccAccessRule[] mCarrierConfigAccessRules = null;

        /**
         * Whether Uicc applications are configured to enable or not.
         */
        private boolean mAreUiccApplicationsEnabled = true;

        /**
         * the port index of the Uicc card.
         */
        private int mPortIndex = TelephonyManager.INVALID_PORT_INDEX;

        /**
         * Subscription's preferred usage setting.
         */
        @UsageSetting
        private int mUsageSetting = SubscriptionManager.USAGE_SETTING_UNKNOWN;

        /**
         * {@code true} if it is a non-terrestrial network subscription, {@code false} otherwise.
         */
        private boolean mIsOnlyNonTerrestrialNetwork = false;

        private int mTransferStatus = 0;

        /**
         * Service capabilities bitmasks the subscription supports.
         */
        private int mServiceCapabilities = 0;

        /**
         * Default constructor.
         */
        public Builder() {
        }

        /**
         * Constructor from {@link SubscriptionInfo}.
         *
         * @param info The subscription info.
         */
        public Builder(@NonNull SubscriptionInfo info) {
            mId = info.mId;
            mIccId = info.mIccId;
            mSimSlotIndex = info.mSimSlotIndex;
            mDisplayName = info.mDisplayName;
            mCarrierName = info.mCarrierName;
            mDisplayNameSource = info.mDisplayNameSource;
            mIconTint = info.mIconTint;
            mNumber = info.mNumber;
            mDataRoaming = info.mDataRoaming;
            mIconBitmap = info.mIconBitmap;
            mMcc = info.mMcc;
            mMnc = info.mMnc;
            mEhplmns = info.mEhplmns;
            mHplmns = info.mHplmns;
            mCountryIso = info.mCountryIso;
            mIsEmbedded = info.mIsEmbedded;
            mNativeAccessRules = info.mNativeAccessRules;
            mCardString = info.mCardString;
            mCardId = info.mCardId;
            mIsOpportunistic = info.mIsOpportunistic;
            mGroupUuid = info.mGroupUuid;
            mIsGroupDisabled = info.mIsGroupDisabled;
            mCarrierId = info.mCarrierId;
            mProfileClass = info.mProfileClass;
            mType = info.mType;
            mGroupOwner = info.mGroupOwner;
            mCarrierConfigAccessRules = info.mCarrierConfigAccessRules;
            mAreUiccApplicationsEnabled = info.mAreUiccApplicationsEnabled;
            mPortIndex = info.mPortIndex;
            mUsageSetting = info.mUsageSetting;
            mIsOnlyNonTerrestrialNetwork = info.mIsOnlyNonTerrestrialNetwork;
            mServiceCapabilities = info.mServiceCapabilities;
            mTransferStatus = info.mTransferStatus;
        }

        /**
         * Set the subscription id.
         *
         * @param id The subscription id.
         * @return The builder.
         */
        @NonNull
        public Builder setId(int id) {
            mId = id;
            return this;
        }

        /**
         * Set the ICCID of the SIM that is associated with this subscription.
         *
         * @param iccId The ICCID of the SIM that is associated with this subscription.
         * @return The builder.
         */
        @NonNull
        public Builder setIccId(@Nullable String iccId) {
            mIccId = TextUtils.emptyIfNull(iccId);
            return this;
        }

        /**
         * Set the SIM index of the slot that currently contains the subscription. Set to
         * {@link SubscriptionManager#INVALID_SIM_SLOT_INDEX} if the subscription is inactive.
         *
         * @param simSlotIndex The SIM slot index.
         * @return The builder.
         */
        @NonNull
        public Builder setSimSlotIndex(int simSlotIndex) {
            mSimSlotIndex = simSlotIndex;
            return this;
        }

        /**
         * The name displayed to the user that identifies this subscription. This name is used
         * in Settings page and can be renamed by the user.
         *
         * @param displayName The display name.
         * @return The builder.
         */
        @NonNull
        public Builder setDisplayName(@Nullable CharSequence displayName) {
            mDisplayName = displayName == null ? "" : displayName;
            return this;
        }

        /**
         * The name displayed to the user that identifies subscription provider name. This name
         * is the SPN displayed in status bar and many other places. Can't be renamed by the user.
         *
         * @param carrierName The carrier name.
         * @return The builder.
         */
        @NonNull
        public Builder setCarrierName(@Nullable CharSequence carrierName) {
            mCarrierName = carrierName == null ? "" : carrierName;
            return this;
        }

        /**
         * Set the source of the display name.
         *
         * @param displayNameSource The source of the display name.
         * @return The builder.
         *
         * @see SubscriptionInfo#getDisplayName()
         */
        @NonNull
        public Builder setDisplayNameSource(@SimDisplayNameSource int displayNameSource) {
            mDisplayNameSource = displayNameSource;
            return this;
        }

        /**
         * Set the color to be used for tinting the icon when displaying to the user.
         *
         * @param iconTint The color to be used for tinting the icon when displaying to the user.
         * @return The builder.
         */
        @NonNull
        public Builder setIconTint(int iconTint) {
            mIconTint = iconTint;
            return this;
        }

        /**
         * Set the number presented to the user identify this subscription.
         *
         * @param number the number presented to the user identify this subscription.
         * @return The builder.
         */
        @NonNull
        public Builder setNumber(@Nullable String number) {
            mNumber = TextUtils.emptyIfNull(number);
            return this;
        }

        /**
         * Set whether user enables data roaming for this subscription or not.
         *
         * @param dataRoaming Data roaming mode. Either
         * {@link SubscriptionManager#DATA_ROAMING_ENABLE} or
         * {@link SubscriptionManager#DATA_ROAMING_DISABLE}
         * @return The builder.
         */
        @NonNull
        public Builder setDataRoaming(int dataRoaming) {
            mDataRoaming = dataRoaming;
            return this;
        }

        /**
         * Set SIM icon bitmap cache.
         *
         * @param iconBitmap SIM icon bitmap cache.
         * @return The builder.
         */
        @NonNull
        public Builder setIcon(@Nullable Bitmap iconBitmap) {
            mIconBitmap = iconBitmap;
            return this;
        }

        /**
         * Set the mobile country code.
         *
         * @param mcc The mobile country code.
         * @return The builder.
         */
        @NonNull
        public Builder setMcc(@Nullable String mcc) {
            mMcc = mcc;
            return this;
        }

        /**
         * Set the mobile network code.
         *
         * @param mnc Mobile network code.
         * @return The builder.
         */
        @NonNull
        public Builder setMnc(@Nullable String mnc) {
            mMnc = mnc;
            return this;
        }

        /**
         * Set EHPLMNs associated with the subscription.
         *
         * @param ehplmns EHPLMNs associated with the subscription.
         * @return The builder.
         */
        @NonNull
        public Builder setEhplmns(@Nullable String[] ehplmns) {
            mEhplmns = ehplmns == null ? new String[0] : ehplmns;
            return this;
        }

        /**
         * Set HPLMNs associated with the subscription.
         *
         * @param hplmns HPLMNs associated with the subscription.
         * @return The builder.
         */
        @NonNull
        public Builder setHplmns(@Nullable String[] hplmns) {
            mHplmns = hplmns == null ? new String[0] : hplmns;
            return this;
        }

        /**
         * Set the ISO country code for the subscription's provider.
         *
         * @param countryIso The ISO country code for the subscription's provider.
         * @return The builder.
         */
        @NonNull
        public Builder setCountryIso(@Nullable String countryIso) {
            mCountryIso = TextUtils.emptyIfNull(countryIso);
            return this;
        }

        /**
         * Set whether the subscription is from eSIM or not.
         *
         * @param isEmbedded {@code true} if the subscription is from eSIM.
         * @return The builder.
         */
        @NonNull
        public Builder setEmbedded(boolean isEmbedded) {
            mIsEmbedded = isEmbedded;
            return this;
        }

        /**
         * Set the native access rules for this subscription, if it is embedded and defines any.
         * This does not include access rules for non-embedded subscriptions.
         *
         * @param nativeAccessRules The native access rules for this subscription.
         * @return The builder.
         */
        @NonNull
        public Builder setNativeAccessRules(@Nullable UiccAccessRule[] nativeAccessRules) {
            mNativeAccessRules = nativeAccessRules;
            return this;
        }

        /**
         * Set the card string of the SIM card.
         *
         * @param cardString The card string of the SIM card.
         * @return The builder.
         *
         * @see #getCardString()
         */
        @NonNull
        public Builder setCardString(@Nullable String cardString) {
            mCardString = TextUtils.emptyIfNull(cardString);
            return this;
        }

        /**
         * Set the card ID of the SIM card which contains the subscription.
         *
         * @param cardId The card ID of the SIM card which contains the subscription.
         * @return The builder.
         */
        @NonNull
        public Builder setCardId(int cardId) {
            mCardId = cardId;
            return this;
        }

        /**
         * Set whether the subscription is opportunistic or not.
         *
         * @param isOpportunistic {@code true} if the subscription is opportunistic.
         * @return The builder.
         */
        @NonNull
        public Builder setOpportunistic(boolean isOpportunistic) {
            mIsOpportunistic = isOpportunistic;
            return this;
        }

        /**
         * Set the group UUID of the subscription group.
         *
         * @param groupUuid The group UUID.
         * @return The builder.
         *
         * @see #getGroupUuid()
         */
        @NonNull
        public Builder setGroupUuid(@Nullable String groupUuid) {
            mGroupUuid = TextUtils.isEmpty(groupUuid) ? null : ParcelUuid.fromString(groupUuid);
            return this;
        }

        /**
         * Whether group of the subscription is disabled. This is only useful if it's a grouped
         * opportunistic subscription. In this case, if all primary (non-opportunistic)
         * subscriptions in the group are deactivated (unplugged pSIM or deactivated eSIM profile),
         * we should disable this opportunistic subscription.
         *
         * @param isGroupDisabled {@code true} if group of the subscription is disabled.
         * @return The builder.
         */
        @NonNull
        public Builder setGroupDisabled(boolean isGroupDisabled) {
            mIsGroupDisabled = isGroupDisabled;
            return this;
        }

        /**
         * Set the subscription carrier id.
         *
         * @param carrierId The carrier id.
         * @return The builder
         *
         * @see TelephonyManager#getSimCarrierId()
         */
        @NonNull
        public Builder setCarrierId(int carrierId) {
            mCarrierId = carrierId;
            return this;
        }

        /**
         * Set the profile class populated from the profile metadata if present.
         *
         * @param profileClass the profile class populated from the profile metadata if present.
         * @return The builder
         *
         * @see #getProfileClass()
         */
        @NonNull
        public Builder setProfileClass(@ProfileClass int profileClass) {
            mProfileClass = profileClass;
            return this;
        }

        /**
         * Set the subscription type.
         *
         * @param type Subscription type.
         * @return The builder.
         */
        @NonNull
        public Builder setType(@SubscriptionType int type) {
            mType = type;
            return this;
        }

        /**
         * Set the owner package of group the subscription belongs to.
         *
         * @param groupOwner Owner package of group the subscription belongs to.
         * @return The builder.
         */
        @NonNull
        public Builder setGroupOwner(@Nullable String groupOwner) {
            mGroupOwner = TextUtils.emptyIfNull(groupOwner);
            return this;
        }

        /**
         * Set the carrier certificates for this subscription that are saved in carrier configs.
         * This does not include access rules from the Uicc, whether embedded or non-embedded.
         *
         * @param carrierConfigAccessRules The carrier certificates for this subscription.
         * @return The builder.
         */
        @NonNull
        public Builder setCarrierConfigAccessRules(
                @Nullable UiccAccessRule[] carrierConfigAccessRules) {
            mCarrierConfigAccessRules = carrierConfigAccessRules;
            return this;
        }

        /**
         * Set whether Uicc applications are configured to enable or not.
         *
         * @param uiccApplicationsEnabled {@code true} if Uicc applications are configured to
         * enable.
         * @return The builder.
         */
        @NonNull
        public Builder setUiccApplicationsEnabled(boolean uiccApplicationsEnabled) {
            mAreUiccApplicationsEnabled = uiccApplicationsEnabled;
            return this;
        }

        /**
         * Set the port index of the Uicc card.
         *
         * @param portIndex The port index of the Uicc card.
         * @return The builder.
         */
        @NonNull
        public Builder setPortIndex(int portIndex) {
            mPortIndex = portIndex;
            return this;
        }

        /**
         * Set subscription's preferred usage setting.
         *
         * @param usageSetting Subscription's preferred usage setting.
         * @return The builder.
         */
        @NonNull
        public Builder setUsageSetting(@UsageSetting int usageSetting) {
            mUsageSetting = usageSetting;
            return this;
        }

        /**
         * Set whether the subscription is exclusively used for non-terrestrial networks or not.
         *
         * @param isOnlyNonTerrestrialNetwork {@code true} if the subscription is for NTN,
         * {@code false} otherwise.
         * @return The builder.
         */
        @NonNull
        public Builder setOnlyNonTerrestrialNetwork(boolean isOnlyNonTerrestrialNetwork) {
            mIsOnlyNonTerrestrialNetwork = isOnlyNonTerrestrialNetwork;
            return this;
        }

        /**
         * Set the service capabilities that the subscription supports.
         *
         * @param capabilities Bitmask combination of SubscriptionManager
         *                     .SERVICE_CAPABILITY_XXX.
         * @return The builder.
         *
         * @throws IllegalArgumentException when any capability is not supported.
         */
        @NonNull
        @FlaggedApi(Flags.FLAG_DATA_ONLY_CELLULAR_SERVICE)
        public Builder setServiceCapabilities(
                @NonNull @SubscriptionManager.ServiceCapability Set<Integer> capabilities) {
            int combinedCapabilities = 0;
            for (int capability : capabilities) {
                if (capability < SubscriptionManager.SERVICE_CAPABILITY_VOICE
                        || capability > SubscriptionManager.SERVICE_CAPABILITY_MAX) {
                    throw new IllegalArgumentException(
                            "Invalid service capability value: " + capability);
                }
                combinedCapabilities |= SubscriptionManager.serviceCapabilityToBitmask(capability);
            }
            mServiceCapabilities = combinedCapabilities;
            return this;
        }
         /**
         * Set subscription's transfer status
         *
         * @param status Subscription's transfer status
         * @return The builder.
         */
        @FlaggedApi(Flags.FLAG_SUPPORT_PSIM_TO_ESIM_CONVERSION)
        @NonNull
        public Builder setTransferStatus(@TransferStatus int status) {
            mTransferStatus = status;
            return this;
        }

        /**
         * Build the {@link SubscriptionInfo}.
         *
         * @return The {@link SubscriptionInfo} instance.
         */
        public SubscriptionInfo build() {
            return new SubscriptionInfo(this);
        }
    }
}
