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