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