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