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