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