2 * Copyright (C) 2006 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package android.telephony;
19 import com.android.i18n.phonenumbers.NumberParseException;
20 import com.android.i18n.phonenumbers.PhoneNumberUtil;
21 import com.android.i18n.phonenumbers.PhoneNumberUtil.PhoneNumberFormat;
22 import com.android.i18n.phonenumbers.Phonenumber.PhoneNumber;
23 import com.android.i18n.phonenumbers.ShortNumberUtil;
25 import android.content.Context;
26 import android.content.Intent;
27 import android.database.Cursor;
28 import android.location.CountryDetector;
29 import android.net.Uri;
30 import android.os.SystemProperties;
31 import android.provider.Contacts;
32 import android.provider.ContactsContract;
33 import android.text.Editable;
34 import android.text.Spannable;
35 import android.text.SpannableStringBuilder;
36 import android.text.TextUtils;
37 import android.text.style.TtsSpan;
38 import android.util.SparseIntArray;
40 import static com.android.internal.telephony.TelephonyProperties.PROPERTY_OPERATOR_IDP_STRING;
42 import java.util.Locale;
43 import java.util.regex.Matcher;
44 import java.util.regex.Pattern;
47 * Various utilities for dealing with phone number strings.
49 public class PhoneNumberUtils
54 * (See "What is a phone number?" doc)
55 * 'p' --- GSM pause character, same as comma
56 * 'n' --- GSM wild character
57 * 'w' --- GSM wait character
59 public static final char PAUSE = ',';
60 public static final char WAIT = ';';
61 public static final char WILD = 'N';
64 * Calling Line Identification Restriction (CLIR)
66 private static final String CLIR_ON = "*31#";
67 private static final String CLIR_OFF = "#31#";
71 * See TS 24.008 section 10.5.4.7 for details.
72 * These are the only really useful TOA values
74 public static final int TOA_International = 0x91;
75 public static final int TOA_Unknown = 0x81;
77 static final String LOG_TAG = "PhoneNumberUtils";
78 private static final boolean DBG = false;
81 * global-phone-number = ["+"] 1*( DIGIT / written-sep )
82 * written-sep = ("-"/".")
84 private static final Pattern GLOBAL_PHONE_NUMBER_PATTERN =
85 Pattern.compile("[\\+]?[0-9.-]+");
87 /** True if c is ISO-LATIN characters 0-9 */
90 return c >= '0' && c <= '9';
93 /** True if c is ISO-LATIN characters 0-9, *, # */
94 public final static boolean
96 return (c >= '0' && c <= '9') || c == '*' || c == '#';
99 /** True if c is ISO-LATIN characters 0-9, *, # , +, WILD */
100 public final static boolean
102 return (c >= '0' && c <= '9') || c == '*' || c == '#' || c == '+' || c == WILD;
105 /** True if c is ISO-LATIN characters 0-9, *, # , + (no WILD) */
106 public final static boolean
107 isReallyDialable(char c) {
108 return (c >= '0' && c <= '9') || c == '*' || c == '#' || c == '+';
111 /** True if c is ISO-LATIN characters 0-9, *, # , +, WILD, WAIT, PAUSE */
112 public final static boolean
113 isNonSeparator(char c) {
114 return (c >= '0' && c <= '9') || c == '*' || c == '#' || c == '+'
115 || c == WILD || c == WAIT || c == PAUSE;
118 /** This any anything to the right of this char is part of the
119 * post-dial string (eg this is PAUSE or WAIT)
121 public final static boolean
122 isStartsPostDial (char c) {
123 return c == PAUSE || c == WAIT;
126 private static boolean
128 return c == 'p'||c == 'P';
131 private static boolean
133 return c == 'w'||c == 'W';
137 /** Returns true if ch is not dialable or alpha char */
138 private static boolean isSeparator(char ch) {
139 return !isDialable(ch) && !(('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z'));
142 /** Extracts the phone number from an Intent.
144 * @param intent the intent to get the number of
145 * @param context a context to use for database access
147 * @return the phone number that would be called by the intent, or
148 * <code>null</code> if the number cannot be found.
150 public static String getNumberFromIntent(Intent intent, Context context) {
151 String number = null;
153 Uri uri = intent.getData();
159 String scheme = uri.getScheme();
161 if (scheme.equals("tel") || scheme.equals("sip")) {
162 return uri.getSchemeSpecificPart();
165 if (context == null) {
169 String type = intent.resolveType(context);
170 String phoneColumn = null;
172 // Correctly read out the phone entry based on requested provider
173 final String authority = uri.getAuthority();
174 if (Contacts.AUTHORITY.equals(authority)) {
175 phoneColumn = Contacts.People.Phones.NUMBER;
176 } else if (ContactsContract.AUTHORITY.equals(authority)) {
177 phoneColumn = ContactsContract.CommonDataKinds.Phone.NUMBER;
180 final Cursor c = context.getContentResolver().query(uri, new String[] {
182 }, null, null, null);
185 if (c.moveToFirst()) {
186 number = c.getString(c.getColumnIndex(phoneColumn));
196 /** Extracts the network address portion and canonicalizes
197 * (filters out separators.)
198 * Network address portion is everything up to DTMF control digit
199 * separators (pause or wait), but without non-dialable characters.
201 * Please note that the GSM wild character is allowed in the result.
202 * This must be resolved before dialing.
204 * Returns null if phoneNumber == null
207 extractNetworkPortion(String phoneNumber) {
208 if (phoneNumber == null) {
212 int len = phoneNumber.length();
213 StringBuilder ret = new StringBuilder(len);
215 for (int i = 0; i < len; i++) {
216 char c = phoneNumber.charAt(i);
217 // Character.digit() supports ASCII and Unicode digits (fullwidth, Arabic-Indic, etc.)
218 int digit = Character.digit(c, 10);
221 } else if (c == '+') {
222 // Allow '+' as first character or after CLIR MMI prefix
223 String prefix = ret.toString();
224 if (prefix.length() == 0 || prefix.equals(CLIR_ON) || prefix.equals(CLIR_OFF)) {
227 } else if (isDialable(c)) {
229 } else if (isStartsPostDial (c)) {
234 return ret.toString();
238 * Extracts the network address portion and canonicalize.
240 * This function is equivalent to extractNetworkPortion(), except
241 * for allowing the PLUS character to occur at arbitrary positions
242 * in the address portion, not just the first position.
246 public static String extractNetworkPortionAlt(String phoneNumber) {
247 if (phoneNumber == null) {
251 int len = phoneNumber.length();
252 StringBuilder ret = new StringBuilder(len);
253 boolean haveSeenPlus = false;
255 for (int i = 0; i < len; i++) {
256 char c = phoneNumber.charAt(i);
265 } else if (isStartsPostDial (c)) {
270 return ret.toString();
274 * Strips separators from a phone number string.
275 * @param phoneNumber phone number to strip.
276 * @return phone string stripped of separators.
278 public static String stripSeparators(String phoneNumber) {
279 if (phoneNumber == null) {
282 int len = phoneNumber.length();
283 StringBuilder ret = new StringBuilder(len);
285 for (int i = 0; i < len; i++) {
286 char c = phoneNumber.charAt(i);
287 // Character.digit() supports ASCII and Unicode digits (fullwidth, Arabic-Indic, etc.)
288 int digit = Character.digit(c, 10);
291 } else if (isNonSeparator(c)) {
296 return ret.toString();
300 * Translates keypad letters to actual digits (e.g. 1-800-GOOG-411 will
301 * become 1-800-4664-411), and then strips all separators (e.g. 1-800-4664-411 will become
304 * @see #convertKeypadLettersToDigits(String)
305 * @see #stripSeparators(String)
309 public static String convertAndStrip(String phoneNumber) {
310 return stripSeparators(convertKeypadLettersToDigits(phoneNumber));
314 * Converts pause and tonewait pause characters
315 * to Android representation.
316 * RFC 3601 says pause is 'p' and tonewait is 'w'.
319 public static String convertPreDial(String phoneNumber) {
320 if (phoneNumber == null) {
323 int len = phoneNumber.length();
324 StringBuilder ret = new StringBuilder(len);
326 for (int i = 0; i < len; i++) {
327 char c = phoneNumber.charAt(i);
331 } else if (isToneWait(c)) {
336 return ret.toString();
339 /** or -1 if both are negative */
341 minPositive (int a, int b) {
342 if (a >= 0 && b >= 0) {
343 return (a < b) ? a : b;
344 } else if (a >= 0) { /* && b < 0 */
346 } else if (b >= 0) { /* && a < 0 */
348 } else { /* a < 0 && b < 0 */
353 private static void log(String msg) {
354 Rlog.d(LOG_TAG, msg);
356 /** index of the last character of the network portion
357 * (eg anything after is a post-dial string)
360 indexOfLastNetworkChar(String a) {
365 origLength = a.length();
367 pIndex = a.indexOf(PAUSE);
368 wIndex = a.indexOf(WAIT);
370 trimIndex = minPositive(pIndex, wIndex);
373 return origLength - 1;
375 return trimIndex - 1;
380 * Extracts the post-dial sequence of DTMF control digits, pauses, and
381 * waits. Strips separators. This string may be empty, but will not be null
382 * unless phoneNumber == null.
384 * Returns null if phoneNumber == null
388 extractPostDialPortion(String phoneNumber) {
389 if (phoneNumber == null) return null;
392 StringBuilder ret = new StringBuilder();
394 trimIndex = indexOfLastNetworkChar (phoneNumber);
396 for (int i = trimIndex + 1, s = phoneNumber.length()
399 char c = phoneNumber.charAt(i);
400 if (isNonSeparator(c)) {
405 return ret.toString();
409 * Compare phone numbers a and b, return true if they're identical enough for caller ID purposes.
411 public static boolean compare(String a, String b) {
412 // We've used loose comparation at least Eclair, which may change in the future.
414 return compare(a, b, false);
418 * Compare phone numbers a and b, and return true if they're identical
419 * enough for caller ID purposes. Checks a resource to determine whether
420 * to use a strict or loose comparison algorithm.
422 public static boolean compare(Context context, String a, String b) {
423 boolean useStrict = context.getResources().getBoolean(
424 com.android.internal.R.bool.config_use_strict_phone_number_comparation);
425 return compare(a, b, useStrict);
429 * @hide only for testing.
431 public static boolean compare(String a, String b, boolean useStrictComparation) {
432 return (useStrictComparation ? compareStrictly(a, b) : compareLoosely(a, b));
436 * Compare phone numbers a and b, return true if they're identical
437 * enough for caller ID purposes.
439 * - Compares from right to left
440 * - requires MIN_MATCH (7) characters to match
441 * - handles common trunk prefixes and international prefixes
442 * (basically, everything except the Russian trunk prefix)
444 * Note that this method does not return false even when the two phone numbers
445 * are not exactly same; rather; we can call this method "similar()", not "equals()".
449 public static boolean
450 compareLoosely(String a, String b) {
453 int numNonDialableCharsInA = 0;
454 int numNonDialableCharsInB = 0;
456 if (a == null || b == null) return a == b;
458 if (a.length() == 0 || b.length() == 0) {
462 ia = indexOfLastNetworkChar (a);
463 ib = indexOfLastNetworkChar (b);
466 while (ia >= 0 && ib >=0) {
468 boolean skipCmp = false;
472 if (!isDialable(ca)) {
475 numNonDialableCharsInA++;
480 if (!isDialable(cb)) {
483 numNonDialableCharsInB++;
487 if (cb != ca && ca != WILD && cb != WILD) {
490 ia--; ib--; matched++;
494 if (matched < MIN_MATCH) {
495 int effectiveALen = a.length() - numNonDialableCharsInA;
496 int effectiveBLen = b.length() - numNonDialableCharsInB;
499 // if the number of dialable chars in a and b match, but the matched chars < MIN_MATCH,
500 // treat them as equal (i.e. 404-04 and 40404)
501 if (effectiveALen == effectiveBLen && effectiveALen == matched) {
508 // At least one string has matched completely;
509 if (matched >= MIN_MATCH && (ia < 0 || ib < 0)) {
514 * Now, what remains must be one of the following for a
517 * - a '+' on one and a '00' or a '011' on the other
518 * - a '0' on one and a (+,00)<country code> on the other
519 * (for this, a '0' and a '00' prefix would have succeeded above)
522 if (matchIntlPrefix(a, ia + 1)
523 && matchIntlPrefix (b, ib +1)
528 if (matchTrunkPrefix(a, ia + 1)
529 && matchIntlPrefixAndCC(b, ib +1)
534 if (matchTrunkPrefix(b, ib + 1)
535 && matchIntlPrefixAndCC(a, ia +1)
546 public static boolean
547 compareStrictly(String a, String b) {
548 return compareStrictly(a, b, true);
554 public static boolean
555 compareStrictly(String a, String b, boolean acceptInvalidCCCPrefix) {
556 if (a == null || b == null) {
558 } else if (a.length() == 0 && b.length() == 0) {
562 int forwardIndexA = 0;
563 int forwardIndexB = 0;
565 CountryCallingCodeAndNewIndex cccA =
566 tryGetCountryCallingCodeAndNewIndex(a, acceptInvalidCCCPrefix);
567 CountryCallingCodeAndNewIndex cccB =
568 tryGetCountryCallingCodeAndNewIndex(b, acceptInvalidCCCPrefix);
569 boolean bothHasCountryCallingCode = false;
570 boolean okToIgnorePrefix = true;
571 boolean trunkPrefixIsOmittedA = false;
572 boolean trunkPrefixIsOmittedB = false;
573 if (cccA != null && cccB != null) {
574 if (cccA.countryCallingCode != cccB.countryCallingCode) {
575 // Different Country Calling Code. Must be different phone number.
578 // When both have ccc, do not ignore trunk prefix. Without this,
579 // "+81123123" becomes same as "+810123123" (+81 == Japan)
580 okToIgnorePrefix = false;
581 bothHasCountryCallingCode = true;
582 forwardIndexA = cccA.newIndex;
583 forwardIndexB = cccB.newIndex;
584 } else if (cccA == null && cccB == null) {
585 // When both do not have ccc, do not ignore trunk prefix. Without this,
586 // "123123" becomes same as "0123123"
587 okToIgnorePrefix = false;
590 forwardIndexA = cccA.newIndex;
592 int tmp = tryGetTrunkPrefixOmittedIndex(b, 0);
595 trunkPrefixIsOmittedA = true;
599 forwardIndexB = cccB.newIndex;
601 int tmp = tryGetTrunkPrefixOmittedIndex(b, 0);
604 trunkPrefixIsOmittedB = true;
609 int backwardIndexA = a.length() - 1;
610 int backwardIndexB = b.length() - 1;
611 while (backwardIndexA >= forwardIndexA && backwardIndexB >= forwardIndexB) {
612 boolean skip_compare = false;
613 final char chA = a.charAt(backwardIndexA);
614 final char chB = b.charAt(backwardIndexB);
615 if (isSeparator(chA)) {
619 if (isSeparator(chB)) {
633 if (okToIgnorePrefix) {
634 if ((trunkPrefixIsOmittedA && forwardIndexA <= backwardIndexA) ||
635 !checkPrefixIsIgnorable(a, forwardIndexA, backwardIndexA)) {
636 if (acceptInvalidCCCPrefix) {
637 // Maybe the code handling the special case for Thailand makes the
638 // result garbled, so disable the code and try again.
639 // e.g. "16610001234" must equal to "6610001234", but with
640 // Thailand-case handling code, they become equal to each other.
642 // Note: we select simplicity rather than adding some complicated
643 // logic here for performance(like "checking whether remaining
644 // numbers are just 66 or not"), assuming inputs are small
646 return compare(a, b, false);
651 if ((trunkPrefixIsOmittedB && forwardIndexB <= backwardIndexB) ||
652 !checkPrefixIsIgnorable(b, forwardIndexA, backwardIndexB)) {
653 if (acceptInvalidCCCPrefix) {
654 return compare(a, b, false);
660 // In the US, 1-650-555-1234 must be equal to 650-555-1234,
661 // while 090-1234-1234 must not be equal to 90-1234-1234 in Japan.
662 // This request exists just in US (with 1 trunk (NDD) prefix).
663 // In addition, "011 11 7005554141" must not equal to "+17005554141",
664 // while "011 1 7005554141" must equal to "+17005554141"
666 // In this comparison, we ignore the prefix '1' just once, when
667 // - at least either does not have CCC, or
668 // - the remaining non-separator number is 1
669 boolean maybeNamp = !bothHasCountryCallingCode;
670 while (backwardIndexA >= forwardIndexA) {
671 final char chA = a.charAt(backwardIndexA);
672 if (isDialable(chA)) {
673 if (maybeNamp && tryGetISODigit(chA) == 1) {
681 while (backwardIndexB >= forwardIndexB) {
682 final char chB = b.charAt(backwardIndexB);
683 if (isDialable(chB)) {
684 if (maybeNamp && tryGetISODigit(chB) == 1) {
698 * Returns the rightmost MIN_MATCH (5) characters in the network portion
699 * in *reversed* order
701 * This can be used to do a database lookup against the column
702 * that stores getStrippedReversed()
704 * Returns null if phoneNumber == null
707 toCallerIDMinMatch(String phoneNumber) {
708 String np = extractNetworkPortionAlt(phoneNumber);
709 return internalGetStrippedReversed(np, MIN_MATCH);
713 * Returns the network portion reversed.
714 * This string is intended to go into an index column for a
717 * Returns null if phoneNumber == null
720 getStrippedReversed(String phoneNumber) {
721 String np = extractNetworkPortionAlt(phoneNumber);
723 if (np == null) return null;
725 return internalGetStrippedReversed(np, np.length());
729 * Returns the last numDigits of the reversed phone number
730 * Returns null if np == null
732 private static String
733 internalGetStrippedReversed(String np, int numDigits) {
734 if (np == null) return null;
736 StringBuilder ret = new StringBuilder(numDigits);
737 int length = np.length();
739 for (int i = length - 1, s = length
740 ; i >= 0 && (s - i) <= numDigits ; i--
742 char c = np.charAt(i);
747 return ret.toString();
751 * Basically: makes sure there's a + in front of a
752 * TOA_International number
754 * Returns null if s == null
757 stringFromStringAndTOA(String s, int TOA) {
758 if (s == null) return null;
760 if (TOA == TOA_International && s.length() > 0 && s.charAt(0) != '+') {
768 * Returns the TOA for the given dial string
769 * Basically, returns TOA_International if there's a + prefix
773 toaFromString(String s) {
774 if (s != null && s.length() > 0 && s.charAt(0) == '+') {
775 return TOA_International;
782 * 3GPP TS 24.008 10.5.4.7
783 * Called Party BCD Number
785 * See Also TS 51.011 10.5.1 "dialing number/ssc string"
786 * and TS 11.11 "10.3.1 EF adn (Abbreviated dialing numbers)"
788 * @param bytes the data buffer
789 * @param offset should point to the TOA (aka. TON/NPI) octet after the length byte
790 * @param length is the number of bytes including TOA byte
791 * and must be at least 2
793 * @return partial string on invalid decode
795 * FIXME(mkf) support alphanumeric address type
796 * currently implemented in SMSMessage.getAddress()
799 calledPartyBCDToString (byte[] bytes, int offset, int length) {
800 boolean prependPlus = false;
801 StringBuilder ret = new StringBuilder(1 + length * 2);
807 //Only TON field should be taken in consideration
808 if ((bytes[offset] & 0xf0) == (TOA_International & 0xf0)) {
812 internalCalledPartyBCDFragmentToString(
813 ret, bytes, offset + 1, length - 1);
815 if (prependPlus && ret.length() == 0) {
816 // If the only thing there is a prepended plus, return ""
821 // This is an "international number" and should have
822 // a plus prepended to the dialing number. But there
823 // can also be GSM MMI codes as defined in TS 22.030 6.5.2
824 // so we need to handle those also.
826 // http://web.telia.com/~u47904776/gsmkode.htm
827 // has a nice list of some of these GSM codes.
830 // **21*+886988171479#
841 // Odd ball cases that some phones handled
842 // where there is no dialing number so they
846 String retString = ret.toString();
847 Pattern p = Pattern.compile("(^[#*])(.*)([#*])(.*)(#)$");
848 Matcher m = p.matcher(retString);
850 if ("".equals(m.group(2))) {
851 // Started with two [#*] ends with #
852 // So no dialing number and we'll just
853 // append a +, this handles **21#+
854 ret = new StringBuilder();
855 ret.append(m.group(1));
856 ret.append(m.group(3));
857 ret.append(m.group(4));
858 ret.append(m.group(5));
861 // Starts with [#*] and ends with #
862 // Assume group 4 is a dialing number
863 // such as *21*+1234554#
864 ret = new StringBuilder();
865 ret.append(m.group(1));
866 ret.append(m.group(2));
867 ret.append(m.group(3));
869 ret.append(m.group(4));
870 ret.append(m.group(5));
873 p = Pattern.compile("(^[#*])(.*)([#*])(.*)");
874 m = p.matcher(retString);
876 // Starts with [#*] and only one other [#*]
877 // Assume the data after last [#*] is dialing
878 // number (i.e. group 4) such as *31#+11234567890.
879 // This also includes the odd ball *21#+
880 ret = new StringBuilder();
881 ret.append(m.group(1));
882 ret.append(m.group(2));
883 ret.append(m.group(3));
885 ret.append(m.group(4));
887 // Does NOT start with [#*] just prepend '+'
888 ret = new StringBuilder();
890 ret.append(retString);
895 return ret.toString();
899 internalCalledPartyBCDFragmentToString(
900 StringBuilder sb, byte [] bytes, int offset, int length) {
901 for (int i = offset ; i < length + offset ; i++) {
905 c = bcdToChar((byte)(bytes[i] & 0xf));
912 // FIXME(mkf) TS 23.040 9.1.2.3 says
913 // "if a mobile receives 1111 in a position prior to
914 // the last semi-octet then processing shall commence with
915 // the next semi-octet and the intervening
916 // semi-octet shall be ignored"
917 // How does this jive with 24.008 10.5.4.7
919 b = (byte)((bytes[i] >> 4) & 0xf);
921 if (b == 0xf && i + 1 == length + offset) {
937 * Like calledPartyBCDToString, but field does not start with a
938 * TOA byte. For example: SIM ADN extension fields
942 calledPartyBCDFragmentToString(byte [] bytes, int offset, int length) {
943 StringBuilder ret = new StringBuilder(length * 2);
945 internalCalledPartyBCDFragmentToString(ret, bytes, offset, length);
947 return ret.toString();
950 /** returns 0 on invalid value */
954 return (char)('0' + b);
956 case 0xa: return '*';
957 case 0xb: return '#';
958 case 0xc: return PAUSE;
959 case 0xd: return WILD;
967 if (c >= '0' && c <= '9') {
969 } else if (c == '*') {
971 } else if (c == '#') {
973 } else if (c == PAUSE) {
975 } else if (c == WILD) {
977 } else if (c == WAIT) {
980 throw new RuntimeException ("invalid char for BCD " + c);
985 * Return true iff the network portion of <code>address</code> is,
986 * as far as we can tell on the device, suitable for use as an SMS
987 * destination address.
989 public static boolean isWellFormedSmsAddress(String address) {
990 String networkPortion =
991 PhoneNumberUtils.extractNetworkPortion(address);
993 return (!(networkPortion.equals("+")
994 || TextUtils.isEmpty(networkPortion)))
995 && isDialable(networkPortion);
998 public static boolean isGlobalPhoneNumber(String phoneNumber) {
999 if (TextUtils.isEmpty(phoneNumber)) {
1003 Matcher match = GLOBAL_PHONE_NUMBER_PATTERN.matcher(phoneNumber);
1004 return match.matches();
1007 private static boolean isDialable(String address) {
1008 for (int i = 0, count = address.length(); i < count; i++) {
1009 if (!isDialable(address.charAt(i))) {
1016 private static boolean isNonSeparator(String address) {
1017 for (int i = 0, count = address.length(); i < count; i++) {
1018 if (!isNonSeparator(address.charAt(i))) {
1025 * Note: calls extractNetworkPortion(), so do not use for
1026 * SIM EF[ADN] style records
1028 * Returns null if network portion is empty.
1030 public static byte[]
1031 networkPortionToCalledPartyBCD(String s) {
1032 String networkPortion = extractNetworkPortion(s);
1033 return numberToCalledPartyBCDHelper(networkPortion, false);
1037 * Same as {@link #networkPortionToCalledPartyBCD}, but includes a
1038 * one-byte length prefix.
1040 public static byte[]
1041 networkPortionToCalledPartyBCDWithLength(String s) {
1042 String networkPortion = extractNetworkPortion(s);
1043 return numberToCalledPartyBCDHelper(networkPortion, true);
1047 * Convert a dialing number to BCD byte array
1049 * @param number dialing number string
1050 * if the dialing number starts with '+', set to international TOA
1051 * @return BCD byte array
1053 public static byte[]
1054 numberToCalledPartyBCD(String number) {
1055 return numberToCalledPartyBCDHelper(number, false);
1059 * If includeLength is true, prepend a one-byte length value to
1062 private static byte[]
1063 numberToCalledPartyBCDHelper(String number, boolean includeLength) {
1064 int numberLenReal = number.length();
1065 int numberLenEffective = numberLenReal;
1066 boolean hasPlus = number.indexOf('+') != -1;
1067 if (hasPlus) numberLenEffective--;
1069 if (numberLenEffective == 0) return null;
1071 int resultLen = (numberLenEffective + 1) / 2; // Encoded numbers require only 4 bits each.
1072 int extraBytes = 1; // Prepended TOA byte.
1073 if (includeLength) extraBytes++; // Optional prepended length byte.
1074 resultLen += extraBytes;
1076 byte[] result = new byte[resultLen];
1079 for (int i = 0; i < numberLenReal; i++) {
1080 char c = number.charAt(i);
1081 if (c == '+') continue;
1082 int shift = ((digitCount & 0x01) == 1) ? 4 : 0;
1083 result[extraBytes + (digitCount >> 1)] |= (byte)((charToBCD(c) & 0x0F) << shift);
1087 // 1-fill any trailing odd nibble/quartet.
1088 if ((digitCount & 0x01) == 1) result[extraBytes + (digitCount >> 1)] |= 0xF0;
1091 if (includeLength) result[offset++] = (byte)(resultLen - 1);
1092 result[offset] = (byte)(hasPlus ? TOA_International : TOA_Unknown);
1097 //================ Number formatting =========================
1099 /** The current locale is unknown, look for a country code or don't format */
1100 public static final int FORMAT_UNKNOWN = 0;
1101 /** NANP formatting */
1102 public static final int FORMAT_NANP = 1;
1103 /** Japanese formatting */
1104 public static final int FORMAT_JAPAN = 2;
1106 /** List of country codes for countries that use the NANP */
1107 private static final String[] NANP_COUNTRIES = new String[] {
1108 "US", // United States
1110 "AS", // American Samoa
1112 "AG", // Antigua and Barbuda
1116 "VG", // British Virgin Islands
1117 "KY", // Cayman Islands
1119 "DO", // Dominican Republic
1123 "PR", // Puerto Rico
1125 "MP", // Northern Mariana Islands
1126 "KN", // Saint Kitts and Nevis
1127 "LC", // Saint Lucia
1128 "VC", // Saint Vincent and the Grenadines
1129 "TT", // Trinidad and Tobago
1130 "TC", // Turks and Caicos Islands
1131 "VI", // U.S. Virgin Islands
1135 * Breaks the given number down and formats it according to the rules
1136 * for the country the number is from.
1138 * @param source The phone number to format
1139 * @return A locally acceptable formatting of the input, or the raw input if
1140 * formatting rules aren't known for the number
1142 * @deprecated Use link #formatNumber(String phoneNumber, String defaultCountryIso) instead
1145 public static String formatNumber(String source) {
1146 SpannableStringBuilder text = new SpannableStringBuilder(source);
1147 formatNumber(text, getFormatTypeForLocale(Locale.getDefault()));
1148 return text.toString();
1152 * Formats the given number with the given formatting type. Currently
1153 * {@link #FORMAT_NANP} and {@link #FORMAT_JAPAN} are supported as a formating type.
1155 * @param source the phone number to format
1156 * @param defaultFormattingType The default formatting rules to apply if the number does
1157 * not begin with +[country_code]
1158 * @return The phone number formatted with the given formatting type.
1161 * @deprecated Use link #formatNumber(String phoneNumber, String defaultCountryIso) instead
1164 public static String formatNumber(String source, int defaultFormattingType) {
1165 SpannableStringBuilder text = new SpannableStringBuilder(source);
1166 formatNumber(text, defaultFormattingType);
1167 return text.toString();
1171 * Returns the phone number formatting type for the given locale.
1173 * @param locale The locale of interest, usually {@link Locale#getDefault()}
1174 * @return The formatting type for the given locale, or FORMAT_UNKNOWN if the formatting
1175 * rules are not known for the given locale
1177 * @deprecated Use link #formatNumber(String phoneNumber, String defaultCountryIso) instead
1180 public static int getFormatTypeForLocale(Locale locale) {
1181 String country = locale.getCountry();
1183 return getFormatTypeFromCountryCode(country);
1187 * Formats a phone number in-place. Currently {@link #FORMAT_JAPAN} and {@link #FORMAT_NANP}
1188 * is supported as a second argument.
1190 * @param text The number to be formatted, will be modified with the formatting
1191 * @param defaultFormattingType The default formatting rules to apply if the number does
1192 * not begin with +[country_code]
1194 * @deprecated Use link #formatNumber(String phoneNumber, String defaultCountryIso) instead
1197 public static void formatNumber(Editable text, int defaultFormattingType) {
1198 int formatType = defaultFormattingType;
1200 if (text.length() > 2 && text.charAt(0) == '+') {
1201 if (text.charAt(1) == '1') {
1202 formatType = FORMAT_NANP;
1203 } else if (text.length() >= 3 && text.charAt(1) == '8'
1204 && text.charAt(2) == '1') {
1205 formatType = FORMAT_JAPAN;
1207 formatType = FORMAT_UNKNOWN;
1211 switch (formatType) {
1213 formatNanpNumber(text);
1216 formatJapaneseNumber(text);
1218 case FORMAT_UNKNOWN:
1224 private static final int NANP_STATE_DIGIT = 1;
1225 private static final int NANP_STATE_PLUS = 2;
1226 private static final int NANP_STATE_ONE = 3;
1227 private static final int NANP_STATE_DASH = 4;
1230 * Formats a phone number in-place using the NANP formatting rules. Numbers will be formatted
1241 * @param text the number to be formatted, will be modified with the formatting
1243 * @deprecated Use link #formatNumber(String phoneNumber, String defaultCountryIso) instead
1246 public static void formatNanpNumber(Editable text) {
1247 int length = text.length();
1248 if (length > "+1-nnn-nnn-nnnn".length()) {
1249 // The string is too long to be formatted
1251 } else if (length <= 5) {
1252 // The string is either a shortcode or too short to be formatted
1256 CharSequence saved = text.subSequence(0, length);
1258 // Strip the dashes first, as we're going to add them back
1260 length = text.length();
1262 // When scanning the number we record where dashes need to be added,
1263 // if they're non-0 at the end of the scan the dashes will be added in
1264 // the proper places.
1265 int dashPositions[] = new int[3];
1268 int state = NANP_STATE_DIGIT;
1270 for (int i = 0; i < length; i++) {
1271 char c = text.charAt(i);
1274 if (numDigits == 0 || state == NANP_STATE_PLUS) {
1275 state = NANP_STATE_ONE;
1288 if (state == NANP_STATE_PLUS) {
1289 // Only NANP number supported for now
1290 text.replace(0, length, saved);
1292 } else if (state == NANP_STATE_ONE) {
1293 // Found either +1 or 1, follow it up with a dash
1294 dashPositions[numDashes++] = i;
1295 } else if (state != NANP_STATE_DASH && (numDigits == 3 || numDigits == 6)) {
1296 // Found a digit that should be after a dash that isn't
1297 dashPositions[numDashes++] = i;
1299 state = NANP_STATE_DIGIT;
1304 state = NANP_STATE_DASH;
1309 // Plus is only allowed as the first character
1310 state = NANP_STATE_PLUS;
1315 // Unknown character, bail on formatting
1316 text.replace(0, length, saved);
1321 if (numDigits == 7) {
1322 // With 7 digits we want xxx-xxxx, not xxx-xxx-x
1326 // Actually put the dashes in place
1327 for (int i = 0; i < numDashes; i++) {
1328 int pos = dashPositions[i];
1329 text.replace(pos + i, pos + i, "-");
1332 // Remove trailing dashes
1333 int len = text.length();
1335 if (text.charAt(len - 1) == '-') {
1336 text.delete(len - 1, len);
1345 * Formats a phone number in-place using the Japanese formatting rules.
1346 * Numbers will be formatted as:
1356 * @param text the number to be formatted, will be modified with
1359 * @deprecated Use link #formatNumber(String phoneNumber, String defaultCountryIso) instead
1362 public static void formatJapaneseNumber(Editable text) {
1363 JapanesePhoneNumberFormatter.format(text);
1367 * Removes all dashes from the number.
1369 * @param text the number to clear from dashes
1371 private static void removeDashes(Editable text) {
1373 while (p < text.length()) {
1374 if (text.charAt(p) == '-') {
1375 text.delete(p, p + 1);
1383 * Formats the specified {@code phoneNumber} to the E.164 representation.
1385 * @param phoneNumber the phone number to format.
1386 * @param defaultCountryIso the ISO 3166-1 two letters country code.
1387 * @return the E.164 representation, or null if the given phone number is not valid.
1389 public static String formatNumberToE164(String phoneNumber, String defaultCountryIso) {
1390 return formatNumberInternal(phoneNumber, defaultCountryIso, PhoneNumberFormat.E164);
1394 * Formats the specified {@code phoneNumber} to the RFC3966 representation.
1396 * @param phoneNumber the phone number to format.
1397 * @param defaultCountryIso the ISO 3166-1 two letters country code.
1398 * @return the RFC3966 representation, or null if the given phone number is not valid.
1400 public static String formatNumberToRFC3966(String phoneNumber, String defaultCountryIso) {
1401 return formatNumberInternal(phoneNumber, defaultCountryIso, PhoneNumberFormat.RFC3966);
1405 * Formats the raw phone number (string) using the specified {@code formatIdentifier}.
1407 * The given phone number must have an area code and could have a country code.
1409 * The defaultCountryIso is used to validate the given number and generate the formatted number
1410 * if the specified number doesn't have a country code.
1412 * @param rawPhoneNumber The phone number to format.
1413 * @param defaultCountryIso The ISO 3166-1 two letters country code.
1414 * @param formatIdentifier The (enum) identifier of the desired format.
1415 * @return the formatted representation, or null if the specified number is not valid.
1417 private static String formatNumberInternal(
1418 String rawPhoneNumber, String defaultCountryIso, PhoneNumberFormat formatIdentifier) {
1420 PhoneNumberUtil util = PhoneNumberUtil.getInstance();
1422 PhoneNumber phoneNumber = util.parse(rawPhoneNumber, defaultCountryIso);
1423 if (util.isValidNumber(phoneNumber)) {
1424 return util.format(phoneNumber, formatIdentifier);
1426 } catch (NumberParseException ignored) { }
1432 * Format a phone number.
1434 * If the given number doesn't have the country code, the phone will be
1435 * formatted to the default country's convention.
1437 * @param phoneNumber
1438 * the number to be formatted.
1439 * @param defaultCountryIso
1440 * the ISO 3166-1 two letters country code whose convention will
1441 * be used if the given number doesn't have the country code.
1442 * @return the formatted number, or null if the given number is not valid.
1444 public static String formatNumber(String phoneNumber, String defaultCountryIso) {
1445 // Do not attempt to format numbers that start with a hash or star symbol.
1446 if (phoneNumber.startsWith("#") || phoneNumber.startsWith("*")) {
1450 PhoneNumberUtil util = PhoneNumberUtil.getInstance();
1451 String result = null;
1453 PhoneNumber pn = util.parseAndKeepRawInput(phoneNumber, defaultCountryIso);
1454 result = util.formatInOriginalFormat(pn, defaultCountryIso);
1455 } catch (NumberParseException e) {
1461 * Format the phone number only if the given number hasn't been formatted.
1463 * The number which has only dailable character is treated as not being
1466 * @param phoneNumber
1467 * the number to be formatted.
1468 * @param phoneNumberE164
1469 * the E164 format number whose country code is used if the given
1470 * phoneNumber doesn't have the country code.
1471 * @param defaultCountryIso
1472 * the ISO 3166-1 two letters country code whose convention will
1473 * be used if the phoneNumberE164 is null or invalid, or if phoneNumber
1475 * @return the formatted number if the given number has been formatted,
1476 * otherwise, return the given number.
1478 public static String formatNumber(
1479 String phoneNumber, String phoneNumberE164, String defaultCountryIso) {
1480 int len = phoneNumber.length();
1481 for (int i = 0; i < len; i++) {
1482 if (!isDialable(phoneNumber.charAt(i))) {
1486 PhoneNumberUtil util = PhoneNumberUtil.getInstance();
1487 // Get the country code from phoneNumberE164
1488 if (phoneNumberE164 != null && phoneNumberE164.length() >= 2
1489 && phoneNumberE164.charAt(0) == '+') {
1491 // The number to be parsed is in E164 format, so the default region used doesn't
1493 PhoneNumber pn = util.parse(phoneNumberE164, "ZZ");
1494 String regionCode = util.getRegionCodeForNumber(pn);
1495 if (!TextUtils.isEmpty(regionCode) &&
1496 // This makes sure phoneNumber doesn't contain an IDD
1497 normalizeNumber(phoneNumber).indexOf(phoneNumberE164.substring(1)) <= 0) {
1498 defaultCountryIso = regionCode;
1500 } catch (NumberParseException e) {
1503 String result = formatNumber(phoneNumber, defaultCountryIso);
1504 return result != null ? result : phoneNumber;
1508 * Normalize a phone number by removing the characters other than digits. If
1509 * the given number has keypad letters, the letters will be converted to
1512 * @param phoneNumber the number to be normalized.
1513 * @return the normalized number.
1515 public static String normalizeNumber(String phoneNumber) {
1516 if (TextUtils.isEmpty(phoneNumber)) {
1520 StringBuilder sb = new StringBuilder();
1521 int len = phoneNumber.length();
1522 for (int i = 0; i < len; i++) {
1523 char c = phoneNumber.charAt(i);
1524 // Character.digit() supports ASCII and Unicode digits (fullwidth, Arabic-Indic, etc.)
1525 int digit = Character.digit(c, 10);
1528 } else if (sb.length() == 0 && c == '+') {
1530 } else if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) {
1531 return normalizeNumber(PhoneNumberUtils.convertKeypadLettersToDigits(phoneNumber));
1534 return sb.toString();
1538 * Replaces all unicode(e.g. Arabic, Persian) digits with their decimal digit equivalents.
1540 * @param number the number to perform the replacement on.
1541 * @return the replaced number.
1543 public static String replaceUnicodeDigits(String number) {
1544 StringBuilder normalizedDigits = new StringBuilder(number.length());
1545 for (char c : number.toCharArray()) {
1546 int digit = Character.digit(c, 10);
1548 normalizedDigits.append(digit);
1550 normalizedDigits.append(c);
1553 return normalizedDigits.toString();
1556 // Three and four digit phone numbers for either special services,
1557 // or 3-6 digit addresses from the network (eg carrier-originated SMS messages) should
1560 // This constant used to be 5, but SMS short codes has increased in length and
1561 // can be easily 6 digits now days. Most countries have SMS short code length between
1562 // 3 to 6 digits. The exceptions are
1564 // Australia: Short codes are six or eight digits in length, starting with the prefix "19"
1565 // followed by an additional four or six digits and two.
1566 // Czech Republic: Codes are seven digits in length for MO and five (not billed) or
1567 // eight (billed) for MT direction
1569 // see http://en.wikipedia.org/wiki/Short_code#Regional_differences for reference
1571 // However, in order to loose match 650-555-1212 and 555-1212, we need to set the min match
1573 static final int MIN_MATCH = 7;
1576 * Checks a given number against the list of
1577 * emergency numbers provided by the RIL and SIM card.
1579 * @param number the number to look up.
1580 * @return true if the number is in the list of emergency numbers
1581 * listed in the RIL / SIM, otherwise return false.
1583 public static boolean isEmergencyNumber(String number) {
1584 return isEmergencyNumber(getDefaultVoiceSubId(), number);
1588 * Checks a given number against the list of
1589 * emergency numbers provided by the RIL and SIM card.
1591 * @param subId the subscription id of the SIM.
1592 * @param number the number to look up.
1593 * @return true if the number is in the list of emergency numbers
1594 * listed in the RIL / SIM, otherwise return false.
1597 public static boolean isEmergencyNumber(int subId, String number) {
1598 // Return true only if the specified number *exactly* matches
1599 // one of the emergency numbers listed by the RIL / SIM.
1600 return isEmergencyNumberInternal(subId, number, true /* useExactMatch */);
1604 * Checks if given number might *potentially* result in
1605 * a call to an emergency service on the current network.
1607 * Specifically, this method will return true if the specified number
1608 * is an emergency number according to the list managed by the RIL or
1609 * SIM, *or* if the specified number simply starts with the same
1610 * digits as any of the emergency numbers listed in the RIL / SIM.
1612 * This method is intended for internal use by the phone app when
1613 * deciding whether to allow ACTION_CALL intents from 3rd party apps
1614 * (where we're required to *not* allow emergency calls to be placed.)
1616 * @param number the number to look up.
1617 * @return true if the number is in the list of emergency numbers
1618 * listed in the RIL / SIM, *or* if the number starts with the
1619 * same digits as any of those emergency numbers.
1623 public static boolean isPotentialEmergencyNumber(String number) {
1624 return isPotentialEmergencyNumber(getDefaultVoiceSubId(), number);
1628 * Checks if given number might *potentially* result in
1629 * a call to an emergency service on the current network.
1631 * Specifically, this method will return true if the specified number
1632 * is an emergency number according to the list managed by the RIL or
1633 * SIM, *or* if the specified number simply starts with the same
1634 * digits as any of the emergency numbers listed in the RIL / SIM.
1636 * This method is intended for internal use by the phone app when
1637 * deciding whether to allow ACTION_CALL intents from 3rd party apps
1638 * (where we're required to *not* allow emergency calls to be placed.)
1640 * @param subId the subscription id of the SIM.
1641 * @param number the number to look up.
1642 * @return true if the number is in the list of emergency numbers
1643 * listed in the RIL / SIM, *or* if the number starts with the
1644 * same digits as any of those emergency numbers.
1647 public static boolean isPotentialEmergencyNumber(int subId, String number) {
1648 // Check against the emergency numbers listed by the RIL / SIM,
1649 // and *don't* require an exact match.
1650 return isEmergencyNumberInternal(subId, number, false /* useExactMatch */);
1654 * Helper function for isEmergencyNumber(String) and
1655 * isPotentialEmergencyNumber(String).
1657 * @param number the number to look up.
1659 * @param useExactMatch if true, consider a number to be an emergency
1660 * number only if it *exactly* matches a number listed in
1661 * the RIL / SIM. If false, a number is considered to be an
1662 * emergency number if it simply starts with the same digits
1663 * as any of the emergency numbers listed in the RIL / SIM.
1664 * (Setting useExactMatch to false allows you to identify
1665 * number that could *potentially* result in emergency calls
1666 * since many networks will actually ignore trailing digits
1667 * after a valid emergency number.)
1669 * @return true if the number is in the list of emergency numbers
1670 * listed in the RIL / sim, otherwise return false.
1672 private static boolean isEmergencyNumberInternal(String number, boolean useExactMatch) {
1673 return isEmergencyNumberInternal(getDefaultVoiceSubId(), number, useExactMatch);
1677 * Helper function for isEmergencyNumber(String) and
1678 * isPotentialEmergencyNumber(String).
1680 * @param subId the subscription id of the SIM.
1681 * @param number the number to look up.
1683 * @param useExactMatch if true, consider a number to be an emergency
1684 * number only if it *exactly* matches a number listed in
1685 * the RIL / SIM. If false, a number is considered to be an
1686 * emergency number if it simply starts with the same digits
1687 * as any of the emergency numbers listed in the RIL / SIM.
1688 * (Setting useExactMatch to false allows you to identify
1689 * number that could *potentially* result in emergency calls
1690 * since many networks will actually ignore trailing digits
1691 * after a valid emergency number.)
1693 * @return true if the number is in the list of emergency numbers
1694 * listed in the RIL / sim, otherwise return false.
1696 private static boolean isEmergencyNumberInternal(int subId, String number,
1697 boolean useExactMatch) {
1698 return isEmergencyNumberInternal(subId, number, null, useExactMatch);
1702 * Checks if a given number is an emergency number for a specific country.
1704 * @param number the number to look up.
1705 * @param defaultCountryIso the specific country which the number should be checked against
1706 * @return if the number is an emergency number for the specific country, then return true,
1711 public static boolean isEmergencyNumber(String number, String defaultCountryIso) {
1712 return isEmergencyNumber(getDefaultVoiceSubId(), number, defaultCountryIso);
1716 * Checks if a given number is an emergency number for a specific country.
1718 * @param subId the subscription id of the SIM.
1719 * @param number the number to look up.
1720 * @param defaultCountryIso the specific country which the number should be checked against
1721 * @return if the number is an emergency number for the specific country, then return true,
1725 public static boolean isEmergencyNumber(int subId, String number, String defaultCountryIso) {
1726 return isEmergencyNumberInternal(subId, number,
1728 true /* useExactMatch */);
1732 * Checks if a given number might *potentially* result in a call to an
1733 * emergency service, for a specific country.
1735 * Specifically, this method will return true if the specified number
1736 * is an emergency number in the specified country, *or* if the number
1737 * simply starts with the same digits as any emergency number for that
1740 * This method is intended for internal use by the phone app when
1741 * deciding whether to allow ACTION_CALL intents from 3rd party apps
1742 * (where we're required to *not* allow emergency calls to be placed.)
1744 * @param number the number to look up.
1745 * @param defaultCountryIso the specific country which the number should be checked against
1746 * @return true if the number is an emergency number for the specific
1747 * country, *or* if the number starts with the same digits as
1748 * any of those emergency numbers.
1752 public static boolean isPotentialEmergencyNumber(String number, String defaultCountryIso) {
1753 return isPotentialEmergencyNumber(getDefaultVoiceSubId(), number, defaultCountryIso);
1757 * Checks if a given number might *potentially* result in a call to an
1758 * emergency service, for a specific country.
1760 * Specifically, this method will return true if the specified number
1761 * is an emergency number in the specified country, *or* if the number
1762 * simply starts with the same digits as any emergency number for that
1765 * This method is intended for internal use by the phone app when
1766 * deciding whether to allow ACTION_CALL intents from 3rd party apps
1767 * (where we're required to *not* allow emergency calls to be placed.)
1769 * @param subId the subscription id of the SIM.
1770 * @param number the number to look up.
1771 * @param defaultCountryIso the specific country which the number should be checked against
1772 * @return true if the number is an emergency number for the specific
1773 * country, *or* if the number starts with the same digits as
1774 * any of those emergency numbers.
1777 public static boolean isPotentialEmergencyNumber(int subId, String number,
1778 String defaultCountryIso) {
1779 return isEmergencyNumberInternal(subId, number,
1781 false /* useExactMatch */);
1785 * Helper function for isEmergencyNumber(String, String) and
1786 * isPotentialEmergencyNumber(String, String).
1788 * @param number the number to look up.
1789 * @param defaultCountryIso the specific country which the number should be checked against
1790 * @param useExactMatch if true, consider a number to be an emergency
1791 * number only if it *exactly* matches a number listed in
1792 * the RIL / SIM. If false, a number is considered to be an
1793 * emergency number if it simply starts with the same digits
1794 * as any of the emergency numbers listed in the RIL / SIM.
1796 * @return true if the number is an emergency number for the specified country.
1798 private static boolean isEmergencyNumberInternal(String number,
1799 String defaultCountryIso,
1800 boolean useExactMatch) {
1801 return isEmergencyNumberInternal(getDefaultVoiceSubId(), number, defaultCountryIso,
1806 * Helper function for isEmergencyNumber(String, String) and
1807 * isPotentialEmergencyNumber(String, String).
1809 * @param subId the subscription id of the SIM.
1810 * @param number the number to look up.
1811 * @param defaultCountryIso the specific country which the number should be checked against
1812 * @param useExactMatch if true, consider a number to be an emergency
1813 * number only if it *exactly* matches a number listed in
1814 * the RIL / SIM. If false, a number is considered to be an
1815 * emergency number if it simply starts with the same digits
1816 * as any of the emergency numbers listed in the RIL / SIM.
1818 * @return true if the number is an emergency number for the specified country.
1821 private static boolean isEmergencyNumberInternal(int subId, String number,
1822 String defaultCountryIso,
1823 boolean useExactMatch) {
1824 // If the number passed in is null, just return false:
1825 if (number == null) return false;
1827 // If the number passed in is a SIP address, return false, since the
1828 // concept of "emergency numbers" is only meaningful for calls placed
1829 // over the cell network.
1830 // (Be sure to do this check *before* calling extractNetworkPortionAlt(),
1831 // since the whole point of extractNetworkPortionAlt() is to filter out
1832 // any non-dialable characters (which would turn 'abc911def@example.com'
1833 // into '911', for example.))
1834 if (isUriNumber(number)) {
1838 // Strip the separators from the number before comparing it
1840 number = extractNetworkPortionAlt(number);
1842 Rlog.d(LOG_TAG, "subId:" + subId + ", defaultCountryIso:" +
1843 ((defaultCountryIso == null) ? "NULL" : defaultCountryIso));
1845 String emergencyNumbers = "";
1846 int slotId = SubscriptionManager.getSlotId(subId);
1848 // retrieve the list of emergency numbers
1849 // check read-write ecclist property first
1850 String ecclist = (slotId <= 0) ? "ril.ecclist" : ("ril.ecclist" + slotId);
1852 emergencyNumbers = SystemProperties.get(ecclist, "");
1854 Rlog.d(LOG_TAG, "slotId:" + slotId + ", emergencyNumbers: " + emergencyNumbers);
1856 if (TextUtils.isEmpty(emergencyNumbers)) {
1857 // then read-only ecclist property since old RIL only uses this
1858 emergencyNumbers = SystemProperties.get("ro.ril.ecclist");
1861 if (!TextUtils.isEmpty(emergencyNumbers)) {
1862 // searches through the comma-separated list for a match,
1863 // return true if one is found.
1864 for (String emergencyNum : emergencyNumbers.split(",")) {
1865 // It is not possible to append additional digits to an emergency number to dial
1866 // the number in Brazil - it won't connect.
1867 if (useExactMatch || "BR".equalsIgnoreCase(defaultCountryIso)) {
1868 if (number.equals(emergencyNum)) {
1872 if (number.startsWith(emergencyNum)) {
1877 // no matches found against the list!
1881 Rlog.d(LOG_TAG, "System property doesn't provide any emergency numbers."
1882 + " Use embedded logic for determining ones.");
1884 // If slot id is invalid, means that there is no sim card.
1885 // According spec 3GPP TS22.101, the following numbers should be
1886 // ECC numbers when SIM/USIM is not present.
1887 emergencyNumbers = ((slotId < 0) ? "112,911,000,08,110,118,119,999" : "112,911");
1889 for (String emergencyNum : emergencyNumbers.split(",")) {
1890 if (useExactMatch) {
1891 if (number.equals(emergencyNum)) {
1895 if (number.startsWith(emergencyNum)) {
1901 // No ecclist system property, so use our own list.
1902 if (defaultCountryIso != null) {
1903 ShortNumberUtil util = new ShortNumberUtil();
1904 if (useExactMatch) {
1905 return util.isEmergencyNumber(number, defaultCountryIso);
1907 return util.connectsToEmergencyNumber(number, defaultCountryIso);
1915 * Checks if a given number is an emergency number for the country that the user is in.
1917 * @param number the number to look up.
1918 * @param context the specific context which the number should be checked against
1919 * @return true if the specified number is an emergency number for the country the user
1922 public static boolean isLocalEmergencyNumber(Context context, String number) {
1923 return isLocalEmergencyNumber(context, getDefaultVoiceSubId(), number);
1927 * Checks if a given number is an emergency number for the country that the user is in.
1929 * @param subId the subscription id of the SIM.
1930 * @param number the number to look up.
1931 * @param context the specific context which the number should be checked against
1932 * @return true if the specified number is an emergency number for the country the user
1936 public static boolean isLocalEmergencyNumber(Context context, int subId, String number) {
1937 return isLocalEmergencyNumberInternal(subId, number,
1939 true /* useExactMatch */);
1943 * Checks if a given number might *potentially* result in a call to an
1944 * emergency service, for the country that the user is in. The current
1945 * country is determined using the CountryDetector.
1947 * Specifically, this method will return true if the specified number
1948 * is an emergency number in the current country, *or* if the number
1949 * simply starts with the same digits as any emergency number for the
1952 * This method is intended for internal use by the phone app when
1953 * deciding whether to allow ACTION_CALL intents from 3rd party apps
1954 * (where we're required to *not* allow emergency calls to be placed.)
1956 * @param number the number to look up.
1957 * @param context the specific context which the number should be checked against
1958 * @return true if the specified number is an emergency number for a local country, based on the
1961 * @see android.location.CountryDetector
1964 public static boolean isPotentialLocalEmergencyNumber(Context context, String number) {
1965 return isPotentialLocalEmergencyNumber(context, getDefaultVoiceSubId(), number);
1969 * Checks if a given number might *potentially* result in a call to an
1970 * emergency service, for the country that the user is in. The current
1971 * country is determined using the CountryDetector.
1973 * Specifically, this method will return true if the specified number
1974 * is an emergency number in the current country, *or* if the number
1975 * simply starts with the same digits as any emergency number for the
1978 * This method is intended for internal use by the phone app when
1979 * deciding whether to allow ACTION_CALL intents from 3rd party apps
1980 * (where we're required to *not* allow emergency calls to be placed.)
1982 * @param subId the subscription id of the SIM.
1983 * @param number the number to look up.
1984 * @param context the specific context which the number should be checked against
1985 * @return true if the specified number is an emergency number for a local country, based on the
1990 public static boolean isPotentialLocalEmergencyNumber(Context context, int subId,
1992 return isLocalEmergencyNumberInternal(subId, number,
1994 false /* useExactMatch */);
1998 * Helper function for isLocalEmergencyNumber() and
1999 * isPotentialLocalEmergencyNumber().
2001 * @param number the number to look up.
2002 * @param context the specific context which the number should be checked against
2003 * @param useExactMatch if true, consider a number to be an emergency
2004 * number only if it *exactly* matches a number listed in
2005 * the RIL / SIM. If false, a number is considered to be an
2006 * emergency number if it simply starts with the same digits
2007 * as any of the emergency numbers listed in the RIL / SIM.
2009 * @return true if the specified number is an emergency number for a
2010 * local country, based on the CountryDetector.
2012 * @see android.location.CountryDetector
2015 private static boolean isLocalEmergencyNumberInternal(String number,
2017 boolean useExactMatch) {
2018 return isLocalEmergencyNumberInternal(getDefaultVoiceSubId(), number, context,
2023 * Helper function for isLocalEmergencyNumber() and
2024 * isPotentialLocalEmergencyNumber().
2026 * @param subId the subscription id of the SIM.
2027 * @param number the number to look up.
2028 * @param context the specific context which the number should be checked against
2029 * @param useExactMatch if true, consider a number to be an emergency
2030 * number only if it *exactly* matches a number listed in
2031 * the RIL / SIM. If false, a number is considered to be an
2032 * emergency number if it simply starts with the same digits
2033 * as any of the emergency numbers listed in the RIL / SIM.
2035 * @return true if the specified number is an emergency number for a
2036 * local country, based on the CountryDetector.
2039 private static boolean isLocalEmergencyNumberInternal(int subId, String number,
2041 boolean useExactMatch) {
2043 CountryDetector detector = (CountryDetector) context.getSystemService(
2044 Context.COUNTRY_DETECTOR);
2045 if (detector != null && detector.detectCountry() != null) {
2046 countryIso = detector.detectCountry().getCountryIso();
2048 Locale locale = context.getResources().getConfiguration().locale;
2049 countryIso = locale.getCountry();
2050 Rlog.w(LOG_TAG, "No CountryDetector; falling back to countryIso based on locale: "
2053 return isEmergencyNumberInternal(subId, number, countryIso, useExactMatch);
2057 * isVoiceMailNumber: checks a given number against the voicemail
2058 * number provided by the RIL and SIM card. The caller must have
2059 * the READ_PHONE_STATE credential.
2061 * @param number the number to look up.
2062 * @return true if the number is in the list of voicemail. False
2063 * otherwise, including if the caller does not have the permission
2064 * to read the VM number.
2066 public static boolean isVoiceMailNumber(String number) {
2067 return isVoiceMailNumber(SubscriptionManager.getDefaultSubId(), number);
2071 * isVoiceMailNumber: checks a given number against the voicemail
2072 * number provided by the RIL and SIM card. The caller must have
2073 * the READ_PHONE_STATE credential.
2075 * @param subId the subscription id of the SIM.
2076 * @param number the number to look up.
2077 * @return true if the number is in the list of voicemail. False
2078 * otherwise, including if the caller does not have the permission
2079 * to read the VM number.
2082 public static boolean isVoiceMailNumber(int subId, String number) {
2086 vmNumber = TelephonyManager.getDefault().getVoiceMailNumber(subId);
2087 } catch (SecurityException ex) {
2091 // Strip the separators from the number before comparing it
2093 number = extractNetworkPortionAlt(number);
2095 // compare tolerates null so we need to make sure that we
2096 // don't return true when both are null.
2097 return !TextUtils.isEmpty(number) && compare(number, vmNumber);
2101 * Translates any alphabetic letters (i.e. [A-Za-z]) in the
2102 * specified phone number into the equivalent numeric digits,
2103 * according to the phone keypad letter mapping described in
2104 * ITU E.161 and ISO/IEC 9995-8.
2106 * @return the input string, with alpha letters converted to numeric
2107 * digits using the phone keypad letter mapping. For example,
2108 * an input of "1-800-GOOG-411" will return "1-800-4664-411".
2110 public static String convertKeypadLettersToDigits(String input) {
2111 if (input == null) {
2114 int len = input.length();
2119 char[] out = input.toCharArray();
2121 for (int i = 0; i < len; i++) {
2123 // If this char isn't in KEYPAD_MAP at all, just leave it alone.
2124 out[i] = (char) KEYPAD_MAP.get(c, c);
2127 return new String(out);
2131 * The phone keypad letter mapping (see ITU E.161 or ISO/IEC 9995-8.)
2132 * TODO: This should come from a resource.
2134 private static final SparseIntArray KEYPAD_MAP = new SparseIntArray();
2136 KEYPAD_MAP.put('a', '2'); KEYPAD_MAP.put('b', '2'); KEYPAD_MAP.put('c', '2');
2137 KEYPAD_MAP.put('A', '2'); KEYPAD_MAP.put('B', '2'); KEYPAD_MAP.put('C', '2');
2139 KEYPAD_MAP.put('d', '3'); KEYPAD_MAP.put('e', '3'); KEYPAD_MAP.put('f', '3');
2140 KEYPAD_MAP.put('D', '3'); KEYPAD_MAP.put('E', '3'); KEYPAD_MAP.put('F', '3');
2142 KEYPAD_MAP.put('g', '4'); KEYPAD_MAP.put('h', '4'); KEYPAD_MAP.put('i', '4');
2143 KEYPAD_MAP.put('G', '4'); KEYPAD_MAP.put('H', '4'); KEYPAD_MAP.put('I', '4');
2145 KEYPAD_MAP.put('j', '5'); KEYPAD_MAP.put('k', '5'); KEYPAD_MAP.put('l', '5');
2146 KEYPAD_MAP.put('J', '5'); KEYPAD_MAP.put('K', '5'); KEYPAD_MAP.put('L', '5');
2148 KEYPAD_MAP.put('m', '6'); KEYPAD_MAP.put('n', '6'); KEYPAD_MAP.put('o', '6');
2149 KEYPAD_MAP.put('M', '6'); KEYPAD_MAP.put('N', '6'); KEYPAD_MAP.put('O', '6');
2151 KEYPAD_MAP.put('p', '7'); KEYPAD_MAP.put('q', '7'); KEYPAD_MAP.put('r', '7'); KEYPAD_MAP.put('s', '7');
2152 KEYPAD_MAP.put('P', '7'); KEYPAD_MAP.put('Q', '7'); KEYPAD_MAP.put('R', '7'); KEYPAD_MAP.put('S', '7');
2154 KEYPAD_MAP.put('t', '8'); KEYPAD_MAP.put('u', '8'); KEYPAD_MAP.put('v', '8');
2155 KEYPAD_MAP.put('T', '8'); KEYPAD_MAP.put('U', '8'); KEYPAD_MAP.put('V', '8');
2157 KEYPAD_MAP.put('w', '9'); KEYPAD_MAP.put('x', '9'); KEYPAD_MAP.put('y', '9'); KEYPAD_MAP.put('z', '9');
2158 KEYPAD_MAP.put('W', '9'); KEYPAD_MAP.put('X', '9'); KEYPAD_MAP.put('Y', '9'); KEYPAD_MAP.put('Z', '9');
2161 //================ Plus Code formatting =========================
2162 private static final char PLUS_SIGN_CHAR = '+';
2163 private static final String PLUS_SIGN_STRING = "+";
2164 private static final String NANP_IDP_STRING = "011";
2165 private static final int NANP_LENGTH = 10;
2168 * This function checks if there is a plus sign (+) in the passed-in dialing number.
2169 * If there is, it processes the plus sign based on the default telephone
2170 * numbering plan of the system when the phone is activated and the current
2171 * telephone numbering plan of the system that the phone is camped on.
2172 * Currently, we only support the case that the default and current telephone
2173 * numbering plans are North American Numbering Plan(NANP).
2175 * The passed-in dialStr should only contain the valid format as described below,
2176 * 1) the 1st character in the dialStr should be one of the really dialable
2177 * characters listed below
2178 * ISO-LATIN characters 0-9, *, # , +
2179 * 2) the dialStr should already strip out the separator characters,
2180 * every character in the dialStr should be one of the non separator characters
2182 * ISO-LATIN characters 0-9, *, # , +, WILD, WAIT, PAUSE
2184 * Otherwise, this function returns the dial string passed in
2186 * @param dialStr the original dial string
2187 * @return the converted dial string if the current/default countries belong to NANP,
2188 * and if there is the "+" in the original dial string. Otherwise, the original dial
2191 * This API is for CDMA only
2193 * @hide TODO: pending API Council approval
2195 public static String cdmaCheckAndProcessPlusCode(String dialStr) {
2196 if (!TextUtils.isEmpty(dialStr)) {
2197 if (isReallyDialable(dialStr.charAt(0)) &&
2198 isNonSeparator(dialStr)) {
2199 String currIso = TelephonyManager.getDefault().getNetworkCountryIso();
2200 String defaultIso = TelephonyManager.getDefault().getSimCountryIso();
2201 if (!TextUtils.isEmpty(currIso) && !TextUtils.isEmpty(defaultIso)) {
2202 return cdmaCheckAndProcessPlusCodeByNumberFormat(dialStr,
2203 getFormatTypeFromCountryCode(currIso),
2204 getFormatTypeFromCountryCode(defaultIso));
2212 * Process phone number for CDMA, converting plus code using the home network number format.
2213 * This is used for outgoing SMS messages.
2215 * @param dialStr the original dial string
2216 * @return the converted dial string
2217 * @hide for internal use
2219 public static String cdmaCheckAndProcessPlusCodeForSms(String dialStr) {
2220 if (!TextUtils.isEmpty(dialStr)) {
2221 if (isReallyDialable(dialStr.charAt(0)) && isNonSeparator(dialStr)) {
2222 String defaultIso = TelephonyManager.getDefault().getSimCountryIso();
2223 if (!TextUtils.isEmpty(defaultIso)) {
2224 int format = getFormatTypeFromCountryCode(defaultIso);
2225 return cdmaCheckAndProcessPlusCodeByNumberFormat(dialStr, format, format);
2233 * This function should be called from checkAndProcessPlusCode only
2234 * And it is used for test purpose also.
2236 * It checks the dial string by looping through the network portion,
2237 * post dial portion 1, post dial porting 2, etc. If there is any
2238 * plus sign, then process the plus sign.
2239 * Currently, this function supports the plus sign conversion within NANP only.
2240 * Specifically, it handles the plus sign in the following ways:
2241 * 1)+1NANP,remove +, e.g.
2242 * +18475797000 is converted to 18475797000,
2243 * 2)+NANP or +non-NANP Numbers,replace + with the current NANP IDP, e.g,
2244 * +8475797000 is converted to 0118475797000,
2245 * +11875767800 is converted to 01111875767800
2246 * 3)+1NANP in post dial string(s), e.g.
2247 * 8475797000;+18475231753 is converted to 8475797000;18475231753
2250 * @param dialStr the original dial string
2251 * @param currFormat the numbering system of the current country that the phone is camped on
2252 * @param defaultFormat the numbering system of the country that the phone is activated on
2253 * @return the converted dial string if the current/default countries belong to NANP,
2254 * and if there is the "+" in the original dial string. Otherwise, the original dial
2259 public static String
2260 cdmaCheckAndProcessPlusCodeByNumberFormat(String dialStr,int currFormat,int defaultFormat) {
2261 String retStr = dialStr;
2263 boolean useNanp = (currFormat == defaultFormat) && (currFormat == FORMAT_NANP);
2265 // Checks if the plus sign character is in the passed-in dial string
2266 if (dialStr != null &&
2267 dialStr.lastIndexOf(PLUS_SIGN_STRING) != -1) {
2269 // Handle case where default and current telephone numbering plans are NANP.
2270 String postDialStr = null;
2271 String tempDialStr = dialStr;
2273 // Sets the retStr to null since the conversion will be performed below.
2275 if (DBG) log("checkAndProcessPlusCode,dialStr=" + dialStr);
2276 // This routine is to process the plus sign in the dial string by loop through
2277 // the network portion, post dial portion 1, post dial portion 2... etc. if
2280 String networkDialStr;
2281 // Format the string based on the rules for the country the number is from,
2282 // and the current country the phone is camped
2284 networkDialStr = extractNetworkPortion(tempDialStr);
2286 networkDialStr = extractNetworkPortionAlt(tempDialStr);
2290 networkDialStr = processPlusCode(networkDialStr, useNanp);
2292 // Concatenates the string that is converted from network portion
2293 if (!TextUtils.isEmpty(networkDialStr)) {
2294 if (retStr == null) {
2295 retStr = networkDialStr;
2297 retStr = retStr.concat(networkDialStr);
2300 // This should never happen since we checked the if dialStr is null
2301 // and if it contains the plus sign in the beginning of this function.
2302 // The plus sign is part of the network portion.
2303 Rlog.e("checkAndProcessPlusCode: null newDialStr", networkDialStr);
2306 postDialStr = extractPostDialPortion(tempDialStr);
2307 if (!TextUtils.isEmpty(postDialStr)) {
2308 int dialableIndex = findDialableIndexFromPostDialStr(postDialStr);
2310 // dialableIndex should always be greater than 0
2311 if (dialableIndex >= 1) {
2312 retStr = appendPwCharBackToOrigDialStr(dialableIndex,
2313 retStr,postDialStr);
2314 // Skips the P/W character, extracts the dialable portion
2315 tempDialStr = postDialStr.substring(dialableIndex);
2317 // Non-dialable character such as P/W should not be at the end of
2318 // the dial string after P/W processing in CdmaConnection.java
2319 // Set the postDialStr to "" to break out of the loop
2320 if (dialableIndex < 0) {
2323 Rlog.e("wrong postDialStr=", postDialStr);
2326 if (DBG) log("checkAndProcessPlusCode,postDialStr=" + postDialStr);
2327 } while (!TextUtils.isEmpty(postDialStr) && !TextUtils.isEmpty(tempDialStr));
2333 * Wrap the supplied {@code CharSequence} with a {@code TtsSpan}, annotating it as
2334 * containing a phone number in its entirety.
2336 * @param phoneNumber A {@code CharSequence} the entirety of which represents a phone number.
2337 * @return A {@code CharSequence} with appropriate annotations.
2339 public static CharSequence getPhoneTtsSpannable(CharSequence phoneNumber) {
2340 if (phoneNumber == null) {
2343 Spannable spannable = Spannable.Factory.getInstance().newSpannable(phoneNumber);
2344 PhoneNumberUtils.addPhoneTtsSpan(spannable, 0, spannable.length());
2349 * Attach a {@link TtsSpan} to the supplied {@code Spannable} at the indicated location,
2350 * annotating that location as containing a phone number.
2352 * @param s A {@code Spannable} to annotate.
2353 * @param start The starting character position of the phone number in {@code s}.
2354 * @param end The ending character position of the phone number in {@code s}.
2356 public static void addPhoneTtsSpan(Spannable s, int start, int end) {
2357 s.setSpan(getPhoneTtsSpan(s.subSequence(start, end).toString()),
2360 Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
2364 * Wrap the supplied {@code CharSequence} with a {@code TtsSpan}, annotating it as
2365 * containing a phone number in its entirety.
2367 * @param phoneNumber A {@code CharSequence} the entirety of which represents a phone number.
2368 * @return A {@code CharSequence} with appropriate annotations.
2369 * @deprecated Renamed {@link #getPhoneTtsSpannable}.
2374 public static CharSequence ttsSpanAsPhoneNumber(CharSequence phoneNumber) {
2375 return getPhoneTtsSpannable(phoneNumber);
2379 * Attach a {@link TtsSpan} to the supplied {@code Spannable} at the indicated location,
2380 * annotating that location as containing a phone number.
2382 * @param s A {@code Spannable} to annotate.
2383 * @param start The starting character position of the phone number in {@code s}.
2384 * @param end The ending character position of the phone number in {@code s}.
2386 * @deprecated Renamed {@link #addPhoneTtsSpan}.
2391 public static void ttsSpanAsPhoneNumber(Spannable s, int start, int end) {
2392 addPhoneTtsSpan(s, start, end);
2396 * Create a {@code TtsSpan} for the supplied {@code String}.
2398 * @param phoneNumberString A {@code String} the entirety of which represents a phone number.
2399 * @return A {@code TtsSpan} for {@param phoneNumberString}.
2401 public static TtsSpan getPhoneTtsSpan(String phoneNumberString) {
2402 if (phoneNumberString == null) {
2406 // Parse the phone number
2407 final PhoneNumberUtil phoneNumberUtil = PhoneNumberUtil.getInstance();
2408 PhoneNumber phoneNumber = null;
2410 // Don't supply a defaultRegion so this fails for non-international numbers because
2411 // we don't want to TalkBalk to read a country code (e.g. +1) if it is not already
2413 phoneNumber = phoneNumberUtil.parse(phoneNumberString, /* defaultRegion */ null);
2414 } catch (NumberParseException ignored) {
2417 // Build a telephone tts span
2418 final TtsSpan.TelephoneBuilder builder = new TtsSpan.TelephoneBuilder();
2419 if (phoneNumber == null) {
2420 // Strip separators otherwise TalkBack will be silent
2421 // (this behavior was observed with TalkBalk 4.0.2 from their alpha channel)
2422 builder.setNumberParts(splitAtNonNumerics(phoneNumberString));
2424 if (phoneNumber.hasCountryCode()) {
2425 builder.setCountryCode(Integer.toString(phoneNumber.getCountryCode()));
2427 builder.setNumberParts(Long.toString(phoneNumber.getNationalNumber()));
2429 return builder.build();
2432 // Split a phone number like "+20(123)-456#" using spaces, ignoring anything that is not
2433 // a digit, to produce a result like "20 123 456".
2434 private static String splitAtNonNumerics(CharSequence number) {
2435 StringBuilder sb = new StringBuilder(number.length());
2436 for (int i = 0; i < number.length(); i++) {
2437 sb.append(PhoneNumberUtils.isISODigit(number.charAt(i))
2441 // It is very important to remove extra spaces. At time of writing, any leading or trailing
2442 // spaces, or any sequence of more than one space, will confuse TalkBack and cause the TTS
2443 // span to be non-functional!
2444 return sb.toString().replaceAll(" +", " ").trim();
2447 private static String getCurrentIdp(boolean useNanp) {
2450 ps = NANP_IDP_STRING;
2452 // in case, there is no IDD is found, we shouldn't convert it.
2453 ps = SystemProperties.get(PROPERTY_OPERATOR_IDP_STRING, PLUS_SIGN_STRING);
2458 private static boolean isTwoToNine (char c) {
2459 if (c >= '2' && c <= '9') {
2466 private static int getFormatTypeFromCountryCode (String country) {
2467 // Check for the NANP countries
2468 int length = NANP_COUNTRIES.length;
2469 for (int i = 0; i < length; i++) {
2470 if (NANP_COUNTRIES[i].compareToIgnoreCase(country) == 0) {
2474 if ("jp".compareToIgnoreCase(country) == 0) {
2475 return FORMAT_JAPAN;
2477 return FORMAT_UNKNOWN;
2481 * This function checks if the passed in string conforms to the NANP format
2482 * i.e. NXX-NXX-XXXX, N is any digit 2-9 and X is any digit 0-9
2485 public static boolean isNanp (String dialStr) {
2486 boolean retVal = false;
2487 if (dialStr != null) {
2488 if (dialStr.length() == NANP_LENGTH) {
2489 if (isTwoToNine(dialStr.charAt(0)) &&
2490 isTwoToNine(dialStr.charAt(3))) {
2492 for (int i=1; i<NANP_LENGTH; i++ ) {
2493 char c=dialStr.charAt(i);
2494 if (!PhoneNumberUtils.isISODigit(c)) {
2502 Rlog.e("isNanp: null dialStr passed in", dialStr);
2508 * This function checks if the passed in string conforms to 1-NANP format
2510 private static boolean isOneNanp(String dialStr) {
2511 boolean retVal = false;
2512 if (dialStr != null) {
2513 String newDialStr = dialStr.substring(1);
2514 if ((dialStr.charAt(0) == '1') && isNanp(newDialStr)) {
2518 Rlog.e("isOneNanp: null dialStr passed in", dialStr);
2524 * Determines if the specified number is actually a URI
2525 * (i.e. a SIP address) rather than a regular PSTN phone number,
2526 * based on whether or not the number contains an "@" character.
2530 * @return true if number contains @
2532 public static boolean isUriNumber(String number) {
2533 // Note we allow either "@" or "%40" to indicate a URI, in case
2534 // the passed-in string is URI-escaped. (Neither "@" nor "%40"
2535 // will ever be found in a legal PSTN number.)
2536 return number != null && (number.contains("@") || number.contains("%40"));
2540 * @return the "username" part of the specified SIP address,
2541 * i.e. the part before the "@" character (or "%40").
2543 * @param number SIP address of the form "username@domainname"
2544 * (or the URI-escaped equivalent "username%40domainname")
2549 public static String getUsernameFromUriNumber(String number) {
2550 // The delimiter between username and domain name can be
2551 // either "@" or "%40" (the URI-escaped equivalent.)
2552 int delimiterIndex = number.indexOf('@');
2553 if (delimiterIndex < 0) {
2554 delimiterIndex = number.indexOf("%40");
2556 if (delimiterIndex < 0) {
2558 "getUsernameFromUriNumber: no delimiter found in SIP addr '" + number + "'");
2559 delimiterIndex = number.length();
2561 return number.substring(0, delimiterIndex);
2565 * This function handles the plus code conversion
2566 * If the number format is
2567 * 1)+1NANP,remove +,
2568 * 2)other than +1NANP, any + numbers,replace + with the current IDP
2570 private static String processPlusCode(String networkDialStr, boolean useNanp) {
2571 String retStr = networkDialStr;
2573 if (DBG) log("processPlusCode, networkDialStr = " + networkDialStr
2574 + "for NANP = " + useNanp);
2575 // If there is a plus sign at the beginning of the dial string,
2576 // Convert the plus sign to the default IDP since it's an international number
2577 if (networkDialStr != null &&
2578 networkDialStr.charAt(0) == PLUS_SIGN_CHAR &&
2579 networkDialStr.length() > 1) {
2580 String newStr = networkDialStr.substring(1);
2581 // TODO: for nonNanp, should the '+' be removed if following number is country code
2582 if (useNanp && isOneNanp(newStr)) {
2583 // Remove the leading plus sign
2586 // Replaces the plus sign with the default IDP
2587 retStr = networkDialStr.replaceFirst("[+]", getCurrentIdp(useNanp));
2590 if (DBG) log("processPlusCode, retStr=" + retStr);
2594 // This function finds the index of the dialable character(s)
2595 // in the post dial string
2596 private static int findDialableIndexFromPostDialStr(String postDialStr) {
2597 for (int index = 0;index < postDialStr.length();index++) {
2598 char c = postDialStr.charAt(index);
2599 if (isReallyDialable(c)) {
2606 // This function appends the non-dialable P/W character to the original
2607 // dial string based on the dialable index passed in
2608 private static String
2609 appendPwCharBackToOrigDialStr(int dialableIndex,String origStr, String dialStr) {
2612 // There is only 1 P/W character before the dialable characters
2613 if (dialableIndex == 1) {
2614 StringBuilder ret = new StringBuilder(origStr);
2615 ret = ret.append(dialStr.charAt(0));
2616 retStr = ret.toString();
2618 // It means more than 1 P/W characters in the post dial string,
2619 // appends to retStr
2620 String nonDigitStr = dialStr.substring(0,dialableIndex);
2621 retStr = origStr.concat(nonDigitStr);
2626 //===== Beginning of utility methods used in compareLoosely() =====
2629 * Phone numbers are stored in "lookup" form in the database
2630 * as reversed strings to allow for caller ID lookup
2632 * This method takes a phone number and makes a valid SQL "LIKE"
2633 * string that will match the lookup form
2636 /** all of a up to len must be an international prefix or
2637 * separators/non-dialing digits
2639 private static boolean
2640 matchIntlPrefix(String a, int len) {
2641 /* '([^0-9*#+pwn]\+[^0-9*#+pwn] | [^0-9*#+pwn]0(0|11)[^0-9*#+pwn] )$' */
2645 for (int i = 0 ; i < len ; i++) {
2646 char c = a.charAt(i);
2650 if (c == '+') state = 1;
2651 else if (c == '0') state = 2;
2652 else if (isNonSeparator(c)) return false;
2656 if (c == '0') state = 3;
2657 else if (c == '1') state = 4;
2658 else if (isNonSeparator(c)) return false;
2662 if (c == '1') state = 5;
2663 else if (isNonSeparator(c)) return false;
2667 if (isNonSeparator(c)) return false;
2673 return state == 1 || state == 3 || state == 5;
2676 /** all of 'a' up to len must be a (+|00|011)country code)
2677 * We're fast and loose with the country code. Any \d{1,3} matches */
2678 private static boolean
2679 matchIntlPrefixAndCC(String a, int len) {
2680 /* [^0-9*#+pwn]*(\+|0(0|11)\d\d?\d? [^0-9*#+pwn] $ */
2681 /* 0 1 2 3 45 6 7 8 */
2684 for (int i = 0 ; i < len ; i++ ) {
2685 char c = a.charAt(i);
2689 if (c == '+') state = 1;
2690 else if (c == '0') state = 2;
2691 else if (isNonSeparator(c)) return false;
2695 if (c == '0') state = 3;
2696 else if (c == '1') state = 4;
2697 else if (isNonSeparator(c)) return false;
2701 if (c == '1') state = 5;
2702 else if (isNonSeparator(c)) return false;
2708 if (isISODigit(c)) state = 6;
2709 else if (isNonSeparator(c)) return false;
2714 if (isISODigit(c)) state++;
2715 else if (isNonSeparator(c)) return false;
2719 if (isNonSeparator(c)) return false;
2723 return state == 6 || state == 7 || state == 8;
2726 /** all of 'a' up to len must match non-US trunk prefix ('0') */
2727 private static boolean
2728 matchTrunkPrefix(String a, int len) {
2733 for (int i = 0 ; i < len ; i++) {
2734 char c = a.charAt(i);
2736 if (c == '0' && !found) {
2738 } else if (isNonSeparator(c)) {
2746 //===== End of utility methods used only in compareLoosely() =====
2748 //===== Beginning of utility methods used only in compareStrictly() ====
2751 * If true, the number is country calling code.
2753 private static final boolean COUNTRY_CALLING_CALL[] = {
2754 true, true, false, false, false, false, false, true, false, false,
2755 false, false, false, false, false, false, false, false, false, false,
2756 true, false, false, false, false, false, false, true, true, false,
2757 true, true, true, true, true, false, true, false, false, true,
2758 true, false, false, true, true, true, true, true, true, true,
2759 false, true, true, true, true, true, true, true, true, false,
2760 true, true, true, true, true, true, true, false, false, false,
2761 false, false, false, false, false, false, false, false, false, false,
2762 false, true, true, true, true, false, true, false, false, true,
2763 true, true, true, true, true, true, false, false, true, false,
2765 private static final int CCC_LENGTH = COUNTRY_CALLING_CALL.length;
2768 * @return true when input is valid Country Calling Code.
2770 private static boolean isCountryCallingCode(int countryCallingCodeCandidate) {
2771 return countryCallingCodeCandidate > 0 && countryCallingCodeCandidate < CCC_LENGTH &&
2772 COUNTRY_CALLING_CALL[countryCallingCodeCandidate];
2776 * Returns integer corresponding to the input if input "ch" is
2777 * ISO-LATIN characters 0-9.
2778 * Returns -1 otherwise
2780 private static int tryGetISODigit(char ch) {
2781 if ('0' <= ch && ch <= '9') {
2788 private static class CountryCallingCodeAndNewIndex {
2789 public final int countryCallingCode;
2790 public final int newIndex;
2791 public CountryCallingCodeAndNewIndex(int countryCode, int newIndex) {
2792 this.countryCallingCode = countryCode;
2793 this.newIndex = newIndex;
2798 * Note that this function does not strictly care the country calling code with
2799 * 3 length (like Morocco: +212), assuming it is enough to use the first two
2800 * digit to compare two phone numbers.
2802 private static CountryCallingCodeAndNewIndex tryGetCountryCallingCodeAndNewIndex(
2803 String str, boolean acceptThailandCase) {
2805 // ^[^0-9*#+]*((\+|0(0|11)\d\d?|166) [^0-9*#+] $
2806 // 0 1 2 3 45 6 7 89
2808 // In all the states, this function ignores separator characters.
2809 // "166" is the special case for the call from Thailand to the US. Uguu!
2812 final int length = str.length();
2813 for (int i = 0 ; i < length ; i++ ) {
2814 char ch = str.charAt(i);
2817 if (ch == '+') state = 1;
2818 else if (ch == '0') state = 2;
2819 else if (ch == '1') {
2820 if (acceptThailandCase) {
2825 } else if (isDialable(ch)) {
2831 if (ch == '0') state = 3;
2832 else if (ch == '1') state = 4;
2833 else if (isDialable(ch)) {
2839 if (ch == '1') state = 5;
2840 else if (isDialable(ch)) {
2851 int ret = tryGetISODigit(ch);
2853 ccc = ccc * 10 + ret;
2854 if (ccc >= 100 || isCountryCallingCode(ccc)) {
2855 return new CountryCallingCodeAndNewIndex(ccc, i + 1);
2857 if (state == 1 || state == 3 || state == 5) {
2862 } else if (isDialable(ch)) {
2868 if (ch == '6') state = 9;
2869 else if (isDialable(ch)) {
2875 return new CountryCallingCodeAndNewIndex(66, i + 1);
2888 * Currently this function simply ignore the first digit assuming it is
2889 * trunk prefix. Actually trunk prefix is different in each country.
2892 * "+79161234567" equals "89161234567" (Russian trunk digit is 8)
2893 * "+33123456789" equals "0123456789" (French trunk digit is 0)
2896 private static int tryGetTrunkPrefixOmittedIndex(String str, int currentIndex) {
2897 int length = str.length();
2898 for (int i = currentIndex ; i < length ; i++) {
2899 final char ch = str.charAt(i);
2900 if (tryGetISODigit(ch) >= 0) {
2902 } else if (isDialable(ch)) {
2910 * Return true if the prefix of "str" is "ignorable". Here, "ignorable" means
2911 * that "str" has only one digit and separator characters. The one digit is
2912 * assumed to be trunk prefix.
2914 private static boolean checkPrefixIsIgnorable(final String str,
2915 int forwardIndex, int backwardIndex) {
2916 boolean trunk_prefix_was_read = false;
2917 while (backwardIndex >= forwardIndex) {
2918 if (tryGetISODigit(str.charAt(backwardIndex)) >= 0) {
2919 if (trunk_prefix_was_read) {
2920 // More than one digit appeared, meaning that "a" and "b"
2924 // Ignore just one digit, assuming it is trunk prefix.
2925 trunk_prefix_was_read = true;
2927 } else if (isDialable(str.charAt(backwardIndex))) {
2928 // Trunk prefix is a digit, not "*", "#"...
2938 * Returns Default voice subscription Id.
2940 private static int getDefaultVoiceSubId() {
2941 return SubscriptionManager.getDefaultVoiceSubId();
2943 //==== End of utility methods used only in compareStrictly() =====