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