content: /*
   * 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 android.annotation.Nullable;
  import android.annotation.SystemApi;
  import android.annotation.UnsupportedAppUsage;
  import android.content.Context;
  import android.content.pm.PackageInfo;
  import android.content.pm.PackageManager;
  import android.graphics.Bitmap;
  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.Build;
  import android.os.Parcel;
  import android.os.ParcelUuid;
  import android.os.Parcelable;
  import android.text.TextUtils;
  import android.util.DisplayMetrics;
  import android.util.Log;
  
  import java.util.ArrayList;
  import java.util.Arrays;
  import java.util.Collections;
  import java.util.List;
  import java.util.Objects;
  
  /**
   * 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 int mId;
  
      /**
       * The GID for a SIM that maybe associated with this subscription, empty if unknown
       */
      private String mIccId;
  
      /**
       * The index of the slot that currently contains the subscription
       * and not necessarily unique and maybe INVALID_SLOT_ID if unknown
       */
      private int mSimSlotIndex;
  
      /**
       * The name displayed to the user that identifies this subscription
       */
      private CharSequence mDisplayName;
  
      /**
       * String that identifies SPN/PLMN
       * TODO : Add a new field that identifies only SPN for a sim
       */
      private CharSequence mCarrierName;
  
      /**
       * The subscription carrier id.
       * @see TelephonyManager#getSimCarrierId()
       */
      private int mCarrierId;
  
      /**
       * The source of the name, NAME_SOURCE_DEFAULT_SOURCE, NAME_SOURCE_SIM_SPN,
       * NAME_SOURCE_SIM_PNN, or NAME_SOURCE_USER_INPUT.
       */
      private int mNameSource;
  
      /**
       * The color to be used for tinting the icon when displaying to the user
       */
      private int mIconTint;
  
      /**
       * A number presented to the user identify this subscription
       */
      private String mNumber;
  
      /**
       * Data roaming state, DATA_ROAMING_ENABLE, DATA_ROAMING_DISABLE
       */
      private int mDataRoaming;
  
      /**
       * SIM Icon bitmap
       */
      private Bitmap mIconBitmap;
  
      /**
       * Mobile Country Code
       */
      private String mMcc;
  
      /**
       * Mobile Network Code
       */
      private String mMnc;
  
      /**
       * EHPLMNs associated with the subscription
       */
      private String[] mEhplmns;
  
      /**
       * HPLMNs associated with the subscription
       */
      private String[] mHplmns;
  
      /**
       * ISO Country code for the subscription's provider
       */
      private String mCountryIso;
  
      /**
       * Whether the subscription is an embedded one.
       */
      private boolean mIsEmbedded;
  
      /**
       * The access rules for this subscription, if it is embedded and defines any.
       */
      @Nullable
      private UiccAccessRule[] mNativeAccessRules;
  
      /**
       * The carrier certificates for this subscription that are saved in carrier configs.
       * The other carrier certificates are embedded on Uicc and stored as part of mNativeAccessRules.
       */
      @Nullable
      private UiccAccessRule[] mCarrierConfigAccessRules;
  
      /**
       * 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.
       */
      private String mCardString;
  
      /**
       * The card ID of the SIM card. This maps uniquely to the card string.
       */
      private int mCardId;
  
      /**
       * Whether the subscription is opportunistic.
       */
      private boolean mIsOpportunistic;
  
      /**
       * A UUID assigned to the subscription group. It returns null if not assigned.
       * Check {@link SubscriptionManager#createSubscriptionGroup(List)} for more details.
       */
      @Nullable
      private ParcelUuid mGroupUUID;
  
      /**
       * A package name that specifies who created the group. Null if mGroupUUID is null.
       */
      private String mGroupOwner;
  
      /**
       * 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;
  
      /**
       * Profile class, PROFILE_CLASS_TESTING, PROFILE_CLASS_OPERATIONAL
       * PROFILE_CLASS_PROVISIONING, or PROFILE_CLASS_UNSET.
       * A profile on the eUICC can be defined as test, operational, provisioning, or unset.
       * The profile class will be populated from the profile metadata if present. Otherwise,
       * the profile class defaults to unset if there is no profile metadata or the subscription
       * is not on an eUICC ({@link #isEmbedded} returns false).
       */
      private int mProfileClass;
  
      /**
       * Type of subscription
       */
      private int mSubscriptionType;
  
      /**
       * @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) {
          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_DEFAULT,
                  SubscriptionManager.SUBSCRIPTION_TYPE_LOCAL_SIM, null, null);
      }
  
      /**
       * @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,
              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);
      }
  
      /**
       * @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) {
          this.mId = id;
          this.mIccId = iccId;
          this.mSimSlotIndex = simSlotIndex;
          this.mDisplayName = displayName;
          this.mCarrierName = carrierName;
          this.mNameSource = nameSource;
          this.mIconTint = iconTint;
          this.mNumber = number;
          this.mDataRoaming = roaming;
          this.mIconBitmap = icon;
          this.mMcc = mcc;
          this.mMnc = mnc;
          this.mCountryIso = countryIso;
          this.mIsEmbedded = isEmbedded;
          this.mNativeAccessRules = nativeAccessRules;
          this.mCardString = 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.mSubscriptionType = subType;
          this.mGroupOwner = groupOwner;
          this.mCarrierConfigAccessRules = carrierConfigAccessRules;
      }
  
      /**
       * @return the subscription ID.
       */
      public int getSubscriptionId() {
          return this.mId;
      }
  
      /**
       * @return the ICC ID.
       */
      public String getIccId() {
          return this.mIccId;
      }
  
      /**
       * @return the slot index of this Subscription's SIM card.
       */
      public int getSimSlotIndex() {
          return this.mSimSlotIndex;
      }
  
      /**
       * @return the carrier id of this Subscription carrier.
       * @see TelephonyManager#getSimCarrierId()
       */
      public int getCarrierId() {
          return this.mCarrierId;
      }
  
      /**
       * @return the name displayed to the user that identifies this subscription
       */
      public CharSequence getDisplayName() {
          return this.mDisplayName;
      }
  
      /**
       * Sets the name displayed to the user that identifies this subscription
       * @hide
       */
      @UnsupportedAppUsage
      public void setDisplayName(CharSequence name) {
          this.mDisplayName = name;
      }
  
      /**
       * @return the name displayed to the user that identifies Subscription provider name
       */
      public CharSequence getCarrierName() {
          return this.mCarrierName;
      }
  
      /**
       * Sets the name displayed to the user that identifies Subscription provider name
       * @hide
       */
      public void setCarrierName(CharSequence name) {
          this.mCarrierName = name;
      }
  
      /**
       * @return the source of the name, eg NAME_SOURCE_DEFAULT_SOURCE, NAME_SOURCE_SIM_SPN or
       * NAME_SOURCE_USER_INPUT.
       * @hide
       */
      @UnsupportedAppUsage
      public int getNameSource() {
          return this.mNameSource;
      }
  
      /**
       * @hide
       */
      public void setAssociatedPlmns(String[] ehplmns, String[] hplmns) {
          mEhplmns = ehplmns;
          mHplmns = hplmns;
      }
  
      /**
       * 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) {
          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 = String.format("%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;
      }
  
      /**
       * Sets the color displayed to the user that identifies this subscription
       * @hide
       */
      @UnsupportedAppUsage
      public void setIconTint(int iconTint) {
          this.mIconTint = iconTint;
      }
  
      /**
       * @return the number of this subscription.
       */
      public String getNumber() {
          return mNumber;
      }
  
      /**
       * @return the data roaming state for this subscription, either
       * {@link SubscriptionManager#DATA_ROAMING_ENABLE} or {@link SubscriptionManager#DATA_ROAMING_DISABLE}.
       */
      public int getDataRoaming() {
          return this.mDataRoaming;
      }
  
      /**
       * @return the MCC.
       * @deprecated Use {@link #getMccString()} instead.
       */
      @Deprecated
      public int getMcc() {
          try {
              return this.mMcc == null ? 0 : Integer.valueOf(this.mMcc);
          } catch (NumberFormatException e) {
              Log.w(SubscriptionInfo.class.getSimpleName(), "MCC string is not a number");
              return 0;
          }
      }
  
      /**
       * @return the MNC.
       * @deprecated Use {@link #getMncString()} instead.
       */
      @Deprecated
      public int getMnc() {
          try {
              return this.mMnc == null ? 0 : Integer.valueOf(this.mMnc);
          } catch (NumberFormatException e) {
              Log.w(SubscriptionInfo.class.getSimpleName(), "MNC string is not a number");
              return 0;
          }
      }
  
      /**
       * @return The MCC, as a string.
       */
      public @Nullable String getMccString() {
          return this.mMcc;
      }
  
      /**
       * @return The MNC, as a string.
       */
      public @Nullable String getMncString() {
          return this.mMnc;
      }
  
      /**
       * @return the ISO country code
       */
      public String getCountryIso() {
          return this.mCountryIso;
      }
  
      /** @return whether the subscription is an eUICC one. */
      public boolean isEmbedded() {
          return this.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;
      }
  
      /**
       * 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
       * it will return null.
       */
      public @Nullable ParcelUuid getGroupUuid() {
          return mGroupUUID;
      }
  
      /**
       * @hide
       */
      public List<String> getEhplmns() {
          return mEhplmns == null ? Collections.emptyList() : Arrays.asList(mEhplmns);
      }
  
      /**
       * @hide
       */
      public List<String> getHplmns() {
          return mHplmns == null ? Collections.emptyList() : Arrays.asList(mHplmns);
      }
  
      /**
       * Return owner package of group the subscription belongs to.
       *
       * @hide
       */
      public @Nullable String getGroupOwner() {
          return mGroupOwner;
      }
  
      /**
       * @return the profile class of this subscription.
       * @hide
       */
      @SystemApi
      public @SubscriptionManager.ProfileClass int getProfileClass() {
          return this.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 subscription
       */
      public @SubscriptionManager.SubscriptionType int getSubscriptionType() {
          return mSubscriptionType;
      }
  
      /**
       * 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 = getAllAccessRules();
          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.
       * TODO and fix it properly in R / master: either deprecate this and have 3 APIs
       *  native + carrier + all, or have this return all by default.
       * @throws UnsupportedOperationException if this subscription is not embedded.
       * @hide
       */
      @SystemApi
      public @Nullable List<UiccAccessRule> getAccessRules() {
          if (mNativeAccessRules == null) return null;
          return Arrays.asList(mNativeAccessRules);
      }
  
      /**
       * @return the {@link UiccAccessRule}s that are both stored on Uicc and in carrierConfigs
       * dictating who is authorized to manage this subscription.
       * @hide
       */
      public @Nullable List<UiccAccessRule> getAllAccessRules() {
          List<UiccAccessRule> merged = new ArrayList<>();
          if (mNativeAccessRules != null) {
              merged.addAll(getAccessRules());
          }
          if (mCarrierConfigAccessRules != null) {
              merged.addAll(Arrays.asList(mCarrierConfigAccessRules));
          }
          return merged.isEmpty() ? null : merged;
      }
  
      /**
       * @return the card string of the SIM card which contains the subscription. The card string is
       * the ICCID for UICCs or the EID for eUICCs.
       * @hide
       * //TODO rename usages in LPA: UiccSlotUtil.java, UiccSlotsManager.java, UiccSlotInfoTest.java
       */
      public String getCardString() {
          return this.mCardString;
      }
  
      /**
       * Returns the card ID of the SIM card which contains the subscription (see
       * {@link UiccCardInfo#getCardId()}.
       * @return the cardId
       */
      public int getCardId() {
          return this.mCardId;
      }
  
      /**
       * Set whether the subscription's group is disabled.
       * @hide
       */
      public void setGroupDisabled(boolean isGroupDisabled) {
          this.mIsGroupDisabled = isGroupDisabled;
      }
  
      /**
       * Return whether the subscription's group is disabled.
       * @hide
       */
      public boolean isGroupDisabled() {
          return mIsGroupDisabled;
      }
  
      public static final @android.annotation.NonNull Parcelable.Creator<SubscriptionInfo> CREATOR = new Parcelable.Creator<SubscriptionInfo>() {
          @Override
          public SubscriptionInfo createFromParcel(Parcel source) {
              int id = source.readInt();
              String iccId = source.readString();
              int simSlotIndex = source.readInt();
              CharSequence displayName = source.readCharSequence();
              CharSequence carrierName = source.readCharSequence();
              int nameSource = source.readInt();
              int iconTint = source.readInt();
              String number = source.readString();
              int dataRoaming = source.readInt();
              String mcc = source.readString();
              String mnc = source.readString();
              String countryIso = source.readString();
              Bitmap iconBitmap = source.readParcelable(Bitmap.class.getClassLoader());
              boolean isEmbedded = source.readBoolean();
              UiccAccessRule[] nativeAccessRules = source.createTypedArray(UiccAccessRule.CREATOR);
              String cardString = source.readString();
              int cardId = source.readInt();
              boolean isOpportunistic = source.readBoolean();
              String groupUUID = source.readString();
              boolean isGroupDisabled = source.readBoolean();
              int carrierid = source.readInt();
              int profileClass = source.readInt();
              int subType = source.readInt();
              String[] ehplmns = source.readStringArray();
              String[] hplmns = source.readStringArray();
              String groupOwner = source.readString();
              UiccAccessRule[] carrierConfigAccessRules = source.createTypedArray(
                  UiccAccessRule.CREATOR);
  
              SubscriptionInfo info = new SubscriptionInfo(id, iccId, simSlotIndex, displayName,
                      carrierName, nameSource, iconTint, number, dataRoaming, iconBitmap, mcc, mnc,
                      countryIso, isEmbedded, nativeAccessRules, cardString, cardId, isOpportunistic,
                      groupUUID, isGroupDisabled, carrierid, profileClass, subType, groupOwner,
                      carrierConfigAccessRules);
              info.setAssociatedPlmns(ehplmns, hplmns);
              return info;
          }
  
          @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);
          dest.writeCharSequence(mDisplayName);
          dest.writeCharSequence(mCarrierName);
          dest.writeInt(mNameSource);
          dest.writeInt(mIconTint);
          dest.writeString(mNumber);
          dest.writeInt(mDataRoaming);
          dest.writeString(mMcc);
          dest.writeString(mMnc);
          dest.writeString(mCountryIso);
          dest.writeParcelable(mIconBitmap, flags);
          dest.writeBoolean(mIsEmbedded);
          dest.writeTypedArray(mNativeAccessRules, flags);
          dest.writeString(mCardString);
          dest.writeInt(mCardId);
          dest.writeBoolean(mIsOpportunistic);
          dest.writeString(mGroupUUID == null ? null : mGroupUUID.toString());
          dest.writeBoolean(mIsGroupDisabled);
          dest.writeInt(mCarrierId);
          dest.writeInt(mProfileClass);
          dest.writeInt(mSubscriptionType);
          dest.writeStringArray(mEhplmns);
          dest.writeStringArray(mHplmns);
          dest.writeString(mGroupOwner);
          dest.writeTypedArray(mCarrierConfigAccessRules, flags);
      }
  
      @Override
      public int describeContents() {
          return 0;
      }
  
      /**
       * @hide
       */
      public static String givePrintableIccid(String iccId) {
          String iccIdToPrint = null;
          if (iccId != null) {
              if (iccId.length() > 9 && !Build.IS_DEBUGGABLE) {
                  iccIdToPrint = iccId.substring(0, 9) + Rlog.pii(false, iccId.substring(9));
              } else {
                  iccIdToPrint = iccId;
              }
          }
          return iccIdToPrint;
      }
  
      @Override
      public String toString() {
          String iccIdToPrint = givePrintableIccid(mIccId);
          String cardStringToPrint = givePrintableIccid(mCardString);
          return "{id=" + mId + ", iccId=" + iccIdToPrint + " simSlotIndex=" + mSimSlotIndex
                  + " carrierId=" + mCarrierId + " displayName=" + mDisplayName
                  + " carrierName=" + mCarrierName + " nameSource=" + mNameSource
                  + " iconTint=" + mIconTint + " mNumber=" + Rlog.pii(Build.IS_DEBUGGABLE, mNumber)
                  + " dataRoaming=" + mDataRoaming + " iconBitmap=" + mIconBitmap + " mcc " + mMcc
                  + " mnc " + mMnc + "mCountryIso=" + mCountryIso + " isEmbedded " + mIsEmbedded
                  + " nativeAccessRules " + Arrays.toString(mNativeAccessRules)
                  + " cardString=" + cardStringToPrint + " cardId=" + mCardId
                  + " isOpportunistic " + mIsOpportunistic + " mGroupUUID=" + mGroupUUID
                  + " mIsGroupDisabled=" + mIsGroupDisabled
                  + " profileClass=" + mProfileClass
                  + " ehplmns = " + Arrays.toString(mEhplmns)
                  + " hplmns = " + Arrays.toString(mHplmns)
                  + " subscriptionType=" + mSubscriptionType
                  + " mGroupOwner=" + mGroupOwner
                  + " carrierConfigAccessRules=" + mCarrierConfigAccessRules + "}";
      }
  
      @Override
      public int hashCode() {
          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);
      }
  
      @Override
      public boolean equals(Object obj) {
          if (obj == null) return false;
          if (obj == this) return true;
  
          SubscriptionInfo toCompare;
          try {
              toCompare = (SubscriptionInfo) obj;
          } catch (ClassCastException ex) {
              return false;
          }
  
          return mId == toCompare.mId
                  && mSimSlotIndex == toCompare.mSimSlotIndex
                  && mNameSource == toCompare.mNameSource
                  && mIconTint == toCompare.mIconTint
                  && mDataRoaming == toCompare.mDataRoaming
                  && mIsEmbedded == toCompare.mIsEmbedded
                  && mIsOpportunistic == toCompare.mIsOpportunistic
                  && mIsGroupDisabled == toCompare.mIsGroupDisabled
                  && mCarrierId == toCompare.mCarrierId
                  && Objects.equals(mGroupUUID, toCompare.mGroupUUID)
                  && Objects.equals(mIccId, toCompare.mIccId)
                  && Objects.equals(mNumber, toCompare.mNumber)
                  && Objects.equals(mMcc, toCompare.mMcc)
                  && Objects.equals(mMnc, toCompare.mMnc)
                  && Objects.equals(mCountryIso, toCompare.mCountryIso)
                  && Objects.equals(mCardString, toCompare.mCardString)
                  && Objects.equals(mCardId, toCompare.mCardId)
                  && Objects.equals(mGroupOwner, toCompare.mGroupOwner)
                  && TextUtils.equals(mDisplayName, toCompare.mDisplayName)
                  && TextUtils.equals(mCarrierName, toCompare.mCarrierName)
                  && Arrays.equals(mNativeAccessRules, toCompare.mNativeAccessRules)
                  && mProfileClass == toCompare.mProfileClass
                  && Arrays.equals(mEhplmns, toCompare.mEhplmns)
                  && Arrays.equals(mHplmns, toCompare.mHplmns);
      }
  }
  