content: /*
   * Copyright (C) 2006 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 com.android.internal.telephony.uicc;
  
  import android.annotation.UnsupportedAppUsage;
  import android.os.Parcel;
  import android.os.Parcelable;
  import android.telephony.PhoneNumberUtils;
  import android.telephony.Rlog;
  import android.text.TextUtils;
  
  import com.android.internal.telephony.GsmAlphabet;
  
  import java.util.Arrays;
  
  
  /**
   *
   * Used to load or store ADNs (Abbreviated Dialing Numbers).
   *
   * {@hide}
   *
   */
  public class AdnRecord implements Parcelable {
      static final String LOG_TAG = "AdnRecord";
  
      //***** Instance Variables
  
      @UnsupportedAppUsage
      String mAlphaTag = null;
      @UnsupportedAppUsage
      String mNumber = null;
      @UnsupportedAppUsage
      String[] mEmails;
      @UnsupportedAppUsage
      int mExtRecord = 0xff;
      @UnsupportedAppUsage
      int mEfid;                   // or 0 if none
      @UnsupportedAppUsage
      int mRecordNumber;           // or 0 if none
  
  
      //***** Constants
  
      // In an ADN record, everything but the alpha identifier
      // is in a footer that's 14 bytes
      static final int FOOTER_SIZE_BYTES = 14;
  
      // Maximum size of the un-extended number field
      static final int MAX_NUMBER_SIZE_BYTES = 11;
  
      static final int EXT_RECORD_LENGTH_BYTES = 13;
      static final int EXT_RECORD_TYPE_ADDITIONAL_DATA = 2;
      static final int EXT_RECORD_TYPE_MASK = 3;
      static final int MAX_EXT_CALLED_PARTY_LENGTH = 0xa;
  
      // ADN offset
      static final int ADN_BCD_NUMBER_LENGTH = 0;
      static final int ADN_TON_AND_NPI = 1;
      static final int ADN_DIALING_NUMBER_START = 2;
      static final int ADN_DIALING_NUMBER_END = 11;
      static final int ADN_CAPABILITY_ID = 12;
      static final int ADN_EXTENSION_ID = 13;
  
      //***** Static Methods
  
      @UnsupportedAppUsage
      public static final Parcelable.Creator<AdnRecord> CREATOR
              = new Parcelable.Creator<AdnRecord>() {
          @Override
          public AdnRecord createFromParcel(Parcel source) {
              int efid;
              int recordNumber;
              String alphaTag;
              String number;
              String[] emails;
  
              efid = source.readInt();
              recordNumber = source.readInt();
              alphaTag = source.readString();
              number = source.readString();
              emails = source.readStringArray();
  
              return new AdnRecord(efid, recordNumber, alphaTag, number, emails);
          }
  
          @Override
          public AdnRecord[] newArray(int size) {
              return new AdnRecord[size];
          }
      };
  
  
      //***** Constructor
      @UnsupportedAppUsage
      public AdnRecord (byte[] record) {
          this(0, 0, record);
      }
  
      @UnsupportedAppUsage
      public AdnRecord (int efid, int recordNumber, byte[] record) {
          this.mEfid = efid;
          this.mRecordNumber = recordNumber;
          parseRecord(record);
      }
  
      @UnsupportedAppUsage
      public AdnRecord (String alphaTag, String number) {
          this(0, 0, alphaTag, number);
      }
  
      @UnsupportedAppUsage
      public AdnRecord (String alphaTag, String number, String[] emails) {
          this(0, 0, alphaTag, number, emails);
      }
  
      @UnsupportedAppUsage
      public AdnRecord (int efid, int recordNumber, String alphaTag, String number, String[] emails) {
          this.mEfid = efid;
          this.mRecordNumber = recordNumber;
          this.mAlphaTag = alphaTag;
          this.mNumber = number;
          this.mEmails = emails;
      }
  
      @UnsupportedAppUsage
      public AdnRecord(int efid, int recordNumber, String alphaTag, String number) {
          this.mEfid = efid;
          this.mRecordNumber = recordNumber;
          this.mAlphaTag = alphaTag;
          this.mNumber = number;
          this.mEmails = null;
      }
  
      //***** Instance Methods
  
      public String getAlphaTag() {
          return mAlphaTag;
      }
  
      public int getEfid() {
          return mEfid;
      }
  
      public int getRecId() {
          return mRecordNumber;
      }
  
      @UnsupportedAppUsage
      public String getNumber() {
          return mNumber;
      }
  
      public void setNumber(String number) {
          mNumber = number;
      }
  
      @UnsupportedAppUsage
      public String[] getEmails() {
          return mEmails;
      }
  
      @UnsupportedAppUsage
      public void setEmails(String[] emails) {
          this.mEmails = emails;
      }
  
      @Override
      public String toString() {
          return "ADN Record '" + mAlphaTag + "' '" + Rlog.pii(LOG_TAG, mNumber) + " "
                  + Rlog.pii(LOG_TAG, mEmails) + "'";
      }
  
      @UnsupportedAppUsage
      public boolean isEmpty() {
          return TextUtils.isEmpty(mAlphaTag) && TextUtils.isEmpty(mNumber) && mEmails == null;
      }
  
      public boolean hasExtendedRecord() {
          return mExtRecord != 0 && mExtRecord != 0xff;
      }
  
      /** Helper function for {@link #isEqual}. */
      private static boolean stringCompareNullEqualsEmpty(String s1, String s2) {
          if (s1 == s2) {
              return true;
          }
          if (s1 == null) {
              s1 = "";
          }
          if (s2 == null) {
              s2 = "";
          }
          return (s1.equals(s2));
      }
  
      public boolean isEqual(AdnRecord adn) {
          return ( stringCompareNullEqualsEmpty(mAlphaTag, adn.mAlphaTag) &&
                  stringCompareNullEqualsEmpty(mNumber, adn.mNumber) &&
                  Arrays.equals(mEmails, adn.mEmails));
      }
      //***** Parcelable Implementation
  
      @Override
      public int describeContents() {
          return 0;
      }
  
      @Override
      public void writeToParcel(Parcel dest, int flags) {
          dest.writeInt(mEfid);
          dest.writeInt(mRecordNumber);
          dest.writeString(mAlphaTag);
          dest.writeString(mNumber);
          dest.writeStringArray(mEmails);
      }
  
      /**
       * Build adn hex byte array based on record size
       * The format of byte array is defined in 51.011 10.5.1
       *
       * @param recordSize is the size X of EF record
       * @return hex byte[recordSize] to be written to EF record
       *          return null for wrong format of dialing number or tag
       */
      @UnsupportedAppUsage
      public byte[] buildAdnString(int recordSize) {
          byte[] bcdNumber;
          byte[] byteTag;
          byte[] adnString;
          int footerOffset = recordSize - FOOTER_SIZE_BYTES;
  
          // create an empty record
          adnString = new byte[recordSize];
          for (int i = 0; i < recordSize; i++) {
              adnString[i] = (byte) 0xFF;
          }
  
          if (TextUtils.isEmpty(mNumber)) {
              Rlog.w(LOG_TAG, "[buildAdnString] Empty dialing number");
              return adnString;   // return the empty record (for delete)
          } else if (mNumber.length()
                  > (ADN_DIALING_NUMBER_END - ADN_DIALING_NUMBER_START + 1) * 2) {
              Rlog.w(LOG_TAG,
                      "[buildAdnString] Max length of dialing number is 20");
              return null;
          }
  
          byteTag = !TextUtils.isEmpty(mAlphaTag) ? GsmAlphabet.stringToGsm8BitPacked(mAlphaTag)
                  : new byte[0];
  
          if (byteTag.length > footerOffset) {
              Rlog.w(LOG_TAG, "[buildAdnString] Max length of tag is " + footerOffset);
              return null;
          } else {
              bcdNumber = PhoneNumberUtils.numberToCalledPartyBCD(
                      mNumber, PhoneNumberUtils.BCD_EXTENDED_TYPE_EF_ADN);
  
              System.arraycopy(bcdNumber, 0, adnString,
                      footerOffset + ADN_TON_AND_NPI, bcdNumber.length);
  
              adnString[footerOffset + ADN_BCD_NUMBER_LENGTH]
                      = (byte) (bcdNumber.length);
              adnString[footerOffset + ADN_CAPABILITY_ID]
                      = (byte) 0xFF; // Capability Id
              adnString[footerOffset + ADN_EXTENSION_ID]
                      = (byte) 0xFF; // Extension Record Id
  
              if (byteTag.length > 0) {
                  System.arraycopy(byteTag, 0, adnString, 0, byteTag.length);
              }
  
              return adnString;
          }
      }
  
      /**
       * See TS 51.011 10.5.10
       */
      public void
      appendExtRecord (byte[] extRecord) {
          try {
              if (extRecord.length != EXT_RECORD_LENGTH_BYTES) {
                  return;
              }
  
              if ((extRecord[0] & EXT_RECORD_TYPE_MASK)
                      != EXT_RECORD_TYPE_ADDITIONAL_DATA) {
                  return;
              }
  
              if ((0xff & extRecord[1]) > MAX_EXT_CALLED_PARTY_LENGTH) {
                  // invalid or empty record
                  return;
              }
  
              mNumber += PhoneNumberUtils.calledPartyBCDFragmentToString(
                      extRecord,
                      2,
                      0xff & extRecord[1],
                      PhoneNumberUtils.BCD_EXTENDED_TYPE_EF_ADN);
  
              // We don't support ext record chaining.
  
          } catch (RuntimeException ex) {
              Rlog.w(LOG_TAG, "Error parsing AdnRecord ext record", ex);
          }
      }
  
      //***** Private Methods
  
      /**
       * alphaTag and number are set to null on invalid format
       */
      private void
      parseRecord(byte[] record) {
          try {
              mAlphaTag = IccUtils.adnStringFieldToString(
                              record, 0, record.length - FOOTER_SIZE_BYTES);
  
              int footerOffset = record.length - FOOTER_SIZE_BYTES;
  
              int numberLength = 0xff & record[footerOffset];
  
              if (numberLength > MAX_NUMBER_SIZE_BYTES) {
                  // Invalid number length
                  mNumber = "";
                  return;
              }
  
              // Please note 51.011 10.5.1:
              //
              // "If the Dialling Number/SSC String does not contain
              // a dialling number, e.g. a control string deactivating
              // a service, the TON/NPI byte shall be set to 'FF' by
              // the ME (see note 2)."
  
              mNumber = PhoneNumberUtils.calledPartyBCDToString(
                      record,
                      footerOffset + 1,
                      numberLength,
                      PhoneNumberUtils.BCD_EXTENDED_TYPE_EF_ADN);
  
  
              mExtRecord = 0xff & record[record.length - 1];
  
              mEmails = null;
  
          } catch (RuntimeException ex) {
              Rlog.w(LOG_TAG, "Error parsing AdnRecord", ex);
              mNumber = "";
              mAlphaTag = "";
              mEmails = null;
          }
      }
  }
  