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