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.telephony.Rlog;
     23 import android.text.TextUtils;
     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 int getEfid() {
    142         return mEfid;
    143     }
    144 
    145     public int getRecId() {
    146         return mRecordNumber;
    147     }
    148 
    149     public String getNumber() {
    150         return mNumber;
    151     }
    152 
    153     public void setNumber(String number) {
    154         mNumber = number;
    155     }
    156 
    157     public String[] getEmails() {
    158         return mEmails;
    159     }
    160 
    161     public void setEmails(String[] emails) {
    162         this.mEmails = emails;
    163     }
    164 
    165     @Override
    166     public String toString() {
    167         return "ADN Record '" + mAlphaTag + "' '" + Rlog.pii(LOG_TAG, mNumber) + " "
    168                 + Rlog.pii(LOG_TAG, mEmails) + "'";
    169     }
    170 
    171     public boolean isEmpty() {
    172         return TextUtils.isEmpty(mAlphaTag) && TextUtils.isEmpty(mNumber) && mEmails == null;
    173     }
    174 
    175     public boolean hasExtendedRecord() {
    176         return mExtRecord != 0 && mExtRecord != 0xff;
    177     }
    178 
    179     /** Helper function for {@link #isEqual}. */
    180     private static boolean stringCompareNullEqualsEmpty(String s1, String s2) {
    181         if (s1 == s2) {
    182             return true;
    183         }
    184         if (s1 == null) {
    185             s1 = "";
    186         }
    187         if (s2 == null) {
    188             s2 = "";
    189         }
    190         return (s1.equals(s2));
    191     }
    192 
    193     public boolean isEqual(AdnRecord adn) {
    194         return ( stringCompareNullEqualsEmpty(mAlphaTag, adn.mAlphaTag) &&
    195                 stringCompareNullEqualsEmpty(mNumber, adn.mNumber) &&
    196                 Arrays.equals(mEmails, adn.mEmails));
    197     }
    198     //***** Parcelable Implementation
    199 
    200     @Override
    201     public int describeContents() {
    202         return 0;
    203     }
    204 
    205     @Override
    206     public void writeToParcel(Parcel dest, int flags) {
    207         dest.writeInt(mEfid);
    208         dest.writeInt(mRecordNumber);
    209         dest.writeString(mAlphaTag);
    210         dest.writeString(mNumber);
    211         dest.writeStringArray(mEmails);
    212     }
    213 
    214     /**
    215      * Build adn hex byte array based on record size
    216      * The format of byte array is defined in 51.011 10.5.1
    217      *
    218      * @param recordSize is the size X of EF record
    219      * @return hex byte[recordSize] to be written to EF record
    220      *          return null for wrong format of dialing number or tag
    221      */
    222     public byte[] buildAdnString(int recordSize) {
    223         byte[] bcdNumber;
    224         byte[] byteTag;
    225         byte[] adnString;
    226         int footerOffset = recordSize - FOOTER_SIZE_BYTES;
    227 
    228         // create an empty record
    229         adnString = new byte[recordSize];
    230         for (int i = 0; i < recordSize; i++) {
    231             adnString[i] = (byte) 0xFF;
    232         }
    233 
    234         if (TextUtils.isEmpty(mNumber)) {
    235             Rlog.w(LOG_TAG, "[buildAdnString] Empty dialing number");
    236             return adnString;   // return the empty record (for delete)
    237         } else if (mNumber.length()
    238                 > (ADN_DIALING_NUMBER_END - ADN_DIALING_NUMBER_START + 1) * 2) {
    239             Rlog.w(LOG_TAG,
    240                     "[buildAdnString] Max length of dialing number is 20");
    241             return null;
    242         }
    243 
    244         byteTag = !TextUtils.isEmpty(mAlphaTag) ? GsmAlphabet.stringToGsm8BitPacked(mAlphaTag)
    245                 : new byte[0];
    246 
    247         if (byteTag.length > footerOffset) {
    248             Rlog.w(LOG_TAG, "[buildAdnString] Max length of tag is " + footerOffset);
    249             return null;
    250         } else {
    251             bcdNumber = PhoneNumberUtils.numberToCalledPartyBCD(
    252                     mNumber, PhoneNumberUtils.BCD_EXTENDED_TYPE_EF_ADN);
    253 
    254             System.arraycopy(bcdNumber, 0, adnString,
    255                     footerOffset + ADN_TON_AND_NPI, bcdNumber.length);
    256 
    257             adnString[footerOffset + ADN_BCD_NUMBER_LENGTH]
    258                     = (byte) (bcdNumber.length);
    259             adnString[footerOffset + ADN_CAPABILITY_ID]
    260                     = (byte) 0xFF; // Capability Id
    261             adnString[footerOffset + ADN_EXTENSION_ID]
    262                     = (byte) 0xFF; // Extension Record Id
    263 
    264             if (byteTag.length > 0) {
    265                 System.arraycopy(byteTag, 0, adnString, 0, byteTag.length);
    266             }
    267 
    268             return adnString;
    269         }
    270     }
    271 
    272     /**
    273      * See TS 51.011 10.5.10
    274      */
    275     public void
    276     appendExtRecord (byte[] extRecord) {
    277         try {
    278             if (extRecord.length != EXT_RECORD_LENGTH_BYTES) {
    279                 return;
    280             }
    281 
    282             if ((extRecord[0] & EXT_RECORD_TYPE_MASK)
    283                     != EXT_RECORD_TYPE_ADDITIONAL_DATA) {
    284                 return;
    285             }
    286 
    287             if ((0xff & extRecord[1]) > MAX_EXT_CALLED_PARTY_LENGTH) {
    288                 // invalid or empty record
    289                 return;
    290             }
    291 
    292             mNumber += PhoneNumberUtils.calledPartyBCDFragmentToString(
    293                     extRecord,
    294                     2,
    295                     0xff & extRecord[1],
    296                     PhoneNumberUtils.BCD_EXTENDED_TYPE_EF_ADN);
    297 
    298             // We don't support ext record chaining.
    299 
    300         } catch (RuntimeException ex) {
    301             Rlog.w(LOG_TAG, "Error parsing AdnRecord ext record", ex);
    302         }
    303     }
    304 
    305     //***** Private Methods
    306 
    307     /**
    308      * alphaTag and number are set to null on invalid format
    309      */
    310     private void
    311     parseRecord(byte[] record) {
    312         try {
    313             mAlphaTag = IccUtils.adnStringFieldToString(
    314                             record, 0, record.length - FOOTER_SIZE_BYTES);
    315 
    316             int footerOffset = record.length - FOOTER_SIZE_BYTES;
    317 
    318             int numberLength = 0xff & record[footerOffset];
    319 
    320             if (numberLength > MAX_NUMBER_SIZE_BYTES) {
    321                 // Invalid number length
    322                 mNumber = "";
    323                 return;
    324             }
    325 
    326             // Please note 51.011 10.5.1:
    327             //
    328             // "If the Dialling Number/SSC String does not contain
    329             // a dialling number, e.g. a control string deactivating
    330             // a service, the TON/NPI byte shall be set to 'FF' by
    331             // the ME (see note 2)."
    332 
    333             mNumber = PhoneNumberUtils.calledPartyBCDToString(
    334                     record,
    335                     footerOffset + 1,
    336                     numberLength,
    337                     PhoneNumberUtils.BCD_EXTENDED_TYPE_EF_ADN);
    338 
    339 
    340             mExtRecord = 0xff & record[record.length - 1];
    341 
    342             mEmails = null;
    343 
    344         } catch (RuntimeException ex) {
    345             Rlog.w(LOG_TAG, "Error parsing AdnRecord", ex);
    346             mNumber = "";
    347             mAlphaTag = "";
    348             mEmails = null;
    349         }
    350     }
    351 }
    352