Home | History | Annotate | Download | only in uicc
      1 /*
      2  * Copyright (C) 2006 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.internal.telephony.uicc;
     18 
     19 import android.os.Parcel;
     20 import android.os.Parcelable;
     21 import android.telephony.PhoneNumberUtils;
     22 import android.text.TextUtils;
     23 import android.telephony.Rlog;
     24 
     25 import com.android.internal.telephony.GsmAlphabet;
     26 
     27 import java.util.Arrays;
     28 
     29 
     30 /**
     31  *
     32  * Used to load or store ADNs (Abbreviated Dialing Numbers).
     33  *
     34  * {@hide}
     35  *
     36  */
     37 public class AdnRecord implements Parcelable {
     38     static final String LOG_TAG = "AdnRecord";
     39 
     40     //***** Instance Variables
     41 
     42     String mAlphaTag = null;
     43     String mNumber = null;
     44     String[] mEmails;
     45     int mExtRecord = 0xff;
     46     int mEfid;                   // or 0 if none
     47     int mRecordNumber;           // or 0 if none
     48 
     49 
     50     //***** Constants
     51 
     52     // In an ADN record, everything but the alpha identifier
     53     // is in a footer that's 14 bytes
     54     static final int FOOTER_SIZE_BYTES = 14;
     55 
     56     // Maximum size of the un-extended number field
     57     static final int MAX_NUMBER_SIZE_BYTES = 11;
     58 
     59     static final int EXT_RECORD_LENGTH_BYTES = 13;
     60     static final int EXT_RECORD_TYPE_ADDITIONAL_DATA = 2;
     61     static final int EXT_RECORD_TYPE_MASK = 3;
     62     static final int MAX_EXT_CALLED_PARTY_LENGTH = 0xa;
     63 
     64     // ADN offset
     65     static final int ADN_BCD_NUMBER_LENGTH = 0;
     66     static final int ADN_TON_AND_NPI = 1;
     67     static final int ADN_DIALING_NUMBER_START = 2;
     68     static final int ADN_DIALING_NUMBER_END = 11;
     69     static final int ADN_CAPABILITY_ID = 12;
     70     static final int ADN_EXTENSION_ID = 13;
     71 
     72     //***** Static Methods
     73 
     74     public static final Parcelable.Creator<AdnRecord> CREATOR
     75             = new Parcelable.Creator<AdnRecord>() {
     76         @Override
     77         public AdnRecord createFromParcel(Parcel source) {
     78             int efid;
     79             int recordNumber;
     80             String alphaTag;
     81             String number;
     82             String[] emails;
     83 
     84             efid = source.readInt();
     85             recordNumber = source.readInt();
     86             alphaTag = source.readString();
     87             number = source.readString();
     88             emails = source.readStringArray();
     89 
     90             return new AdnRecord(efid, recordNumber, alphaTag, number, emails);
     91         }
     92 
     93         @Override
     94         public AdnRecord[] newArray(int size) {
     95             return new AdnRecord[size];
     96         }
     97     };
     98 
     99 
    100     //***** Constructor
    101     public AdnRecord (byte[] record) {
    102         this(0, 0, record);
    103     }
    104 
    105     public AdnRecord (int efid, int recordNumber, byte[] record) {
    106         this.mEfid = efid;
    107         this.mRecordNumber = recordNumber;
    108         parseRecord(record);
    109     }
    110 
    111     public AdnRecord (String alphaTag, String number) {
    112         this(0, 0, alphaTag, number);
    113     }
    114 
    115     public AdnRecord (String alphaTag, String number, String[] emails) {
    116         this(0, 0, alphaTag, number, emails);
    117     }
    118 
    119     public AdnRecord (int efid, int recordNumber, String alphaTag, String number, String[] emails) {
    120         this.mEfid = efid;
    121         this.mRecordNumber = recordNumber;
    122         this.mAlphaTag = alphaTag;
    123         this.mNumber = number;
    124         this.mEmails = emails;
    125     }
    126 
    127     public AdnRecord(int efid, int recordNumber, String alphaTag, String number) {
    128         this.mEfid = efid;
    129         this.mRecordNumber = recordNumber;
    130         this.mAlphaTag = alphaTag;
    131         this.mNumber = number;
    132         this.mEmails = null;
    133     }
    134 
    135     //***** Instance Methods
    136 
    137     public String getAlphaTag() {
    138         return mAlphaTag;
    139     }
    140 
    141     public String getNumber() {
    142         return mNumber;
    143     }
    144 
    145     public String[] getEmails() {
    146         return mEmails;
    147     }
    148 
    149     public void setEmails(String[] emails) {
    150         this.mEmails = emails;
    151     }
    152 
    153     @Override
    154     public String toString() {
    155         return "ADN Record '" + mAlphaTag + "' '" + mNumber + " " + mEmails + "'";
    156     }
    157 
    158     public boolean isEmpty() {
    159         return TextUtils.isEmpty(mAlphaTag) && TextUtils.isEmpty(mNumber) && mEmails == null;
    160     }
    161 
    162     public boolean hasExtendedRecord() {
    163         return mExtRecord != 0 && mExtRecord != 0xff;
    164     }
    165 
    166     /** Helper function for {@link #isEqual}. */
    167     private static boolean stringCompareNullEqualsEmpty(String s1, String s2) {
    168         if (s1 == s2) {
    169             return true;
    170         }
    171         if (s1 == null) {
    172             s1 = "";
    173         }
    174         if (s2 == null) {
    175             s2 = "";
    176         }
    177         return (s1.equals(s2));
    178     }
    179 
    180     public boolean isEqual(AdnRecord adn) {
    181         return ( stringCompareNullEqualsEmpty(mAlphaTag, adn.mAlphaTag) &&
    182                 stringCompareNullEqualsEmpty(mNumber, adn.mNumber) &&
    183                 Arrays.equals(mEmails, adn.mEmails));
    184     }
    185     //***** Parcelable Implementation
    186 
    187     @Override
    188     public int describeContents() {
    189         return 0;
    190     }
    191 
    192     @Override
    193     public void writeToParcel(Parcel dest, int flags) {
    194         dest.writeInt(mEfid);
    195         dest.writeInt(mRecordNumber);
    196         dest.writeString(mAlphaTag);
    197         dest.writeString(mNumber);
    198         dest.writeStringArray(mEmails);
    199     }
    200 
    201     /**
    202      * Build adn hex byte array based on record size
    203      * The format of byte array is defined in 51.011 10.5.1
    204      *
    205      * @param recordSize is the size X of EF record
    206      * @return hex byte[recordSize] to be written to EF record
    207      *          return null for wrong format of dialing number or tag
    208      */
    209     public byte[] buildAdnString(int recordSize) {
    210         byte[] bcdNumber;
    211         byte[] byteTag;
    212         byte[] adnString;
    213         int footerOffset = recordSize - FOOTER_SIZE_BYTES;
    214 
    215         // create an empty record
    216         adnString = new byte[recordSize];
    217         for (int i = 0; i < recordSize; i++) {
    218             adnString[i] = (byte) 0xFF;
    219         }
    220 
    221         if (TextUtils.isEmpty(mNumber)) {
    222             Rlog.w(LOG_TAG, "[buildAdnString] Empty dialing number");
    223             return adnString;   // return the empty record (for delete)
    224         } else if (mNumber.length()
    225                 > (ADN_DIALING_NUMBER_END - ADN_DIALING_NUMBER_START + 1) * 2) {
    226             Rlog.w(LOG_TAG,
    227                     "[buildAdnString] Max length of dialing number is 20");
    228             return null;
    229         } else if (mAlphaTag != null && mAlphaTag.length() > footerOffset) {
    230             Rlog.w(LOG_TAG,
    231                     "[buildAdnString] Max length of tag is " + footerOffset);
    232             return null;
    233         } else {
    234             bcdNumber = PhoneNumberUtils.numberToCalledPartyBCD(mNumber);
    235 
    236             System.arraycopy(bcdNumber, 0, adnString,
    237                     footerOffset + ADN_TON_AND_NPI, bcdNumber.length);
    238 
    239             adnString[footerOffset + ADN_BCD_NUMBER_LENGTH]
    240                     = (byte) (bcdNumber.length);
    241             adnString[footerOffset + ADN_CAPABILITY_ID]
    242                     = (byte) 0xFF; // Capability Id
    243             adnString[footerOffset + ADN_EXTENSION_ID]
    244                     = (byte) 0xFF; // Extension Record Id
    245 
    246             if (!TextUtils.isEmpty(mAlphaTag)) {
    247                 byteTag = GsmAlphabet.stringToGsm8BitPacked(mAlphaTag);
    248                 System.arraycopy(byteTag, 0, adnString, 0, byteTag.length);
    249             }
    250 
    251             return adnString;
    252         }
    253     }
    254 
    255     /**
    256      * See TS 51.011 10.5.10
    257      */
    258     public void
    259     appendExtRecord (byte[] extRecord) {
    260         try {
    261             if (extRecord.length != EXT_RECORD_LENGTH_BYTES) {
    262                 return;
    263             }
    264 
    265             if ((extRecord[0] & EXT_RECORD_TYPE_MASK)
    266                     != EXT_RECORD_TYPE_ADDITIONAL_DATA) {
    267                 return;
    268             }
    269 
    270             if ((0xff & extRecord[1]) > MAX_EXT_CALLED_PARTY_LENGTH) {
    271                 // invalid or empty record
    272                 return;
    273             }
    274 
    275             mNumber += PhoneNumberUtils.calledPartyBCDFragmentToString(
    276                                         extRecord, 2, 0xff & extRecord[1]);
    277 
    278             // We don't support ext record chaining.
    279 
    280         } catch (RuntimeException ex) {
    281             Rlog.w(LOG_TAG, "Error parsing AdnRecord ext record", ex);
    282         }
    283     }
    284 
    285     //***** Private Methods
    286 
    287     /**
    288      * alphaTag and number are set to null on invalid format
    289      */
    290     private void
    291     parseRecord(byte[] record) {
    292         try {
    293             mAlphaTag = IccUtils.adnStringFieldToString(
    294                             record, 0, record.length - FOOTER_SIZE_BYTES);
    295 
    296             int footerOffset = record.length - FOOTER_SIZE_BYTES;
    297 
    298             int numberLength = 0xff & record[footerOffset];
    299 
    300             if (numberLength > MAX_NUMBER_SIZE_BYTES) {
    301                 // Invalid number length
    302                 mNumber = "";
    303                 return;
    304             }
    305 
    306             // Please note 51.011 10.5.1:
    307             //
    308             // "If the Dialling Number/SSC String does not contain
    309             // a dialling number, e.g. a control string deactivating
    310             // a service, the TON/NPI byte shall be set to 'FF' by
    311             // the ME (see note 2)."
    312 
    313             mNumber = PhoneNumberUtils.calledPartyBCDToString(
    314                             record, footerOffset + 1, numberLength);
    315 
    316 
    317             mExtRecord = 0xff & record[record.length - 1];
    318 
    319             mEmails = null;
    320 
    321         } catch (RuntimeException ex) {
    322             Rlog.w(LOG_TAG, "Error parsing AdnRecord", ex);
    323             mNumber = "";
    324             mAlphaTag = "";
    325             mEmails = null;
    326         }
    327     }
    328 }
    329