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