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