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