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