Home | History | Annotate | Download | only in telephony
      1 /*
      2  * Copyright (C) 2006 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package android.telephony;
     18 
     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.ShortNumberInfo;
     24 
     25 import android.annotation.IntDef;
     26 import android.content.Context;
     27 import android.content.Intent;
     28 import android.content.res.Resources;
     29 import android.database.Cursor;
     30 import android.location.CountryDetector;
     31 import android.net.Uri;
     32 import android.os.SystemProperties;
     33 import android.os.PersistableBundle;
     34 import android.provider.Contacts;
     35 import android.provider.ContactsContract;
     36 import android.telecom.PhoneAccount;
     37 import android.text.Editable;
     38 import android.text.Spannable;
     39 import android.text.SpannableStringBuilder;
     40 import android.text.TextUtils;
     41 import android.text.style.TtsSpan;
     42 import android.util.SparseIntArray;
     43 
     44 import static com.android.internal.telephony.TelephonyProperties.PROPERTY_OPERATOR_IDP_STRING;
     45 
     46 import java.lang.annotation.Retention;
     47 import java.lang.annotation.RetentionPolicy;
     48 import java.util.Locale;
     49 import java.util.regex.Matcher;
     50 import java.util.regex.Pattern;
     51 
     52 /**
     53  * Various utilities for dealing with phone number strings.
     54  */
     55 public class PhoneNumberUtils {
     56     /** {@hide} */
     57     @IntDef(prefix = "BCD_EXTENDED_TYPE_", value = {
     58             BCD_EXTENDED_TYPE_EF_ADN,
     59             BCD_EXTENDED_TYPE_CALLED_PARTY,
     60     })
     61     @Retention(RetentionPolicy.SOURCE)
     62     public @interface BcdExtendType {}
     63 
     64     /*
     65      * The BCD extended type used to determine the extended char for the digit which is greater than
     66      * 9.
     67      *
     68      * see TS 51.011 section 10.5.1 EF_ADN(Abbreviated dialling numbers)
     69      */
     70     public static final int BCD_EXTENDED_TYPE_EF_ADN = 1;
     71 
     72     /*
     73      * The BCD extended type used to determine the extended char for the digit which is greater than
     74      * 9.
     75      *
     76      * see TS 24.008 section 10.5.4.7 Called party BCD number
     77      */
     78     public static final int BCD_EXTENDED_TYPE_CALLED_PARTY = 2;
     79 
     80     /*
     81      * Special characters
     82      *
     83      * (See "What is a phone number?" doc)
     84      * 'p' --- GSM pause character, same as comma
     85      * 'n' --- GSM wild character
     86      * 'w' --- GSM wait character
     87      */
     88     public static final char PAUSE = ',';
     89     public static final char WAIT = ';';
     90     public static final char WILD = 'N';
     91 
     92     /*
     93      * Calling Line Identification Restriction (CLIR)
     94      */
     95     private static final String CLIR_ON = "*31#";
     96     private static final String CLIR_OFF = "#31#";
     97 
     98     /*
     99      * TOA = TON + NPI
    100      * See TS 24.008 section 10.5.4.7 for details.
    101      * These are the only really useful TOA values
    102      */
    103     public static final int TOA_International = 0x91;
    104     public static final int TOA_Unknown = 0x81;
    105 
    106     static final String LOG_TAG = "PhoneNumberUtils";
    107     private static final boolean DBG = false;
    108 
    109     private static final String BCD_EF_ADN_EXTENDED = "*#,N;";
    110     private static final String BCD_CALLED_PARTY_EXTENDED = "*#abc";
    111 
    112     /*
    113      * global-phone-number = ["+"] 1*( DIGIT / written-sep )
    114      * written-sep         = ("-"/".")
    115      */
    116     private static final Pattern GLOBAL_PHONE_NUMBER_PATTERN =
    117             Pattern.compile("[\\+]?[0-9.-]+");
    118 
    119     /** True if c is ISO-LATIN characters 0-9 */
    120     public static boolean
    121     isISODigit (char c) {
    122         return c >= '0' && c <= '9';
    123     }
    124 
    125     /** True if c is ISO-LATIN characters 0-9, *, # */
    126     public final static boolean
    127     is12Key(char c) {
    128         return (c >= '0' && c <= '9') || c == '*' || c == '#';
    129     }
    130 
    131     /** True if c is ISO-LATIN characters 0-9, *, # , +, WILD  */
    132     public final static boolean
    133     isDialable(char c) {
    134         return (c >= '0' && c <= '9') || c == '*' || c == '#' || c == '+' || c == WILD;
    135     }
    136 
    137     /** True if c is ISO-LATIN characters 0-9, *, # , + (no WILD)  */
    138     public final static boolean
    139     isReallyDialable(char c) {
    140         return (c >= '0' && c <= '9') || c == '*' || c == '#' || c == '+';
    141     }
    142 
    143     /** True if c is ISO-LATIN characters 0-9, *, # , +, WILD, WAIT, PAUSE   */
    144     public final static boolean
    145     isNonSeparator(char c) {
    146         return (c >= '0' && c <= '9') || c == '*' || c == '#' || c == '+'
    147                 || c == WILD || c == WAIT || c == PAUSE;
    148     }
    149 
    150     /** This any anything to the right of this char is part of the
    151      *  post-dial string (eg this is PAUSE or WAIT)
    152      */
    153     public final static boolean
    154     isStartsPostDial (char c) {
    155         return c == PAUSE || c == WAIT;
    156     }
    157 
    158     private static boolean
    159     isPause (char c){
    160         return c == 'p'||c == 'P';
    161     }
    162 
    163     private static boolean
    164     isToneWait (char c){
    165         return c == 'w'||c == 'W';
    166     }
    167 
    168 
    169     /** Returns true if ch is not dialable or alpha char */
    170     private static boolean isSeparator(char ch) {
    171         return !isDialable(ch) && !(('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z'));
    172     }
    173 
    174     /** Extracts the phone number from an Intent.
    175      *
    176      * @param intent the intent to get the number of
    177      * @param context a context to use for database access
    178      *
    179      * @return the phone number that would be called by the intent, or
    180      *         <code>null</code> if the number cannot be found.
    181      */
    182     public static String getNumberFromIntent(Intent intent, Context context) {
    183         String number = null;
    184 
    185         Uri uri = intent.getData();
    186 
    187         if (uri == null) {
    188             return null;
    189         }
    190 
    191         String scheme = uri.getScheme();
    192 
    193         if (scheme.equals("tel") || scheme.equals("sip")) {
    194             return uri.getSchemeSpecificPart();
    195         }
    196 
    197         if (context == null) {
    198             return null;
    199         }
    200 
    201         String type = intent.resolveType(context);
    202         String phoneColumn = null;
    203 
    204         // Correctly read out the phone entry based on requested provider
    205         final String authority = uri.getAuthority();
    206         if (Contacts.AUTHORITY.equals(authority)) {
    207             phoneColumn = Contacts.People.Phones.NUMBER;
    208         } else if (ContactsContract.AUTHORITY.equals(authority)) {
    209             phoneColumn = ContactsContract.CommonDataKinds.Phone.NUMBER;
    210         }
    211 
    212         Cursor c = null;
    213         try {
    214             c = context.getContentResolver().query(uri, new String[] { phoneColumn },
    215                     null, null, null);
    216             if (c != null) {
    217                 if (c.moveToFirst()) {
    218                     number = c.getString(c.getColumnIndex(phoneColumn));
    219                 }
    220             }
    221         } catch (RuntimeException e) {
    222             Rlog.e(LOG_TAG, "Error getting phone number.", e);
    223         } finally {
    224             if (c != null) {
    225                 c.close();
    226             }
    227         }
    228 
    229         return number;
    230     }
    231 
    232     /** Extracts the network address portion and canonicalizes
    233      *  (filters out separators.)
    234      *  Network address portion is everything up to DTMF control digit
    235      *  separators (pause or wait), but without non-dialable characters.
    236      *
    237      *  Please note that the GSM wild character is allowed in the result.
    238      *  This must be resolved before dialing.
    239      *
    240      *  Returns null if phoneNumber == null
    241      */
    242     public static String
    243     extractNetworkPortion(String phoneNumber) {
    244         if (phoneNumber == null) {
    245             return null;
    246         }
    247 
    248         int len = phoneNumber.length();
    249         StringBuilder ret = new StringBuilder(len);
    250 
    251         for (int i = 0; i < len; i++) {
    252             char c = phoneNumber.charAt(i);
    253             // Character.digit() supports ASCII and Unicode digits (fullwidth, Arabic-Indic, etc.)
    254             int digit = Character.digit(c, 10);
    255             if (digit != -1) {
    256                 ret.append(digit);
    257             } else if (c == '+') {
    258                 // Allow '+' as first character or after CLIR MMI prefix
    259                 String prefix = ret.toString();
    260                 if (prefix.length() == 0 || prefix.equals(CLIR_ON) || prefix.equals(CLIR_OFF)) {
    261                     ret.append(c);
    262                 }
    263             } else if (isDialable(c)) {
    264                 ret.append(c);
    265             } else if (isStartsPostDial (c)) {
    266                 break;
    267             }
    268         }
    269 
    270         return ret.toString();
    271     }
    272 
    273     /**
    274      * Extracts the network address portion and canonicalize.
    275      *
    276      * This function is equivalent to extractNetworkPortion(), except
    277      * for allowing the PLUS character to occur at arbitrary positions
    278      * in the address portion, not just the first position.
    279      *
    280      * @hide
    281      */
    282     public static String extractNetworkPortionAlt(String phoneNumber) {
    283         if (phoneNumber == null) {
    284             return null;
    285         }
    286 
    287         int len = phoneNumber.length();
    288         StringBuilder ret = new StringBuilder(len);
    289         boolean haveSeenPlus = false;
    290 
    291         for (int i = 0; i < len; i++) {
    292             char c = phoneNumber.charAt(i);
    293             if (c == '+') {
    294                 if (haveSeenPlus) {
    295                     continue;
    296                 }
    297                 haveSeenPlus = true;
    298             }
    299             if (isDialable(c)) {
    300                 ret.append(c);
    301             } else if (isStartsPostDial (c)) {
    302                 break;
    303             }
    304         }
    305 
    306         return ret.toString();
    307     }
    308 
    309     /**
    310      * Strips separators from a phone number string.
    311      * @param phoneNumber phone number to strip.
    312      * @return phone string stripped of separators.
    313      */
    314     public static String stripSeparators(String phoneNumber) {
    315         if (phoneNumber == null) {
    316             return null;
    317         }
    318         int len = phoneNumber.length();
    319         StringBuilder ret = new StringBuilder(len);
    320 
    321         for (int i = 0; i < len; i++) {
    322             char c = phoneNumber.charAt(i);
    323             // Character.digit() supports ASCII and Unicode digits (fullwidth, Arabic-Indic, etc.)
    324             int digit = Character.digit(c, 10);
    325             if (digit != -1) {
    326                 ret.append(digit);
    327             } else if (isNonSeparator(c)) {
    328                 ret.append(c);
    329             }
    330         }
    331 
    332         return ret.toString();
    333     }
    334 
    335     /**
    336      * Translates keypad letters to actual digits (e.g. 1-800-GOOG-411 will
    337      * become 1-800-4664-411), and then strips all separators (e.g. 1-800-4664-411 will become
    338      * 18004664411).
    339      *
    340      * @see #convertKeypadLettersToDigits(String)
    341      * @see #stripSeparators(String)
    342      *
    343      * @hide
    344      */
    345     public static String convertAndStrip(String phoneNumber) {
    346         return stripSeparators(convertKeypadLettersToDigits(phoneNumber));
    347     }
    348 
    349     /**
    350      * Converts pause and tonewait pause characters
    351      * to Android representation.
    352      * RFC 3601 says pause is 'p' and tonewait is 'w'.
    353      * @hide
    354      */
    355     public static String convertPreDial(String phoneNumber) {
    356         if (phoneNumber == null) {
    357             return null;
    358         }
    359         int len = phoneNumber.length();
    360         StringBuilder ret = new StringBuilder(len);
    361 
    362         for (int i = 0; i < len; i++) {
    363             char c = phoneNumber.charAt(i);
    364 
    365             if (isPause(c)) {
    366                 c = PAUSE;
    367             } else if (isToneWait(c)) {
    368                 c = WAIT;
    369             }
    370             ret.append(c);
    371         }
    372         return ret.toString();
    373     }
    374 
    375     /** or -1 if both are negative */
    376     static private int
    377     minPositive (int a, int b) {
    378         if (a >= 0 && b >= 0) {
    379             return (a < b) ? a : b;
    380         } else if (a >= 0) { /* && b < 0 */
    381             return a;
    382         } else if (b >= 0) { /* && a < 0 */
    383             return b;
    384         } else { /* a < 0 && b < 0 */
    385             return -1;
    386         }
    387     }
    388 
    389     private static void log(String msg) {
    390         Rlog.d(LOG_TAG, msg);
    391     }
    392     /** index of the last character of the network portion
    393      *  (eg anything after is a post-dial string)
    394      */
    395     static private int
    396     indexOfLastNetworkChar(String a) {
    397         int pIndex, wIndex;
    398         int origLength;
    399         int trimIndex;
    400 
    401         origLength = a.length();
    402 
    403         pIndex = a.indexOf(PAUSE);
    404         wIndex = a.indexOf(WAIT);
    405 
    406         trimIndex = minPositive(pIndex, wIndex);
    407 
    408         if (trimIndex < 0) {
    409             return origLength - 1;
    410         } else {
    411             return trimIndex - 1;
    412         }
    413     }
    414 
    415     /**
    416      * Extracts the post-dial sequence of DTMF control digits, pauses, and
    417      * waits. Strips separators. This string may be empty, but will not be null
    418      * unless phoneNumber == null.
    419      *
    420      * Returns null if phoneNumber == null
    421      */
    422 
    423     public static String
    424     extractPostDialPortion(String phoneNumber) {
    425         if (phoneNumber == null) return null;
    426 
    427         int trimIndex;
    428         StringBuilder ret = new StringBuilder();
    429 
    430         trimIndex = indexOfLastNetworkChar (phoneNumber);
    431 
    432         for (int i = trimIndex + 1, s = phoneNumber.length()
    433                 ; i < s; i++
    434         ) {
    435             char c = phoneNumber.charAt(i);
    436             if (isNonSeparator(c)) {
    437                 ret.append(c);
    438             }
    439         }
    440 
    441         return ret.toString();
    442     }
    443 
    444     /**
    445      * Compare phone numbers a and b, return true if they're identical enough for caller ID purposes.
    446      */
    447     public static boolean compare(String a, String b) {
    448         // We've used loose comparation at least Eclair, which may change in the future.
    449 
    450         return compare(a, b, false);
    451     }
    452 
    453     /**
    454      * Compare phone numbers a and b, and return true if they're identical
    455      * enough for caller ID purposes. Checks a resource to determine whether
    456      * to use a strict or loose comparison algorithm.
    457      */
    458     public static boolean compare(Context context, String a, String b) {
    459         boolean useStrict = context.getResources().getBoolean(
    460                com.android.internal.R.bool.config_use_strict_phone_number_comparation);
    461         return compare(a, b, useStrict);
    462     }
    463 
    464     /**
    465      * @hide only for testing.
    466      */
    467     public static boolean compare(String a, String b, boolean useStrictComparation) {
    468         return (useStrictComparation ? compareStrictly(a, b) : compareLoosely(a, b));
    469     }
    470 
    471     /**
    472      * Compare phone numbers a and b, return true if they're identical
    473      * enough for caller ID purposes.
    474      *
    475      * - Compares from right to left
    476      * - requires MIN_MATCH (7) characters to match
    477      * - handles common trunk prefixes and international prefixes
    478      *   (basically, everything except the Russian trunk prefix)
    479      *
    480      * Note that this method does not return false even when the two phone numbers
    481      * are not exactly same; rather; we can call this method "similar()", not "equals()".
    482      *
    483      * @hide
    484      */
    485     public static boolean
    486     compareLoosely(String a, String b) {
    487         int ia, ib;
    488         int matched;
    489         int numNonDialableCharsInA = 0;
    490         int numNonDialableCharsInB = 0;
    491 
    492         if (a == null || b == null) return a == b;
    493 
    494         if (a.length() == 0 || b.length() == 0) {
    495             return false;
    496         }
    497 
    498         ia = indexOfLastNetworkChar (a);
    499         ib = indexOfLastNetworkChar (b);
    500         matched = 0;
    501 
    502         while (ia >= 0 && ib >=0) {
    503             char ca, cb;
    504             boolean skipCmp = false;
    505 
    506             ca = a.charAt(ia);
    507 
    508             if (!isDialable(ca)) {
    509                 ia--;
    510                 skipCmp = true;
    511                 numNonDialableCharsInA++;
    512             }
    513 
    514             cb = b.charAt(ib);
    515 
    516             if (!isDialable(cb)) {
    517                 ib--;
    518                 skipCmp = true;
    519                 numNonDialableCharsInB++;
    520             }
    521 
    522             if (!skipCmp) {
    523                 if (cb != ca && ca != WILD && cb != WILD) {
    524                     break;
    525                 }
    526                 ia--; ib--; matched++;
    527             }
    528         }
    529 
    530         if (matched < MIN_MATCH) {
    531             int effectiveALen = a.length() - numNonDialableCharsInA;
    532             int effectiveBLen = b.length() - numNonDialableCharsInB;
    533 
    534 
    535             // if the number of dialable chars in a and b match, but the matched chars < MIN_MATCH,
    536             // treat them as equal (i.e. 404-04 and 40404)
    537             if (effectiveALen == effectiveBLen && effectiveALen == matched) {
    538                 return true;
    539             }
    540 
    541             return false;
    542         }
    543 
    544         // At least one string has matched completely;
    545         if (matched >= MIN_MATCH && (ia < 0 || ib < 0)) {
    546             return true;
    547         }
    548 
    549         /*
    550          * Now, what remains must be one of the following for a
    551          * match:
    552          *
    553          *  - a '+' on one and a '00' or a '011' on the other
    554          *  - a '0' on one and a (+,00)<country code> on the other
    555          *     (for this, a '0' and a '00' prefix would have succeeded above)
    556          */
    557 
    558         if (matchIntlPrefix(a, ia + 1)
    559             && matchIntlPrefix (b, ib +1)
    560         ) {
    561             return true;
    562         }
    563 
    564         if (matchTrunkPrefix(a, ia + 1)
    565             && matchIntlPrefixAndCC(b, ib +1)
    566         ) {
    567             return true;
    568         }
    569 
    570         if (matchTrunkPrefix(b, ib + 1)
    571             && matchIntlPrefixAndCC(a, ia +1)
    572         ) {
    573             return true;
    574         }
    575 
    576         return false;
    577     }
    578 
    579     /**
    580      * @hide
    581      */
    582     public static boolean
    583     compareStrictly(String a, String b) {
    584         return compareStrictly(a, b, true);
    585     }
    586 
    587     /**
    588      * @hide
    589      */
    590     public static boolean
    591     compareStrictly(String a, String b, boolean acceptInvalidCCCPrefix) {
    592         if (a == null || b == null) {
    593             return a == b;
    594         } else if (a.length() == 0 && b.length() == 0) {
    595             return false;
    596         }
    597 
    598         int forwardIndexA = 0;
    599         int forwardIndexB = 0;
    600 
    601         CountryCallingCodeAndNewIndex cccA =
    602             tryGetCountryCallingCodeAndNewIndex(a, acceptInvalidCCCPrefix);
    603         CountryCallingCodeAndNewIndex cccB =
    604             tryGetCountryCallingCodeAndNewIndex(b, acceptInvalidCCCPrefix);
    605         boolean bothHasCountryCallingCode = false;
    606         boolean okToIgnorePrefix = true;
    607         boolean trunkPrefixIsOmittedA = false;
    608         boolean trunkPrefixIsOmittedB = false;
    609         if (cccA != null && cccB != null) {
    610             if (cccA.countryCallingCode != cccB.countryCallingCode) {
    611                 // Different Country Calling Code. Must be different phone number.
    612                 return false;
    613             }
    614             // When both have ccc, do not ignore trunk prefix. Without this,
    615             // "+81123123" becomes same as "+810123123" (+81 == Japan)
    616             okToIgnorePrefix = false;
    617             bothHasCountryCallingCode = true;
    618             forwardIndexA = cccA.newIndex;
    619             forwardIndexB = cccB.newIndex;
    620         } else if (cccA == null && cccB == null) {
    621             // When both do not have ccc, do not ignore trunk prefix. Without this,
    622             // "123123" becomes same as "0123123"
    623             okToIgnorePrefix = false;
    624         } else {
    625             if (cccA != null) {
    626                 forwardIndexA = cccA.newIndex;
    627             } else {
    628                 int tmp = tryGetTrunkPrefixOmittedIndex(b, 0);
    629                 if (tmp >= 0) {
    630                     forwardIndexA = tmp;
    631                     trunkPrefixIsOmittedA = true;
    632                 }
    633             }
    634             if (cccB != null) {
    635                 forwardIndexB = cccB.newIndex;
    636             } else {
    637                 int tmp = tryGetTrunkPrefixOmittedIndex(b, 0);
    638                 if (tmp >= 0) {
    639                     forwardIndexB = tmp;
    640                     trunkPrefixIsOmittedB = true;
    641                 }
    642             }
    643         }
    644 
    645         int backwardIndexA = a.length() - 1;
    646         int backwardIndexB = b.length() - 1;
    647         while (backwardIndexA >= forwardIndexA && backwardIndexB >= forwardIndexB) {
    648             boolean skip_compare = false;
    649             final char chA = a.charAt(backwardIndexA);
    650             final char chB = b.charAt(backwardIndexB);
    651             if (isSeparator(chA)) {
    652                 backwardIndexA--;
    653                 skip_compare = true;
    654             }
    655             if (isSeparator(chB)) {
    656                 backwardIndexB--;
    657                 skip_compare = true;
    658             }
    659 
    660             if (!skip_compare) {
    661                 if (chA != chB) {
    662                     return false;
    663                 }
    664                 backwardIndexA--;
    665                 backwardIndexB--;
    666             }
    667         }
    668 
    669         if (okToIgnorePrefix) {
    670             if ((trunkPrefixIsOmittedA && forwardIndexA <= backwardIndexA) ||
    671                 !checkPrefixIsIgnorable(a, forwardIndexA, backwardIndexA)) {
    672                 if (acceptInvalidCCCPrefix) {
    673                     // Maybe the code handling the special case for Thailand makes the
    674                     // result garbled, so disable the code and try again.
    675                     // e.g. "16610001234" must equal to "6610001234", but with
    676                     //      Thailand-case handling code, they become equal to each other.
    677                     //
    678                     // Note: we select simplicity rather than adding some complicated
    679                     //       logic here for performance(like "checking whether remaining
    680                     //       numbers are just 66 or not"), assuming inputs are small
    681                     //       enough.
    682                     return compare(a, b, false);
    683                 } else {
    684                     return false;
    685                 }
    686             }
    687             if ((trunkPrefixIsOmittedB && forwardIndexB <= backwardIndexB) ||
    688                 !checkPrefixIsIgnorable(b, forwardIndexA, backwardIndexB)) {
    689                 if (acceptInvalidCCCPrefix) {
    690                     return compare(a, b, false);
    691                 } else {
    692                     return false;
    693                 }
    694             }
    695         } else {
    696             // In the US, 1-650-555-1234 must be equal to 650-555-1234,
    697             // while 090-1234-1234 must not be equal to 90-1234-1234 in Japan.
    698             // This request exists just in US (with 1 trunk (NDD) prefix).
    699             // In addition, "011 11 7005554141" must not equal to "+17005554141",
    700             // while "011 1 7005554141" must equal to "+17005554141"
    701             //
    702             // In this comparison, we ignore the prefix '1' just once, when
    703             // - at least either does not have CCC, or
    704             // - the remaining non-separator number is 1
    705             boolean maybeNamp = !bothHasCountryCallingCode;
    706             while (backwardIndexA >= forwardIndexA) {
    707                 final char chA = a.charAt(backwardIndexA);
    708                 if (isDialable(chA)) {
    709                     if (maybeNamp && tryGetISODigit(chA) == 1) {
    710                         maybeNamp = false;
    711                     } else {
    712                         return false;
    713                     }
    714                 }
    715                 backwardIndexA--;
    716             }
    717             while (backwardIndexB >= forwardIndexB) {
    718                 final char chB = b.charAt(backwardIndexB);
    719                 if (isDialable(chB)) {
    720                     if (maybeNamp && tryGetISODigit(chB) == 1) {
    721                         maybeNamp = false;
    722                     } else {
    723                         return false;
    724                     }
    725                 }
    726                 backwardIndexB--;
    727             }
    728         }
    729 
    730         return true;
    731     }
    732 
    733     /**
    734      * Returns the rightmost MIN_MATCH (5) characters in the network portion
    735      * in *reversed* order
    736      *
    737      * This can be used to do a database lookup against the column
    738      * that stores getStrippedReversed()
    739      *
    740      * Returns null if phoneNumber == null
    741      */
    742     public static String
    743     toCallerIDMinMatch(String phoneNumber) {
    744         String np = extractNetworkPortionAlt(phoneNumber);
    745         return internalGetStrippedReversed(np, MIN_MATCH);
    746     }
    747 
    748     /**
    749      * Returns the network portion reversed.
    750      * This string is intended to go into an index column for a
    751      * database lookup.
    752      *
    753      * Returns null if phoneNumber == null
    754      */
    755     public static String
    756     getStrippedReversed(String phoneNumber) {
    757         String np = extractNetworkPortionAlt(phoneNumber);
    758 
    759         if (np == null) return null;
    760 
    761         return internalGetStrippedReversed(np, np.length());
    762     }
    763 
    764     /**
    765      * Returns the last numDigits of the reversed phone number
    766      * Returns null if np == null
    767      */
    768     private static String
    769     internalGetStrippedReversed(String np, int numDigits) {
    770         if (np == null) return null;
    771 
    772         StringBuilder ret = new StringBuilder(numDigits);
    773         int length = np.length();
    774 
    775         for (int i = length - 1, s = length
    776             ; i >= 0 && (s - i) <= numDigits ; i--
    777         ) {
    778             char c = np.charAt(i);
    779 
    780             ret.append(c);
    781         }
    782 
    783         return ret.toString();
    784     }
    785 
    786     /**
    787      * Basically: makes sure there's a + in front of a
    788      * TOA_International number
    789      *
    790      * Returns null if s == null
    791      */
    792     public static String
    793     stringFromStringAndTOA(String s, int TOA) {
    794         if (s == null) return null;
    795 
    796         if (TOA == TOA_International && s.length() > 0 && s.charAt(0) != '+') {
    797             return "+" + s;
    798         }
    799 
    800         return s;
    801     }
    802 
    803     /**
    804      * Returns the TOA for the given dial string
    805      * Basically, returns TOA_International if there's a + prefix
    806      */
    807 
    808     public static int
    809     toaFromString(String s) {
    810         if (s != null && s.length() > 0 && s.charAt(0) == '+') {
    811             return TOA_International;
    812         }
    813 
    814         return TOA_Unknown;
    815     }
    816 
    817     /**
    818      *  3GPP TS 24.008 10.5.4.7
    819      *  Called Party BCD Number
    820      *
    821      *  See Also TS 51.011 10.5.1 "dialing number/ssc string"
    822      *  and TS 11.11 "10.3.1 EF adn (Abbreviated dialing numbers)"
    823      *
    824      * @param bytes the data buffer
    825      * @param offset should point to the TOA (aka. TON/NPI) octet after the length byte
    826      * @param length is the number of bytes including TOA byte
    827      *                and must be at least 2
    828      *
    829      * @return partial string on invalid decode
    830      *
    831      * @deprecated use {@link #calledPartyBCDToString(byte[], int, int, int)} instead. Calling this
    832      * method is equivalent to calling {@link #calledPartyBCDToString(byte[], int, int)} with
    833      * {@link #BCD_EXTENDED_TYPE_EF_ADN} as the extended type.
    834      */
    835     @Deprecated
    836     public static String calledPartyBCDToString(byte[] bytes, int offset, int length) {
    837         return calledPartyBCDToString(bytes, offset, length, BCD_EXTENDED_TYPE_EF_ADN);
    838     }
    839 
    840     /**
    841      *  3GPP TS 24.008 10.5.4.7
    842      *  Called Party BCD Number
    843      *
    844      *  See Also TS 51.011 10.5.1 "dialing number/ssc string"
    845      *  and TS 11.11 "10.3.1 EF adn (Abbreviated dialing numbers)"
    846      *
    847      * @param bytes the data buffer
    848      * @param offset should point to the TOA (aka. TON/NPI) octet after the length byte
    849      * @param length is the number of bytes including TOA byte
    850      *                and must be at least 2
    851      * @param bcdExtType used to determine the extended bcd coding
    852      * @see #BCD_EXTENDED_TYPE_EF_ADN
    853      * @see #BCD_EXTENDED_TYPE_CALLED_PARTY
    854      *
    855      */
    856     public static String calledPartyBCDToString(
    857             byte[] bytes, int offset, int length, @BcdExtendType int bcdExtType) {
    858         boolean prependPlus = false;
    859         StringBuilder ret = new StringBuilder(1 + length * 2);
    860 
    861         if (length < 2) {
    862             return "";
    863         }
    864 
    865         //Only TON field should be taken in consideration
    866         if ((bytes[offset] & 0xf0) == (TOA_International & 0xf0)) {
    867             prependPlus = true;
    868         }
    869 
    870         internalCalledPartyBCDFragmentToString(
    871                 ret, bytes, offset + 1, length - 1, bcdExtType);
    872 
    873         if (prependPlus && ret.length() == 0) {
    874             // If the only thing there is a prepended plus, return ""
    875             return "";
    876         }
    877 
    878         if (prependPlus) {
    879             // This is an "international number" and should have
    880             // a plus prepended to the dialing number. But there
    881             // can also be GSM MMI codes as defined in TS 22.030 6.5.2
    882             // so we need to handle those also.
    883             //
    884             // http://web.telia.com/~u47904776/gsmkode.htm
    885             // has a nice list of some of these GSM codes.
    886             //
    887             // Examples are:
    888             //   **21*+886988171479#
    889             //   **21*8311234567#
    890             //   *21#
    891             //   #21#
    892             //   *#21#
    893             //   *31#+11234567890
    894             //   #31#+18311234567
    895             //   #31#8311234567
    896             //   18311234567
    897             //   +18311234567#
    898             //   +18311234567
    899             // Odd ball cases that some phones handled
    900             // where there is no dialing number so they
    901             // append the "+"
    902             //   *21#+
    903             //   **21#+
    904             String retString = ret.toString();
    905             Pattern p = Pattern.compile("(^[#*])(.*)([#*])(.*)(#)$");
    906             Matcher m = p.matcher(retString);
    907             if (m.matches()) {
    908                 if ("".equals(m.group(2))) {
    909                     // Started with two [#*] ends with #
    910                     // So no dialing number and we'll just
    911                     // append a +, this handles **21#+
    912                     ret = new StringBuilder();
    913                     ret.append(m.group(1));
    914                     ret.append(m.group(3));
    915                     ret.append(m.group(4));
    916                     ret.append(m.group(5));
    917                     ret.append("+");
    918                 } else {
    919                     // Starts with [#*] and ends with #
    920                     // Assume group 4 is a dialing number
    921                     // such as *21*+1234554#
    922                     ret = new StringBuilder();
    923                     ret.append(m.group(1));
    924                     ret.append(m.group(2));
    925                     ret.append(m.group(3));
    926                     ret.append("+");
    927                     ret.append(m.group(4));
    928                     ret.append(m.group(5));
    929                 }
    930             } else {
    931                 p = Pattern.compile("(^[#*])(.*)([#*])(.*)");
    932                 m = p.matcher(retString);
    933                 if (m.matches()) {
    934                     // Starts with [#*] and only one other [#*]
    935                     // Assume the data after last [#*] is dialing
    936                     // number (i.e. group 4) such as *31#+11234567890.
    937                     // This also includes the odd ball *21#+
    938                     ret = new StringBuilder();
    939                     ret.append(m.group(1));
    940                     ret.append(m.group(2));
    941                     ret.append(m.group(3));
    942                     ret.append("+");
    943                     ret.append(m.group(4));
    944                 } else {
    945                     // Does NOT start with [#*] just prepend '+'
    946                     ret = new StringBuilder();
    947                     ret.append('+');
    948                     ret.append(retString);
    949                 }
    950             }
    951         }
    952 
    953         return ret.toString();
    954     }
    955 
    956     private static void internalCalledPartyBCDFragmentToString(
    957             StringBuilder sb, byte [] bytes, int offset, int length,
    958             @BcdExtendType int bcdExtType) {
    959         for (int i = offset ; i < length + offset ; i++) {
    960             byte b;
    961             char c;
    962 
    963             c = bcdToChar((byte)(bytes[i] & 0xf), bcdExtType);
    964 
    965             if (c == 0) {
    966                 return;
    967             }
    968             sb.append(c);
    969 
    970             // FIXME(mkf) TS 23.040 9.1.2.3 says
    971             // "if a mobile receives 1111 in a position prior to
    972             // the last semi-octet then processing shall commence with
    973             // the next semi-octet and the intervening
    974             // semi-octet shall be ignored"
    975             // How does this jive with 24.008 10.5.4.7
    976 
    977             b = (byte)((bytes[i] >> 4) & 0xf);
    978 
    979             if (b == 0xf && i + 1 == length + offset) {
    980                 //ignore final 0xf
    981                 break;
    982             }
    983 
    984             c = bcdToChar(b, bcdExtType);
    985             if (c == 0) {
    986                 return;
    987             }
    988 
    989             sb.append(c);
    990         }
    991 
    992     }
    993 
    994     /**
    995      * Like calledPartyBCDToString, but field does not start with a
    996      * TOA byte. For example: SIM ADN extension fields
    997      *
    998      * @deprecated use {@link #calledPartyBCDFragmentToString(byte[], int, int, int)} instead.
    999      * Calling this method is equivalent to calling
   1000      * {@link #calledPartyBCDFragmentToString(byte[], int, int, int)} with
   1001      * {@link #BCD_EXTENDED_TYPE_EF_ADN} as the extended type.
   1002      */
   1003     @Deprecated
   1004     public static String calledPartyBCDFragmentToString(byte[] bytes, int offset, int length) {
   1005         return calledPartyBCDFragmentToString(bytes, offset, length, BCD_EXTENDED_TYPE_EF_ADN);
   1006     }
   1007 
   1008     /**
   1009      * Like calledPartyBCDToString, but field does not start with a
   1010      * TOA byte. For example: SIM ADN extension fields
   1011      */
   1012     public static String calledPartyBCDFragmentToString(
   1013             byte[] bytes, int offset, int length, @BcdExtendType int bcdExtType) {
   1014         StringBuilder ret = new StringBuilder(length * 2);
   1015         internalCalledPartyBCDFragmentToString(ret, bytes, offset, length, bcdExtType);
   1016         return ret.toString();
   1017     }
   1018 
   1019     /**
   1020      * Returns the correspond character for given {@code b} based on {@code bcdExtType}, or 0 on
   1021      * invalid code.
   1022      */
   1023     private static char bcdToChar(byte b, @BcdExtendType int bcdExtType) {
   1024         if (b < 0xa) {
   1025             return (char) ('0' + b);
   1026         }
   1027 
   1028         String extended = null;
   1029         if (BCD_EXTENDED_TYPE_EF_ADN == bcdExtType) {
   1030             extended = BCD_EF_ADN_EXTENDED;
   1031         } else if (BCD_EXTENDED_TYPE_CALLED_PARTY == bcdExtType) {
   1032             extended = BCD_CALLED_PARTY_EXTENDED;
   1033         }
   1034         if (extended == null || b - 0xa >= extended.length()) {
   1035             return 0;
   1036         }
   1037 
   1038         return extended.charAt(b - 0xa);
   1039     }
   1040 
   1041     private static int charToBCD(char c, @BcdExtendType int bcdExtType) {
   1042         if ('0' <= c && c <= '9') {
   1043             return c - '0';
   1044         }
   1045 
   1046         String extended = null;
   1047         if (BCD_EXTENDED_TYPE_EF_ADN == bcdExtType) {
   1048             extended = BCD_EF_ADN_EXTENDED;
   1049         } else if (BCD_EXTENDED_TYPE_CALLED_PARTY == bcdExtType) {
   1050             extended = BCD_CALLED_PARTY_EXTENDED;
   1051         }
   1052         if (extended == null || extended.indexOf(c) == -1) {
   1053             throw new RuntimeException("invalid char for BCD " + c);
   1054         }
   1055         return 0xa + extended.indexOf(c);
   1056     }
   1057 
   1058     /**
   1059      * Return true iff the network portion of <code>address</code> is,
   1060      * as far as we can tell on the device, suitable for use as an SMS
   1061      * destination address.
   1062      */
   1063     public static boolean isWellFormedSmsAddress(String address) {
   1064         String networkPortion =
   1065                 PhoneNumberUtils.extractNetworkPortion(address);
   1066 
   1067         return (!(networkPortion.equals("+")
   1068                   || TextUtils.isEmpty(networkPortion)))
   1069                && isDialable(networkPortion);
   1070     }
   1071 
   1072     public static boolean isGlobalPhoneNumber(String phoneNumber) {
   1073         if (TextUtils.isEmpty(phoneNumber)) {
   1074             return false;
   1075         }
   1076 
   1077         Matcher match = GLOBAL_PHONE_NUMBER_PATTERN.matcher(phoneNumber);
   1078         return match.matches();
   1079     }
   1080 
   1081     private static boolean isDialable(String address) {
   1082         for (int i = 0, count = address.length(); i < count; i++) {
   1083             if (!isDialable(address.charAt(i))) {
   1084                 return false;
   1085             }
   1086         }
   1087         return true;
   1088     }
   1089 
   1090     private static boolean isNonSeparator(String address) {
   1091         for (int i = 0, count = address.length(); i < count; i++) {
   1092             if (!isNonSeparator(address.charAt(i))) {
   1093                 return false;
   1094             }
   1095         }
   1096         return true;
   1097     }
   1098     /**
   1099      * Note: calls extractNetworkPortion(), so do not use for
   1100      * SIM EF[ADN] style records
   1101      *
   1102      * Returns null if network portion is empty.
   1103      */
   1104     public static byte[] networkPortionToCalledPartyBCD(String s) {
   1105         String networkPortion = extractNetworkPortion(s);
   1106         return numberToCalledPartyBCDHelper(
   1107                 networkPortion, false, BCD_EXTENDED_TYPE_EF_ADN);
   1108     }
   1109 
   1110     /**
   1111      * Same as {@link #networkPortionToCalledPartyBCD}, but includes a
   1112      * one-byte length prefix.
   1113      */
   1114     public static byte[] networkPortionToCalledPartyBCDWithLength(String s) {
   1115         String networkPortion = extractNetworkPortion(s);
   1116         return numberToCalledPartyBCDHelper(
   1117                 networkPortion, true, BCD_EXTENDED_TYPE_EF_ADN);
   1118     }
   1119 
   1120     /**
   1121      * Convert a dialing number to BCD byte array
   1122      *
   1123      * @param number dialing number string. If the dialing number starts with '+', set to
   1124      * international TOA
   1125      *
   1126      * @return BCD byte array
   1127      *
   1128      * @deprecated use {@link #numberToCalledPartyBCD(String, int)} instead. Calling this method
   1129      * is equivalent to calling {@link #numberToCalledPartyBCD(String, int)} with
   1130      * {@link #BCD_EXTENDED_TYPE_EF_ADN} as the extended type.
   1131      */
   1132     @Deprecated
   1133     public static byte[] numberToCalledPartyBCD(String number) {
   1134         return numberToCalledPartyBCD(number, BCD_EXTENDED_TYPE_EF_ADN);
   1135     }
   1136 
   1137     /**
   1138      * Convert a dialing number to BCD byte array
   1139      *
   1140      * @param number dialing number string. If the dialing number starts with '+', set to
   1141      * international TOA
   1142      * @param bcdExtType used to determine the extended bcd coding
   1143      * @see #BCD_EXTENDED_TYPE_EF_ADN
   1144      * @see #BCD_EXTENDED_TYPE_CALLED_PARTY
   1145      *
   1146      * @return BCD byte array
   1147      */
   1148     public static byte[] numberToCalledPartyBCD(String number, @BcdExtendType int bcdExtType) {
   1149         return numberToCalledPartyBCDHelper(number, false, bcdExtType);
   1150     }
   1151 
   1152     /**
   1153      * If includeLength is true, prepend a one-byte length value to
   1154      * the return array.
   1155      */
   1156     private static byte[] numberToCalledPartyBCDHelper(
   1157             String number, boolean includeLength, @BcdExtendType int bcdExtType) {
   1158         int numberLenReal = number.length();
   1159         int numberLenEffective = numberLenReal;
   1160         boolean hasPlus = number.indexOf('+') != -1;
   1161         if (hasPlus) numberLenEffective--;
   1162 
   1163         if (numberLenEffective == 0) return null;
   1164 
   1165         int resultLen = (numberLenEffective + 1) / 2;  // Encoded numbers require only 4 bits each.
   1166         int extraBytes = 1;                            // Prepended TOA byte.
   1167         if (includeLength) extraBytes++;               // Optional prepended length byte.
   1168         resultLen += extraBytes;
   1169 
   1170         byte[] result = new byte[resultLen];
   1171 
   1172         int digitCount = 0;
   1173         for (int i = 0; i < numberLenReal; i++) {
   1174             char c = number.charAt(i);
   1175             if (c == '+') continue;
   1176             int shift = ((digitCount & 0x01) == 1) ? 4 : 0;
   1177             result[extraBytes + (digitCount >> 1)] |=
   1178                     (byte)((charToBCD(c, bcdExtType) & 0x0F) << shift);
   1179             digitCount++;
   1180         }
   1181 
   1182         // 1-fill any trailing odd nibble/quartet.
   1183         if ((digitCount & 0x01) == 1) result[extraBytes + (digitCount >> 1)] |= 0xF0;
   1184 
   1185         int offset = 0;
   1186         if (includeLength) result[offset++] = (byte)(resultLen - 1);
   1187         result[offset] = (byte)(hasPlus ? TOA_International : TOA_Unknown);
   1188 
   1189         return result;
   1190     }
   1191 
   1192     //================ Number formatting =========================
   1193 
   1194     /** The current locale is unknown, look for a country code or don't format */
   1195     public static final int FORMAT_UNKNOWN = 0;
   1196     /** NANP formatting */
   1197     public static final int FORMAT_NANP = 1;
   1198     /** Japanese formatting */
   1199     public static final int FORMAT_JAPAN = 2;
   1200 
   1201     /** List of country codes for countries that use the NANP */
   1202     private static final String[] NANP_COUNTRIES = new String[] {
   1203         "US", // United States
   1204         "CA", // Canada
   1205         "AS", // American Samoa
   1206         "AI", // Anguilla
   1207         "AG", // Antigua and Barbuda
   1208         "BS", // Bahamas
   1209         "BB", // Barbados
   1210         "BM", // Bermuda
   1211         "VG", // British Virgin Islands
   1212         "KY", // Cayman Islands
   1213         "DM", // Dominica
   1214         "DO", // Dominican Republic
   1215         "GD", // Grenada
   1216         "GU", // Guam
   1217         "JM", // Jamaica
   1218         "PR", // Puerto Rico
   1219         "MS", // Montserrat
   1220         "MP", // Northern Mariana Islands
   1221         "KN", // Saint Kitts and Nevis
   1222         "LC", // Saint Lucia
   1223         "VC", // Saint Vincent and the Grenadines
   1224         "TT", // Trinidad and Tobago
   1225         "TC", // Turks and Caicos Islands
   1226         "VI", // U.S. Virgin Islands
   1227     };
   1228 
   1229     private static final String KOREA_ISO_COUNTRY_CODE = "KR";
   1230 
   1231     private static final String JAPAN_ISO_COUNTRY_CODE = "JP";
   1232 
   1233     /**
   1234      * Breaks the given number down and formats it according to the rules
   1235      * for the country the number is from.
   1236      *
   1237      * @param source The phone number to format
   1238      * @return A locally acceptable formatting of the input, or the raw input if
   1239      *  formatting rules aren't known for the number
   1240      *
   1241      * @deprecated Use link #formatNumber(String phoneNumber, String defaultCountryIso) instead
   1242      */
   1243     @Deprecated
   1244     public static String formatNumber(String source) {
   1245         SpannableStringBuilder text = new SpannableStringBuilder(source);
   1246         formatNumber(text, getFormatTypeForLocale(Locale.getDefault()));
   1247         return text.toString();
   1248     }
   1249 
   1250     /**
   1251      * Formats the given number with the given formatting type. Currently
   1252      * {@link #FORMAT_NANP} and {@link #FORMAT_JAPAN} are supported as a formating type.
   1253      *
   1254      * @param source the phone number to format
   1255      * @param defaultFormattingType The default formatting rules to apply if the number does
   1256      * not begin with +[country_code]
   1257      * @return The phone number formatted with the given formatting type.
   1258      *
   1259      * @hide
   1260      * @deprecated Use link #formatNumber(String phoneNumber, String defaultCountryIso) instead
   1261      */
   1262     @Deprecated
   1263     public static String formatNumber(String source, int defaultFormattingType) {
   1264         SpannableStringBuilder text = new SpannableStringBuilder(source);
   1265         formatNumber(text, defaultFormattingType);
   1266         return text.toString();
   1267     }
   1268 
   1269     /**
   1270      * Returns the phone number formatting type for the given locale.
   1271      *
   1272      * @param locale The locale of interest, usually {@link Locale#getDefault()}
   1273      * @return The formatting type for the given locale, or FORMAT_UNKNOWN if the formatting
   1274      * rules are not known for the given locale
   1275      *
   1276      * @deprecated Use link #formatNumber(String phoneNumber, String defaultCountryIso) instead
   1277      */
   1278     @Deprecated
   1279     public static int getFormatTypeForLocale(Locale locale) {
   1280         String country = locale.getCountry();
   1281 
   1282         return getFormatTypeFromCountryCode(country);
   1283     }
   1284 
   1285     /**
   1286      * Formats a phone number in-place. Currently {@link #FORMAT_JAPAN} and {@link #FORMAT_NANP}
   1287      * is supported as a second argument.
   1288      *
   1289      * @param text The number to be formatted, will be modified with the formatting
   1290      * @param defaultFormattingType The default formatting rules to apply if the number does
   1291      * not begin with +[country_code]
   1292      *
   1293      * @deprecated Use link #formatNumber(String phoneNumber, String defaultCountryIso) instead
   1294      */
   1295     @Deprecated
   1296     public static void formatNumber(Editable text, int defaultFormattingType) {
   1297         int formatType = defaultFormattingType;
   1298 
   1299         if (text.length() > 2 && text.charAt(0) == '+') {
   1300             if (text.charAt(1) == '1') {
   1301                 formatType = FORMAT_NANP;
   1302             } else if (text.length() >= 3 && text.charAt(1) == '8'
   1303                 && text.charAt(2) == '1') {
   1304                 formatType = FORMAT_JAPAN;
   1305             } else {
   1306                 formatType = FORMAT_UNKNOWN;
   1307             }
   1308         }
   1309 
   1310         switch (formatType) {
   1311             case FORMAT_NANP:
   1312                 formatNanpNumber(text);
   1313                 return;
   1314             case FORMAT_JAPAN:
   1315                 formatJapaneseNumber(text);
   1316                 return;
   1317             case FORMAT_UNKNOWN:
   1318                 removeDashes(text);
   1319                 return;
   1320         }
   1321     }
   1322 
   1323     private static final int NANP_STATE_DIGIT = 1;
   1324     private static final int NANP_STATE_PLUS = 2;
   1325     private static final int NANP_STATE_ONE = 3;
   1326     private static final int NANP_STATE_DASH = 4;
   1327 
   1328     /**
   1329      * Formats a phone number in-place using the NANP formatting rules. Numbers will be formatted
   1330      * as:
   1331      *
   1332      * <p><code>
   1333      * xxxxx
   1334      * xxx-xxxx
   1335      * xxx-xxx-xxxx
   1336      * 1-xxx-xxx-xxxx
   1337      * +1-xxx-xxx-xxxx
   1338      * </code></p>
   1339      *
   1340      * @param text the number to be formatted, will be modified with the formatting
   1341      *
   1342      * @deprecated Use link #formatNumber(String phoneNumber, String defaultCountryIso) instead
   1343      */
   1344     @Deprecated
   1345     public static void formatNanpNumber(Editable text) {
   1346         int length = text.length();
   1347         if (length > "+1-nnn-nnn-nnnn".length()) {
   1348             // The string is too long to be formatted
   1349             return;
   1350         } else if (length <= 5) {
   1351             // The string is either a shortcode or too short to be formatted
   1352             return;
   1353         }
   1354 
   1355         CharSequence saved = text.subSequence(0, length);
   1356 
   1357         // Strip the dashes first, as we're going to add them back
   1358         removeDashes(text);
   1359         length = text.length();
   1360 
   1361         // When scanning the number we record where dashes need to be added,
   1362         // if they're non-0 at the end of the scan the dashes will be added in
   1363         // the proper places.
   1364         int dashPositions[] = new int[3];
   1365         int numDashes = 0;
   1366 
   1367         int state = NANP_STATE_DIGIT;
   1368         int numDigits = 0;
   1369         for (int i = 0; i < length; i++) {
   1370             char c = text.charAt(i);
   1371             switch (c) {
   1372                 case '1':
   1373                     if (numDigits == 0 || state == NANP_STATE_PLUS) {
   1374                         state = NANP_STATE_ONE;
   1375                         break;
   1376                     }
   1377                     // fall through
   1378                 case '2':
   1379                 case '3':
   1380                 case '4':
   1381                 case '5':
   1382                 case '6':
   1383                 case '7':
   1384                 case '8':
   1385                 case '9':
   1386                 case '0':
   1387                     if (state == NANP_STATE_PLUS) {
   1388                         // Only NANP number supported for now
   1389                         text.replace(0, length, saved);
   1390                         return;
   1391                     } else if (state == NANP_STATE_ONE) {
   1392                         // Found either +1 or 1, follow it up with a dash
   1393                         dashPositions[numDashes++] = i;
   1394                     } else if (state != NANP_STATE_DASH && (numDigits == 3 || numDigits == 6)) {
   1395                         // Found a digit that should be after a dash that isn't
   1396                         dashPositions[numDashes++] = i;
   1397                     }
   1398                     state = NANP_STATE_DIGIT;
   1399                     numDigits++;
   1400                     break;
   1401 
   1402                 case '-':
   1403                     state = NANP_STATE_DASH;
   1404                     break;
   1405 
   1406                 case '+':
   1407                     if (i == 0) {
   1408                         // Plus is only allowed as the first character
   1409                         state = NANP_STATE_PLUS;
   1410                         break;
   1411                     }
   1412                     // Fall through
   1413                 default:
   1414                     // Unknown character, bail on formatting
   1415                     text.replace(0, length, saved);
   1416                     return;
   1417             }
   1418         }
   1419 
   1420         if (numDigits == 7) {
   1421             // With 7 digits we want xxx-xxxx, not xxx-xxx-x
   1422             numDashes--;
   1423         }
   1424 
   1425         // Actually put the dashes in place
   1426         for (int i = 0; i < numDashes; i++) {
   1427             int pos = dashPositions[i];
   1428             text.replace(pos + i, pos + i, "-");
   1429         }
   1430 
   1431         // Remove trailing dashes
   1432         int len = text.length();
   1433         while (len > 0) {
   1434             if (text.charAt(len - 1) == '-') {
   1435                 text.delete(len - 1, len);
   1436                 len--;
   1437             } else {
   1438                 break;
   1439             }
   1440         }
   1441     }
   1442 
   1443     /**
   1444      * Formats a phone number in-place using the Japanese formatting rules.
   1445      * Numbers will be formatted as:
   1446      *
   1447      * <p><code>
   1448      * 03-xxxx-xxxx
   1449      * 090-xxxx-xxxx
   1450      * 0120-xxx-xxx
   1451      * +81-3-xxxx-xxxx
   1452      * +81-90-xxxx-xxxx
   1453      * </code></p>
   1454      *
   1455      * @param text the number to be formatted, will be modified with
   1456      * the formatting
   1457      *
   1458      * @deprecated Use link #formatNumber(String phoneNumber, String defaultCountryIso) instead
   1459      */
   1460     @Deprecated
   1461     public static void formatJapaneseNumber(Editable text) {
   1462         JapanesePhoneNumberFormatter.format(text);
   1463     }
   1464 
   1465     /**
   1466      * Removes all dashes from the number.
   1467      *
   1468      * @param text the number to clear from dashes
   1469      */
   1470     private static void removeDashes(Editable text) {
   1471         int p = 0;
   1472         while (p < text.length()) {
   1473             if (text.charAt(p) == '-') {
   1474                 text.delete(p, p + 1);
   1475            } else {
   1476                 p++;
   1477            }
   1478         }
   1479     }
   1480 
   1481     /**
   1482      * Formats the specified {@code phoneNumber} to the E.164 representation.
   1483      *
   1484      * @param phoneNumber the phone number to format.
   1485      * @param defaultCountryIso the ISO 3166-1 two letters country code.
   1486      * @return the E.164 representation, or null if the given phone number is not valid.
   1487      */
   1488     public static String formatNumberToE164(String phoneNumber, String defaultCountryIso) {
   1489         return formatNumberInternal(phoneNumber, defaultCountryIso, PhoneNumberFormat.E164);
   1490     }
   1491 
   1492     /**
   1493      * Formats the specified {@code phoneNumber} to the RFC3966 representation.
   1494      *
   1495      * @param phoneNumber the phone number to format.
   1496      * @param defaultCountryIso the ISO 3166-1 two letters country code.
   1497      * @return the RFC3966 representation, or null if the given phone number is not valid.
   1498      */
   1499     public static String formatNumberToRFC3966(String phoneNumber, String defaultCountryIso) {
   1500         return formatNumberInternal(phoneNumber, defaultCountryIso, PhoneNumberFormat.RFC3966);
   1501     }
   1502 
   1503     /**
   1504      * Formats the raw phone number (string) using the specified {@code formatIdentifier}.
   1505      * <p>
   1506      * The given phone number must have an area code and could have a country code.
   1507      * <p>
   1508      * The defaultCountryIso is used to validate the given number and generate the formatted number
   1509      * if the specified number doesn't have a country code.
   1510      *
   1511      * @param rawPhoneNumber The phone number to format.
   1512      * @param defaultCountryIso The ISO 3166-1 two letters country code.
   1513      * @param formatIdentifier The (enum) identifier of the desired format.
   1514      * @return the formatted representation, or null if the specified number is not valid.
   1515      */
   1516     private static String formatNumberInternal(
   1517             String rawPhoneNumber, String defaultCountryIso, PhoneNumberFormat formatIdentifier) {
   1518 
   1519         PhoneNumberUtil util = PhoneNumberUtil.getInstance();
   1520         try {
   1521             PhoneNumber phoneNumber = util.parse(rawPhoneNumber, defaultCountryIso);
   1522             if (util.isValidNumber(phoneNumber)) {
   1523                 return util.format(phoneNumber, formatIdentifier);
   1524             }
   1525         } catch (NumberParseException ignored) { }
   1526 
   1527         return null;
   1528     }
   1529 
   1530     /**
   1531      * Determines if a {@param phoneNumber} is international if dialed from
   1532      * {@param defaultCountryIso}.
   1533      *
   1534      * @param phoneNumber The phone number.
   1535      * @param defaultCountryIso The current country ISO.
   1536      * @return {@code true} if the number is international, {@code false} otherwise.
   1537      * @hide
   1538      */
   1539     public static boolean isInternationalNumber(String phoneNumber, String defaultCountryIso) {
   1540         // If no phone number is provided, it can't be international.
   1541         if (TextUtils.isEmpty(phoneNumber)) {
   1542             return false;
   1543         }
   1544 
   1545         // If it starts with # or * its not international.
   1546         if (phoneNumber.startsWith("#") || phoneNumber.startsWith("*")) {
   1547             return false;
   1548         }
   1549 
   1550         PhoneNumberUtil util = PhoneNumberUtil.getInstance();
   1551         try {
   1552             PhoneNumber pn = util.parseAndKeepRawInput(phoneNumber, defaultCountryIso);
   1553             return pn.getCountryCode() != util.getCountryCodeForRegion(defaultCountryIso);
   1554         } catch (NumberParseException e) {
   1555             return false;
   1556         }
   1557     }
   1558 
   1559     /**
   1560      * Format a phone number.
   1561      * <p>
   1562      * If the given number doesn't have the country code, the phone will be
   1563      * formatted to the default country's convention.
   1564      *
   1565      * @param phoneNumber
   1566      *            the number to be formatted.
   1567      * @param defaultCountryIso
   1568      *            the ISO 3166-1 two letters country code whose convention will
   1569      *            be used if the given number doesn't have the country code.
   1570      * @return the formatted number, or null if the given number is not valid.
   1571      */
   1572     public static String formatNumber(String phoneNumber, String defaultCountryIso) {
   1573         // Do not attempt to format numbers that start with a hash or star symbol.
   1574         if (phoneNumber.startsWith("#") || phoneNumber.startsWith("*")) {
   1575             return phoneNumber;
   1576         }
   1577 
   1578         PhoneNumberUtil util = PhoneNumberUtil.getInstance();
   1579         String result = null;
   1580         try {
   1581             PhoneNumber pn = util.parseAndKeepRawInput(phoneNumber, defaultCountryIso);
   1582 
   1583             if (KOREA_ISO_COUNTRY_CODE.equalsIgnoreCase(defaultCountryIso) &&
   1584                     (pn.getCountryCode() == util.getCountryCodeForRegion(KOREA_ISO_COUNTRY_CODE)) &&
   1585                     (pn.getCountryCodeSource() ==
   1586                             PhoneNumber.CountryCodeSource.FROM_NUMBER_WITH_PLUS_SIGN)) {
   1587                 /**
   1588                  * Need to reformat any local Korean phone numbers (when the user is in Korea) with
   1589                  * country code to corresponding national format which would replace the leading
   1590                  * +82 with 0.
   1591                  */
   1592                 result = util.format(pn, PhoneNumberUtil.PhoneNumberFormat.NATIONAL);
   1593             } else if (JAPAN_ISO_COUNTRY_CODE.equalsIgnoreCase(defaultCountryIso) &&
   1594                     pn.getCountryCode() == util.getCountryCodeForRegion(JAPAN_ISO_COUNTRY_CODE) &&
   1595                     (pn.getCountryCodeSource() ==
   1596                             PhoneNumber.CountryCodeSource.FROM_NUMBER_WITH_PLUS_SIGN)) {
   1597                 /**
   1598                  * Need to reformat Japanese phone numbers (when user is in Japan) with the national
   1599                  * dialing format.
   1600                  */
   1601                 result = util.format(pn, PhoneNumberUtil.PhoneNumberFormat.NATIONAL);
   1602             } else {
   1603                 result = util.formatInOriginalFormat(pn, defaultCountryIso);
   1604             }
   1605         } catch (NumberParseException e) {
   1606         }
   1607         return result;
   1608     }
   1609 
   1610     /**
   1611      * Format the phone number only if the given number hasn't been formatted.
   1612      * <p>
   1613      * The number which has only dailable character is treated as not being
   1614      * formatted.
   1615      *
   1616      * @param phoneNumber
   1617      *            the number to be formatted.
   1618      * @param phoneNumberE164
   1619      *            the E164 format number whose country code is used if the given
   1620      *            phoneNumber doesn't have the country code.
   1621      * @param defaultCountryIso
   1622      *            the ISO 3166-1 two letters country code whose convention will
   1623      *            be used if the phoneNumberE164 is null or invalid, or if phoneNumber
   1624      *            contains IDD.
   1625      * @return the formatted number if the given number has been formatted,
   1626      *            otherwise, return the given number.
   1627      */
   1628     public static String formatNumber(
   1629             String phoneNumber, String phoneNumberE164, String defaultCountryIso) {
   1630         int len = phoneNumber.length();
   1631         for (int i = 0; i < len; i++) {
   1632             if (!isDialable(phoneNumber.charAt(i))) {
   1633                 return phoneNumber;
   1634             }
   1635         }
   1636         PhoneNumberUtil util = PhoneNumberUtil.getInstance();
   1637         // Get the country code from phoneNumberE164
   1638         if (phoneNumberE164 != null && phoneNumberE164.length() >= 2
   1639                 && phoneNumberE164.charAt(0) == '+') {
   1640             try {
   1641                 // The number to be parsed is in E164 format, so the default region used doesn't
   1642                 // matter.
   1643                 PhoneNumber pn = util.parse(phoneNumberE164, "ZZ");
   1644                 String regionCode = util.getRegionCodeForNumber(pn);
   1645                 if (!TextUtils.isEmpty(regionCode) &&
   1646                     // This makes sure phoneNumber doesn't contain an IDD
   1647                     normalizeNumber(phoneNumber).indexOf(phoneNumberE164.substring(1)) <= 0) {
   1648                     defaultCountryIso = regionCode;
   1649                 }
   1650             } catch (NumberParseException e) {
   1651             }
   1652         }
   1653         String result = formatNumber(phoneNumber, defaultCountryIso);
   1654         return result != null ? result : phoneNumber;
   1655     }
   1656 
   1657     /**
   1658      * Normalize a phone number by removing the characters other than digits. If
   1659      * the given number has keypad letters, the letters will be converted to
   1660      * digits first.
   1661      *
   1662      * @param phoneNumber the number to be normalized.
   1663      * @return the normalized number.
   1664      */
   1665     public static String normalizeNumber(String phoneNumber) {
   1666         if (TextUtils.isEmpty(phoneNumber)) {
   1667             return "";
   1668         }
   1669 
   1670         StringBuilder sb = new StringBuilder();
   1671         int len = phoneNumber.length();
   1672         for (int i = 0; i < len; i++) {
   1673             char c = phoneNumber.charAt(i);
   1674             // Character.digit() supports ASCII and Unicode digits (fullwidth, Arabic-Indic, etc.)
   1675             int digit = Character.digit(c, 10);
   1676             if (digit != -1) {
   1677                 sb.append(digit);
   1678             } else if (sb.length() == 0 && c == '+') {
   1679                 sb.append(c);
   1680             } else if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) {
   1681                 return normalizeNumber(PhoneNumberUtils.convertKeypadLettersToDigits(phoneNumber));
   1682             }
   1683         }
   1684         return sb.toString();
   1685     }
   1686 
   1687     /**
   1688      * Replaces all unicode(e.g. Arabic, Persian) digits with their decimal digit equivalents.
   1689      *
   1690      * @param number the number to perform the replacement on.
   1691      * @return the replaced number.
   1692      */
   1693     public static String replaceUnicodeDigits(String number) {
   1694         StringBuilder normalizedDigits = new StringBuilder(number.length());
   1695         for (char c : number.toCharArray()) {
   1696             int digit = Character.digit(c, 10);
   1697             if (digit != -1) {
   1698                 normalizedDigits.append(digit);
   1699             } else {
   1700                 normalizedDigits.append(c);
   1701             }
   1702         }
   1703         return normalizedDigits.toString();
   1704     }
   1705 
   1706     // Three and four digit phone numbers for either special services,
   1707     // or 3-6 digit addresses from the network (eg carrier-originated SMS messages) should
   1708     // not match.
   1709     //
   1710     // This constant used to be 5, but SMS short codes has increased in length and
   1711     // can be easily 6 digits now days. Most countries have SMS short code length between
   1712     // 3 to 6 digits. The exceptions are
   1713     //
   1714     // Australia: Short codes are six or eight digits in length, starting with the prefix "19"
   1715     //            followed by an additional four or six digits and two.
   1716     // Czechia: Codes are seven digits in length for MO and five (not billed) or
   1717     //            eight (billed) for MT direction
   1718     //
   1719     // see http://en.wikipedia.org/wiki/Short_code#Regional_differences for reference
   1720     //
   1721     // However, in order to loose match 650-555-1212 and 555-1212, we need to set the min match
   1722     // to 7.
   1723     static final int MIN_MATCH = 7;
   1724 
   1725     /**
   1726      * Checks a given number against the list of
   1727      * emergency numbers provided by the RIL and SIM card.
   1728      *
   1729      * @param number the number to look up.
   1730      * @return true if the number is in the list of emergency numbers
   1731      *         listed in the RIL / SIM, otherwise return false.
   1732      */
   1733     public static boolean isEmergencyNumber(String number) {
   1734         return isEmergencyNumber(getDefaultVoiceSubId(), number);
   1735     }
   1736 
   1737     /**
   1738      * Checks a given number against the list of
   1739      * emergency numbers provided by the RIL and SIM card.
   1740      *
   1741      * @param subId the subscription id of the SIM.
   1742      * @param number the number to look up.
   1743      * @return true if the number is in the list of emergency numbers
   1744      *         listed in the RIL / SIM, otherwise return false.
   1745      * @hide
   1746      */
   1747     public static boolean isEmergencyNumber(int subId, String number) {
   1748         // Return true only if the specified number *exactly* matches
   1749         // one of the emergency numbers listed by the RIL / SIM.
   1750         return isEmergencyNumberInternal(subId, number, true /* useExactMatch */);
   1751     }
   1752 
   1753     /**
   1754      * Checks if given number might *potentially* result in
   1755      * a call to an emergency service on the current network.
   1756      *
   1757      * Specifically, this method will return true if the specified number
   1758      * is an emergency number according to the list managed by the RIL or
   1759      * SIM, *or* if the specified number simply starts with the same
   1760      * digits as any of the emergency numbers listed in the RIL / SIM.
   1761      *
   1762      * This method is intended for internal use by the phone app when
   1763      * deciding whether to allow ACTION_CALL intents from 3rd party apps
   1764      * (where we're required to *not* allow emergency calls to be placed.)
   1765      *
   1766      * @param number the number to look up.
   1767      * @return true if the number is in the list of emergency numbers
   1768      *         listed in the RIL / SIM, *or* if the number starts with the
   1769      *         same digits as any of those emergency numbers.
   1770      *
   1771      * @hide
   1772      */
   1773     public static boolean isPotentialEmergencyNumber(String number) {
   1774         return isPotentialEmergencyNumber(getDefaultVoiceSubId(), number);
   1775     }
   1776 
   1777     /**
   1778      * Checks if given number might *potentially* result in
   1779      * a call to an emergency service on the current network.
   1780      *
   1781      * Specifically, this method will return true if the specified number
   1782      * is an emergency number according to the list managed by the RIL or
   1783      * SIM, *or* if the specified number simply starts with the same
   1784      * digits as any of the emergency numbers listed in the RIL / SIM.
   1785      *
   1786      * This method is intended for internal use by the phone app when
   1787      * deciding whether to allow ACTION_CALL intents from 3rd party apps
   1788      * (where we're required to *not* allow emergency calls to be placed.)
   1789      *
   1790      * @param subId the subscription id of the SIM.
   1791      * @param number the number to look up.
   1792      * @return true if the number is in the list of emergency numbers
   1793      *         listed in the RIL / SIM, *or* if the number starts with the
   1794      *         same digits as any of those emergency numbers.
   1795      * @hide
   1796      */
   1797     public static boolean isPotentialEmergencyNumber(int subId, String number) {
   1798         // Check against the emergency numbers listed by the RIL / SIM,
   1799         // and *don't* require an exact match.
   1800         return isEmergencyNumberInternal(subId, number, false /* useExactMatch */);
   1801     }
   1802 
   1803     /**
   1804      * Helper function for isEmergencyNumber(String) and
   1805      * isPotentialEmergencyNumber(String).
   1806      *
   1807      * @param number the number to look up.
   1808      *
   1809      * @param useExactMatch if true, consider a number to be an emergency
   1810      *           number only if it *exactly* matches a number listed in
   1811      *           the RIL / SIM.  If false, a number is considered to be an
   1812      *           emergency number if it simply starts with the same digits
   1813      *           as any of the emergency numbers listed in the RIL / SIM.
   1814      *           (Setting useExactMatch to false allows you to identify
   1815      *           number that could *potentially* result in emergency calls
   1816      *           since many networks will actually ignore trailing digits
   1817      *           after a valid emergency number.)
   1818      *
   1819      * @return true if the number is in the list of emergency numbers
   1820      *         listed in the RIL / sim, otherwise return false.
   1821      */
   1822     private static boolean isEmergencyNumberInternal(String number, boolean useExactMatch) {
   1823         return isEmergencyNumberInternal(getDefaultVoiceSubId(), number, useExactMatch);
   1824     }
   1825 
   1826     /**
   1827      * Helper function for isEmergencyNumber(String) and
   1828      * isPotentialEmergencyNumber(String).
   1829      *
   1830      * @param subId the subscription id of the SIM.
   1831      * @param number the number to look up.
   1832      *
   1833      * @param useExactMatch if true, consider a number to be an emergency
   1834      *           number only if it *exactly* matches a number listed in
   1835      *           the RIL / SIM.  If false, a number is considered to be an
   1836      *           emergency number if it simply starts with the same digits
   1837      *           as any of the emergency numbers listed in the RIL / SIM.
   1838      *           (Setting useExactMatch to false allows you to identify
   1839      *           number that could *potentially* result in emergency calls
   1840      *           since many networks will actually ignore trailing digits
   1841      *           after a valid emergency number.)
   1842      *
   1843      * @return true if the number is in the list of emergency numbers
   1844      *         listed in the RIL / sim, otherwise return false.
   1845      */
   1846     private static boolean isEmergencyNumberInternal(int subId, String number,
   1847             boolean useExactMatch) {
   1848         return isEmergencyNumberInternal(subId, number, null, useExactMatch);
   1849     }
   1850 
   1851     /**
   1852      * Checks if a given number is an emergency number for a specific country.
   1853      *
   1854      * @param number the number to look up.
   1855      * @param defaultCountryIso the specific country which the number should be checked against
   1856      * @return if the number is an emergency number for the specific country, then return true,
   1857      * otherwise false
   1858      *
   1859      * @hide
   1860      */
   1861     public static boolean isEmergencyNumber(String number, String defaultCountryIso) {
   1862             return isEmergencyNumber(getDefaultVoiceSubId(), number, defaultCountryIso);
   1863     }
   1864 
   1865     /**
   1866      * Checks if a given number is an emergency number for a specific country.
   1867      *
   1868      * @param subId the subscription id of the SIM.
   1869      * @param number the number to look up.
   1870      * @param defaultCountryIso the specific country which the number should be checked against
   1871      * @return if the number is an emergency number for the specific country, then return true,
   1872      * otherwise false
   1873      * @hide
   1874      */
   1875     public static boolean isEmergencyNumber(int subId, String number, String defaultCountryIso) {
   1876         return isEmergencyNumberInternal(subId, number,
   1877                                          defaultCountryIso,
   1878                                          true /* useExactMatch */);
   1879     }
   1880 
   1881     /**
   1882      * Checks if a given number might *potentially* result in a call to an
   1883      * emergency service, for a specific country.
   1884      *
   1885      * Specifically, this method will return true if the specified number
   1886      * is an emergency number in the specified country, *or* if the number
   1887      * simply starts with the same digits as any emergency number for that
   1888      * country.
   1889      *
   1890      * This method is intended for internal use by the phone app when
   1891      * deciding whether to allow ACTION_CALL intents from 3rd party apps
   1892      * (where we're required to *not* allow emergency calls to be placed.)
   1893      *
   1894      * @param number the number to look up.
   1895      * @param defaultCountryIso the specific country which the number should be checked against
   1896      * @return true if the number is an emergency number for the specific
   1897      *         country, *or* if the number starts with the same digits as
   1898      *         any of those emergency numbers.
   1899      *
   1900      * @hide
   1901      */
   1902     public static boolean isPotentialEmergencyNumber(String number, String defaultCountryIso) {
   1903         return isPotentialEmergencyNumber(getDefaultVoiceSubId(), number, defaultCountryIso);
   1904     }
   1905 
   1906     /**
   1907      * Checks if a given number might *potentially* result in a call to an
   1908      * emergency service, for a specific country.
   1909      *
   1910      * Specifically, this method will return true if the specified number
   1911      * is an emergency number in the specified country, *or* if the number
   1912      * simply starts with the same digits as any emergency number for that
   1913      * country.
   1914      *
   1915      * This method is intended for internal use by the phone app when
   1916      * deciding whether to allow ACTION_CALL intents from 3rd party apps
   1917      * (where we're required to *not* allow emergency calls to be placed.)
   1918      *
   1919      * @param subId the subscription id of the SIM.
   1920      * @param number the number to look up.
   1921      * @param defaultCountryIso the specific country which the number should be checked against
   1922      * @return true if the number is an emergency number for the specific
   1923      *         country, *or* if the number starts with the same digits as
   1924      *         any of those emergency numbers.
   1925      * @hide
   1926      */
   1927     public static boolean isPotentialEmergencyNumber(int subId, String number,
   1928             String defaultCountryIso) {
   1929         return isEmergencyNumberInternal(subId, number,
   1930                                          defaultCountryIso,
   1931                                          false /* useExactMatch */);
   1932     }
   1933 
   1934     /**
   1935      * Helper function for isEmergencyNumber(String, String) and
   1936      * isPotentialEmergencyNumber(String, String).
   1937      *
   1938      * @param number the number to look up.
   1939      * @param defaultCountryIso the specific country which the number should be checked against
   1940      * @param useExactMatch if true, consider a number to be an emergency
   1941      *           number only if it *exactly* matches a number listed in
   1942      *           the RIL / SIM.  If false, a number is considered to be an
   1943      *           emergency number if it simply starts with the same digits
   1944      *           as any of the emergency numbers listed in the RIL / SIM.
   1945      *
   1946      * @return true if the number is an emergency number for the specified country.
   1947      */
   1948     private static boolean isEmergencyNumberInternal(String number,
   1949                                                      String defaultCountryIso,
   1950                                                      boolean useExactMatch) {
   1951         return isEmergencyNumberInternal(getDefaultVoiceSubId(), number, defaultCountryIso,
   1952                 useExactMatch);
   1953     }
   1954 
   1955     /**
   1956      * Helper function for isEmergencyNumber(String, String) and
   1957      * isPotentialEmergencyNumber(String, String).
   1958      *
   1959      * @param subId the subscription id of the SIM.
   1960      * @param number the number to look up.
   1961      * @param defaultCountryIso the specific country which the number should be checked against
   1962      * @param useExactMatch if true, consider a number to be an emergency
   1963      *           number only if it *exactly* matches a number listed in
   1964      *           the RIL / SIM.  If false, a number is considered to be an
   1965      *           emergency number if it simply starts with the same digits
   1966      *           as any of the emergency numbers listed in the RIL / SIM.
   1967      *
   1968      * @return true if the number is an emergency number for the specified country.
   1969      * @hide
   1970      */
   1971     private static boolean isEmergencyNumberInternal(int subId, String number,
   1972                                                      String defaultCountryIso,
   1973                                                      boolean useExactMatch) {
   1974         // If the number passed in is null, just return false:
   1975         if (number == null) return false;
   1976 
   1977         // If the number passed in is a SIP address, return false, since the
   1978         // concept of "emergency numbers" is only meaningful for calls placed
   1979         // over the cell network.
   1980         // (Be sure to do this check *before* calling extractNetworkPortionAlt(),
   1981         // since the whole point of extractNetworkPortionAlt() is to filter out
   1982         // any non-dialable characters (which would turn 'abc911def (at) example.com'
   1983         // into '911', for example.))
   1984         if (isUriNumber(number)) {
   1985             return false;
   1986         }
   1987 
   1988         // Strip the separators from the number before comparing it
   1989         // to the list.
   1990         number = extractNetworkPortionAlt(number);
   1991 
   1992         String emergencyNumbers = "";
   1993         int slotId = SubscriptionManager.getSlotIndex(subId);
   1994 
   1995         // retrieve the list of emergency numbers
   1996         // check read-write ecclist property first
   1997         String ecclist = (slotId <= 0) ? "ril.ecclist" : ("ril.ecclist" + slotId);
   1998 
   1999         emergencyNumbers = SystemProperties.get(ecclist, "");
   2000 
   2001         Rlog.d(LOG_TAG, "slotId:" + slotId + " subId:" + subId + " country:"
   2002                 + defaultCountryIso + " emergencyNumbers: " +  emergencyNumbers);
   2003 
   2004         if (TextUtils.isEmpty(emergencyNumbers)) {
   2005             // then read-only ecclist property since old RIL only uses this
   2006             emergencyNumbers = SystemProperties.get("ro.ril.ecclist");
   2007         }
   2008 
   2009         if (!TextUtils.isEmpty(emergencyNumbers)) {
   2010             // searches through the comma-separated list for a match,
   2011             // return true if one is found.
   2012             for (String emergencyNum : emergencyNumbers.split(",")) {
   2013                 // It is not possible to append additional digits to an emergency number to dial
   2014                 // the number in Brazil - it won't connect.
   2015                 if (useExactMatch || "BR".equalsIgnoreCase(defaultCountryIso)) {
   2016                     if (number.equals(emergencyNum)) {
   2017                         return true;
   2018                     }
   2019                 } else {
   2020                     if (number.startsWith(emergencyNum)) {
   2021                         return true;
   2022                     }
   2023                 }
   2024             }
   2025             // no matches found against the list!
   2026             return false;
   2027         }
   2028 
   2029         Rlog.d(LOG_TAG, "System property doesn't provide any emergency numbers."
   2030                 + " Use embedded logic for determining ones.");
   2031 
   2032         // If slot id is invalid, means that there is no sim card.
   2033         // According spec 3GPP TS22.101, the following numbers should be
   2034         // ECC numbers when SIM/USIM is not present.
   2035         emergencyNumbers = ((slotId < 0) ? "112,911,000,08,110,118,119,999" : "112,911");
   2036 
   2037         for (String emergencyNum : emergencyNumbers.split(",")) {
   2038             if (useExactMatch) {
   2039                 if (number.equals(emergencyNum)) {
   2040                     return true;
   2041                 }
   2042             } else {
   2043                 if (number.startsWith(emergencyNum)) {
   2044                     return true;
   2045                 }
   2046             }
   2047         }
   2048 
   2049         // No ecclist system property, so use our own list.
   2050         if (defaultCountryIso != null) {
   2051             ShortNumberInfo info = ShortNumberInfo.getInstance();
   2052             if (useExactMatch) {
   2053                 return info.isEmergencyNumber(number, defaultCountryIso);
   2054             } else {
   2055                 return info.connectsToEmergencyNumber(number, defaultCountryIso);
   2056             }
   2057         }
   2058 
   2059         return false;
   2060     }
   2061 
   2062     /**
   2063      * Checks if a given number is an emergency number for the country that the user is in.
   2064      *
   2065      * @param number the number to look up.
   2066      * @param context the specific context which the number should be checked against
   2067      * @return true if the specified number is an emergency number for the country the user
   2068      * is currently in.
   2069      */
   2070     public static boolean isLocalEmergencyNumber(Context context, String number) {
   2071         return isLocalEmergencyNumber(context, getDefaultVoiceSubId(), number);
   2072     }
   2073 
   2074     /**
   2075      * Checks if a given number is an emergency number for the country that the user is in.
   2076      *
   2077      * @param subId the subscription id of the SIM.
   2078      * @param number the number to look up.
   2079      * @param context the specific context which the number should be checked against
   2080      * @return true if the specified number is an emergency number for the country the user
   2081      * is currently in.
   2082      * @hide
   2083      */
   2084     public static boolean isLocalEmergencyNumber(Context context, int subId, String number) {
   2085         return isLocalEmergencyNumberInternal(subId, number,
   2086                                               context,
   2087                                               true /* useExactMatch */);
   2088     }
   2089 
   2090     /**
   2091      * Checks if a given number might *potentially* result in a call to an
   2092      * emergency service, for the country that the user is in. The current
   2093      * country is determined using the CountryDetector.
   2094      *
   2095      * Specifically, this method will return true if the specified number
   2096      * is an emergency number in the current country, *or* if the number
   2097      * simply starts with the same digits as any emergency number for the
   2098      * current country.
   2099      *
   2100      * This method is intended for internal use by the phone app when
   2101      * deciding whether to allow ACTION_CALL intents from 3rd party apps
   2102      * (where we're required to *not* allow emergency calls to be placed.)
   2103      *
   2104      * @param number the number to look up.
   2105      * @param context the specific context which the number should be checked against
   2106      * @return true if the specified number is an emergency number for a local country, based on the
   2107      *              CountryDetector.
   2108      *
   2109      * @see android.location.CountryDetector
   2110      * @hide
   2111      */
   2112     public static boolean isPotentialLocalEmergencyNumber(Context context, String number) {
   2113         return isPotentialLocalEmergencyNumber(context, getDefaultVoiceSubId(), number);
   2114     }
   2115 
   2116     /**
   2117      * Checks if a given number might *potentially* result in a call to an
   2118      * emergency service, for the country that the user is in. The current
   2119      * country is determined using the CountryDetector.
   2120      *
   2121      * Specifically, this method will return true if the specified number
   2122      * is an emergency number in the current country, *or* if the number
   2123      * simply starts with the same digits as any emergency number for the
   2124      * current country.
   2125      *
   2126      * This method is intended for internal use by the phone app when
   2127      * deciding whether to allow ACTION_CALL intents from 3rd party apps
   2128      * (where we're required to *not* allow emergency calls to be placed.)
   2129      *
   2130      * @param subId the subscription id of the SIM.
   2131      * @param number the number to look up.
   2132      * @param context the specific context which the number should be checked against
   2133      * @return true if the specified number is an emergency number for a local country, based on the
   2134      *              CountryDetector.
   2135      *
   2136      * @hide
   2137      */
   2138     public static boolean isPotentialLocalEmergencyNumber(Context context, int subId,
   2139             String number) {
   2140         return isLocalEmergencyNumberInternal(subId, number,
   2141                                               context,
   2142                                               false /* useExactMatch */);
   2143     }
   2144 
   2145     /**
   2146      * Helper function for isLocalEmergencyNumber() and
   2147      * isPotentialLocalEmergencyNumber().
   2148      *
   2149      * @param number the number to look up.
   2150      * @param context the specific context which the number should be checked against
   2151      * @param useExactMatch if true, consider a number to be an emergency
   2152      *           number only if it *exactly* matches a number listed in
   2153      *           the RIL / SIM.  If false, a number is considered to be an
   2154      *           emergency number if it simply starts with the same digits
   2155      *           as any of the emergency numbers listed in the RIL / SIM.
   2156      *
   2157      * @return true if the specified number is an emergency number for a
   2158      *              local country, based on the CountryDetector.
   2159      *
   2160      * @see android.location.CountryDetector
   2161      * @hide
   2162      */
   2163     private static boolean isLocalEmergencyNumberInternal(String number,
   2164                                                           Context context,
   2165                                                           boolean useExactMatch) {
   2166         return isLocalEmergencyNumberInternal(getDefaultVoiceSubId(), number, context,
   2167                 useExactMatch);
   2168     }
   2169 
   2170     /**
   2171      * Helper function for isLocalEmergencyNumber() and
   2172      * isPotentialLocalEmergencyNumber().
   2173      *
   2174      * @param subId the subscription id of the SIM.
   2175      * @param number the number to look up.
   2176      * @param context the specific context which the number should be checked against
   2177      * @param useExactMatch if true, consider a number to be an emergency
   2178      *           number only if it *exactly* matches a number listed in
   2179      *           the RIL / SIM.  If false, a number is considered to be an
   2180      *           emergency number if it simply starts with the same digits
   2181      *           as any of the emergency numbers listed in the RIL / SIM.
   2182      *
   2183      * @return true if the specified number is an emergency number for a
   2184      *              local country, based on the CountryDetector.
   2185      * @hide
   2186      */
   2187     private static boolean isLocalEmergencyNumberInternal(int subId, String number,
   2188                                                           Context context,
   2189                                                           boolean useExactMatch) {
   2190         String countryIso;
   2191         CountryDetector detector = (CountryDetector) context.getSystemService(
   2192                 Context.COUNTRY_DETECTOR);
   2193         if (detector != null && detector.detectCountry() != null) {
   2194             countryIso = detector.detectCountry().getCountryIso();
   2195         } else {
   2196             Locale locale = context.getResources().getConfiguration().locale;
   2197             countryIso = locale.getCountry();
   2198             Rlog.w(LOG_TAG, "No CountryDetector; falling back to countryIso based on locale: "
   2199                     + countryIso);
   2200         }
   2201         return isEmergencyNumberInternal(subId, number, countryIso, useExactMatch);
   2202     }
   2203 
   2204     /**
   2205      * isVoiceMailNumber: checks a given number against the voicemail
   2206      *   number provided by the RIL and SIM card. The caller must have
   2207      *   the READ_PHONE_STATE credential.
   2208      *
   2209      * @param number the number to look up.
   2210      * @return true if the number is in the list of voicemail. False
   2211      * otherwise, including if the caller does not have the permission
   2212      * to read the VM number.
   2213      */
   2214     public static boolean isVoiceMailNumber(String number) {
   2215         return isVoiceMailNumber(SubscriptionManager.getDefaultSubscriptionId(), number);
   2216     }
   2217 
   2218     /**
   2219      * isVoiceMailNumber: checks a given number against the voicemail
   2220      *   number provided by the RIL and SIM card. The caller must have
   2221      *   the READ_PHONE_STATE credential.
   2222      *
   2223      * @param subId the subscription id of the SIM.
   2224      * @param number the number to look up.
   2225      * @return true if the number is in the list of voicemail. False
   2226      * otherwise, including if the caller does not have the permission
   2227      * to read the VM number.
   2228      * @hide
   2229      */
   2230     public static boolean isVoiceMailNumber(int subId, String number) {
   2231         return isVoiceMailNumber(null, subId, number);
   2232     }
   2233 
   2234     /**
   2235      * isVoiceMailNumber: checks a given number against the voicemail
   2236      *   number provided by the RIL and SIM card. The caller must have
   2237      *   the READ_PHONE_STATE credential.
   2238      *
   2239      * @param context {@link Context}.
   2240      * @param subId the subscription id of the SIM.
   2241      * @param number the number to look up.
   2242      * @return true if the number is in the list of voicemail. False
   2243      * otherwise, including if the caller does not have the permission
   2244      * to read the VM number.
   2245      * @hide
   2246      */
   2247     public static boolean isVoiceMailNumber(Context context, int subId, String number) {
   2248         String vmNumber, mdn;
   2249         try {
   2250             final TelephonyManager tm;
   2251             if (context == null) {
   2252                 tm = TelephonyManager.getDefault();
   2253                 if (DBG) log("isVoiceMailNumber: default tm");
   2254             } else {
   2255                 tm = TelephonyManager.from(context);
   2256                 if (DBG) log("isVoiceMailNumber: tm from context");
   2257             }
   2258             vmNumber = tm.getVoiceMailNumber(subId);
   2259             mdn = tm.getLine1Number(subId);
   2260             if (DBG) log("isVoiceMailNumber: mdn=" + mdn + ", vmNumber=" + vmNumber
   2261                     + ", number=" + number);
   2262         } catch (SecurityException ex) {
   2263             if (DBG) log("isVoiceMailNumber: SecurityExcpetion caught");
   2264             return false;
   2265         }
   2266         // Strip the separators from the number before comparing it
   2267         // to the list.
   2268         number = extractNetworkPortionAlt(number);
   2269         if (TextUtils.isEmpty(number)) {
   2270             if (DBG) log("isVoiceMailNumber: number is empty after stripping");
   2271             return false;
   2272         }
   2273 
   2274         // check if the carrier considers MDN to be an additional voicemail number
   2275         boolean compareWithMdn = false;
   2276         if (context != null) {
   2277             CarrierConfigManager configManager = (CarrierConfigManager)
   2278                     context.getSystemService(Context.CARRIER_CONFIG_SERVICE);
   2279             if (configManager != null) {
   2280                 PersistableBundle b = configManager.getConfigForSubId(subId);
   2281                 if (b != null) {
   2282                     compareWithMdn = b.getBoolean(CarrierConfigManager.
   2283                             KEY_MDN_IS_ADDITIONAL_VOICEMAIL_NUMBER_BOOL);
   2284                     if (DBG) log("isVoiceMailNumber: compareWithMdn=" + compareWithMdn);
   2285                 }
   2286             }
   2287         }
   2288 
   2289         if (compareWithMdn) {
   2290             if (DBG) log("isVoiceMailNumber: treating mdn as additional vm number");
   2291             return compare(number, vmNumber) || compare(number, mdn);
   2292         } else {
   2293             if (DBG) log("isVoiceMailNumber: returning regular compare");
   2294             return compare(number, vmNumber);
   2295         }
   2296     }
   2297 
   2298     /**
   2299      * Translates any alphabetic letters (i.e. [A-Za-z]) in the
   2300      * specified phone number into the equivalent numeric digits,
   2301      * according to the phone keypad letter mapping described in
   2302      * ITU E.161 and ISO/IEC 9995-8.
   2303      *
   2304      * @return the input string, with alpha letters converted to numeric
   2305      *         digits using the phone keypad letter mapping.  For example,
   2306      *         an input of "1-800-GOOG-411" will return "1-800-4664-411".
   2307      */
   2308     public static String convertKeypadLettersToDigits(String input) {
   2309         if (input == null) {
   2310             return input;
   2311         }
   2312         int len = input.length();
   2313         if (len == 0) {
   2314             return input;
   2315         }
   2316 
   2317         char[] out = input.toCharArray();
   2318 
   2319         for (int i = 0; i < len; i++) {
   2320             char c = out[i];
   2321             // If this char isn't in KEYPAD_MAP at all, just leave it alone.
   2322             out[i] = (char) KEYPAD_MAP.get(c, c);
   2323         }
   2324 
   2325         return new String(out);
   2326     }
   2327 
   2328     /**
   2329      * The phone keypad letter mapping (see ITU E.161 or ISO/IEC 9995-8.)
   2330      * TODO: This should come from a resource.
   2331      */
   2332     private static final SparseIntArray KEYPAD_MAP = new SparseIntArray();
   2333     static {
   2334         KEYPAD_MAP.put('a', '2'); KEYPAD_MAP.put('b', '2'); KEYPAD_MAP.put('c', '2');
   2335         KEYPAD_MAP.put('A', '2'); KEYPAD_MAP.put('B', '2'); KEYPAD_MAP.put('C', '2');
   2336 
   2337         KEYPAD_MAP.put('d', '3'); KEYPAD_MAP.put('e', '3'); KEYPAD_MAP.put('f', '3');
   2338         KEYPAD_MAP.put('D', '3'); KEYPAD_MAP.put('E', '3'); KEYPAD_MAP.put('F', '3');
   2339 
   2340         KEYPAD_MAP.put('g', '4'); KEYPAD_MAP.put('h', '4'); KEYPAD_MAP.put('i', '4');
   2341         KEYPAD_MAP.put('G', '4'); KEYPAD_MAP.put('H', '4'); KEYPAD_MAP.put('I', '4');
   2342 
   2343         KEYPAD_MAP.put('j', '5'); KEYPAD_MAP.put('k', '5'); KEYPAD_MAP.put('l', '5');
   2344         KEYPAD_MAP.put('J', '5'); KEYPAD_MAP.put('K', '5'); KEYPAD_MAP.put('L', '5');
   2345 
   2346         KEYPAD_MAP.put('m', '6'); KEYPAD_MAP.put('n', '6'); KEYPAD_MAP.put('o', '6');
   2347         KEYPAD_MAP.put('M', '6'); KEYPAD_MAP.put('N', '6'); KEYPAD_MAP.put('O', '6');
   2348 
   2349         KEYPAD_MAP.put('p', '7'); KEYPAD_MAP.put('q', '7'); KEYPAD_MAP.put('r', '7'); KEYPAD_MAP.put('s', '7');
   2350         KEYPAD_MAP.put('P', '7'); KEYPAD_MAP.put('Q', '7'); KEYPAD_MAP.put('R', '7'); KEYPAD_MAP.put('S', '7');
   2351 
   2352         KEYPAD_MAP.put('t', '8'); KEYPAD_MAP.put('u', '8'); KEYPAD_MAP.put('v', '8');
   2353         KEYPAD_MAP.put('T', '8'); KEYPAD_MAP.put('U', '8'); KEYPAD_MAP.put('V', '8');
   2354 
   2355         KEYPAD_MAP.put('w', '9'); KEYPAD_MAP.put('x', '9'); KEYPAD_MAP.put('y', '9'); KEYPAD_MAP.put('z', '9');
   2356         KEYPAD_MAP.put('W', '9'); KEYPAD_MAP.put('X', '9'); KEYPAD_MAP.put('Y', '9'); KEYPAD_MAP.put('Z', '9');
   2357     }
   2358 
   2359     //================ Plus Code formatting =========================
   2360     private static final char PLUS_SIGN_CHAR = '+';
   2361     private static final String PLUS_SIGN_STRING = "+";
   2362     private static final String NANP_IDP_STRING = "011";
   2363     private static final int NANP_LENGTH = 10;
   2364 
   2365     /**
   2366      * This function checks if there is a plus sign (+) in the passed-in dialing number.
   2367      * If there is, it processes the plus sign based on the default telephone
   2368      * numbering plan of the system when the phone is activated and the current
   2369      * telephone numbering plan of the system that the phone is camped on.
   2370      * Currently, we only support the case that the default and current telephone
   2371      * numbering plans are North American Numbering Plan(NANP).
   2372      *
   2373      * The passed-in dialStr should only contain the valid format as described below,
   2374      * 1) the 1st character in the dialStr should be one of the really dialable
   2375      *    characters listed below
   2376      *    ISO-LATIN characters 0-9, *, # , +
   2377      * 2) the dialStr should already strip out the separator characters,
   2378      *    every character in the dialStr should be one of the non separator characters
   2379      *    listed below
   2380      *    ISO-LATIN characters 0-9, *, # , +, WILD, WAIT, PAUSE
   2381      *
   2382      * Otherwise, this function returns the dial string passed in
   2383      *
   2384      * @param dialStr the original dial string
   2385      * @return the converted dial string if the current/default countries belong to NANP,
   2386      * and if there is the "+" in the original dial string. Otherwise, the original dial
   2387      * string returns.
   2388      *
   2389      * This API is for CDMA only
   2390      *
   2391      * @hide TODO: pending API Council approval
   2392      */
   2393     public static String cdmaCheckAndProcessPlusCode(String dialStr) {
   2394         if (!TextUtils.isEmpty(dialStr)) {
   2395             if (isReallyDialable(dialStr.charAt(0)) &&
   2396                 isNonSeparator(dialStr)) {
   2397                 String currIso = TelephonyManager.getDefault().getNetworkCountryIso();
   2398                 String defaultIso = TelephonyManager.getDefault().getSimCountryIso();
   2399                 if (!TextUtils.isEmpty(currIso) && !TextUtils.isEmpty(defaultIso)) {
   2400                     return cdmaCheckAndProcessPlusCodeByNumberFormat(dialStr,
   2401                             getFormatTypeFromCountryCode(currIso),
   2402                             getFormatTypeFromCountryCode(defaultIso));
   2403                 }
   2404             }
   2405         }
   2406         return dialStr;
   2407     }
   2408 
   2409     /**
   2410      * Process phone number for CDMA, converting plus code using the home network number format.
   2411      * This is used for outgoing SMS messages.
   2412      *
   2413      * @param dialStr the original dial string
   2414      * @return the converted dial string
   2415      * @hide for internal use
   2416      */
   2417     public static String cdmaCheckAndProcessPlusCodeForSms(String dialStr) {
   2418         if (!TextUtils.isEmpty(dialStr)) {
   2419             if (isReallyDialable(dialStr.charAt(0)) && isNonSeparator(dialStr)) {
   2420                 String defaultIso = TelephonyManager.getDefault().getSimCountryIso();
   2421                 if (!TextUtils.isEmpty(defaultIso)) {
   2422                     int format = getFormatTypeFromCountryCode(defaultIso);
   2423                     return cdmaCheckAndProcessPlusCodeByNumberFormat(dialStr, format, format);
   2424                 }
   2425             }
   2426         }
   2427         return dialStr;
   2428     }
   2429 
   2430     /**
   2431      * This function should be called from checkAndProcessPlusCode only
   2432      * And it is used for test purpose also.
   2433      *
   2434      * It checks the dial string by looping through the network portion,
   2435      * post dial portion 1, post dial porting 2, etc. If there is any
   2436      * plus sign, then process the plus sign.
   2437      * Currently, this function supports the plus sign conversion within NANP only.
   2438      * Specifically, it handles the plus sign in the following ways:
   2439      * 1)+1NANP,remove +, e.g.
   2440      *   +18475797000 is converted to 18475797000,
   2441      * 2)+NANP or +non-NANP Numbers,replace + with the current NANP IDP, e.g,
   2442      *   +8475797000 is converted to 0118475797000,
   2443      *   +11875767800 is converted to 01111875767800
   2444      * 3)+1NANP in post dial string(s), e.g.
   2445      *   8475797000;+18475231753 is converted to 8475797000;18475231753
   2446      *
   2447      *
   2448      * @param dialStr the original dial string
   2449      * @param currFormat the numbering system of the current country that the phone is camped on
   2450      * @param defaultFormat the numbering system of the country that the phone is activated on
   2451      * @return the converted dial string if the current/default countries belong to NANP,
   2452      * and if there is the "+" in the original dial string. Otherwise, the original dial
   2453      * string returns.
   2454      *
   2455      * @hide
   2456      */
   2457     public static String
   2458     cdmaCheckAndProcessPlusCodeByNumberFormat(String dialStr,int currFormat,int defaultFormat) {
   2459         String retStr = dialStr;
   2460 
   2461         boolean useNanp = (currFormat == defaultFormat) && (currFormat == FORMAT_NANP);
   2462 
   2463         // Checks if the plus sign character is in the passed-in dial string
   2464         if (dialStr != null &&
   2465             dialStr.lastIndexOf(PLUS_SIGN_STRING) != -1) {
   2466 
   2467             // Handle case where default and current telephone numbering plans are NANP.
   2468             String postDialStr = null;
   2469             String tempDialStr = dialStr;
   2470 
   2471             // Sets the retStr to null since the conversion will be performed below.
   2472             retStr = null;
   2473             if (DBG) log("checkAndProcessPlusCode,dialStr=" + dialStr);
   2474             // This routine is to process the plus sign in the dial string by loop through
   2475             // the network portion, post dial portion 1, post dial portion 2... etc. if
   2476             // applied
   2477             do {
   2478                 String networkDialStr;
   2479                 // Format the string based on the rules for the country the number is from,
   2480                 // and the current country the phone is camped
   2481                 if (useNanp) {
   2482                     networkDialStr = extractNetworkPortion(tempDialStr);
   2483                 } else  {
   2484                     networkDialStr = extractNetworkPortionAlt(tempDialStr);
   2485 
   2486                 }
   2487 
   2488                 networkDialStr = processPlusCode(networkDialStr, useNanp);
   2489 
   2490                 // Concatenates the string that is converted from network portion
   2491                 if (!TextUtils.isEmpty(networkDialStr)) {
   2492                     if (retStr == null) {
   2493                         retStr = networkDialStr;
   2494                     } else {
   2495                         retStr = retStr.concat(networkDialStr);
   2496                     }
   2497                 } else {
   2498                     // This should never happen since we checked the if dialStr is null
   2499                     // and if it contains the plus sign in the beginning of this function.
   2500                     // The plus sign is part of the network portion.
   2501                     Rlog.e("checkAndProcessPlusCode: null newDialStr", networkDialStr);
   2502                     return dialStr;
   2503                 }
   2504                 postDialStr = extractPostDialPortion(tempDialStr);
   2505                 if (!TextUtils.isEmpty(postDialStr)) {
   2506                     int dialableIndex = findDialableIndexFromPostDialStr(postDialStr);
   2507 
   2508                     // dialableIndex should always be greater than 0
   2509                     if (dialableIndex >= 1) {
   2510                         retStr = appendPwCharBackToOrigDialStr(dialableIndex,
   2511                                  retStr,postDialStr);
   2512                         // Skips the P/W character, extracts the dialable portion
   2513                         tempDialStr = postDialStr.substring(dialableIndex);
   2514                     } else {
   2515                         // Non-dialable character such as P/W should not be at the end of
   2516                         // the dial string after P/W processing in GsmCdmaConnection.java
   2517                         // Set the postDialStr to "" to break out of the loop
   2518                         if (dialableIndex < 0) {
   2519                             postDialStr = "";
   2520                         }
   2521                         Rlog.e("wrong postDialStr=", postDialStr);
   2522                     }
   2523                 }
   2524                 if (DBG) log("checkAndProcessPlusCode,postDialStr=" + postDialStr);
   2525             } while (!TextUtils.isEmpty(postDialStr) && !TextUtils.isEmpty(tempDialStr));
   2526         }
   2527         return retStr;
   2528     }
   2529 
   2530     /**
   2531      * Wrap the supplied {@code CharSequence} with a {@code TtsSpan}, annotating it as
   2532      * containing a phone number in its entirety.
   2533      *
   2534      * @param phoneNumber A {@code CharSequence} the entirety of which represents a phone number.
   2535      * @return A {@code CharSequence} with appropriate annotations.
   2536      */
   2537     public static CharSequence createTtsSpannable(CharSequence phoneNumber) {
   2538         if (phoneNumber == null) {
   2539             return null;
   2540         }
   2541         Spannable spannable = Spannable.Factory.getInstance().newSpannable(phoneNumber);
   2542         PhoneNumberUtils.addTtsSpan(spannable, 0, spannable.length());
   2543         return spannable;
   2544     }
   2545 
   2546     /**
   2547      * Attach a {@link TtsSpan} to the supplied {@code Spannable} at the indicated location,
   2548      * annotating that location as containing a phone number.
   2549      *
   2550      * @param s A {@code Spannable} to annotate.
   2551      * @param start The starting character position of the phone number in {@code s}.
   2552      * @param endExclusive The position after the ending character in the phone number {@code s}.
   2553      */
   2554     public static void addTtsSpan(Spannable s, int start, int endExclusive) {
   2555         s.setSpan(createTtsSpan(s.subSequence(start, endExclusive).toString()),
   2556                 start,
   2557                 endExclusive,
   2558                 Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
   2559     }
   2560 
   2561     /**
   2562      * Wrap the supplied {@code CharSequence} with a {@code TtsSpan}, annotating it as
   2563      * containing a phone number in its entirety.
   2564      *
   2565      * @param phoneNumber A {@code CharSequence} the entirety of which represents a phone number.
   2566      * @return A {@code CharSequence} with appropriate annotations.
   2567      * @deprecated Renamed {@link #createTtsSpannable}.
   2568      *
   2569      * @hide
   2570      */
   2571     @Deprecated
   2572     public static CharSequence ttsSpanAsPhoneNumber(CharSequence phoneNumber) {
   2573         return createTtsSpannable(phoneNumber);
   2574     }
   2575 
   2576     /**
   2577      * Attach a {@link TtsSpan} to the supplied {@code Spannable} at the indicated location,
   2578      * annotating that location as containing a phone number.
   2579      *
   2580      * @param s A {@code Spannable} to annotate.
   2581      * @param start The starting character position of the phone number in {@code s}.
   2582      * @param end The ending character position of the phone number in {@code s}.
   2583      *
   2584      * @deprecated Renamed {@link #addTtsSpan}.
   2585      *
   2586      * @hide
   2587      */
   2588     @Deprecated
   2589     public static void ttsSpanAsPhoneNumber(Spannable s, int start, int end) {
   2590         addTtsSpan(s, start, end);
   2591     }
   2592 
   2593     /**
   2594      * Create a {@code TtsSpan} for the supplied {@code String}.
   2595      *
   2596      * @param phoneNumberString A {@code String} the entirety of which represents a phone number.
   2597      * @return A {@code TtsSpan} for {@param phoneNumberString}.
   2598      */
   2599     public static TtsSpan createTtsSpan(String phoneNumberString) {
   2600         if (phoneNumberString == null) {
   2601             return null;
   2602         }
   2603 
   2604         // Parse the phone number
   2605         final PhoneNumberUtil phoneNumberUtil = PhoneNumberUtil.getInstance();
   2606         PhoneNumber phoneNumber = null;
   2607         try {
   2608             // Don't supply a defaultRegion so this fails for non-international numbers because
   2609             // we don't want to TalkBalk to read a country code (e.g. +1) if it is not already
   2610             // present
   2611             phoneNumber = phoneNumberUtil.parse(phoneNumberString, /* defaultRegion */ null);
   2612         } catch (NumberParseException ignored) {
   2613         }
   2614 
   2615         // Build a telephone tts span
   2616         final TtsSpan.TelephoneBuilder builder = new TtsSpan.TelephoneBuilder();
   2617         if (phoneNumber == null) {
   2618             // Strip separators otherwise TalkBack will be silent
   2619             // (this behavior was observed with TalkBalk 4.0.2 from their alpha channel)
   2620             builder.setNumberParts(splitAtNonNumerics(phoneNumberString));
   2621         } else {
   2622             if (phoneNumber.hasCountryCode()) {
   2623                 builder.setCountryCode(Integer.toString(phoneNumber.getCountryCode()));
   2624             }
   2625             builder.setNumberParts(Long.toString(phoneNumber.getNationalNumber()));
   2626         }
   2627         return builder.build();
   2628     }
   2629 
   2630     // Split a phone number like "+20(123)-456#" using spaces, ignoring anything that is not
   2631     // a digit or the characters * and #, to produce a result like "20 123 456#".
   2632     private static String splitAtNonNumerics(CharSequence number) {
   2633         StringBuilder sb = new StringBuilder(number.length());
   2634         for (int i = 0; i < number.length(); i++) {
   2635             sb.append(PhoneNumberUtils.is12Key(number.charAt(i))
   2636                     ? number.charAt(i)
   2637                     : " ");
   2638         }
   2639         // It is very important to remove extra spaces. At time of writing, any leading or trailing
   2640         // spaces, or any sequence of more than one space, will confuse TalkBack and cause the TTS
   2641         // span to be non-functional!
   2642         return sb.toString().replaceAll(" +", " ").trim();
   2643     }
   2644 
   2645     private static String getCurrentIdp(boolean useNanp) {
   2646         String ps = null;
   2647         if (useNanp) {
   2648             ps = NANP_IDP_STRING;
   2649         } else {
   2650             // in case, there is no IDD is found, we shouldn't convert it.
   2651             ps = SystemProperties.get(PROPERTY_OPERATOR_IDP_STRING, PLUS_SIGN_STRING);
   2652         }
   2653         return ps;
   2654     }
   2655 
   2656     private static boolean isTwoToNine (char c) {
   2657         if (c >= '2' && c <= '9') {
   2658             return true;
   2659         } else {
   2660             return false;
   2661         }
   2662     }
   2663 
   2664     private static int getFormatTypeFromCountryCode (String country) {
   2665         // Check for the NANP countries
   2666         int length = NANP_COUNTRIES.length;
   2667         for (int i = 0; i < length; i++) {
   2668             if (NANP_COUNTRIES[i].compareToIgnoreCase(country) == 0) {
   2669                 return FORMAT_NANP;
   2670             }
   2671         }
   2672         if ("jp".compareToIgnoreCase(country) == 0) {
   2673             return FORMAT_JAPAN;
   2674         }
   2675         return FORMAT_UNKNOWN;
   2676     }
   2677 
   2678     /**
   2679      * This function checks if the passed in string conforms to the NANP format
   2680      * i.e. NXX-NXX-XXXX, N is any digit 2-9 and X is any digit 0-9
   2681      * @hide
   2682      */
   2683     public static boolean isNanp (String dialStr) {
   2684         boolean retVal = false;
   2685         if (dialStr != null) {
   2686             if (dialStr.length() == NANP_LENGTH) {
   2687                 if (isTwoToNine(dialStr.charAt(0)) &&
   2688                     isTwoToNine(dialStr.charAt(3))) {
   2689                     retVal = true;
   2690                     for (int i=1; i<NANP_LENGTH; i++ ) {
   2691                         char c=dialStr.charAt(i);
   2692                         if (!PhoneNumberUtils.isISODigit(c)) {
   2693                             retVal = false;
   2694                             break;
   2695                         }
   2696                     }
   2697                 }
   2698             }
   2699         } else {
   2700             Rlog.e("isNanp: null dialStr passed in", dialStr);
   2701         }
   2702         return retVal;
   2703     }
   2704 
   2705    /**
   2706     * This function checks if the passed in string conforms to 1-NANP format
   2707     */
   2708     private static boolean isOneNanp(String dialStr) {
   2709         boolean retVal = false;
   2710         if (dialStr != null) {
   2711             String newDialStr = dialStr.substring(1);
   2712             if ((dialStr.charAt(0) == '1') && isNanp(newDialStr)) {
   2713                 retVal = true;
   2714             }
   2715         } else {
   2716             Rlog.e("isOneNanp: null dialStr passed in", dialStr);
   2717         }
   2718         return retVal;
   2719     }
   2720 
   2721     /**
   2722      * Determines if the specified number is actually a URI
   2723      * (i.e. a SIP address) rather than a regular PSTN phone number,
   2724      * based on whether or not the number contains an "@" character.
   2725      *
   2726      * @hide
   2727      * @param number
   2728      * @return true if number contains @
   2729      */
   2730     public static boolean isUriNumber(String number) {
   2731         // Note we allow either "@" or "%40" to indicate a URI, in case
   2732         // the passed-in string is URI-escaped.  (Neither "@" nor "%40"
   2733         // will ever be found in a legal PSTN number.)
   2734         return number != null && (number.contains("@") || number.contains("%40"));
   2735     }
   2736 
   2737     /**
   2738      * @return the "username" part of the specified SIP address,
   2739      *         i.e. the part before the "@" character (or "%40").
   2740      *
   2741      * @param number SIP address of the form "username@domainname"
   2742      *               (or the URI-escaped equivalent "username%40domainname")
   2743      * @see #isUriNumber
   2744      *
   2745      * @hide
   2746      */
   2747     public static String getUsernameFromUriNumber(String number) {
   2748         // The delimiter between username and domain name can be
   2749         // either "@" or "%40" (the URI-escaped equivalent.)
   2750         int delimiterIndex = number.indexOf('@');
   2751         if (delimiterIndex < 0) {
   2752             delimiterIndex = number.indexOf("%40");
   2753         }
   2754         if (delimiterIndex < 0) {
   2755             Rlog.w(LOG_TAG,
   2756                   "getUsernameFromUriNumber: no delimiter found in SIP addr '" + number + "'");
   2757             delimiterIndex = number.length();
   2758         }
   2759         return number.substring(0, delimiterIndex);
   2760     }
   2761 
   2762     /**
   2763      * Given a {@link Uri} with a {@code sip} scheme, attempts to build an equivalent {@code tel}
   2764      * scheme {@link Uri}.  If the source {@link Uri} does not contain a valid number, or is not
   2765      * using the {@code sip} scheme, the original {@link Uri} is returned.
   2766      *
   2767      * @param source The {@link Uri} to convert.
   2768      * @return The equivalent {@code tel} scheme {@link Uri}.
   2769      *
   2770      * @hide
   2771      */
   2772     public static Uri convertSipUriToTelUri(Uri source) {
   2773         // A valid SIP uri has the format: sip:user:password@host:port;uri-parameters?headers
   2774         // Per RFC3261, the "user" can be a telephone number.
   2775         // For example: sip:1650555121;phone-context=blah.com (at) host.com
   2776         // In this case, the phone number is in the user field of the URI, and the parameters can be
   2777         // ignored.
   2778         //
   2779         // A SIP URI can also specify a phone number in a format similar to:
   2780         // sip:+1-212-555-1212 (at) something.com;user=phone
   2781         // In this case, the phone number is again in user field and the parameters can be ignored.
   2782         // We can get the user field in these instances by splitting the string on the @, ;, or :
   2783         // and looking at the first found item.
   2784 
   2785         String scheme = source.getScheme();
   2786 
   2787         if (!PhoneAccount.SCHEME_SIP.equals(scheme)) {
   2788             // Not a sip URI, bail.
   2789             return source;
   2790         }
   2791 
   2792         String number = source.getSchemeSpecificPart();
   2793         String numberParts[] = number.split("[@;:]");
   2794 
   2795         if (numberParts.length == 0) {
   2796             // Number not found, bail.
   2797             return source;
   2798         }
   2799         number = numberParts[0];
   2800 
   2801         return Uri.fromParts(PhoneAccount.SCHEME_TEL, number, null);
   2802     }
   2803 
   2804     /**
   2805      * This function handles the plus code conversion
   2806      * If the number format is
   2807      * 1)+1NANP,remove +,
   2808      * 2)other than +1NANP, any + numbers,replace + with the current IDP
   2809      */
   2810     private static String processPlusCode(String networkDialStr, boolean useNanp) {
   2811         String retStr = networkDialStr;
   2812 
   2813         if (DBG) log("processPlusCode, networkDialStr = " + networkDialStr
   2814                 + "for NANP = " + useNanp);
   2815         // If there is a plus sign at the beginning of the dial string,
   2816         // Convert the plus sign to the default IDP since it's an international number
   2817         if (networkDialStr != null &&
   2818             networkDialStr.charAt(0) == PLUS_SIGN_CHAR &&
   2819             networkDialStr.length() > 1) {
   2820             String newStr = networkDialStr.substring(1);
   2821             // TODO: for nonNanp, should the '+' be removed if following number is country code
   2822             if (useNanp && isOneNanp(newStr)) {
   2823                 // Remove the leading plus sign
   2824                 retStr = newStr;
   2825             } else {
   2826                 // Replaces the plus sign with the default IDP
   2827                 retStr = networkDialStr.replaceFirst("[+]", getCurrentIdp(useNanp));
   2828             }
   2829         }
   2830         if (DBG) log("processPlusCode, retStr=" + retStr);
   2831         return retStr;
   2832     }
   2833 
   2834     // This function finds the index of the dialable character(s)
   2835     // in the post dial string
   2836     private static int findDialableIndexFromPostDialStr(String postDialStr) {
   2837         for (int index = 0;index < postDialStr.length();index++) {
   2838              char c = postDialStr.charAt(index);
   2839              if (isReallyDialable(c)) {
   2840                 return index;
   2841              }
   2842         }
   2843         return -1;
   2844     }
   2845 
   2846     // This function appends the non-dialable P/W character to the original
   2847     // dial string based on the dialable index passed in
   2848     private static String
   2849     appendPwCharBackToOrigDialStr(int dialableIndex,String origStr, String dialStr) {
   2850         String retStr;
   2851 
   2852         // There is only 1 P/W character before the dialable characters
   2853         if (dialableIndex == 1) {
   2854             StringBuilder ret = new StringBuilder(origStr);
   2855             ret = ret.append(dialStr.charAt(0));
   2856             retStr = ret.toString();
   2857         } else {
   2858             // It means more than 1 P/W characters in the post dial string,
   2859             // appends to retStr
   2860             String nonDigitStr = dialStr.substring(0,dialableIndex);
   2861             retStr = origStr.concat(nonDigitStr);
   2862         }
   2863         return retStr;
   2864     }
   2865 
   2866     //===== Beginning of utility methods used in compareLoosely() =====
   2867 
   2868     /**
   2869      * Phone numbers are stored in "lookup" form in the database
   2870      * as reversed strings to allow for caller ID lookup
   2871      *
   2872      * This method takes a phone number and makes a valid SQL "LIKE"
   2873      * string that will match the lookup form
   2874      *
   2875      */
   2876     /** all of a up to len must be an international prefix or
   2877      *  separators/non-dialing digits
   2878      */
   2879     private static boolean
   2880     matchIntlPrefix(String a, int len) {
   2881         /* '([^0-9*#+pwn]\+[^0-9*#+pwn] | [^0-9*#+pwn]0(0|11)[^0-9*#+pwn] )$' */
   2882         /*        0       1                           2 3 45               */
   2883 
   2884         int state = 0;
   2885         for (int i = 0 ; i < len ; i++) {
   2886             char c = a.charAt(i);
   2887 
   2888             switch (state) {
   2889                 case 0:
   2890                     if      (c == '+') state = 1;
   2891                     else if (c == '0') state = 2;
   2892                     else if (isNonSeparator(c)) return false;
   2893                 break;
   2894 
   2895                 case 2:
   2896                     if      (c == '0') state = 3;
   2897                     else if (c == '1') state = 4;
   2898                     else if (isNonSeparator(c)) return false;
   2899                 break;
   2900 
   2901                 case 4:
   2902                     if      (c == '1') state = 5;
   2903                     else if (isNonSeparator(c)) return false;
   2904                 break;
   2905 
   2906                 default:
   2907                     if (isNonSeparator(c)) return false;
   2908                 break;
   2909 
   2910             }
   2911         }
   2912 
   2913         return state == 1 || state == 3 || state == 5;
   2914     }
   2915 
   2916     /** all of 'a' up to len must be a (+|00|011)country code)
   2917      *  We're fast and loose with the country code. Any \d{1,3} matches */
   2918     private static boolean
   2919     matchIntlPrefixAndCC(String a, int len) {
   2920         /*  [^0-9*#+pwn]*(\+|0(0|11)\d\d?\d? [^0-9*#+pwn] $ */
   2921         /*      0          1 2 3 45  6 7  8                 */
   2922 
   2923         int state = 0;
   2924         for (int i = 0 ; i < len ; i++ ) {
   2925             char c = a.charAt(i);
   2926 
   2927             switch (state) {
   2928                 case 0:
   2929                     if      (c == '+') state = 1;
   2930                     else if (c == '0') state = 2;
   2931                     else if (isNonSeparator(c)) return false;
   2932                 break;
   2933 
   2934                 case 2:
   2935                     if      (c == '0') state = 3;
   2936                     else if (c == '1') state = 4;
   2937                     else if (isNonSeparator(c)) return false;
   2938                 break;
   2939 
   2940                 case 4:
   2941                     if      (c == '1') state = 5;
   2942                     else if (isNonSeparator(c)) return false;
   2943                 break;
   2944 
   2945                 case 1:
   2946                 case 3:
   2947                 case 5:
   2948                     if      (isISODigit(c)) state = 6;
   2949                     else if (isNonSeparator(c)) return false;
   2950                 break;
   2951 
   2952                 case 6:
   2953                 case 7:
   2954                     if      (isISODigit(c)) state++;
   2955                     else if (isNonSeparator(c)) return false;
   2956                 break;
   2957 
   2958                 default:
   2959                     if (isNonSeparator(c)) return false;
   2960             }
   2961         }
   2962 
   2963         return state == 6 || state == 7 || state == 8;
   2964     }
   2965 
   2966     /** all of 'a' up to len must match non-US trunk prefix ('0') */
   2967     private static boolean
   2968     matchTrunkPrefix(String a, int len) {
   2969         boolean found;
   2970 
   2971         found = false;
   2972 
   2973         for (int i = 0 ; i < len ; i++) {
   2974             char c = a.charAt(i);
   2975 
   2976             if (c == '0' && !found) {
   2977                 found = true;
   2978             } else if (isNonSeparator(c)) {
   2979                 return false;
   2980             }
   2981         }
   2982 
   2983         return found;
   2984     }
   2985 
   2986     //===== End of utility methods used only in compareLoosely() =====
   2987 
   2988     //===== Beginning of utility methods used only in compareStrictly() ====
   2989 
   2990     /*
   2991      * If true, the number is country calling code.
   2992      */
   2993     private static final boolean COUNTRY_CALLING_CALL[] = {
   2994         true, true, false, false, false, false, false, true, false, false,
   2995         false, false, false, false, false, false, false, false, false, false,
   2996         true, false, false, false, false, false, false, true, true, false,
   2997         true, true, true, true, true, false, true, false, false, true,
   2998         true, false, false, true, true, true, true, true, true, true,
   2999         false, true, true, true, true, true, true, true, true, false,
   3000         true, true, true, true, true, true, true, false, false, false,
   3001         false, false, false, false, false, false, false, false, false, false,
   3002         false, true, true, true, true, false, true, false, false, true,
   3003         true, true, true, true, true, true, false, false, true, false,
   3004     };
   3005     private static final int CCC_LENGTH = COUNTRY_CALLING_CALL.length;
   3006 
   3007     /**
   3008      * @return true when input is valid Country Calling Code.
   3009      */
   3010     private static boolean isCountryCallingCode(int countryCallingCodeCandidate) {
   3011         return countryCallingCodeCandidate > 0 && countryCallingCodeCandidate < CCC_LENGTH &&
   3012                 COUNTRY_CALLING_CALL[countryCallingCodeCandidate];
   3013     }
   3014 
   3015     /**
   3016      * Returns integer corresponding to the input if input "ch" is
   3017      * ISO-LATIN characters 0-9.
   3018      * Returns -1 otherwise
   3019      */
   3020     private static int tryGetISODigit(char ch) {
   3021         if ('0' <= ch && ch <= '9') {
   3022             return ch - '0';
   3023         } else {
   3024             return -1;
   3025         }
   3026     }
   3027 
   3028     private static class CountryCallingCodeAndNewIndex {
   3029         public final int countryCallingCode;
   3030         public final int newIndex;
   3031         public CountryCallingCodeAndNewIndex(int countryCode, int newIndex) {
   3032             this.countryCallingCode = countryCode;
   3033             this.newIndex = newIndex;
   3034         }
   3035     }
   3036 
   3037     /*
   3038      * Note that this function does not strictly care the country calling code with
   3039      * 3 length (like Morocco: +212), assuming it is enough to use the first two
   3040      * digit to compare two phone numbers.
   3041      */
   3042     private static CountryCallingCodeAndNewIndex tryGetCountryCallingCodeAndNewIndex(
   3043         String str, boolean acceptThailandCase) {
   3044         // Rough regexp:
   3045         //  ^[^0-9*#+]*((\+|0(0|11)\d\d?|166) [^0-9*#+] $
   3046         //         0        1 2 3 45  6 7  89
   3047         //
   3048         // In all the states, this function ignores separator characters.
   3049         // "166" is the special case for the call from Thailand to the US. Uguu!
   3050         int state = 0;
   3051         int ccc = 0;
   3052         final int length = str.length();
   3053         for (int i = 0 ; i < length ; i++ ) {
   3054             char ch = str.charAt(i);
   3055             switch (state) {
   3056                 case 0:
   3057                     if      (ch == '+') state = 1;
   3058                     else if (ch == '0') state = 2;
   3059                     else if (ch == '1') {
   3060                         if (acceptThailandCase) {
   3061                             state = 8;
   3062                         } else {
   3063                             return null;
   3064                         }
   3065                     } else if (isDialable(ch)) {
   3066                         return null;
   3067                     }
   3068                 break;
   3069 
   3070                 case 2:
   3071                     if      (ch == '0') state = 3;
   3072                     else if (ch == '1') state = 4;
   3073                     else if (isDialable(ch)) {
   3074                         return null;
   3075                     }
   3076                 break;
   3077 
   3078                 case 4:
   3079                     if      (ch == '1') state = 5;
   3080                     else if (isDialable(ch)) {
   3081                         return null;
   3082                     }
   3083                 break;
   3084 
   3085                 case 1:
   3086                 case 3:
   3087                 case 5:
   3088                 case 6:
   3089                 case 7:
   3090                     {
   3091                         int ret = tryGetISODigit(ch);
   3092                         if (ret > 0) {
   3093                             ccc = ccc * 10 + ret;
   3094                             if (ccc >= 100 || isCountryCallingCode(ccc)) {
   3095                                 return new CountryCallingCodeAndNewIndex(ccc, i + 1);
   3096                             }
   3097                             if (state == 1 || state == 3 || state == 5) {
   3098                                 state = 6;
   3099                             } else {
   3100                                 state++;
   3101                             }
   3102                         } else if (isDialable(ch)) {
   3103                             return null;
   3104                         }
   3105                     }
   3106                     break;
   3107                 case 8:
   3108                     if (ch == '6') state = 9;
   3109                     else if (isDialable(ch)) {
   3110                         return null;
   3111                     }
   3112                     break;
   3113                 case 9:
   3114                     if (ch == '6') {
   3115                         return new CountryCallingCodeAndNewIndex(66, i + 1);
   3116                     } else {
   3117                         return null;
   3118                     }
   3119                 default:
   3120                     return null;
   3121             }
   3122         }
   3123 
   3124         return null;
   3125     }
   3126 
   3127     /**
   3128      * Currently this function simply ignore the first digit assuming it is
   3129      * trunk prefix. Actually trunk prefix is different in each country.
   3130      *
   3131      * e.g.
   3132      * "+79161234567" equals "89161234567" (Russian trunk digit is 8)
   3133      * "+33123456789" equals "0123456789" (French trunk digit is 0)
   3134      *
   3135      */
   3136     private static int tryGetTrunkPrefixOmittedIndex(String str, int currentIndex) {
   3137         int length = str.length();
   3138         for (int i = currentIndex ; i < length ; i++) {
   3139             final char ch = str.charAt(i);
   3140             if (tryGetISODigit(ch) >= 0) {
   3141                 return i + 1;
   3142             } else if (isDialable(ch)) {
   3143                 return -1;
   3144             }
   3145         }
   3146         return -1;
   3147     }
   3148 
   3149     /**
   3150      * Return true if the prefix of "str" is "ignorable". Here, "ignorable" means
   3151      * that "str" has only one digit and separator characters. The one digit is
   3152      * assumed to be trunk prefix.
   3153      */
   3154     private static boolean checkPrefixIsIgnorable(final String str,
   3155             int forwardIndex, int backwardIndex) {
   3156         boolean trunk_prefix_was_read = false;
   3157         while (backwardIndex >= forwardIndex) {
   3158             if (tryGetISODigit(str.charAt(backwardIndex)) >= 0) {
   3159                 if (trunk_prefix_was_read) {
   3160                     // More than one digit appeared, meaning that "a" and "b"
   3161                     // is different.
   3162                     return false;
   3163                 } else {
   3164                     // Ignore just one digit, assuming it is trunk prefix.
   3165                     trunk_prefix_was_read = true;
   3166                 }
   3167             } else if (isDialable(str.charAt(backwardIndex))) {
   3168                 // Trunk prefix is a digit, not "*", "#"...
   3169                 return false;
   3170             }
   3171             backwardIndex--;
   3172         }
   3173 
   3174         return true;
   3175     }
   3176 
   3177     /**
   3178      * Returns Default voice subscription Id.
   3179      */
   3180     private static int getDefaultVoiceSubId() {
   3181         return SubscriptionManager.getDefaultVoiceSubscriptionId();
   3182     }
   3183     //==== End of utility methods used only in compareStrictly() =====
   3184 
   3185 
   3186     /*
   3187      * The config held calling number conversion map, expected to convert to emergency number.
   3188      */
   3189     private static String[] sConvertToEmergencyMap = null;
   3190 
   3191     /**
   3192      * Converts to emergency number based on the conversion map.
   3193      * The conversion map is declared as config_convert_to_emergency_number_map.
   3194      *
   3195      * @param context a context to use for accessing resources
   3196      * @return The converted emergency number if the number matches conversion map,
   3197      * otherwise original number.
   3198      *
   3199      * @hide
   3200      */
   3201     public static String convertToEmergencyNumber(Context context, String number) {
   3202         if (context == null || TextUtils.isEmpty(number)) {
   3203             return number;
   3204         }
   3205 
   3206         String normalizedNumber = normalizeNumber(number);
   3207 
   3208         // The number is already emergency number. Skip conversion.
   3209         if (isEmergencyNumber(normalizedNumber)) {
   3210             return number;
   3211         }
   3212 
   3213         if (sConvertToEmergencyMap == null) {
   3214             sConvertToEmergencyMap = context.getResources().getStringArray(
   3215                     com.android.internal.R.array.config_convert_to_emergency_number_map);
   3216         }
   3217 
   3218         // The conversion map is not defined (this is default). Skip conversion.
   3219         if (sConvertToEmergencyMap == null || sConvertToEmergencyMap.length == 0 ) {
   3220             return number;
   3221         }
   3222 
   3223         for (String convertMap : sConvertToEmergencyMap) {
   3224             if (DBG) log("convertToEmergencyNumber: " + convertMap);
   3225             String[] entry = null;
   3226             String[] filterNumbers = null;
   3227             String convertedNumber = null;
   3228             if (!TextUtils.isEmpty(convertMap)) {
   3229                 entry = convertMap.split(":");
   3230             }
   3231             if (entry != null && entry.length == 2) {
   3232                 convertedNumber = entry[1];
   3233                 if (!TextUtils.isEmpty(entry[0])) {
   3234                     filterNumbers = entry[0].split(",");
   3235                 }
   3236             }
   3237             // Skip if the format of entry is invalid
   3238             if (TextUtils.isEmpty(convertedNumber) || filterNumbers == null
   3239                     || filterNumbers.length == 0) {
   3240                 continue;
   3241             }
   3242 
   3243             for (String filterNumber : filterNumbers) {
   3244                 if (DBG) log("convertToEmergencyNumber: filterNumber = " + filterNumber
   3245                         + ", convertedNumber = " + convertedNumber);
   3246                 if (!TextUtils.isEmpty(filterNumber) && filterNumber.equals(normalizedNumber)) {
   3247                     if (DBG) log("convertToEmergencyNumber: Matched. Successfully converted to: "
   3248                             + convertedNumber);
   3249                     return convertedNumber;
   3250                 }
   3251             }
   3252         }
   3253         return number;
   3254     }
   3255 }
   3256