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 AdnRecord(byte[] record)101 public AdnRecord (byte[] record) { 102 this(0, 0, record); 103 } 104 AdnRecord(int efid, int recordNumber, byte[] record)105 public AdnRecord (int efid, int recordNumber, byte[] record) { 106 this.mEfid = efid; 107 this.mRecordNumber = recordNumber; 108 parseRecord(record); 109 } 110 AdnRecord(String alphaTag, String number)111 public AdnRecord (String alphaTag, String number) { 112 this(0, 0, alphaTag, number); 113 } 114 AdnRecord(String alphaTag, String number, String[] emails)115 public AdnRecord (String alphaTag, String number, String[] emails) { 116 this(0, 0, alphaTag, number, emails); 117 } 118 AdnRecord(int efid, int recordNumber, String alphaTag, String number, String[] emails)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 AdnRecord(int efid, int recordNumber, String alphaTag, String number)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 getAlphaTag()137 public String getAlphaTag() { 138 return mAlphaTag; 139 } 140 getEfid()141 public int getEfid() { 142 return mEfid; 143 } 144 getRecId()145 public int getRecId() { 146 return mRecordNumber; 147 } 148 getNumber()149 public String getNumber() { 150 return mNumber; 151 } 152 setNumber(String number)153 public void setNumber(String number) { 154 mNumber = number; 155 } 156 getEmails()157 public String[] getEmails() { 158 return mEmails; 159 } 160 setEmails(String[] emails)161 public void setEmails(String[] emails) { 162 this.mEmails = emails; 163 } 164 165 @Override toString()166 public String toString() { 167 return "ADN Record '" + mAlphaTag + "' '" + Rlog.pii(LOG_TAG, mNumber) + " " 168 + Rlog.pii(LOG_TAG, mEmails) + "'"; 169 } 170 isEmpty()171 public boolean isEmpty() { 172 return TextUtils.isEmpty(mAlphaTag) && TextUtils.isEmpty(mNumber) && mEmails == null; 173 } 174 hasExtendedRecord()175 public boolean hasExtendedRecord() { 176 return mExtRecord != 0 && mExtRecord != 0xff; 177 } 178 179 /** Helper function for {@link #isEqual}. */ stringCompareNullEqualsEmpty(String s1, String s2)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 isEqual(AdnRecord adn)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 describeContents()201 public int describeContents() { 202 return 0; 203 } 204 205 @Override writeToParcel(Parcel dest, int flags)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 */ buildAdnString(int recordSize)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 appendExtRecord(byte[] extRecord)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 parseRecord(byte[] record)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