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