PhoneNumberUtils.java revision 60d45f0f0320801a16db2ad038453c098e98966c
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 android.telephony; 18 19import android.content.Context; 20import android.content.Intent; 21import android.database.Cursor; 22import android.net.Uri; 23import android.os.SystemProperties; 24import android.provider.Contacts; 25import android.provider.ContactsContract; 26import android.text.Editable; 27import android.text.SpannableStringBuilder; 28import android.text.TextUtils; 29import android.util.Log; 30import android.util.SparseIntArray; 31 32import static com.android.internal.telephony.TelephonyProperties.PROPERTY_ICC_OPERATOR_ISO_COUNTRY; 33import static com.android.internal.telephony.TelephonyProperties.PROPERTY_IDP_STRING; 34import static com.android.internal.telephony.TelephonyProperties.PROPERTY_OPERATOR_ISO_COUNTRY; 35 36import java.util.Locale; 37import java.util.regex.Matcher; 38import java.util.regex.Pattern; 39 40/** 41 * Various utilities for dealing with phone number strings. 42 */ 43public class PhoneNumberUtils 44{ 45 /* 46 * Special characters 47 * 48 * (See "What is a phone number?" doc) 49 * 'p' --- GSM pause character, same as comma 50 * 'n' --- GSM wild character 51 * 'w' --- GSM wait character 52 */ 53 public static final char PAUSE = ','; 54 public static final char WAIT = ';'; 55 public static final char WILD = 'N'; 56 57 /* 58 * TOA = TON + NPI 59 * See TS 24.008 section 10.5.4.7 for details. 60 * These are the only really useful TOA values 61 */ 62 public static final int TOA_International = 0x91; 63 public static final int TOA_Unknown = 0x81; 64 65 static final String LOG_TAG = "PhoneNumberUtils"; 66 private static final boolean DBG = false; 67 68 /* 69 * global-phone-number = ["+"] 1*( DIGIT / written-sep ) 70 * written-sep = ("-"/".") 71 */ 72 private static final Pattern GLOBAL_PHONE_NUMBER_PATTERN = 73 Pattern.compile("[\\+]?[0-9.-]+"); 74 75 /** True if c is ISO-LATIN characters 0-9 */ 76 public static boolean 77 isISODigit (char c) { 78 return c >= '0' && c <= '9'; 79 } 80 81 /** True if c is ISO-LATIN characters 0-9, *, # */ 82 public final static boolean 83 is12Key(char c) { 84 return (c >= '0' && c <= '9') || c == '*' || c == '#'; 85 } 86 87 /** True if c is ISO-LATIN characters 0-9, *, # , +, WILD */ 88 public final static boolean 89 isDialable(char c) { 90 return (c >= '0' && c <= '9') || c == '*' || c == '#' || c == '+' || c == WILD; 91 } 92 93 /** True if c is ISO-LATIN characters 0-9, *, # , + (no WILD) */ 94 public final static boolean 95 isReallyDialable(char c) { 96 return (c >= '0' && c <= '9') || c == '*' || c == '#' || c == '+'; 97 } 98 99 /** True if c is ISO-LATIN characters 0-9, *, # , +, WILD, WAIT, PAUSE */ 100 public final static boolean 101 isNonSeparator(char c) { 102 return (c >= '0' && c <= '9') || c == '*' || c == '#' || c == '+' 103 || c == WILD || c == WAIT || c == PAUSE; 104 } 105 106 /** This any anything to the right of this char is part of the 107 * post-dial string (eg this is PAUSE or WAIT) 108 */ 109 public final static boolean 110 isStartsPostDial (char c) { 111 return c == PAUSE || c == WAIT; 112 } 113 114 /** Extracts the phone number from an Intent. 115 * 116 * @param intent the intent to get the number of 117 * @param context a context to use for database access 118 * 119 * @return the phone number that would be called by the intent, or 120 * <code>null</code> if the number cannot be found. 121 */ 122 public static String getNumberFromIntent(Intent intent, Context context) { 123 String number = null; 124 125 Uri uri = intent.getData(); 126 String scheme = uri.getScheme(); 127 128 if (scheme.equals("tel")) { 129 return uri.getSchemeSpecificPart(); 130 } 131 132 // TODO: We don't check for SecurityException here (requires 133 // READ_PHONE_STATE permission). 134 if (scheme.equals("voicemail")) { 135 return TelephonyManager.getDefault().getVoiceMailNumber(); 136 } 137 138 if (context == null) { 139 return null; 140 } 141 142 String type = intent.resolveType(context); 143 String phoneColumn = null; 144 145 // Correctly read out the phone entry based on requested provider 146 final String authority = uri.getAuthority(); 147 if (Contacts.AUTHORITY.equals(authority)) { 148 phoneColumn = Contacts.People.Phones.NUMBER; 149 } else if (ContactsContract.AUTHORITY.equals(authority)) { 150 phoneColumn = ContactsContract.CommonDataKinds.Phone.NUMBER; 151 } 152 153 final Cursor c = context.getContentResolver().query(uri, new String[] { 154 phoneColumn 155 }, null, null, null); 156 if (c != null) { 157 try { 158 if (c.moveToFirst()) { 159 number = c.getString(c.getColumnIndex(phoneColumn)); 160 } 161 } finally { 162 c.close(); 163 } 164 } 165 166 return number; 167 } 168 169 /** Extracts the network address portion and canonicalizes 170 * (filters out separators.) 171 * Network address portion is everything up to DTMF control digit 172 * separators (pause or wait), but without non-dialable characters. 173 * 174 * Please note that the GSM wild character is allowed in the result. 175 * This must be resolved before dialing. 176 * 177 * Allows + only in the first position in the result string. 178 * 179 * Returns null if phoneNumber == null 180 */ 181 public static String 182 extractNetworkPortion(String phoneNumber) { 183 if (phoneNumber == null) { 184 return null; 185 } 186 187 int len = phoneNumber.length(); 188 StringBuilder ret = new StringBuilder(len); 189 boolean firstCharAdded = false; 190 191 for (int i = 0; i < len; i++) { 192 char c = phoneNumber.charAt(i); 193 if (isDialable(c) && (c != '+' || !firstCharAdded)) { 194 firstCharAdded = true; 195 ret.append(c); 196 } else if (isStartsPostDial (c)) { 197 break; 198 } 199 } 200 201 return ret.toString(); 202 } 203 204 /** 205 * Strips separators from a phone number string. 206 * @param phoneNumber phone number to strip. 207 * @return phone string stripped of separators. 208 */ 209 public static String stripSeparators(String phoneNumber) { 210 if (phoneNumber == null) { 211 return null; 212 } 213 int len = phoneNumber.length(); 214 StringBuilder ret = new StringBuilder(len); 215 216 for (int i = 0; i < len; i++) { 217 char c = phoneNumber.charAt(i); 218 if (isNonSeparator(c)) { 219 ret.append(c); 220 } 221 } 222 223 return ret.toString(); 224 } 225 226 /** or -1 if both are negative */ 227 static private int 228 minPositive (int a, int b) { 229 if (a >= 0 && b >= 0) { 230 return (a < b) ? a : b; 231 } else if (a >= 0) { /* && b < 0 */ 232 return a; 233 } else if (b >= 0) { /* && a < 0 */ 234 return b; 235 } else { /* a < 0 && b < 0 */ 236 return -1; 237 } 238 } 239 240 private static void log(String msg) { 241 Log.d(LOG_TAG, msg); 242 } 243 /** index of the last character of the network portion 244 * (eg anything after is a post-dial string) 245 */ 246 static private int 247 indexOfLastNetworkChar(String a) { 248 int pIndex, wIndex; 249 int origLength; 250 int trimIndex; 251 252 origLength = a.length(); 253 254 pIndex = a.indexOf(PAUSE); 255 wIndex = a.indexOf(WAIT); 256 257 trimIndex = minPositive(pIndex, wIndex); 258 259 if (trimIndex < 0) { 260 return origLength - 1; 261 } else { 262 return trimIndex - 1; 263 } 264 } 265 266 /** 267 * Extracts the post-dial sequence of DTMF control digits, pauses, and 268 * waits. Strips separators. This string may be empty, but will not be null 269 * unless phoneNumber == null. 270 * 271 * Returns null if phoneNumber == null 272 */ 273 274 public static String 275 extractPostDialPortion(String phoneNumber) { 276 if (phoneNumber == null) return null; 277 278 int trimIndex; 279 StringBuilder ret = new StringBuilder(); 280 281 trimIndex = indexOfLastNetworkChar (phoneNumber); 282 283 for (int i = trimIndex + 1, s = phoneNumber.length() 284 ; i < s; i++ 285 ) { 286 char c = phoneNumber.charAt(i); 287 if (isNonSeparator(c)) { 288 ret.append(c); 289 } 290 } 291 292 return ret.toString(); 293 } 294 295 /** 296 * Compare phone numbers a and b, return true if they're identical 297 * enough for caller ID purposes. 298 * 299 * - Compares from right to left 300 * - requires MIN_MATCH (5) characters to match 301 * - handles common trunk prefixes and international prefixes 302 * (basically, everything except the Russian trunk prefix) 303 * 304 * Tolerates nulls 305 */ 306 public static boolean 307 compare(String a, String b) { 308 int ia, ib; 309 int matched; 310 311 if (a == null || b == null) return a == b; 312 313 if (a.length() == 0 || b.length() == 0) { 314 return false; 315 } 316 317 ia = indexOfLastNetworkChar (a); 318 ib = indexOfLastNetworkChar (b); 319 matched = 0; 320 321 while (ia >= 0 && ib >=0) { 322 char ca, cb; 323 boolean skipCmp = false; 324 325 ca = a.charAt(ia); 326 327 if (!isDialable(ca)) { 328 ia--; 329 skipCmp = true; 330 } 331 332 cb = b.charAt(ib); 333 334 if (!isDialable(cb)) { 335 ib--; 336 skipCmp = true; 337 } 338 339 if (!skipCmp) { 340 if (cb != ca && ca != WILD && cb != WILD) { 341 break; 342 } 343 ia--; ib--; matched++; 344 } 345 } 346 347 if (matched < MIN_MATCH) { 348 int aLen = a.length(); 349 350 // if the input strings match, but their lengths < MIN_MATCH, 351 // treat them as equal. 352 if (aLen == b.length() && aLen == matched) { 353 return true; 354 } 355 return false; 356 } 357 358 // At least one string has matched completely; 359 if (matched >= MIN_MATCH && (ia < 0 || ib < 0)) { 360 return true; 361 } 362 363 /* 364 * Now, what remains must be one of the following for a 365 * match: 366 * 367 * - a '+' on one and a '00' or a '011' on the other 368 * - a '0' on one and a (+,00)<country code> on the other 369 * (for this, a '0' and a '00' prefix would have succeeded above) 370 */ 371 372 if (matchIntlPrefix(a, ia + 1) 373 && matchIntlPrefix (b, ib +1) 374 ) { 375 return true; 376 } 377 378 if (matchTrunkPrefix(a, ia + 1) 379 && matchIntlPrefixAndCC(b, ib +1) 380 ) { 381 return true; 382 } 383 384 if (matchTrunkPrefix(b, ib + 1) 385 && matchIntlPrefixAndCC(a, ia +1) 386 ) { 387 return true; 388 } 389 390 return false; 391 } 392 393 /** 394 * Returns the rightmost MIN_MATCH (5) characters in the network portion 395 * in *reversed* order 396 * 397 * This can be used to do a database lookup against the column 398 * that stores getStrippedReversed() 399 * 400 * Returns null if phoneNumber == null 401 */ 402 public static String 403 toCallerIDMinMatch(String phoneNumber) { 404 String np = extractNetworkPortion(phoneNumber); 405 return internalGetStrippedReversed(np, MIN_MATCH); 406 } 407 408 /** 409 * Returns the network portion reversed. 410 * This string is intended to go into an index column for a 411 * database lookup. 412 * 413 * Returns null if phoneNumber == null 414 */ 415 public static String 416 getStrippedReversed(String phoneNumber) { 417 String np = extractNetworkPortion(phoneNumber); 418 419 if (np == null) return null; 420 421 return internalGetStrippedReversed(np, np.length()); 422 } 423 424 /** 425 * Returns the last numDigits of the reversed phone number 426 * Returns null if np == null 427 */ 428 private static String 429 internalGetStrippedReversed(String np, int numDigits) { 430 if (np == null) return null; 431 432 StringBuilder ret = new StringBuilder(numDigits); 433 int length = np.length(); 434 435 for (int i = length - 1, s = length 436 ; i >= 0 && (s - i) <= numDigits ; i-- 437 ) { 438 char c = np.charAt(i); 439 440 ret.append(c); 441 } 442 443 return ret.toString(); 444 } 445 446 /** 447 * Basically: makes sure there's a + in front of a 448 * TOA_International number 449 * 450 * Returns null if s == null 451 */ 452 public static String 453 stringFromStringAndTOA(String s, int TOA) { 454 if (s == null) return null; 455 456 if (TOA == TOA_International && s.length() > 0 && s.charAt(0) != '+') { 457 return "+" + s; 458 } 459 460 return s; 461 } 462 463 /** 464 * Returns the TOA for the given dial string 465 * Basically, returns TOA_International if there's a + prefix 466 */ 467 468 public static int 469 toaFromString(String s) { 470 if (s != null && s.length() > 0 && s.charAt(0) == '+') { 471 return TOA_International; 472 } 473 474 return TOA_Unknown; 475 } 476 477 /** 478 * Phone numbers are stored in "lookup" form in the database 479 * as reversed strings to allow for caller ID lookup 480 * 481 * This method takes a phone number and makes a valid SQL "LIKE" 482 * string that will match the lookup form 483 * 484 */ 485 /** all of a up to len must be an international prefix or 486 * separators/non-dialing digits 487 */ 488 private static boolean 489 matchIntlPrefix(String a, int len) { 490 /* '([^0-9*#+pwn]\+[^0-9*#+pwn] | [^0-9*#+pwn]0(0|11)[^0-9*#+pwn] )$' */ 491 /* 0 1 2 3 45 */ 492 493 int state = 0; 494 for (int i = 0 ; i < len ; i++) { 495 char c = a.charAt(i); 496 497 switch (state) { 498 case 0: 499 if (c == '+') state = 1; 500 else if (c == '0') state = 2; 501 else if (isNonSeparator(c)) return false; 502 break; 503 504 case 2: 505 if (c == '0') state = 3; 506 else if (c == '1') state = 4; 507 else if (isNonSeparator(c)) return false; 508 break; 509 510 case 4: 511 if (c == '1') state = 5; 512 else if (isNonSeparator(c)) return false; 513 break; 514 515 default: 516 if (isNonSeparator(c)) return false; 517 break; 518 519 } 520 } 521 522 return state == 1 || state == 3 || state == 5; 523 } 524 525 /** 526 * 3GPP TS 24.008 10.5.4.7 527 * Called Party BCD Number 528 * 529 * See Also TS 51.011 10.5.1 "dialing number/ssc string" 530 * and TS 11.11 "10.3.1 EF adn (Abbreviated dialing numbers)" 531 * 532 * @param bytes the data buffer 533 * @param offset should point to the TOA (aka. TON/NPI) octet after the length byte 534 * @param length is the number of bytes including TOA byte 535 * and must be at least 2 536 * 537 * @return partial string on invalid decode 538 * 539 * FIXME(mkf) support alphanumeric address type 540 * currently implemented in SMSMessage.getAddress() 541 */ 542 public static String 543 calledPartyBCDToString (byte[] bytes, int offset, int length) { 544 boolean prependPlus = false; 545 StringBuilder ret = new StringBuilder(1 + length * 2); 546 547 if (length < 2) { 548 return ""; 549 } 550 551 if ((bytes[offset] & 0xff) == TOA_International) { 552 prependPlus = true; 553 } 554 555 internalCalledPartyBCDFragmentToString( 556 ret, bytes, offset + 1, length - 1); 557 558 if (prependPlus && ret.length() == 0) { 559 // If the only thing there is a prepended plus, return "" 560 return ""; 561 } 562 563 if (prependPlus) { 564 // This is an "international number" and should have 565 // a plus prepended to the dialing number. But there 566 // can also be Gsm MMI codes as defined in TS 22.030 6.5.2 567 // so we need to handle those also. 568 // 569 // http://web.telia.com/~u47904776/gsmkode.htm is a 570 // has a nice list of some of these GSM codes. 571 // 572 // Examples are: 573 // **21*+886988171479# 574 // **21*8311234567# 575 // *21# 576 // #21# 577 // *#21# 578 // *31#+11234567890 579 // #31#+18311234567 580 // #31#8311234567 581 // 18311234567 582 // +18311234567# 583 // +18311234567 584 // Odd ball cases that some phones handled 585 // where there is no dialing number so they 586 // append the "+" 587 // *21#+ 588 // **21#+ 589 String retString = ret.toString(); 590 Pattern p = Pattern.compile("(^[#*])(.*)([#*])(.*)(#)$"); 591 Matcher m = p.matcher(retString); 592 if (m.matches()) { 593 if ("".equals(m.group(2))) { 594 // Started with two [#*] ends with # 595 // So no dialing number and we'll just 596 // append a +, this handles **21#+ 597 ret = new StringBuilder(); 598 ret.append(m.group(1)); 599 ret.append(m.group(3)); 600 ret.append(m.group(4)); 601 ret.append(m.group(5)); 602 ret.append("+"); 603 } else { 604 // Starts with [#*] and ends with # 605 // Assume group 4 is a dialing number 606 // such as *21*+1234554# 607 ret = new StringBuilder(); 608 ret.append(m.group(1)); 609 ret.append(m.group(2)); 610 ret.append(m.group(3)); 611 ret.append("+"); 612 ret.append(m.group(4)); 613 ret.append(m.group(5)); 614 } 615 } else { 616 p = Pattern.compile("(^[#*])(.*)([#*])(.*)"); 617 m = p.matcher(retString); 618 if (m.matches()) { 619 // Starts with [#*] and only one other [#*] 620 // Assume the data after last [#*] is dialing 621 // number (i.e. group 4) such as *31#+11234567890. 622 // This also includes the odd ball *21#+ 623 ret = new StringBuilder(); 624 ret.append(m.group(1)); 625 ret.append(m.group(2)); 626 ret.append(m.group(3)); 627 ret.append("+"); 628 ret.append(m.group(4)); 629 } else { 630 // Does NOT start with [#*] just prepend '+' 631 ret = new StringBuilder(); 632 ret.append('+'); 633 ret.append(retString); 634 } 635 } 636 } 637 638 return ret.toString(); 639 } 640 641 private static void 642 internalCalledPartyBCDFragmentToString( 643 StringBuilder sb, byte [] bytes, int offset, int length) { 644 for (int i = offset ; i < length + offset ; i++) { 645 byte b; 646 char c; 647 648 c = bcdToChar((byte)(bytes[i] & 0xf)); 649 650 if (c == 0) { 651 return; 652 } 653 sb.append(c); 654 655 // FIXME(mkf) TS 23.040 9.1.2.3 says 656 // "if a mobile receives 1111 in a position prior to 657 // the last semi-octet then processing shall commense with 658 // the next semi-octet and the intervening 659 // semi-octet shall be ignored" 660 // How does this jive with 24,008 10.5.4.7 661 662 b = (byte)((bytes[i] >> 4) & 0xf); 663 664 if (b == 0xf && i + 1 == length + offset) { 665 //ignore final 0xf 666 break; 667 } 668 669 c = bcdToChar(b); 670 if (c == 0) { 671 return; 672 } 673 674 sb.append(c); 675 } 676 677 } 678 679 /** 680 * Like calledPartyBCDToString, but field does not start with a 681 * TOA byte. For example: SIM ADN extension fields 682 */ 683 684 public static String 685 calledPartyBCDFragmentToString(byte [] bytes, int offset, int length) { 686 StringBuilder ret = new StringBuilder(length * 2); 687 688 internalCalledPartyBCDFragmentToString(ret, bytes, offset, length); 689 690 return ret.toString(); 691 } 692 693 /** returns 0 on invalid value */ 694 private static char 695 bcdToChar(byte b) { 696 if (b < 0xa) { 697 return (char)('0' + b); 698 } else switch (b) { 699 case 0xa: return '*'; 700 case 0xb: return '#'; 701 case 0xc: return PAUSE; 702 case 0xd: return WILD; 703 704 default: return 0; 705 } 706 } 707 708 private static int 709 charToBCD(char c) { 710 if (c >= '0' && c <= '9') { 711 return c - '0'; 712 } else if (c == '*') { 713 return 0xa; 714 } else if (c == '#') { 715 return 0xb; 716 } else if (c == PAUSE) { 717 return 0xc; 718 } else if (c == WILD) { 719 return 0xd; 720 } else { 721 throw new RuntimeException ("invalid char for BCD " + c); 722 } 723 } 724 725 /** 726 * Return true iff the network portion of <code>address</code> is, 727 * as far as we can tell on the device, suitable for use as an SMS 728 * destination address. 729 */ 730 public static boolean isWellFormedSmsAddress(String address) { 731 String networkPortion = 732 PhoneNumberUtils.extractNetworkPortion(address); 733 734 return (!(networkPortion.equals("+") 735 || TextUtils.isEmpty(networkPortion))) 736 && isDialable(networkPortion); 737 } 738 739 public static boolean isGlobalPhoneNumber(String phoneNumber) { 740 if (TextUtils.isEmpty(phoneNumber)) { 741 return false; 742 } 743 744 Matcher match = GLOBAL_PHONE_NUMBER_PATTERN.matcher(phoneNumber); 745 return match.matches(); 746 } 747 748 private static boolean isDialable(String address) { 749 for (int i = 0, count = address.length(); i < count; i++) { 750 if (!isDialable(address.charAt(i))) { 751 return false; 752 } 753 } 754 return true; 755 } 756 757 private static boolean isNonSeparator(String address) { 758 for (int i = 0, count = address.length(); i < count; i++) { 759 if (!isNonSeparator(address.charAt(i))) { 760 return false; 761 } 762 } 763 return true; 764 } 765 /** 766 * Note: calls extractNetworkPortion(), so do not use for 767 * SIM EF[ADN] style records 768 * 769 * Returns null if network portion is empty. 770 */ 771 public static byte[] 772 networkPortionToCalledPartyBCD(String s) { 773 String networkPortion = extractNetworkPortion(s); 774 return numberToCalledPartyBCDHelper(networkPortion, false); 775 } 776 777 /** 778 * Same as {@link #networkPortionToCalledPartyBCD}, but includes a 779 * one-byte length prefix. 780 */ 781 public static byte[] 782 networkPortionToCalledPartyBCDWithLength(String s) { 783 String networkPortion = extractNetworkPortion(s); 784 return numberToCalledPartyBCDHelper(networkPortion, true); 785 } 786 787 /** 788 * Convert a dialing number to BCD byte array 789 * 790 * @param number dialing number string 791 * if the dialing number starts with '+', set to internationl TOA 792 * @return BCD byte array 793 */ 794 public static byte[] 795 numberToCalledPartyBCD(String number) { 796 return numberToCalledPartyBCDHelper(number, false); 797 } 798 799 /** 800 * If includeLength is true, prepend a one-byte length value to 801 * the return array. 802 */ 803 private static byte[] 804 numberToCalledPartyBCDHelper(String number, boolean includeLength) { 805 int numberLenReal = number.length(); 806 int numberLenEffective = numberLenReal; 807 boolean hasPlus = number.indexOf('+') != -1; 808 if (hasPlus) numberLenEffective--; 809 810 if (numberLenEffective == 0) return null; 811 812 int resultLen = (numberLenEffective + 1) / 2; // Encoded numbers require only 4 bits each. 813 int extraBytes = 1; // Prepended TOA byte. 814 if (includeLength) extraBytes++; // Optional prepended length byte. 815 resultLen += extraBytes; 816 817 byte[] result = new byte[resultLen]; 818 819 int digitCount = 0; 820 for (int i = 0; i < numberLenReal; i++) { 821 char c = number.charAt(i); 822 if (c == '+') continue; 823 int shift = ((digitCount & 0x01) == 1) ? 4 : 0; 824 result[extraBytes + (digitCount >> 1)] |= (byte)((charToBCD(c) & 0x0F) << shift); 825 digitCount++; 826 } 827 828 // 1-fill any trailing odd nibble/quartet. 829 if ((digitCount & 0x01) == 1) result[extraBytes + (digitCount >> 1)] |= 0xF0; 830 831 int offset = 0; 832 if (includeLength) result[offset++] = (byte)(resultLen - 1); 833 result[offset] = (byte)(hasPlus ? TOA_International : TOA_Unknown); 834 835 return result; 836 } 837 838 /** all of 'a' up to len must match non-US trunk prefix ('0') */ 839 private static boolean 840 matchTrunkPrefix(String a, int len) { 841 boolean found; 842 843 found = false; 844 845 for (int i = 0 ; i < len ; i++) { 846 char c = a.charAt(i); 847 848 if (c == '0' && !found) { 849 found = true; 850 } else if (isNonSeparator(c)) { 851 return false; 852 } 853 } 854 855 return found; 856 } 857 858 /** all of 'a' up to len must be a (+|00|011)country code) 859 * We're fast and loose with the country code. Any \d{1,3} matches */ 860 private static boolean 861 matchIntlPrefixAndCC(String a, int len) { 862 /* [^0-9*#+pwn]*(\+|0(0|11)\d\d?\d? [^0-9*#+pwn] $ */ 863 /* 0 1 2 3 45 6 7 8 */ 864 865 int state = 0; 866 for (int i = 0 ; i < len ; i++ ) { 867 char c = a.charAt(i); 868 869 switch (state) { 870 case 0: 871 if (c == '+') state = 1; 872 else if (c == '0') state = 2; 873 else if (isNonSeparator(c)) return false; 874 break; 875 876 case 2: 877 if (c == '0') state = 3; 878 else if (c == '1') state = 4; 879 else if (isNonSeparator(c)) return false; 880 break; 881 882 case 4: 883 if (c == '1') state = 5; 884 else if (isNonSeparator(c)) return false; 885 break; 886 887 case 1: 888 case 3: 889 case 5: 890 if (isISODigit(c)) state = 6; 891 else if (isNonSeparator(c)) return false; 892 break; 893 894 case 6: 895 case 7: 896 if (isISODigit(c)) state++; 897 else if (isNonSeparator(c)) return false; 898 break; 899 900 default: 901 if (isNonSeparator(c)) return false; 902 } 903 } 904 905 return state == 6 || state == 7 || state == 8; 906 } 907 908 //================ Number formatting ========================= 909 910 /** The current locale is unknown, look for a country code or don't format */ 911 public static final int FORMAT_UNKNOWN = 0; 912 /** NANP formatting */ 913 public static final int FORMAT_NANP = 1; 914 /** Japanese formatting */ 915 public static final int FORMAT_JAPAN = 2; 916 917 /** List of country codes for countries that use the NANP */ 918 private static final String[] NANP_COUNTRIES = new String[] { 919 "US", // United States 920 "CA", // Canada 921 "AS", // American Samoa 922 "AI", // Anguilla 923 "AG", // Antigua and Barbuda 924 "BS", // Bahamas 925 "BB", // Barbados 926 "BM", // Bermuda 927 "VG", // British Virgin Islands 928 "KY", // Cayman Islands 929 "DM", // Dominica 930 "DO", // Dominican Republic 931 "GD", // Grenada 932 "GU", // Guam 933 "JM", // Jamaica 934 "PR", // Puerto Rico 935 "MS", // Montserrat 936 "MP", // Northern Mariana Islands 937 "KN", // Saint Kitts and Nevis 938 "LC", // Saint Lucia 939 "VC", // Saint Vincent and the Grenadines 940 "TT", // Trinidad and Tobago 941 "TC", // Turks and Caicos Islands 942 "VI", // U.S. Virgin Islands 943 }; 944 945 /** 946 * Breaks the given number down and formats it according to the rules 947 * for the country the number is from. 948 * 949 * @param source the phone number to format 950 * @return a locally acceptable formatting of the input, or the raw input if 951 * formatting rules aren't known for the number 952 */ 953 public static String formatNumber(String source) { 954 SpannableStringBuilder text = new SpannableStringBuilder(source); 955 formatNumber(text, getFormatTypeForLocale(Locale.getDefault())); 956 return text.toString(); 957 } 958 959 /** 960 * Returns the phone number formatting type for the given locale. 961 * 962 * @param locale The locale of interest, usually {@link Locale#getDefault()} 963 * @return the formatting type for the given locale, or FORMAT_UNKNOWN if the formatting 964 * rules are not known for the given locale 965 */ 966 public static int getFormatTypeForLocale(Locale locale) { 967 String country = locale.getCountry(); 968 969 return getFormatTypeFromCountryCode(country); 970 } 971 972 /** 973 * Formats a phone number in-place. Currently only supports NANP formatting. 974 * 975 * @param text The number to be formatted, will be modified with the formatting 976 * @param defaultFormattingType The default formatting rules to apply if the number does 977 * not begin with +<country_code> 978 */ 979 public static void formatNumber(Editable text, int defaultFormattingType) { 980 int formatType = defaultFormattingType; 981 982 if (text.length() > 2 && text.charAt(0) == '+') { 983 if (text.charAt(1) == '1') { 984 formatType = FORMAT_NANP; 985 } else if (text.length() >= 3 && text.charAt(1) == '8' 986 && text.charAt(2) == '1') { 987 formatType = FORMAT_JAPAN; 988 } else { 989 return; 990 } 991 } 992 993 switch (formatType) { 994 case FORMAT_NANP: 995 formatNanpNumber(text); 996 return; 997 case FORMAT_JAPAN: 998 formatJapaneseNumber(text); 999 return; 1000 } 1001 } 1002 1003 private static final int NANP_STATE_DIGIT = 1; 1004 private static final int NANP_STATE_PLUS = 2; 1005 private static final int NANP_STATE_ONE = 3; 1006 private static final int NANP_STATE_DASH = 4; 1007 1008 /** 1009 * Formats a phone number in-place using the NANP formatting rules. Numbers will be formatted 1010 * as: 1011 * 1012 * <p><code> 1013 * xxxxx 1014 * xxx-xxxx 1015 * xxx-xxx-xxxx 1016 * 1-xxx-xxx-xxxx 1017 * +1-xxx-xxx-xxxx 1018 * </code></p> 1019 * 1020 * @param text the number to be formatted, will be modified with the formatting 1021 */ 1022 public static void formatNanpNumber(Editable text) { 1023 int length = text.length(); 1024 if (length > "+1-nnn-nnn-nnnn".length()) { 1025 // The string is too long to be formatted 1026 return; 1027 } else if (length <= 5) { 1028 // The string is either a shortcode or too short to be formatted 1029 return; 1030 } 1031 1032 CharSequence saved = text.subSequence(0, length); 1033 1034 // Strip the dashes first, as we're going to add them back 1035 int p = 0; 1036 while (p < text.length()) { 1037 if (text.charAt(p) == '-') { 1038 text.delete(p, p + 1); 1039 } else { 1040 p++; 1041 } 1042 } 1043 length = text.length(); 1044 1045 // When scanning the number we record where dashes need to be added, 1046 // if they're non-0 at the end of the scan the dashes will be added in 1047 // the proper places. 1048 int dashPositions[] = new int[3]; 1049 int numDashes = 0; 1050 1051 int state = NANP_STATE_DIGIT; 1052 int numDigits = 0; 1053 for (int i = 0; i < length; i++) { 1054 char c = text.charAt(i); 1055 switch (c) { 1056 case '1': 1057 if (numDigits == 0 || state == NANP_STATE_PLUS) { 1058 state = NANP_STATE_ONE; 1059 break; 1060 } 1061 // fall through 1062 case '2': 1063 case '3': 1064 case '4': 1065 case '5': 1066 case '6': 1067 case '7': 1068 case '8': 1069 case '9': 1070 case '0': 1071 if (state == NANP_STATE_PLUS) { 1072 // Only NANP number supported for now 1073 text.replace(0, length, saved); 1074 return; 1075 } else if (state == NANP_STATE_ONE) { 1076 // Found either +1 or 1, follow it up with a dash 1077 dashPositions[numDashes++] = i; 1078 } else if (state != NANP_STATE_DASH && (numDigits == 3 || numDigits == 6)) { 1079 // Found a digit that should be after a dash that isn't 1080 dashPositions[numDashes++] = i; 1081 } 1082 state = NANP_STATE_DIGIT; 1083 numDigits++; 1084 break; 1085 1086 case '-': 1087 state = NANP_STATE_DASH; 1088 break; 1089 1090 case '+': 1091 if (i == 0) { 1092 // Plus is only allowed as the first character 1093 state = NANP_STATE_PLUS; 1094 break; 1095 } 1096 // Fall through 1097 default: 1098 // Unknown character, bail on formatting 1099 text.replace(0, length, saved); 1100 return; 1101 } 1102 } 1103 1104 if (numDigits == 7) { 1105 // With 7 digits we want xxx-xxxx, not xxx-xxx-x 1106 numDashes--; 1107 } 1108 1109 // Actually put the dashes in place 1110 for (int i = 0; i < numDashes; i++) { 1111 int pos = dashPositions[i]; 1112 text.replace(pos + i, pos + i, "-"); 1113 } 1114 1115 // Remove trailing dashes 1116 int len = text.length(); 1117 while (len > 0) { 1118 if (text.charAt(len - 1) == '-') { 1119 text.delete(len - 1, len); 1120 len--; 1121 } else { 1122 break; 1123 } 1124 } 1125 } 1126 1127 /** 1128 * Formats a phone number in-place using the Japanese formatting rules. 1129 * Numbers will be formatted as: 1130 * 1131 * <p><code> 1132 * 03-xxxx-xxxx 1133 * 090-xxxx-xxxx 1134 * 0120-xxx-xxx 1135 * +81-3-xxxx-xxxx 1136 * +81-90-xxxx-xxxx 1137 * </code></p> 1138 * 1139 * @param text the number to be formatted, will be modified with 1140 * the formatting 1141 */ 1142 public static void formatJapaneseNumber(Editable text) { 1143 JapanesePhoneNumberFormatter.format(text); 1144 } 1145 1146 // Three and four digit phone numbers for either special services 1147 // or from the network (eg carrier-originated SMS messages) should 1148 // not match 1149 static final int MIN_MATCH = 5; 1150 1151 /** 1152 * isEmergencyNumber: checks a given number against the list of 1153 * emergency numbers provided by the RIL and SIM card. 1154 * 1155 * @param number the number to look up. 1156 * @return if the number is in the list of emergency numbers 1157 * listed in the ril / sim, then return true, otherwise false. 1158 */ 1159 public static boolean isEmergencyNumber(String number) { 1160 // Strip the separators from the number before comparing it 1161 // to the list. 1162 number = extractNetworkPortion(number); 1163 1164 // retrieve the list of emergency numbers 1165 String numbers = SystemProperties.get("ro.ril.ecclist"); 1166 1167 if (!TextUtils.isEmpty(numbers)) { 1168 // searches through the comma-separated list for a match, 1169 // return true if one is found. 1170 for (String emergencyNum : numbers.split(",")) { 1171 if (emergencyNum.equals(number)) { 1172 return true; 1173 } 1174 } 1175 // no matches found against the list! 1176 return false; 1177 } 1178 1179 //no ecclist system property, so use our own list. 1180 return (number.equals("112") || number.equals("911")); 1181 } 1182 1183 /** 1184 * isVoiceMailNumber: checks a given number against the voicemail 1185 * number provided by the RIL and SIM card. The caller must have 1186 * the READ_PHONE_STATE credential. 1187 * 1188 * @param number the number to look up. 1189 * @return true if the number is in the list of voicemail. False 1190 * otherwise, including if the caller does not have the permission 1191 * to read the VM number. 1192 * @hide TODO: pending API Council approval 1193 */ 1194 public static boolean isVoiceMailNumber(String number) { 1195 String vmNumber; 1196 1197 try { 1198 vmNumber = TelephonyManager.getDefault().getVoiceMailNumber(); 1199 } catch (SecurityException ex) { 1200 return false; 1201 } 1202 1203 // Strip the separators from the number before comparing it 1204 // to the list. 1205 number = extractNetworkPortion(number); 1206 1207 // compare tolerates null so we need to make sure that we 1208 // don't return true when both are null. 1209 return !TextUtils.isEmpty(number) && compare(number, vmNumber); 1210 } 1211 1212 /** 1213 * Translates any alphabetic letters (i.e. [A-Za-z]) in the 1214 * specified phone number into the equivalent numeric digits, 1215 * according to the phone keypad letter mapping described in 1216 * ITU E.161 and ISO/IEC 9995-8. 1217 * 1218 * @return the input string, with alpha letters converted to numeric 1219 * digits using the phone keypad letter mapping. For example, 1220 * an input of "1-800-GOOG-411" will return "1-800-4664-411". 1221 */ 1222 public static String convertKeypadLettersToDigits(String input) { 1223 if (input == null) { 1224 return input; 1225 } 1226 int len = input.length(); 1227 if (len == 0) { 1228 return input; 1229 } 1230 1231 char[] out = input.toCharArray(); 1232 1233 for (int i = 0; i < len; i++) { 1234 char c = out[i]; 1235 // If this char isn't in KEYPAD_MAP at all, just leave it alone. 1236 out[i] = (char) KEYPAD_MAP.get(c, c); 1237 } 1238 1239 return new String(out); 1240 } 1241 1242 /** 1243 * The phone keypad letter mapping (see ITU E.161 or ISO/IEC 9995-8.) 1244 * TODO: This should come from a resource. 1245 */ 1246 private static final SparseIntArray KEYPAD_MAP = new SparseIntArray(); 1247 static { 1248 KEYPAD_MAP.put('a', '2'); KEYPAD_MAP.put('b', '2'); KEYPAD_MAP.put('c', '2'); 1249 KEYPAD_MAP.put('A', '2'); KEYPAD_MAP.put('B', '2'); KEYPAD_MAP.put('C', '2'); 1250 1251 KEYPAD_MAP.put('d', '3'); KEYPAD_MAP.put('e', '3'); KEYPAD_MAP.put('f', '3'); 1252 KEYPAD_MAP.put('D', '3'); KEYPAD_MAP.put('E', '3'); KEYPAD_MAP.put('F', '3'); 1253 1254 KEYPAD_MAP.put('g', '4'); KEYPAD_MAP.put('h', '4'); KEYPAD_MAP.put('i', '4'); 1255 KEYPAD_MAP.put('G', '4'); KEYPAD_MAP.put('H', '4'); KEYPAD_MAP.put('I', '4'); 1256 1257 KEYPAD_MAP.put('j', '5'); KEYPAD_MAP.put('k', '5'); KEYPAD_MAP.put('l', '5'); 1258 KEYPAD_MAP.put('J', '5'); KEYPAD_MAP.put('K', '5'); KEYPAD_MAP.put('L', '5'); 1259 1260 KEYPAD_MAP.put('m', '6'); KEYPAD_MAP.put('n', '6'); KEYPAD_MAP.put('o', '6'); 1261 KEYPAD_MAP.put('M', '6'); KEYPAD_MAP.put('N', '6'); KEYPAD_MAP.put('O', '6'); 1262 1263 KEYPAD_MAP.put('p', '7'); KEYPAD_MAP.put('q', '7'); KEYPAD_MAP.put('r', '7'); KEYPAD_MAP.put('s', '7'); 1264 KEYPAD_MAP.put('P', '7'); KEYPAD_MAP.put('Q', '7'); KEYPAD_MAP.put('R', '7'); KEYPAD_MAP.put('S', '7'); 1265 1266 KEYPAD_MAP.put('t', '8'); KEYPAD_MAP.put('u', '8'); KEYPAD_MAP.put('v', '8'); 1267 KEYPAD_MAP.put('T', '8'); KEYPAD_MAP.put('U', '8'); KEYPAD_MAP.put('V', '8'); 1268 1269 KEYPAD_MAP.put('w', '9'); KEYPAD_MAP.put('x', '9'); KEYPAD_MAP.put('y', '9'); KEYPAD_MAP.put('z', '9'); 1270 KEYPAD_MAP.put('W', '9'); KEYPAD_MAP.put('X', '9'); KEYPAD_MAP.put('Y', '9'); KEYPAD_MAP.put('Z', '9'); 1271 } 1272 1273 //================ Plus Code formatting ========================= 1274 private static final char PLUS_SIGN_CHAR = '+'; 1275 private static final String PLUS_SIGN_STRING = "+"; 1276 private static final String NANP_IDP_STRING = "011"; 1277 private static final int NANP_LENGTH = 10; 1278 1279 /** 1280 * This function checks if there is a plus sign (+) in the passed-in dialing number. 1281 * If there is, it processes the plus sign based on the default telephone 1282 * numbering plan of the system when the phone is activated and the current 1283 * telephone numbering plan of the system that the phone is camped on. 1284 * Currently, we only support the case that the default and current telephone 1285 * numbering plans are North American Numbering Plan(NANP). 1286 * 1287 * The passed-in dialStr should only contain the valid format as described below, 1288 * 1) the 1st character in the dialStr should be one of the really dialable 1289 * characters listed below 1290 * ISO-LATIN characters 0-9, *, # , + 1291 * 2) the dialStr should already strip out the separator characters, 1292 * every character in the dialStr should be one of the non separator characters 1293 * listed below 1294 * ISO-LATIN characters 0-9, *, # , +, WILD, WAIT, PAUSE 1295 * 1296 * Otherwise, this function returns the dial string passed in 1297 * 1298 * @param dialStr the original dial string 1299 * @return the converted dial string if the current/default countries belong to NANP, 1300 * and if there is the "+" in the original dial string. Otherwise, the original dial 1301 * string returns. 1302 * 1303 * This API is for CDMA only 1304 * 1305 * @hide TODO: pending API Council approval 1306 */ 1307 public static String cdmaCheckAndProcessPlusCode(String dialStr) { 1308 if (!TextUtils.isEmpty(dialStr)) { 1309 if (isReallyDialable(dialStr.charAt(0)) && 1310 isNonSeparator(dialStr)) { 1311 String currIso = SystemProperties.get(PROPERTY_OPERATOR_ISO_COUNTRY, ""); 1312 String defaultIso = SystemProperties.get(PROPERTY_ICC_OPERATOR_ISO_COUNTRY, ""); 1313 if (!TextUtils.isEmpty(currIso) && !TextUtils.isEmpty(defaultIso)) { 1314 return cdmaCheckAndProcessPlusCodeByNumberFormat(dialStr, 1315 getFormatTypeFromCountryCode(currIso), 1316 getFormatTypeFromCountryCode(defaultIso)); 1317 } 1318 } 1319 } 1320 return dialStr; 1321 } 1322 1323 /** 1324 * This function should be called from checkAndProcessPlusCode only 1325 * And it is used for test purpose also. 1326 * 1327 * It checks the dial string by looping through the network portion, 1328 * post dial portion 1, post dial porting 2, etc. If there is any 1329 * plus sign, then process the plus sign. 1330 * Currently, this function supports the plus sign conversion within NANP only. 1331 * Specifically, it handles the plus sign in the following ways: 1332 * 1)+1NANP,remove +, e.g. 1333 * +18475797000 is converted to 18475797000, 1334 * 2)+NANP or +non-NANP Numbers,replace + with the current NANP IDP, e.g, 1335 * +8475797000 is converted to 0118475797000, 1336 * +11875767800 is converted to 01111875767800 1337 * 3)+1NANP in post dial string(s), e.g. 1338 * 8475797000;+18475231753 is converted to 8475797000;18475231753 1339 * 1340 * 1341 * @param dialStr the original dial string 1342 * @param currFormat the numbering system of the current country that the phone is camped on 1343 * @param defaultFormat the numbering system of the country that the phone is activated on 1344 * @return the converted dial string if the current/default countries belong to NANP, 1345 * and if there is the "+" in the original dial string. Otherwise, the original dial 1346 * string returns. 1347 * 1348 * @hide 1349 */ 1350 public static String 1351 cdmaCheckAndProcessPlusCodeByNumberFormat(String dialStr,int currFormat,int defaultFormt) { 1352 String retStr = dialStr; 1353 1354 // Checks if the plus sign character is in the passed-in dial string 1355 if (dialStr != null && 1356 dialStr.lastIndexOf(PLUS_SIGN_STRING) != -1) { 1357 // Format the string based on the rules for the country the number is from, 1358 // and the current country the phone is camped on. 1359 if ((currFormat == defaultFormt) && (currFormat == FORMAT_NANP)) { 1360 // Handle case where default and current telephone numbering plans are NANP. 1361 String postDialStr = null; 1362 String tempDialStr = dialStr; 1363 1364 // Sets the retStr to null since the conversion will be performed below. 1365 retStr = null; 1366 if (DBG) log("checkAndProcessPlusCode,dialStr=" + dialStr); 1367 // This routine is to process the plus sign in the dial string by loop through 1368 // the network portion, post dial portion 1, post dial portion 2... etc. if 1369 // applied 1370 do { 1371 String networkDialStr; 1372 networkDialStr = extractNetworkPortion(tempDialStr); 1373 // Handles the conversion within NANP 1374 networkDialStr = processPlusCodeWithinNanp(networkDialStr); 1375 1376 // Concatenates the string that is converted from network portion 1377 if (!TextUtils.isEmpty(networkDialStr)) { 1378 if (retStr == null) { 1379 retStr = networkDialStr; 1380 } else { 1381 retStr = retStr.concat(networkDialStr); 1382 } 1383 } else { 1384 // This should never happen since we checked the if dialStr is null 1385 // and if it contains the plus sign in the beginning of this function. 1386 // The plus sign is part of the network portion. 1387 Log.e("checkAndProcessPlusCode: null newDialStr", networkDialStr); 1388 return dialStr; 1389 } 1390 postDialStr = extractPostDialPortion(tempDialStr); 1391 if (!TextUtils.isEmpty(postDialStr)) { 1392 int dialableIndex = findDialableIndexFromPostDialStr(postDialStr); 1393 1394 // dialableIndex should always be greater than 0 1395 if (dialableIndex >= 1) { 1396 retStr = appendPwCharBackToOrigDialStr(dialableIndex, 1397 retStr,postDialStr); 1398 // Skips the P/W character, extracts the dialable portion 1399 tempDialStr = postDialStr.substring(dialableIndex); 1400 } else { 1401 // Non-dialable character such as P/W should not be at the end of 1402 // the dial string after P/W processing in CdmaConnection.java 1403 // Set the postDialStr to "" to break out of the loop 1404 if (dialableIndex < 0) { 1405 postDialStr = ""; 1406 } 1407 Log.e("wrong postDialStr=", postDialStr); 1408 } 1409 } 1410 if (DBG) log("checkAndProcessPlusCode,postDialStr=" + postDialStr); 1411 } while (!TextUtils.isEmpty(postDialStr) && !TextUtils.isEmpty(tempDialStr)); 1412 } else { 1413 // TODO: Support NANP international conversion and other telephone numbering plans. 1414 // Currently the phone is never used in non-NANP system, so return the original 1415 // dial string. 1416 Log.e("checkAndProcessPlusCode:non-NANP not supported", dialStr); 1417 } 1418 } 1419 return retStr; 1420 } 1421 1422 // This function gets the default international dialing prefix 1423 private static String getDefaultIdp( ) { 1424 String ps = null; 1425 SystemProperties.get(PROPERTY_IDP_STRING, ps); 1426 if (TextUtils.isEmpty(ps)) { 1427 ps = NANP_IDP_STRING; 1428 } 1429 return ps; 1430 } 1431 1432 private static boolean isTwoToNine (char c) { 1433 if (c >= '2' && c <= '9') { 1434 return true; 1435 } else { 1436 return false; 1437 } 1438 } 1439 1440 private static int getFormatTypeFromCountryCode (String country) { 1441 // Check for the NANP countries 1442 int length = NANP_COUNTRIES.length; 1443 for (int i = 0; i < length; i++) { 1444 if (NANP_COUNTRIES[i].compareToIgnoreCase(country) == 0) { 1445 return FORMAT_NANP; 1446 } 1447 } 1448 if ("jp".compareToIgnoreCase(country) == 0) { 1449 return FORMAT_JAPAN; 1450 } 1451 return FORMAT_UNKNOWN; 1452 } 1453 1454 /** 1455 * This function checks if the passed in string conforms to the NANP format 1456 * i.e. NXX-NXX-XXXX, N is any digit 2-9 and X is any digit 0-9 1457 */ 1458 private static boolean isNanp (String dialStr) { 1459 boolean retVal = false; 1460 if (dialStr != null) { 1461 if (dialStr.length() == NANP_LENGTH) { 1462 if (isTwoToNine(dialStr.charAt(0)) && 1463 isTwoToNine(dialStr.charAt(3))) { 1464 retVal = true; 1465 for (int i=1; i<NANP_LENGTH; i++ ) { 1466 char c=dialStr.charAt(i); 1467 if (!PhoneNumberUtils.isISODigit(c)) { 1468 retVal = false; 1469 break; 1470 } 1471 } 1472 } 1473 } 1474 } else { 1475 Log.e("isNanp: null dialStr passed in", dialStr); 1476 } 1477 return retVal; 1478 } 1479 1480 /** 1481 * This function checks if the passed in string conforms to 1-NANP format 1482 */ 1483 private static boolean isOneNanp(String dialStr) { 1484 boolean retVal = false; 1485 if (dialStr != null) { 1486 String newDialStr = dialStr.substring(1); 1487 if ((dialStr.charAt(0) == '1') && isNanp(newDialStr)) { 1488 retVal = true; 1489 } 1490 } else { 1491 Log.e("isOneNanp: null dialStr passed in", dialStr); 1492 } 1493 return retVal; 1494 } 1495 1496 /** 1497 * This function handles the plus code conversion within NANP CDMA network 1498 * If the number format is 1499 * 1)+1NANP,remove +, 1500 * 2)other than +1NANP, any + numbers,replace + with the current IDP 1501 */ 1502 private static String processPlusCodeWithinNanp(String networkDialStr) { 1503 String retStr = networkDialStr; 1504 1505 if (DBG) log("processPlusCodeWithinNanp,networkDialStr=" + networkDialStr); 1506 // If there is a plus sign at the beginning of the dial string, 1507 // Convert the plus sign to the default IDP since it's an international number 1508 if (networkDialStr != null & 1509 networkDialStr.charAt(0) == PLUS_SIGN_CHAR && 1510 networkDialStr.length() > 1) { 1511 String newStr = networkDialStr.substring(1); 1512 if (isOneNanp(newStr)) { 1513 // Remove the leading plus sign 1514 retStr = newStr; 1515 } else { 1516 String idpStr = getDefaultIdp(); 1517 // Replaces the plus sign with the default IDP 1518 retStr = networkDialStr.replaceFirst("[+]", idpStr); 1519 } 1520 } 1521 if (DBG) log("processPlusCodeWithinNanp,retStr=" + retStr); 1522 return retStr; 1523 } 1524 1525 // This function finds the index of the dialable character(s) 1526 // in the post dial string 1527 private static int findDialableIndexFromPostDialStr(String postDialStr) { 1528 for (int index = 0;index < postDialStr.length();index++) { 1529 char c = postDialStr.charAt(index); 1530 if (isReallyDialable(c)) { 1531 return index; 1532 } 1533 } 1534 return -1; 1535 } 1536 1537 // This function appends the non-diablable P/W character to the original 1538 // dial string based on the dialable index passed in 1539 private static String 1540 appendPwCharBackToOrigDialStr(int dialableIndex,String origStr, String dialStr) { 1541 String retStr; 1542 1543 // There is only 1 P/W character before the dialable characters 1544 if (dialableIndex == 1) { 1545 StringBuilder ret = new StringBuilder(origStr); 1546 ret = ret.append(dialStr.charAt(0)); 1547 retStr = ret.toString(); 1548 } else { 1549 // It means more than 1 P/W characters in the post dial string, 1550 // appends to retStr 1551 String nonDigitStr = dialStr.substring(0,dialableIndex); 1552 retStr = origStr.concat(nonDigitStr); 1553 } 1554 return retStr; 1555 } 1556} 1557