content: /*
   * Copyright (C) 2006 The Android Open Source Project
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *      http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  
  package android.telephony;
  
  import com.android.i18n.phonenumbers.NumberParseException;
  import com.android.i18n.phonenumbers.PhoneNumberUtil;
  import com.android.i18n.phonenumbers.PhoneNumberUtil.PhoneNumberFormat;
  import com.android.i18n.phonenumbers.Phonenumber.PhoneNumber;
  
  import android.annotation.IntDef;
  import android.annotation.UnsupportedAppUsage;
  import android.content.Context;
  import android.content.Intent;
  import android.database.Cursor;
  import android.location.CountryDetector;
  import android.net.Uri;
  import android.os.PersistableBundle;
  import android.os.SystemProperties;
  import android.provider.Contacts;
  import android.provider.ContactsContract;
  import android.telecom.PhoneAccount;
  import android.text.Editable;
  import android.text.Spannable;
  import android.text.SpannableStringBuilder;
  import android.text.TextUtils;
  import android.text.style.TtsSpan;
  import android.util.SparseIntArray;
  
  import static com.android.internal.telephony.TelephonyProperties.PROPERTY_OPERATOR_IDP_STRING;
  
  import java.lang.annotation.Retention;
  import java.lang.annotation.RetentionPolicy;
  import java.util.Locale;
  import java.util.regex.Matcher;
  import java.util.regex.Pattern;
  
  /**
   * Various utilities for dealing with phone number strings.
   */
  public class PhoneNumberUtils {
      /** {@hide} */
      @IntDef(prefix = "BCD_EXTENDED_TYPE_", value = {
              BCD_EXTENDED_TYPE_EF_ADN,
              BCD_EXTENDED_TYPE_CALLED_PARTY,
      })
      @Retention(RetentionPolicy.SOURCE)
      public @interface BcdExtendType {}
  
      /*
       * The BCD extended type used to determine the extended char for the digit which is greater than
       * 9.
       *
       * see TS 51.011 section 10.5.1 EF_ADN(Abbreviated dialling numbers)
       */
      public static final int BCD_EXTENDED_TYPE_EF_ADN = 1;
  
      /*
       * The BCD extended type used to determine the extended char for the digit which is greater than
       * 9.
       *
       * see TS 24.008 section 10.5.4.7 Called party BCD number
       */
      public static final int BCD_EXTENDED_TYPE_CALLED_PARTY = 2;
  
      /*
       * Special characters
       *
       * (See "What is a phone number?" doc)
       * 'p' --- GSM pause character, same as comma
       * 'n' --- GSM wild character
       * 'w' --- GSM wait character
       */
      public static final char PAUSE = ',';
      public static final char WAIT = ';';
      public static final char WILD = 'N';
  
      /*
       * Calling Line Identification Restriction (CLIR)
       */
      private static final String CLIR_ON = "*31#";
      private static final String CLIR_OFF = "#31#";
  
      /*
       * TOA = TON + NPI
       * See TS 24.008 section 10.5.4.7 for details.
       * These are the only really useful TOA values
       */
      public static final int TOA_International = 0x91;
      public static final int TOA_Unknown = 0x81;
  
      static final String LOG_TAG = "PhoneNumberUtils";
      private static final boolean DBG = false;
  
      private static final String BCD_EF_ADN_EXTENDED = "*#,N;";
      private static final String BCD_CALLED_PARTY_EXTENDED = "*#abc";
  
      /*
       * global-phone-number = ["+"] 1*( DIGIT / written-sep )
       * written-sep         = ("-"/".")
       */
      private static final Pattern GLOBAL_PHONE_NUMBER_PATTERN =
              Pattern.compile("[\\+]?[0-9.-]+");
  
      /** True if c is ISO-LATIN characters 0-9 */
      public static boolean
      isISODigit (char c) {
          return c >= '0' && c <= '9';
      }
  
      /** True if c is ISO-LATIN characters 0-9, *, # */
      public final static boolean
      is12Key(char c) {
          return (c >= '0' && c <= '9') || c == '*' || c == '#';
      }
  
      /** True if c is ISO-LATIN characters 0-9, *, # , +, WILD  */
      public final static boolean
      isDialable(char c) {
          return (c >= '0' && c <= '9') || c == '*' || c == '#' || c == '+' || c == WILD;
      }
  
      /** True if c is ISO-LATIN characters 0-9, *, # , + (no WILD)  */
      public final static boolean
      isReallyDialable(char c) {
          return (c >= '0' && c <= '9') || c == '*' || c == '#' || c == '+';
      }
  
      /** True if c is ISO-LATIN characters 0-9, *, # , +, WILD, WAIT, PAUSE   */
      public final static boolean
      isNonSeparator(char c) {
          return (c >= '0' && c <= '9') || c == '*' || c == '#' || c == '+'
                  || c == WILD || c == WAIT || c == PAUSE;
      }
  
      /** This any anything to the right of this char is part of the
       *  post-dial string (eg this is PAUSE or WAIT)
       */
      public final static boolean
      isStartsPostDial (char c) {
          return c == PAUSE || c == WAIT;
      }
  
      private static boolean
      isPause (char c){
          return c == 'p'||c == 'P';
      }
  
      private static boolean
      isToneWait (char c){
          return c == 'w'||c == 'W';
      }
  
  
      /** Returns true if ch is not dialable or alpha char */
      private static boolean isSeparator(char ch) {
          return !isDialable(ch) && !(('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z'));
      }
  
      /** Extracts the phone number from an Intent.
       *
       * @param intent the intent to get the number of
       * @param context a context to use for database access
       *
       * @return the phone number that would be called by the intent, or
       *         <code>null</code> if the number cannot be found.
       */
      public static String getNumberFromIntent(Intent intent, Context context) {
          String number = null;
  
          Uri uri = intent.getData();
  
          if (uri == null) {
              return null;
          }
  
          String scheme = uri.getScheme();
  
          if (scheme.equals("tel") || scheme.equals("sip")) {
              return uri.getSchemeSpecificPart();
          }
  
          if (context == null) {
              return null;
          }
  
          String type = intent.resolveType(context);
          String phoneColumn = null;
  
          // Correctly read out the phone entry based on requested provider
          final String authority = uri.getAuthority();
          if (Contacts.AUTHORITY.equals(authority)) {
              phoneColumn = Contacts.People.Phones.NUMBER;
          } else if (ContactsContract.AUTHORITY.equals(authority)) {
              phoneColumn = ContactsContract.CommonDataKinds.Phone.NUMBER;
          }
  
          Cursor c = null;
          try {
              c = context.getContentResolver().query(uri, new String[] { phoneColumn },
                      null, null, null);
              if (c != null) {
                  if (c.moveToFirst()) {
                      number = c.getString(c.getColumnIndex(phoneColumn));
                  }
              }
          } catch (RuntimeException e) {
              Rlog.e(LOG_TAG, "Error getting phone number.", e);
          } finally {
              if (c != null) {
                  c.close();
              }
          }
  
          return number;
      }
  
      /** Extracts the network address portion and canonicalizes
       *  (filters out separators.)
       *  Network address portion is everything up to DTMF control digit
       *  separators (pause or wait), but without non-dialable characters.
       *
       *  Please note that the GSM wild character is allowed in the result.
       *  This must be resolved before dialing.
       *
       *  Returns null if phoneNumber == null
       */
      public static String
      extractNetworkPortion(String phoneNumber) {
          if (phoneNumber == null) {
              return null;
          }
  
          int len = phoneNumber.length();
          StringBuilder ret = new StringBuilder(len);
  
          for (int i = 0; i < len; i++) {
              char c = phoneNumber.charAt(i);
              // Character.digit() supports ASCII and Unicode digits (fullwidth, Arabic-Indic, etc.)
              int digit = Character.digit(c, 10);
              if (digit != -1) {
                  ret.append(digit);
              } else if (c == '+') {
                  // Allow '+' as first character or after CLIR MMI prefix
                  String prefix = ret.toString();
                  if (prefix.length() == 0 || prefix.equals(CLIR_ON) || prefix.equals(CLIR_OFF)) {
                      ret.append(c);
                  }
              } else if (isDialable(c)) {
                  ret.append(c);
              } else if (isStartsPostDial (c)) {
                  break;
              }
          }
  
          return ret.toString();
      }
  
      /**
       * Extracts the network address portion and canonicalize.
       *
       * This function is equivalent to extractNetworkPortion(), except
       * for allowing the PLUS character to occur at arbitrary positions
       * in the address portion, not just the first position.
       *
       * @hide
       */
      @UnsupportedAppUsage
      public static String extractNetworkPortionAlt(String phoneNumber) {
          if (phoneNumber == null) {
              return null;
          }
  
          int len = phoneNumber.length();
          StringBuilder ret = new StringBuilder(len);
          boolean haveSeenPlus = false;
  
          for (int i = 0; i < len; i++) {
              char c = phoneNumber.charAt(i);
              if (c == '+') {
                  if (haveSeenPlus) {
                      continue;
                  }
                  haveSeenPlus = true;
              }
              if (isDialable(c)) {
                  ret.append(c);
              } else if (isStartsPostDial (c)) {
                  break;
              }
          }
  
          return ret.toString();
      }
  
      /**
       * Strips separators from a phone number string.
       * @param phoneNumber phone number to strip.
       * @return phone string stripped of separators.
       */
      public static String stripSeparators(String phoneNumber) {
          if (phoneNumber == null) {
              return null;
          }
          int len = phoneNumber.length();
          StringBuilder ret = new StringBuilder(len);
  
          for (int i = 0; i < len; i++) {
              char c = phoneNumber.charAt(i);
              // Character.digit() supports ASCII and Unicode digits (fullwidth, Arabic-Indic, etc.)
              int digit = Character.digit(c, 10);
              if (digit != -1) {
                  ret.append(digit);
              } else if (isNonSeparator(c)) {
                  ret.append(c);
              }
          }
  
          return ret.toString();
      }
  
      /**
       * Translates keypad letters to actual digits (e.g. 1-800-GOOG-411 will
       * become 1-800-4664-411), and then strips all separators (e.g. 1-800-4664-411 will become
       * 18004664411).
       *
       * @see #convertKeypadLettersToDigits(String)
       * @see #stripSeparators(String)
       *
       * @hide
       */
      public static String convertAndStrip(String phoneNumber) {
          return stripSeparators(convertKeypadLettersToDigits(phoneNumber));
      }
  
      /**
       * Converts pause and tonewait pause characters
       * to Android representation.
       * RFC 3601 says pause is 'p' and tonewait is 'w'.
       * @hide
       */
      @UnsupportedAppUsage
      public static String convertPreDial(String phoneNumber) {
          if (phoneNumber == null) {
              return null;
          }
          int len = phoneNumber.length();
          StringBuilder ret = new StringBuilder(len);
  
          for (int i = 0; i < len; i++) {
              char c = phoneNumber.charAt(i);
  
              if (isPause(c)) {
                  c = PAUSE;
              } else if (isToneWait(c)) {
                  c = WAIT;
              }
              ret.append(c);
          }
          return ret.toString();
      }
  
      /** or -1 if both are negative */
      static private int
      minPositive (int a, int b) {
          if (a >= 0 && b >= 0) {
              return (a < b) ? a : b;
          } else if (a >= 0) { /* && b < 0 */
              return a;
          } else if (b >= 0) { /* && a < 0 */
              return b;
          } else { /* a < 0 && b < 0 */
              return -1;
          }
      }
  
      private static void log(String msg) {
          Rlog.d(LOG_TAG, msg);
      }
      /** index of the last character of the network portion
       *  (eg anything after is a post-dial string)
       */
      static private int
      indexOfLastNetworkChar(String a) {
          int pIndex, wIndex;
          int origLength;
          int trimIndex;
  
          origLength = a.length();
  
          pIndex = a.indexOf(PAUSE);
          wIndex = a.indexOf(WAIT);
  
          trimIndex = minPositive(pIndex, wIndex);
  
          if (trimIndex < 0) {
              return origLength - 1;
          } else {
              return trimIndex - 1;
          }
      }
  
      /**
       * Extracts the post-dial sequence of DTMF control digits, pauses, and
       * waits. Strips separators. This string may be empty, but will not be null
       * unless phoneNumber == null.
       *
       * Returns null if phoneNumber == null
       */
  
      public static String
      extractPostDialPortion(String phoneNumber) {
          if (phoneNumber == null) return null;
  
          int trimIndex;
          StringBuilder ret = new StringBuilder();
  
          trimIndex = indexOfLastNetworkChar (phoneNumber);
  
          for (int i = trimIndex + 1, s = phoneNumber.length()
                  ; i < s; i++
          ) {
              char c = phoneNumber.charAt(i);
              if (isNonSeparator(c)) {
                  ret.append(c);
              }
          }
  
          return ret.toString();
      }
  
      /**
       * Compare phone numbers a and b, return true if they're identical enough for caller ID purposes.
       */
      public static boolean compare(String a, String b) {
          // We've used loose comparation at least Eclair, which may change in the future.
  
          return compare(a, b, false);
      }
  
      /**
       * Compare phone numbers a and b, and return true if they're identical
       * enough for caller ID purposes. Checks a resource to determine whether
       * to use a strict or loose comparison algorithm.
       */
      public static boolean compare(Context context, String a, String b) {
          boolean useStrict = context.getResources().getBoolean(
                 com.android.internal.R.bool.config_use_strict_phone_number_comparation);
          return compare(a, b, useStrict);
      }
  
      /**
       * @hide only for testing.
       */
      @UnsupportedAppUsage
      public static boolean compare(String a, String b, boolean useStrictComparation) {
          return (useStrictComparation ? compareStrictly(a, b) : compareLoosely(a, b));
      }
  
      /**
       * Compare phone numbers a and b, return true if they're identical
       * enough for caller ID purposes.
       *
       * - Compares from right to left
       * - requires MIN_MATCH (7) characters to match
       * - handles common trunk prefixes and international prefixes
       *   (basically, everything except the Russian trunk prefix)
       *
       * Note that this method does not return false even when the two phone numbers
       * are not exactly same; rather; we can call this method "similar()", not "equals()".
       *
       * @hide
       */
      @UnsupportedAppUsage
      public static boolean
      compareLoosely(String a, String b) {
          int ia, ib;
          int matched;
          int numNonDialableCharsInA = 0;
          int numNonDialableCharsInB = 0;
  
          if (a == null || b == null) return a == b;
  
          if (a.length() == 0 || b.length() == 0) {
              return false;
          }
  
          ia = indexOfLastNetworkChar (a);
          ib = indexOfLastNetworkChar (b);
          matched = 0;
  
          while (ia >= 0 && ib >=0) {
              char ca, cb;
              boolean skipCmp = false;
  
              ca = a.charAt(ia);
  
              if (!isDialable(ca)) {
                  ia--;
                  skipCmp = true;
                  numNonDialableCharsInA++;
              }
  
              cb = b.charAt(ib);
  
              if (!isDialable(cb)) {
                  ib--;
                  skipCmp = true;
                  numNonDialableCharsInB++;
              }
  
              if (!skipCmp) {
                  if (cb != ca && ca != WILD && cb != WILD) {
                      break;
                  }
                  ia--; ib--; matched++;
              }
          }
  
          if (matched < MIN_MATCH) {
              int effectiveALen = a.length() - numNonDialableCharsInA;
              int effectiveBLen = b.length() - numNonDialableCharsInB;
  
  
              // if the number of dialable chars in a and b match, but the matched chars < MIN_MATCH,
              // treat them as equal (i.e. 404-04 and 40404)
              if (effectiveALen == effectiveBLen && effectiveALen == matched) {
                  return true;
              }
  
              return false;
          }
  
          // At least one string has matched completely;
          if (matched >= MIN_MATCH && (ia < 0 || ib < 0)) {
              return true;
          }
  
          /*
           * Now, what remains must be one of the following for a
           * match:
           *
           *  - a '+' on one and a '00' or a '011' on the other
           *  - a '0' on one and a (+,00)<country code> on the other
           *     (for this, a '0' and a '00' prefix would have succeeded above)
           */
  
          if (matchIntlPrefix(a, ia + 1)
              && matchIntlPrefix (b, ib +1)
          ) {
              return true;
          }
  
          if (matchTrunkPrefix(a, ia + 1)
              && matchIntlPrefixAndCC(b, ib +1)
          ) {
              return true;
          }
  
          if (matchTrunkPrefix(b, ib + 1)
              && matchIntlPrefixAndCC(a, ia +1)
          ) {
              return true;
          }
  
          return false;
      }
  
      /**
       * @hide
       */
      @UnsupportedAppUsage
      public static boolean
      compareStrictly(String a, String b) {
          return compareStrictly(a, b, true);
      }
  
      /**
       * @hide
       */
      @UnsupportedAppUsage
      public static boolean
      compareStrictly(String a, String b, boolean acceptInvalidCCCPrefix) {
          if (a == null || b == null) {
              return a == b;
          } else if (a.length() == 0 && b.length() == 0) {
              return false;
          }
  
          int forwardIndexA = 0;
          int forwardIndexB = 0;
  
          CountryCallingCodeAndNewIndex cccA =
              tryGetCountryCallingCodeAndNewIndex(a, acceptInvalidCCCPrefix);
          CountryCallingCodeAndNewIndex cccB =
              tryGetCountryCallingCodeAndNewIndex(b, acceptInvalidCCCPrefix);
          boolean bothHasCountryCallingCode = false;
          boolean okToIgnorePrefix = true;
          boolean trunkPrefixIsOmittedA = false;
          boolean trunkPrefixIsOmittedB = false;
          if (cccA != null && cccB != null) {
              if (cccA.countryCallingCode != cccB.countryCallingCode) {
                  // Different Country Calling Code. Must be different phone number.
                  return false;
              }
              // When both have ccc, do not ignore trunk prefix. Without this,
              // "+81123123" becomes same as "+810123123" (+81 == Japan)
              okToIgnorePrefix = false;
              bothHasCountryCallingCode = true;
              forwardIndexA = cccA.newIndex;
              forwardIndexB = cccB.newIndex;
          } else if (cccA == null && cccB == null) {
              // When both do not have ccc, do not ignore trunk prefix. Without this,
              // "123123" becomes same as "0123123"
              okToIgnorePrefix = false;
          } else {
              if (cccA != null) {
                  forwardIndexA = cccA.newIndex;
              } else {
                  int tmp = tryGetTrunkPrefixOmittedIndex(b, 0);
                  if (tmp >= 0) {
                      forwardIndexA = tmp;
                      trunkPrefixIsOmittedA = true;
                  }
              }
              if (cccB != null) {
                  forwardIndexB = cccB.newIndex;
              } else {
                  int tmp = tryGetTrunkPrefixOmittedIndex(b, 0);
                  if (tmp >= 0) {
                      forwardIndexB = tmp;
                      trunkPrefixIsOmittedB = true;
                  }
              }
          }
  
          int backwardIndexA = a.length() - 1;
          int backwardIndexB = b.length() - 1;
          while (backwardIndexA >= forwardIndexA && backwardIndexB >= forwardIndexB) {
              boolean skip_compare = false;
              final char chA = a.charAt(backwardIndexA);
              final char chB = b.charAt(backwardIndexB);
              if (isSeparator(chA)) {
                  backwardIndexA--;
                  skip_compare = true;
              }
              if (isSeparator(chB)) {
                  backwardIndexB--;
                  skip_compare = true;
              }
  
              if (!skip_compare) {
                  if (chA != chB) {
                      return false;
                  }
                  backwardIndexA--;
                  backwardIndexB--;
              }
          }
  
          if (okToIgnorePrefix) {
              if ((trunkPrefixIsOmittedA && forwardIndexA <= backwardIndexA) ||
                  !checkPrefixIsIgnorable(a, forwardIndexA, backwardIndexA)) {
                  if (acceptInvalidCCCPrefix) {
                      // Maybe the code handling the special case for Thailand makes the
                      // result garbled, so disable the code and try again.
                      // e.g. "16610001234" must equal to "6610001234", but with
                      //      Thailand-case handling code, they become equal to each other.
                      //
                      // Note: we select simplicity rather than adding some complicated
                      //       logic here for performance(like "checking whether remaining
                      //       numbers are just 66 or not"), assuming inputs are small
                      //       enough.
                      return compare(a, b, false);
                  } else {
                      return false;
                  }
              }
              if ((trunkPrefixIsOmittedB && forwardIndexB <= backwardIndexB) ||
                  !checkPrefixIsIgnorable(b, forwardIndexA, backwardIndexB)) {
                  if (acceptInvalidCCCPrefix) {
                      return compare(a, b, false);
                  } else {
                      return false;
                  }
              }
          } else {
              // In the US, 1-650-555-1234 must be equal to 650-555-1234,
              // while 090-1234-1234 must not be equal to 90-1234-1234 in Japan.
              // This request exists just in US (with 1 trunk (NDD) prefix).
              // In addition, "011 11 7005554141" must not equal to "+17005554141",
              // while "011 1 7005554141" must equal to "+17005554141"
              //
              // In this comparison, we ignore the prefix '1' just once, when
              // - at least either does not have CCC, or
              // - the remaining non-separator number is 1
              boolean maybeNamp = !bothHasCountryCallingCode;
              while (backwardIndexA >= forwardIndexA) {
                  final char chA = a.charAt(backwardIndexA);
                  if (isDialable(chA)) {
                      if (maybeNamp && tryGetISODigit(chA) == 1) {
                          maybeNamp = false;
                      } else {
                          return false;
                      }
                  }
                  backwardIndexA--;
              }
              while (backwardIndexB >= forwardIndexB) {
                  final char chB = b.charAt(backwardIndexB);
                  if (isDialable(chB)) {
                      if (maybeNamp && tryGetISODigit(chB) == 1) {
                          maybeNamp = false;
                      } else {
                          return false;
                      }
                  }
                  backwardIndexB--;
              }
          }
  
          return true;
      }
  
      /**
       * Returns the rightmost MIN_MATCH (5) characters in the network portion
       * in *reversed* order
       *
       * This can be used to do a database lookup against the column
       * that stores getStrippedReversed()
       *
       * Returns null if phoneNumber == null
       */
      public static String
      toCallerIDMinMatch(String phoneNumber) {
          String np = extractNetworkPortionAlt(phoneNumber);
          return internalGetStrippedReversed(np, MIN_MATCH);
      }
  
      /**
       * Returns the network portion reversed.
       * This string is intended to go into an index column for a
       * database lookup.
       *
       * Returns null if phoneNumber == null
       */
      public static String
      getStrippedReversed(String phoneNumber) {
          String np = extractNetworkPortionAlt(phoneNumber);
  
          if (np == null) return null;
  
          return internalGetStrippedReversed(np, np.length());
      }
  
      /**
       * Returns the last numDigits of the reversed phone number
       * Returns null if np == null
       */
      private static String
      internalGetStrippedReversed(String np, int numDigits) {
          if (np == null) return null;
  
          StringBuilder ret = new StringBuilder(numDigits);
          int length = np.length();
  
          for (int i = length - 1, s = length
              ; i >= 0 && (s - i) <= numDigits ; i--
          ) {
              char c = np.charAt(i);
  
              ret.append(c);
          }
  
          return ret.toString();
      }
  
      /**
       * Basically: makes sure there's a + in front of a
       * TOA_International number
       *
       * Returns null if s == null
       */
      public static String
      stringFromStringAndTOA(String s, int TOA) {
          if (s == null) return null;
  
          if (TOA == TOA_International && s.length() > 0 && s.charAt(0) != '+') {
              return "+" + s;
          }
  
          return s;
      }
  
      /**
       * Returns the TOA for the given dial string
       * Basically, returns TOA_International if there's a + prefix
       */
  
      public static int
      toaFromString(String s) {
          if (s != null && s.length() > 0 && s.charAt(0) == '+') {
              return TOA_International;
          }
  
          return TOA_Unknown;
      }
  
      /**
       *  3GPP TS 24.008 10.5.4.7
       *  Called Party BCD Number
       *
       *  See Also TS 51.011 10.5.1 "dialing number/ssc string"
       *  and TS 11.11 "10.3.1 EF adn (Abbreviated dialing numbers)"
       *
       * @param bytes the data buffer
       * @param offset should point to the TOA (aka. TON/NPI) octet after the length byte
       * @param length is the number of bytes including TOA byte
       *                and must be at least 2
       *
       * @return partial string on invalid decode
       *
       * @deprecated use {@link #calledPartyBCDToString(byte[], int, int, int)} instead. Calling this
       * method is equivalent to calling {@link #calledPartyBCDToString(byte[], int, int)} with
       * {@link #BCD_EXTENDED_TYPE_EF_ADN} as the extended type.
       */
      @Deprecated
      public static String calledPartyBCDToString(byte[] bytes, int offset, int length) {
          return calledPartyBCDToString(bytes, offset, length, BCD_EXTENDED_TYPE_EF_ADN);
      }
  
      /**
       *  3GPP TS 24.008 10.5.4.7
       *  Called Party BCD Number
       *
       *  See Also TS 51.011 10.5.1 "dialing number/ssc string"
       *  and TS 11.11 "10.3.1 EF adn (Abbreviated dialing numbers)"
       *
       * @param bytes the data buffer
       * @param offset should point to the TOA (aka. TON/NPI) octet after the length byte
       * @param length is the number of bytes including TOA byte
       *                and must be at least 2
       * @param bcdExtType used to determine the extended bcd coding
       * @see #BCD_EXTENDED_TYPE_EF_ADN
       * @see #BCD_EXTENDED_TYPE_CALLED_PARTY
       *
       */
      public static String calledPartyBCDToString(
              byte[] bytes, int offset, int length, @BcdExtendType int bcdExtType) {
          boolean prependPlus = false;
          StringBuilder ret = new StringBuilder(1 + length * 2);
  
          if (length < 2) {
              return "";
          }
  
          //Only TON field should be taken in consideration
          if ((bytes[offset] & 0xf0) == (TOA_International & 0xf0)) {
              prependPlus = true;
          }
  
          internalCalledPartyBCDFragmentToString(
                  ret, bytes, offset + 1, length - 1, bcdExtType);
  
          if (prependPlus && ret.length() == 0) {
              // If the only thing there is a prepended plus, return ""
              return "";
          }
  
          if (prependPlus) {
              // This is an "international number" and should have
              // a plus prepended to the dialing number. But there
              // can also be GSM MMI codes as defined in TS 22.030 6.5.2
              // so we need to handle those also.
              //
              // http://web.telia.com/~u47904776/gsmkode.htm
              // has a nice list of some of these GSM codes.
              //
              // Examples are:
              //   **21*+886988171479#
              //   **21*8311234567#
              //   *21#
              //   #21#
              //   *#21#
              //   *31#+11234567890
              //   #31#+18311234567
              //   #31#8311234567
              //   18311234567
              //   +18311234567#
              //   +18311234567
              // Odd ball cases that some phones handled
              // where there is no dialing number so they
              // append the "+"
              //   *21#+
              //   **21#+
              String retString = ret.toString();
              Pattern p = Pattern.compile("(^[#*])(.*)([#*])(.*)(#)$");
              Matcher m = p.matcher(retString);
              if (m.matches()) {
                  if ("".equals(m.group(2))) {
                      // Started with two [#*] ends with #
                      // So no dialing number and we'll just
                      // append a +, this handles **21#+
                      ret = new StringBuilder();
                      ret.append(m.group(1));
                      ret.append(m.group(3));
                      ret.append(m.group(4));
                      ret.append(m.group(5));
                      ret.append("+");
                  } else {
                      // Starts with [#*] and ends with #
                      // Assume group 4 is a dialing number
                      // such as *21*+1234554#
                      ret = new StringBuilder();
                      ret.append(m.group(1));
                      ret.append(m.group(2));
                      ret.append(m.group(3));
                      ret.append("+");
                      ret.append(m.group(4));
                      ret.append(m.group(5));
                  }
              } else {
                  p = Pattern.compile("(^[#*])(.*)([#*])(.*)");
                  m = p.matcher(retString);
                  if (m.matches()) {
                      // Starts with [#*] and only one other [#*]
                      // Assume the data after last [#*] is dialing
                      // number (i.e. group 4) such as *31#+11234567890.
                      // This also includes the odd ball *21#+
                      ret = new StringBuilder();
                      ret.append(m.group(1));
                      ret.append(m.group(2));
                      ret.append(m.group(3));
                      ret.append("+");
                      ret.append(m.group(4));
                  } else {
                      // Does NOT start with [#*] just prepend '+'
                      ret = new StringBuilder();
                      ret.append('+');
                      ret.append(retString);
                  }
              }
          }
  
          return ret.toString();
      }
  
      private static void internalCalledPartyBCDFragmentToString(
              StringBuilder sb, byte [] bytes, int offset, int length,
              @BcdExtendType int bcdExtType) {
          for (int i = offset ; i < length + offset ; i++) {
              byte b;
              char c;
  
              c = bcdToChar((byte)(bytes[i] & 0xf), bcdExtType);
  
              if (c == 0) {
                  return;
              }
              sb.append(c);
  
              // FIXME(mkf) TS 23.040 9.1.2.3 says
              // "if a mobile receives 1111 in a position prior to
              // the last semi-octet then processing shall commence with
              // the next semi-octet and the intervening
              // semi-octet shall be ignored"
              // How does this jive with 24.008 10.5.4.7
  
              b = (byte)((bytes[i] >> 4) & 0xf);
  
              if (b == 0xf && i + 1 == length + offset) {
                  //ignore final 0xf
                  break;
              }
  
              c = bcdToChar(b, bcdExtType);
              if (c == 0) {
                  return;
              }
  
              sb.append(c);
          }
  
      }
  
      /**
       * Like calledPartyBCDToString, but field does not start with a
       * TOA byte. For example: SIM ADN extension fields
       *
       * @deprecated use {@link #calledPartyBCDFragmentToString(byte[], int, int, int)} instead.
       * Calling this method is equivalent to calling
       * {@link #calledPartyBCDFragmentToString(byte[], int, int, int)} with
       * {@link #BCD_EXTENDED_TYPE_EF_ADN} as the extended type.
       */
      @Deprecated
      public static String calledPartyBCDFragmentToString(byte[] bytes, int offset, int length) {
          return calledPartyBCDFragmentToString(bytes, offset, length, BCD_EXTENDED_TYPE_EF_ADN);
      }
  
      /**
       * Like calledPartyBCDToString, but field does not start with a
       * TOA byte. For example: SIM ADN extension fields
       */
      public static String calledPartyBCDFragmentToString(
              byte[] bytes, int offset, int length, @BcdExtendType int bcdExtType) {
          StringBuilder ret = new StringBuilder(length * 2);
          internalCalledPartyBCDFragmentToString(ret, bytes, offset, length, bcdExtType);
          return ret.toString();
      }
  
      /**
       * Returns the correspond character for given {@code b} based on {@code bcdExtType}, or 0 on
       * invalid code.
       */
      private static char bcdToChar(byte b, @BcdExtendType int bcdExtType) {
          if (b < 0xa) {
              return (char) ('0' + b);
          }
  
          String extended = null;
          if (BCD_EXTENDED_TYPE_EF_ADN == bcdExtType) {
              extended = BCD_EF_ADN_EXTENDED;
          } else if (BCD_EXTENDED_TYPE_CALLED_PARTY == bcdExtType) {
              extended = BCD_CALLED_PARTY_EXTENDED;
          }
          if (extended == null || b - 0xa >= extended.length()) {
              return 0;
          }
  
          return extended.charAt(b - 0xa);
      }
  
      private static int charToBCD(char c, @BcdExtendType int bcdExtType) {
          if ('0' <= c && c <= '9') {
              return c - '0';
          }
  
          String extended = null;
          if (BCD_EXTENDED_TYPE_EF_ADN == bcdExtType) {
              extended = BCD_EF_ADN_EXTENDED;
          } else if (BCD_EXTENDED_TYPE_CALLED_PARTY == bcdExtType) {
              extended = BCD_CALLED_PARTY_EXTENDED;
          }
          if (extended == null || extended.indexOf(c) == -1) {
              throw new RuntimeException("invalid char for BCD " + c);
          }
          return 0xa + extended.indexOf(c);
      }
  
      /**
       * Return true iff the network portion of <code>address</code> is,
       * as far as we can tell on the device, suitable for use as an SMS
       * destination address.
       */
      public static boolean isWellFormedSmsAddress(String address) {
          String networkPortion =
                  PhoneNumberUtils.extractNetworkPortion(address);
  
          return (!(networkPortion.equals("+")
                    || TextUtils.isEmpty(networkPortion)))
                 && isDialable(networkPortion);
      }
  
      public static boolean isGlobalPhoneNumber(String phoneNumber) {
          if (TextUtils.isEmpty(phoneNumber)) {
              return false;
          }
  
          Matcher match = GLOBAL_PHONE_NUMBER_PATTERN.matcher(phoneNumber);
          return match.matches();
      }
  
      private static boolean isDialable(String address) {
          for (int i = 0, count = address.length(); i < count; i++) {
              if (!isDialable(address.charAt(i))) {
                  return false;
              }
          }
          return true;
      }
  
      private static boolean isNonSeparator(String address) {
          for (int i = 0, count = address.length(); i < count; i++) {
              if (!isNonSeparator(address.charAt(i))) {
                  return false;
              }
          }
          return true;
      }
      /**
       * Note: calls extractNetworkPortion(), so do not use for
       * SIM EF[ADN] style records
       *
       * Returns null if network portion is empty.
       */
      public static byte[] networkPortionToCalledPartyBCD(String s) {
          String networkPortion = extractNetworkPortion(s);
          return numberToCalledPartyBCDHelper(
                  networkPortion, false, BCD_EXTENDED_TYPE_EF_ADN);
      }
  
      /**
       * Same as {@link #networkPortionToCalledPartyBCD}, but includes a
       * one-byte length prefix.
       */
      public static byte[] networkPortionToCalledPartyBCDWithLength(String s) {
          String networkPortion = extractNetworkPortion(s);
          return numberToCalledPartyBCDHelper(
                  networkPortion, true, BCD_EXTENDED_TYPE_EF_ADN);
      }
  
      /**
       * Convert a dialing number to BCD byte array
       *
       * @param number dialing number string. If the dialing number starts with '+', set to
       * international TOA
       *
       * @return BCD byte array
       *
       * @deprecated use {@link #numberToCalledPartyBCD(String, int)} instead. Calling this method
       * is equivalent to calling {@link #numberToCalledPartyBCD(String, int)} with
       * {@link #BCD_EXTENDED_TYPE_EF_ADN} as the extended type.
       */
      @Deprecated
      public static byte[] numberToCalledPartyBCD(String number) {
          return numberToCalledPartyBCD(number, BCD_EXTENDED_TYPE_EF_ADN);
      }
  
      /**
       * Convert a dialing number to BCD byte array
       *
       * @param number dialing number string. If the dialing number starts with '+', set to
       * international TOA
       * @param bcdExtType used to determine the extended bcd coding
       * @see #BCD_EXTENDED_TYPE_EF_ADN
       * @see #BCD_EXTENDED_TYPE_CALLED_PARTY
       *
       * @return BCD byte array
       */
      public static byte[] numberToCalledPartyBCD(String number, @BcdExtendType int bcdExtType) {
          return numberToCalledPartyBCDHelper(number, false, bcdExtType);
      }
  
      /**
       * If includeLength is true, prepend a one-byte length value to
       * the return array.
       */
      private static byte[] numberToCalledPartyBCDHelper(
              String number, boolean includeLength, @BcdExtendType int bcdExtType) {
          int numberLenReal = number.length();
          int numberLenEffective = numberLenReal;
          boolean hasPlus = number.indexOf('+') != -1;
          if (hasPlus) numberLenEffective--;
  
          if (numberLenEffective == 0) return null;
  
          int resultLen = (numberLenEffective + 1) / 2;  // Encoded numbers require only 4 bits each.
          int extraBytes = 1;                            // Prepended TOA byte.
          if (includeLength) extraBytes++;               // Optional prepended length byte.
          resultLen += extraBytes;
  
          byte[] result = new byte[resultLen];
  
          int digitCount = 0;
          for (int i = 0; i < numberLenReal; i++) {
              char c = number.charAt(i);
              if (c == '+') continue;
              int shift = ((digitCount & 0x01) == 1) ? 4 : 0;
              result[extraBytes + (digitCount >> 1)] |=
                      (byte)((charToBCD(c, bcdExtType) & 0x0F) << shift);
              digitCount++;
          }
  
          // 1-fill any trailing odd nibble/quartet.
          if ((digitCount & 0x01) == 1) result[extraBytes + (digitCount >> 1)] |= 0xF0;
  
          int offset = 0;
          if (includeLength) result[offset++] = (byte)(resultLen - 1);
          result[offset] = (byte)(hasPlus ? TOA_International : TOA_Unknown);
  
          return result;
      }
  
      //================ Number formatting =========================
  
      /** The current locale is unknown, look for a country code or don't format */
      public static final int FORMAT_UNKNOWN = 0;
      /** NANP formatting */
      public static final int FORMAT_NANP = 1;
      /** Japanese formatting */
      public static final int FORMAT_JAPAN = 2;
  
      /** List of country codes for countries that use the NANP */
      private static final String[] NANP_COUNTRIES = new String[] {
          "US", // United States
          "CA", // Canada
          "AS", // American Samoa
          "AI", // Anguilla
          "AG", // Antigua and Barbuda
          "BS", // Bahamas
          "BB", // Barbados
          "BM", // Bermuda
          "VG", // British Virgin Islands
          "KY", // Cayman Islands
          "DM", // Dominica
          "DO", // Dominican Republic
          "GD", // Grenada
          "GU", // Guam
          "JM", // Jamaica
          "PR", // Puerto Rico
          "MS", // Montserrat
          "MP", // Northern Mariana Islands
          "KN", // Saint Kitts and Nevis
          "LC", // Saint Lucia
          "VC", // Saint Vincent and the Grenadines
          "TT", // Trinidad and Tobago
          "TC", // Turks and Caicos Islands
          "VI", // U.S. Virgin Islands
      };
  
      private static final String KOREA_ISO_COUNTRY_CODE = "KR";
  
      private static final String JAPAN_ISO_COUNTRY_CODE = "JP";
  
      /**
       * Breaks the given number down and formats it according to the rules
       * for the country the number is from.
       *
       * @param source The phone number to format
       * @return A locally acceptable formatting of the input, or the raw input if
       *  formatting rules aren't known for the number
       *
       * @deprecated Use link #formatNumber(String phoneNumber, String defaultCountryIso) instead
       */
      @Deprecated
      public static String formatNumber(String source) {
          SpannableStringBuilder text = new SpannableStringBuilder(source);
          formatNumber(text, getFormatTypeForLocale(Locale.getDefault()));
          return text.toString();
      }
  
      /**
       * Formats the given number with the given formatting type. Currently
       * {@link #FORMAT_NANP} and {@link #FORMAT_JAPAN} are supported as a formating type.
       *
       * @param source the phone number to format
       * @param defaultFormattingType The default formatting rules to apply if the number does
       * not begin with +[country_code]
       * @return The phone number formatted with the given formatting type.
       *
       * @hide
       * @deprecated Use link #formatNumber(String phoneNumber, String defaultCountryIso) instead
       */
      @Deprecated
      @UnsupportedAppUsage
      public static String formatNumber(String source, int defaultFormattingType) {
          SpannableStringBuilder text = new SpannableStringBuilder(source);
          formatNumber(text, defaultFormattingType);
          return text.toString();
      }
  
      /**
       * Returns the phone number formatting type for the given locale.
       *
       * @param locale The locale of interest, usually {@link Locale#getDefault()}
       * @return The formatting type for the given locale, or FORMAT_UNKNOWN if the formatting
       * rules are not known for the given locale
       *
       * @deprecated Use link #formatNumber(String phoneNumber, String defaultCountryIso) instead
       */
      @Deprecated
      public static int getFormatTypeForLocale(Locale locale) {
          String country = locale.getCountry();
  
          return getFormatTypeFromCountryCode(country);
      }
  
      /**
       * Formats a phone number in-place. Currently {@link #FORMAT_JAPAN} and {@link #FORMAT_NANP}
       * is supported as a second argument.
       *
       * @param text The number to be formatted, will be modified with the formatting
       * @param defaultFormattingType The default formatting rules to apply if the number does
       * not begin with +[country_code]
       *
       * @deprecated Use link #formatNumber(String phoneNumber, String defaultCountryIso) instead
       */
      @Deprecated
      public static void formatNumber(Editable text, int defaultFormattingType) {
          int formatType = defaultFormattingType;
  
          if (text.length() > 2 && text.charAt(0) == '+') {
              if (text.charAt(1) == '1') {
                  formatType = FORMAT_NANP;
              } else if (text.length() >= 3 && text.charAt(1) == '8'
                  && text.charAt(2) == '1') {
                  formatType = FORMAT_JAPAN;
              } else {
                  formatType = FORMAT_UNKNOWN;
              }
          }
  
          switch (formatType) {
              case FORMAT_NANP:
                  formatNanpNumber(text);
                  return;
              case FORMAT_JAPAN:
                  formatJapaneseNumber(text);
                  return;
              case FORMAT_UNKNOWN:
                  removeDashes(text);
                  return;
          }
      }
  
      private static final int NANP_STATE_DIGIT = 1;
      private static final int NANP_STATE_PLUS = 2;
      private static final int NANP_STATE_ONE = 3;
      private static final int NANP_STATE_DASH = 4;
  
      /**
       * Formats a phone number in-place using the NANP formatting rules. Numbers will be formatted
       * as:
       *
       * <p><code>
       * xxxxx
       * xxx-xxxx
       * xxx-xxx-xxxx
       * 1-xxx-xxx-xxxx
       * +1-xxx-xxx-xxxx
       * </code></p>
       *
       * @param text the number to be formatted, will be modified with the formatting
       *
       * @deprecated Use link #formatNumber(String phoneNumber, String defaultCountryIso) instead
       */
      @Deprecated
      public static void formatNanpNumber(Editable text) {
          int length = text.length();
          if (length > "+1-nnn-nnn-nnnn".length()) {
              // The string is too long to be formatted
              return;
          } else if (length <= 5) {
              // The string is either a shortcode or too short to be formatted
              return;
          }
  
          CharSequence saved = text.subSequence(0, length);
  
          // Strip the dashes first, as we're going to add them back
          removeDashes(text);
          length = text.length();
  
          // When scanning the number we record where dashes need to be added,
          // if they're non-0 at the end of the scan the dashes will be added in
          // the proper places.
          int dashPositions[] = new int[3];
          int numDashes = 0;
  
          int state = NANP_STATE_DIGIT;
          int numDigits = 0;
          for (int i = 0; i < length; i++) {
              char c = text.charAt(i);
              switch (c) {
                  case '1':
                      if (numDigits == 0 || state == NANP_STATE_PLUS) {
                          state = NANP_STATE_ONE;
                          break;
                      }
                      // fall through
                  case '2':
                  case '3':
                  case '4':
                  case '5':
                  case '6':
                  case '7':
                  case '8':
                  case '9':
                  case '0':
                      if (state == NANP_STATE_PLUS) {
                          // Only NANP number supported for now
                          text.replace(0, length, saved);
                          return;
                      } else if (state == NANP_STATE_ONE) {
                          // Found either +1 or 1, follow it up with a dash
                          dashPositions[numDashes++] = i;
                      } else if (state != NANP_STATE_DASH && (numDigits == 3 || numDigits == 6)) {
                          // Found a digit that should be after a dash that isn't
                          dashPositions[numDashes++] = i;
                      }
                      state = NANP_STATE_DIGIT;
                      numDigits++;
                      break;
  
                  case '-':
                      state = NANP_STATE_DASH;
                      break;
  
                  case '+':
                      if (i == 0) {
                          // Plus is only allowed as the first character
                          state = NANP_STATE_PLUS;
                          break;
                      }
                      // Fall through
                  default:
                      // Unknown character, bail on formatting
                      text.replace(0, length, saved);
                      return;
              }
          }
  
          if (numDigits == 7) {
              // With 7 digits we want xxx-xxxx, not xxx-xxx-x
              numDashes--;
          }
  
          // Actually put the dashes in place
          for (int i = 0; i < numDashes; i++) {
              int pos = dashPositions[i];
              text.replace(pos + i, pos + i, "-");
          }
  
          // Remove trailing dashes
          int len = text.length();
          while (len > 0) {
              if (text.charAt(len - 1) == '-') {
                  text.delete(len - 1, len);
                  len--;
              } else {
                  break;
              }
          }
      }
  
      /**
       * Formats a phone number in-place using the Japanese formatting rules.
       * Numbers will be formatted as:
       *
       * <p><code>
       * 03-xxxx-xxxx
       * 090-xxxx-xxxx
       * 0120-xxx-xxx
       * +81-3-xxxx-xxxx
       * +81-90-xxxx-xxxx
       * </code></p>
       *
       * @param text the number to be formatted, will be modified with
       * the formatting
       *
       * @deprecated Use link #formatNumber(String phoneNumber, String defaultCountryIso) instead
       */
      @Deprecated
      public static void formatJapaneseNumber(Editable text) {
          JapanesePhoneNumberFormatter.format(text);
      }
  
      /**
       * Removes all dashes from the number.
       *
       * @param text the number to clear from dashes
       */
      private static void removeDashes(Editable text) {
          int p = 0;
          while (p < text.length()) {
              if (text.charAt(p) == '-') {
                  text.delete(p, p + 1);
             } else {
                  p++;
             }
          }
      }
  
      /**
       * Formats the specified {@code phoneNumber} to the E.164 representation.
       *
       * @param phoneNumber the phone number to format.
       * @param defaultCountryIso the ISO 3166-1 two letters country code.
       * @return the E.164 representation, or null if the given phone number is not valid.
       */
      public static String formatNumberToE164(String phoneNumber, String defaultCountryIso) {
          return formatNumberInternal(phoneNumber, defaultCountryIso, PhoneNumberFormat.E164);
      }
  
      /**
       * Formats the specified {@code phoneNumber} to the RFC3966 representation.
       *
       * @param phoneNumber the phone number to format.
       * @param defaultCountryIso the ISO 3166-1 two letters country code.
       * @return the RFC3966 representation, or null if the given phone number is not valid.
       */
      public static String formatNumberToRFC3966(String phoneNumber, String defaultCountryIso) {
          return formatNumberInternal(phoneNumber, defaultCountryIso, PhoneNumberFormat.RFC3966);
      }
  
      /**
       * Formats the raw phone number (string) using the specified {@code formatIdentifier}.
       * <p>
       * The given phone number must have an area code and could have a country code.
       * <p>
       * The defaultCountryIso is used to validate the given number and generate the formatted number
       * if the specified number doesn't have a country code.
       *
       * @param rawPhoneNumber The phone number to format.
       * @param defaultCountryIso The ISO 3166-1 two letters country code.
       * @param formatIdentifier The (enum) identifier of the desired format.
       * @return the formatted representation, or null if the specified number is not valid.
       */
      private static String formatNumberInternal(
              String rawPhoneNumber, String defaultCountryIso, PhoneNumberFormat formatIdentifier) {
  
          PhoneNumberUtil util = PhoneNumberUtil.getInstance();
          try {
              PhoneNumber phoneNumber = util.parse(rawPhoneNumber, defaultCountryIso);
              if (util.isValidNumber(phoneNumber)) {
                  return util.format(phoneNumber, formatIdentifier);
              }
          } catch (NumberParseException ignored) { }
  
          return null;
      }
  
      /**
       * Determines if a {@param phoneNumber} is international if dialed from
       * {@param defaultCountryIso}.
       *
       * @param phoneNumber The phone number.
       * @param defaultCountryIso The current country ISO.
       * @return {@code true} if the number is international, {@code false} otherwise.
       * @hide
       */
      public static boolean isInternationalNumber(String phoneNumber, String defaultCountryIso) {
          // If no phone number is provided, it can't be international.
          if (TextUtils.isEmpty(phoneNumber)) {
              return false;
          }
  
          // If it starts with # or * its not international.
          if (phoneNumber.startsWith("#") || phoneNumber.startsWith("*")) {
              return false;
          }
  
          PhoneNumberUtil util = PhoneNumberUtil.getInstance();
          try {
              PhoneNumber pn = util.parseAndKeepRawInput(phoneNumber, defaultCountryIso);
              return pn.getCountryCode() != util.getCountryCodeForRegion(defaultCountryIso);
          } catch (NumberParseException e) {
              return false;
          }
      }
  
      /**
       * Format a phone number.
       * <p>
       * If the given number doesn't have the country code, the phone will be
       * formatted to the default country's convention.
       *
       * @param phoneNumber
       *            the number to be formatted.
       * @param defaultCountryIso
       *            the ISO 3166-1 two letters country code whose convention will
       *            be used if the given number doesn't have the country code.
       * @return the formatted number, or null if the given number is not valid.
       */
      public static String formatNumber(String phoneNumber, String defaultCountryIso) {
          // Do not attempt to format numbers that start with a hash or star symbol.
          if (phoneNumber.startsWith("#") || phoneNumber.startsWith("*")) {
              return phoneNumber;
          }
  
          PhoneNumberUtil util = PhoneNumberUtil.getInstance();
          String result = null;
          try {
              PhoneNumber pn = util.parseAndKeepRawInput(phoneNumber, defaultCountryIso);
  
              if (KOREA_ISO_COUNTRY_CODE.equalsIgnoreCase(defaultCountryIso) &&
                      (pn.getCountryCode() == util.getCountryCodeForRegion(KOREA_ISO_COUNTRY_CODE)) &&
                      (pn.getCountryCodeSource() ==
                              PhoneNumber.CountryCodeSource.FROM_NUMBER_WITH_PLUS_SIGN)) {
                  /**
                   * Need to reformat any local Korean phone numbers (when the user is in Korea) with
                   * country code to corresponding national format which would replace the leading
                   * +82 with 0.
                   */
                  result = util.format(pn, PhoneNumberUtil.PhoneNumberFormat.NATIONAL);
              } else if (JAPAN_ISO_COUNTRY_CODE.equalsIgnoreCase(defaultCountryIso) &&
                      pn.getCountryCode() == util.getCountryCodeForRegion(JAPAN_ISO_COUNTRY_CODE) &&
                      (pn.getCountryCodeSource() ==
                              PhoneNumber.CountryCodeSource.FROM_NUMBER_WITH_PLUS_SIGN)) {
                  /**
                   * Need to reformat Japanese phone numbers (when user is in Japan) with the national
                   * dialing format.
                   */
                  result = util.format(pn, PhoneNumberUtil.PhoneNumberFormat.NATIONAL);
              } else {
                  result = util.formatInOriginalFormat(pn, defaultCountryIso);
              }
          } catch (NumberParseException e) {
          }
          return result;
      }
  
      /**
       * Format the phone number only if the given number hasn't been formatted.
       * <p>
       * The number which has only dailable character is treated as not being
       * formatted.
       *
       * @param phoneNumber
       *            the number to be formatted.
       * @param phoneNumberE164
       *            the E164 format number whose country code is used if the given
       *            phoneNumber doesn't have the country code.
       * @param defaultCountryIso
       *            the ISO 3166-1 two letters country code whose convention will
       *            be used if the phoneNumberE164 is null or invalid, or if phoneNumber
       *            contains IDD.
       * @return the formatted number if the given number has been formatted,
       *            otherwise, return the given number.
       */
      public static String formatNumber(
              String phoneNumber, String phoneNumberE164, String defaultCountryIso) {
          int len = phoneNumber.length();
          for (int i = 0; i < len; i++) {
              if (!isDialable(phoneNumber.charAt(i))) {
                  return phoneNumber;
              }
          }
          PhoneNumberUtil util = PhoneNumberUtil.getInstance();
          // Get the country code from phoneNumberE164
          if (phoneNumberE164 != null && phoneNumberE164.length() >= 2
                  && phoneNumberE164.charAt(0) == '+') {
              try {
                  // The number to be parsed is in E164 format, so the default region used doesn't
                  // matter.
                  PhoneNumber pn = util.parse(phoneNumberE164, "ZZ");
                  String regionCode = util.getRegionCodeForNumber(pn);
                  if (!TextUtils.isEmpty(regionCode) &&
                      // This makes sure phoneNumber doesn't contain an IDD
                      normalizeNumber(phoneNumber).indexOf(phoneNumberE164.substring(1)) <= 0) {
                      defaultCountryIso = regionCode;
                  }
              } catch (NumberParseException e) {
              }
          }
          String result = formatNumber(phoneNumber, defaultCountryIso);
          return result != null ? result : phoneNumber;
      }
  
      /**
       * Normalize a phone number by removing the characters other than digits. If
       * the given number has keypad letters, the letters will be converted to
       * digits first.
       *
       * @param phoneNumber the number to be normalized.
       * @return the normalized number.
       */
      public static String normalizeNumber(String phoneNumber) {
          if (TextUtils.isEmpty(phoneNumber)) {
              return "";
          }
  
          StringBuilder sb = new StringBuilder();
          int len = phoneNumber.length();
          for (int i = 0; i < len; i++) {
              char c = phoneNumber.charAt(i);
              // Character.digit() supports ASCII and Unicode digits (fullwidth, Arabic-Indic, etc.)
              int digit = Character.digit(c, 10);
              if (digit != -1) {
                  sb.append(digit);
              } else if (sb.length() == 0 && c == '+') {
                  sb.append(c);
              } else if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) {
                  return normalizeNumber(PhoneNumberUtils.convertKeypadLettersToDigits(phoneNumber));
              }
          }
          return sb.toString();
      }
  
      /**
       * Replaces all unicode(e.g. Arabic, Persian) digits with their decimal digit equivalents.
       *
       * @param number the number to perform the replacement on.
       * @return the replaced number.
       */
      public static String replaceUnicodeDigits(String number) {
          StringBuilder normalizedDigits = new StringBuilder(number.length());
          for (char c : number.toCharArray()) {
              int digit = Character.digit(c, 10);
              if (digit != -1) {
                  normalizedDigits.append(digit);
              } else {
                  normalizedDigits.append(c);
              }
          }
          return normalizedDigits.toString();
      }
  
      // Three and four digit phone numbers for either special services,
      // or 3-6 digit addresses from the network (eg carrier-originated SMS messages) should
      // not match.
      //
      // This constant used to be 5, but SMS short codes has increased in length and
      // can be easily 6 digits now days. Most countries have SMS short code length between
      // 3 to 6 digits. The exceptions are
      //
      // Australia: Short codes are six or eight digits in length, starting with the prefix "19"
      //            followed by an additional four or six digits and two.
      // Czechia: Codes are seven digits in length for MO and five (not billed) or
      //            eight (billed) for MT direction
      //
      // see http://en.wikipedia.org/wiki/Short_code#Regional_differences for reference
      //
      // However, in order to loose match 650-555-1212 and 555-1212, we need to set the min match
      // to 7.
      @UnsupportedAppUsage
      static final int MIN_MATCH = 7;
  
      /**
       * Checks a given number against the list of
       * emergency numbers provided by the RIL and SIM card.
       *
       * @param number the number to look up.
       * @return true if the number is in the list of emergency numbers
       *         listed in the RIL / SIM, otherwise return false.
       *
       * @deprecated Please use {@link TelephonyManager#isEmergencyNumber(String)} instead.
       */
      @Deprecated
      public static boolean isEmergencyNumber(String number) {
          return isEmergencyNumber(getDefaultVoiceSubId(), number);
      }
  
      /**
       * Checks a given number against the list of
       * emergency numbers provided by the RIL and SIM card.
       *
       * @param subId the subscription id of the SIM.
       * @param number the number to look up.
       * @return true if the number is in the list of emergency numbers
       *         listed in the RIL / SIM, otherwise return false.
       *
       * @deprecated Please use {@link TelephonyManager#isEmergencyNumber(String)}
       *             instead.
       *
       * @hide
       */
      @Deprecated
      @UnsupportedAppUsage
      public static boolean isEmergencyNumber(int subId, String number) {
          // Return true only if the specified number *exactly* matches
          // one of the emergency numbers listed by the RIL / SIM.
          return isEmergencyNumberInternal(subId, number, true /* useExactMatch */);
      }
  
      /**
       * Checks if given number might *potentially* result in
       * a call to an emergency service on the current network.
       *
       * Specifically, this method will return true if the specified number
       * is an emergency number according to the list managed by the RIL or
       * SIM, *or* if the specified number simply starts with the same
       * digits as any of the emergency numbers listed in the RIL / SIM.
       *
       * This method is intended for internal use by the phone app when
       * deciding whether to allow ACTION_CALL intents from 3rd party apps
       * (where we're required to *not* allow emergency calls to be placed.)
       *
       * @param number the number to look up.
       * @return true if the number is in the list of emergency numbers
       *         listed in the RIL / SIM, *or* if the number starts with the
       *         same digits as any of those emergency numbers.
       *
       * @deprecated Please use {@link TelephonyManager#isPotentialEmergencyNumber(String)}
       *             instead.
       *
       * @hide
       */
      @Deprecated
      public static boolean isPotentialEmergencyNumber(String number) {
          return isPotentialEmergencyNumber(getDefaultVoiceSubId(), number);
      }
  
      /**
       * Checks if given number might *potentially* result in
       * a call to an emergency service on the current network.
       *
       * Specifically, this method will return true if the specified number
       * is an emergency number according to the list managed by the RIL or
       * SIM, *or* if the specified number simply starts with the same
       * digits as any of the emergency numbers listed in the RIL / SIM.
       *
       * This method is intended for internal use by the phone app when
       * deciding whether to allow ACTION_CALL intents from 3rd party apps
       * (where we're required to *not* allow emergency calls to be placed.)
       *
       * @param subId the subscription id of the SIM.
       * @param number the number to look up.
       * @return true if the number is in the list of emergency numbers
       *         listed in the RIL / SIM, *or* if the number starts with the
       *         same digits as any of those emergency numbers.
       *
       * @deprecated Please use {@link TelephonyManager#isPotentialEmergencyNumber(String)}
       *             instead.
       *
       * @hide
       */
      @UnsupportedAppUsage
      @Deprecated
      public static boolean isPotentialEmergencyNumber(int subId, String number) {
          // Check against the emergency numbers listed by the RIL / SIM,
          // and *don't* require an exact match.
          return isEmergencyNumberInternal(subId, number, false /* useExactMatch */);
      }
  
      /**
       * Helper function for isEmergencyNumber(String) and
       * isPotentialEmergencyNumber(String).
       *
       * @param number the number to look up.
       *
       * @param useExactMatch if true, consider a number to be an emergency
       *           number only if it *exactly* matches a number listed in
       *           the RIL / SIM.  If false, a number is considered to be an
       *           emergency number if it simply starts with the same digits
       *           as any of the emergency numbers listed in the RIL / SIM.
       *           (Setting useExactMatch to false allows you to identify
       *           number that could *potentially* result in emergency calls
       *           since many networks will actually ignore trailing digits
       *           after a valid emergency number.)
       *
       * @return true if the number is in the list of emergency numbers
       *         listed in the RIL / sim, otherwise return false.
       */
      private static boolean isEmergencyNumberInternal(String number, boolean useExactMatch) {
          return isEmergencyNumberInternal(getDefaultVoiceSubId(), number, useExactMatch);
      }
  
      /**
       * Helper function for isEmergencyNumber(String) and
       * isPotentialEmergencyNumber(String).
       *
       * @param subId the subscription id of the SIM.
       * @param number the number to look up.
       *
       * @param useExactMatch if true, consider a number to be an emergency
       *           number only if it *exactly* matches a number listed in
       *           the RIL / SIM.  If false, a number is considered to be an
       *           emergency number if it simply starts with the same digits
       *           as any of the emergency numbers listed in the RIL / SIM.
       *           (Setting useExactMatch to false allows you to identify
       *           number that could *potentially* result in emergency calls
       *           since many networks will actually ignore trailing digits
       *           after a valid emergency number.)
       *
       * @return true if the number is in the list of emergency numbers
       *         listed in the RIL / sim, otherwise return false.
       */
      private static boolean isEmergencyNumberInternal(int subId, String number,
              boolean useExactMatch) {
          return isEmergencyNumberInternal(subId, number, null, useExactMatch);
      }
  
      /**
       * Checks if a given number is an emergency number for a specific country.
       *
       * @param number the number to look up.
       * @param defaultCountryIso the specific country which the number should be checked against
       * @return if the number is an emergency number for the specific country, then return true,
       * otherwise false
       *
       * @deprecated Please use {@link TelephonyManager#isEmergencyNumber(String)}
       *             instead.
       *
       * @hide
       */
      @Deprecated
      @UnsupportedAppUsage
      public static boolean isEmergencyNumber(String number, String defaultCountryIso) {
              return isEmergencyNumber(getDefaultVoiceSubId(), number, defaultCountryIso);
      }
  
      /**
       * Checks if a given number is an emergency number for a specific country.
       *
       * @param subId the subscription id of the SIM.
       * @param number the number to look up.
       * @param defaultCountryIso the specific country which the number should be checked against
       * @return if the number is an emergency number for the specific country, then return true,
       * otherwise false
       *
       * @deprecated Please use {@link TelephonyManager#isEmergencyNumber(String)}
       *             instead.
       *
       * @hide
       */
      @Deprecated
      public static boolean isEmergencyNumber(int subId, String number, String defaultCountryIso) {
          return isEmergencyNumberInternal(subId, number,
                                           defaultCountryIso,
                                           true /* useExactMatch */);
      }
  
      /**
       * Checks if a given number might *potentially* result in a call to an
       * emergency service, for a specific country.
       *
       * Specifically, this method will return true if the specified number
       * is an emergency number in the specified country, *or* if the number
       * simply starts with the same digits as any emergency number for that
       * country.
       *
       * This method is intended for internal use by the phone app when
       * deciding whether to allow ACTION_CALL intents from 3rd party apps
       * (where we're required to *not* allow emergency calls to be placed.)
       *
       * @param number the number to look up.
       * @param defaultCountryIso the specific country which the number should be checked against
       * @return true if the number is an emergency number for the specific
       *         country, *or* if the number starts with the same digits as
       *         any of those emergency numbers.
       *
       * @deprecated Please use {@link TelephonyManager#isPotentialEmergencyNumber(String)}
       *             instead.
       *
       * @hide
       */
      @Deprecated
      public static boolean isPotentialEmergencyNumber(String number, String defaultCountryIso) {
          return isPotentialEmergencyNumber(getDefaultVoiceSubId(), number, defaultCountryIso);
      }
  
      /**
       * Checks if a given number might *potentially* result in a call to an
       * emergency service, for a specific country.
       *
       * Specifically, this method will return true if the specified number
       * is an emergency number in the specified country, *or* if the number
       * simply starts with the same digits as any emergency number for that
       * country.
       *
       * This method is intended for internal use by the phone app when
       * deciding whether to allow ACTION_CALL intents from 3rd party apps
       * (where we're required to *not* allow emergency calls to be placed.)
       *
       * @param subId the subscription id of the SIM.
       * @param number the number to look up.
       * @param defaultCountryIso the specific country which the number should be checked against
       * @return true if the number is an emergency number for the specific
       *         country, *or* if the number starts with the same digits as
       *         any of those emergency numbers.
       *
       * @deprecated Please use {@link TelephonyManager#isPotentialEmergencyNumber(String)}
       *             instead.
       *
       * @hide
       */
      @Deprecated
      public static boolean isPotentialEmergencyNumber(int subId, String number,
              String defaultCountryIso) {
          return isEmergencyNumberInternal(subId, number,
                                           defaultCountryIso,
                                           false /* useExactMatch */);
      }
  
      /**
       * Helper function for isEmergencyNumber(String, String) and
       * isPotentialEmergencyNumber(String, String).
       *
       * @param number the number to look up.
       * @param defaultCountryIso the specific country which the number should be checked against
       * @param useExactMatch if true, consider a number to be an emergency
       *           number only if it *exactly* matches a number listed in
       *           the RIL / SIM.  If false, a number is considered to be an
       *           emergency number if it simply starts with the same digits
       *           as any of the emergency numbers listed in the RIL / SIM.
       *
       * @return true if the number is an emergency number for the specified country.
       */
      private static boolean isEmergencyNumberInternal(String number,
                                                       String defaultCountryIso,
                                                       boolean useExactMatch) {
          return isEmergencyNumberInternal(getDefaultVoiceSubId(), number, defaultCountryIso,
                  useExactMatch);
      }
  
      /**
       * Helper function for isEmergencyNumber(String, String) and
       * isPotentialEmergencyNumber(String, String).
       *
       * @param subId the subscription id of the SIM.
       * @param number the number to look up.
       * @param defaultCountryIso the specific country which the number should be checked against
       * @param useExactMatch if true, consider a number to be an emergency
       *           number only if it *exactly* matches a number listed in
       *           the RIL / SIM.  If false, a number is considered to be an
       *           emergency number if it simply starts with the same digits
       *           as any of the emergency numbers listed in the RIL / SIM.
       *
       * @return true if the number is an emergency number for the specified country.
       * @hide
       */
      private static boolean isEmergencyNumberInternal(int subId, String number,
                                                       String defaultCountryIso,
                                                       boolean useExactMatch) {
          try {
              if (useExactMatch) {
                  return TelephonyManager.getDefault().isEmergencyNumber(number);
              } else {
                  return TelephonyManager.getDefault().isPotentialEmergencyNumber(number);
              }
          } catch (RuntimeException ex) {
              Rlog.e(LOG_TAG, "isEmergencyNumberInternal: RuntimeException: " + ex);
          }
          return false;
      }
  
      /**
       * Checks if a given number is an emergency number for the country that the user is in.
       *
       * @param number the number to look up.
       * @param context the specific context which the number should be checked against
       * @return true if the specified number is an emergency number for the country the user
       * is currently in.
       *
       * @deprecated Please use {@link TelephonyManager#isEmergencyNumber(String)}
       *             instead.
       */
      @Deprecated
      public static boolean isLocalEmergencyNumber(Context context, String number) {
          return isLocalEmergencyNumber(context, getDefaultVoiceSubId(), number);
      }
  
      /**
       * Checks if a given number is an emergency number for the country that the user is in.
       *
       * @param subId the subscription id of the SIM.
       * @param number the number to look up.
       * @param context the specific context which the number should be checked against
       * @return true if the specified number is an emergency number for the country the user
       * is currently in.
       *
       * @deprecated Please use {@link TelephonyManager#isEmergencyNumber(String)}
       *             instead.
       *
       * @hide
       */
      @Deprecated
      @UnsupportedAppUsage
      public static boolean isLocalEmergencyNumber(Context context, int subId, String number) {
          return isLocalEmergencyNumberInternal(subId, number,
                                                context,
                                                true /* useExactMatch */);
      }
  
      /**
       * Checks if a given number might *potentially* result in a call to an
       * emergency service, for the country that the user is in. The current
       * country is determined using the CountryDetector.
       *
       * Specifically, this method will return true if the specified number
       * is an emergency number in the current country, *or* if the number
       * simply starts with the same digits as any emergency number for the
       * current country.
       *
       * This method is intended for internal use by the phone app when
       * deciding whether to allow ACTION_CALL intents from 3rd party apps
       * (where we're required to *not* allow emergency calls to be placed.)
       *
       * @param number the number to look up.
       * @param context the specific context which the number should be checked against
       * @return true if the specified number is an emergency number for a local country, based on the
       *              CountryDetector.
       *
       * @see android.location.CountryDetector
       *
       * @deprecated Please use {@link TelephonyManager#isPotentialEmergencyNumber(String)}
       *             instead.
       *
       * @hide
       */
      @Deprecated
      @UnsupportedAppUsage
      public static boolean isPotentialLocalEmergencyNumber(Context context, String number) {
          return isPotentialLocalEmergencyNumber(context, getDefaultVoiceSubId(), number);
      }
  
      /**
       * Checks if a given number might *potentially* result in a call to an
       * emergency service, for the country that the user is in. The current
       * country is determined using the CountryDetector.
       *
       * Specifically, this method will return true if the specified number
       * is an emergency number in the current country, *or* if the number
       * simply starts with the same digits as any emergency number for the
       * current country.
       *
       * This method is intended for internal use by the phone app when
       * deciding whether to allow ACTION_CALL intents from 3rd party apps
       * (where we're required to *not* allow emergency calls to be placed.)
       *
       * @param subId the subscription id of the SIM.
       * @param number the number to look up.
       * @param context the specific context which the number should be checked against
       * @return true if the specified number is an emergency number for a local country, based on the
       *              CountryDetector.
       *
       * @deprecated Please use {@link TelephonyManager#isPotentialEmergencyNumber(String)}
       *             instead.
       *
       * @hide
       */
      @UnsupportedAppUsage
      @Deprecated
      public static boolean isPotentialLocalEmergencyNumber(Context context, int subId,
              String number) {
          return isLocalEmergencyNumberInternal(subId, number,
                                                context,
                                                false /* useExactMatch */);
      }
  
      /**
       * Helper function for isLocalEmergencyNumber() and
       * isPotentialLocalEmergencyNumber().
       *
       * @param number the number to look up.
       * @param context the specific context which the number should be checked against
       * @param useExactMatch if true, consider a number to be an emergency
       *           number only if it *exactly* matches a number listed in
       *           the RIL / SIM.  If false, a number is considered to be an
       *           emergency number if it simply starts with the same digits
       *           as any of the emergency numbers listed in the RIL / SIM.
       *
       * @return true if the specified number is an emergency number for a
       *              local country, based on the CountryDetector.
       *
       * @see android.location.CountryDetector
       * @hide
       */
      private static boolean isLocalEmergencyNumberInternal(String number,
                                                            Context context,
                                                            boolean useExactMatch) {
          return isLocalEmergencyNumberInternal(getDefaultVoiceSubId(), number, context,
                  useExactMatch);
      }
  
      /**
       * Helper function for isLocalEmergencyNumber() and
       * isPotentialLocalEmergencyNumber().
       *
       * @param subId the subscription id of the SIM.
       * @param number the number to look up.
       * @param context the specific context which the number should be checked against
       * @param useExactMatch if true, consider a number to be an emergency
       *           number only if it *exactly* matches a number listed in
       *           the RIL / SIM.  If false, a number is considered to be an
       *           emergency number if it simply starts with the same digits
       *           as any of the emergency numbers listed in the RIL / SIM.
       *
       * @return true if the specified number is an emergency number for a
       *              local country, based on the CountryDetector.
       * @hide
       */
      private static boolean isLocalEmergencyNumberInternal(int subId, String number,
                                                            Context context,
                                                            boolean useExactMatch) {
          String countryIso;
          CountryDetector detector = (CountryDetector) context.getSystemService(
                  Context.COUNTRY_DETECTOR);
          if (detector != null && detector.detectCountry() != null) {
              countryIso = detector.detectCountry().getCountryIso();
          } else {
              Locale locale = context.getResources().getConfiguration().locale;
              countryIso = locale.getCountry();
              Rlog.w(LOG_TAG, "No CountryDetector; falling back to countryIso based on locale: "
                      + countryIso);
          }
          return isEmergencyNumberInternal(subId, number, countryIso, useExactMatch);
      }
  
      /**
       * isVoiceMailNumber: checks a given number against the voicemail
       *   number provided by the RIL and SIM card. The caller must have
       *   the READ_PHONE_STATE credential.
       *
       * @param number the number to look up.
       * @return true if the number is in the list of voicemail. False
       * otherwise, including if the caller does not have the permission
       * to read the VM number.
       */
      public static boolean isVoiceMailNumber(String number) {
          return isVoiceMailNumber(SubscriptionManager.getDefaultSubscriptionId(), number);
      }
  
      /**
       * isVoiceMailNumber: checks a given number against the voicemail
       *   number provided by the RIL and SIM card. The caller must have
       *   the READ_PHONE_STATE credential.
       *
       * @param subId the subscription id of the SIM.
       * @param number the number to look up.
       * @return true if the number is in the list of voicemail. False
       * otherwise, including if the caller does not have the permission
       * to read the VM number.
       * @hide
       */
      public static boolean isVoiceMailNumber(int subId, String number) {
          return isVoiceMailNumber(null, subId, number);
      }
  
      /**
       * isVoiceMailNumber: checks a given number against the voicemail
       *   number provided by the RIL and SIM card. The caller must have
       *   the READ_PHONE_STATE credential.
       *
       * @param context {@link Context}.
       * @param subId the subscription id of the SIM.
       * @param number the number to look up.
       * @return true if the number is in the list of voicemail. False
       * otherwise, including if the caller does not have the permission
       * to read the VM number.
       * @hide
       */
      @UnsupportedAppUsage
      public static boolean isVoiceMailNumber(Context context, int subId, String number) {
          String vmNumber, mdn;
          try {
              final TelephonyManager tm;
              if (context == null) {
                  tm = TelephonyManager.getDefault();
                  if (DBG) log("isVoiceMailNumber: default tm");
              } else {
                  tm = TelephonyManager.from(context);
                  if (DBG) log("isVoiceMailNumber: tm from context");
              }
              vmNumber = tm.getVoiceMailNumber(subId);
              mdn = tm.getLine1Number(subId);
              if (DBG) log("isVoiceMailNumber: mdn=" + mdn + ", vmNumber=" + vmNumber
                      + ", number=" + number);
          } catch (SecurityException ex) {
              if (DBG) log("isVoiceMailNumber: SecurityExcpetion caught");
              return false;
          }
          // Strip the separators from the number before comparing it
          // to the list.
          number = extractNetworkPortionAlt(number);
          if (TextUtils.isEmpty(number)) {
              if (DBG) log("isVoiceMailNumber: number is empty after stripping");
              return false;
          }
  
          // check if the carrier considers MDN to be an additional voicemail number
          boolean compareWithMdn = false;
          if (context != null) {
              CarrierConfigManager configManager = (CarrierConfigManager)
                      context.getSystemService(Context.CARRIER_CONFIG_SERVICE);
              if (configManager != null) {
                  PersistableBundle b = configManager.getConfigForSubId(subId);
                  if (b != null) {
                      compareWithMdn = b.getBoolean(CarrierConfigManager.
                              KEY_MDN_IS_ADDITIONAL_VOICEMAIL_NUMBER_BOOL);
                      if (DBG) log("isVoiceMailNumber: compareWithMdn=" + compareWithMdn);
                  }
              }
          }
  
          if (compareWithMdn) {
              if (DBG) log("isVoiceMailNumber: treating mdn as additional vm number");
              return compare(number, vmNumber) || compare(number, mdn);
          } else {
              if (DBG) log("isVoiceMailNumber: returning regular compare");
              return compare(number, vmNumber);
          }
      }
  
      /**
       * Translates any alphabetic letters (i.e. [A-Za-z]) in the
       * specified phone number into the equivalent numeric digits,
       * according to the phone keypad letter mapping described in
       * ITU E.161 and ISO/IEC 9995-8.
       *
       * @return the input string, with alpha letters converted to numeric
       *         digits using the phone keypad letter mapping.  For example,
       *         an input of "1-800-GOOG-411" will return "1-800-4664-411".
       */
      public static String convertKeypadLettersToDigits(String input) {
          if (input == null) {
              return input;
          }
          int len = input.length();
          if (len == 0) {
              return input;
          }
  
          char[] out = input.toCharArray();
  
          for (int i = 0; i < len; i++) {
              char c = out[i];
              // If this char isn't in KEYPAD_MAP at all, just leave it alone.
              out[i] = (char) KEYPAD_MAP.get(c, c);
          }
  
          return new String(out);
      }
  
      /**
       * The phone keypad letter mapping (see ITU E.161 or ISO/IEC 9995-8.)
       * TODO: This should come from a resource.
       */
      private static final SparseIntArray KEYPAD_MAP = new SparseIntArray();
      static {
          KEYPAD_MAP.put('a', '2'); KEYPAD_MAP.put('b', '2'); KEYPAD_MAP.put('c', '2');
          KEYPAD_MAP.put('A', '2'); KEYPAD_MAP.put('B', '2'); KEYPAD_MAP.put('C', '2');
  
          KEYPAD_MAP.put('d', '3'); KEYPAD_MAP.put('e', '3'); KEYPAD_MAP.put('f', '3');
          KEYPAD_MAP.put('D', '3'); KEYPAD_MAP.put('E', '3'); KEYPAD_MAP.put('F', '3');
  
          KEYPAD_MAP.put('g', '4'); KEYPAD_MAP.put('h', '4'); KEYPAD_MAP.put('i', '4');
          KEYPAD_MAP.put('G', '4'); KEYPAD_MAP.put('H', '4'); KEYPAD_MAP.put('I', '4');
  
          KEYPAD_MAP.put('j', '5'); KEYPAD_MAP.put('k', '5'); KEYPAD_MAP.put('l', '5');
          KEYPAD_MAP.put('J', '5'); KEYPAD_MAP.put('K', '5'); KEYPAD_MAP.put('L', '5');
  
          KEYPAD_MAP.put('m', '6'); KEYPAD_MAP.put('n', '6'); KEYPAD_MAP.put('o', '6');
          KEYPAD_MAP.put('M', '6'); KEYPAD_MAP.put('N', '6'); KEYPAD_MAP.put('O', '6');
  
          KEYPAD_MAP.put('p', '7'); KEYPAD_MAP.put('q', '7'); KEYPAD_MAP.put('r', '7'); KEYPAD_MAP.put('s', '7');
          KEYPAD_MAP.put('P', '7'); KEYPAD_MAP.put('Q', '7'); KEYPAD_MAP.put('R', '7'); KEYPAD_MAP.put('S', '7');
  
          KEYPAD_MAP.put('t', '8'); KEYPAD_MAP.put('u', '8'); KEYPAD_MAP.put('v', '8');
          KEYPAD_MAP.put('T', '8'); KEYPAD_MAP.put('U', '8'); KEYPAD_MAP.put('V', '8');
  
          KEYPAD_MAP.put('w', '9'); KEYPAD_MAP.put('x', '9'); KEYPAD_MAP.put('y', '9'); KEYPAD_MAP.put('z', '9');
          KEYPAD_MAP.put('W', '9'); KEYPAD_MAP.put('X', '9'); KEYPAD_MAP.put('Y', '9'); KEYPAD_MAP.put('Z', '9');
      }
  
      //================ Plus Code formatting =========================
      private static final char PLUS_SIGN_CHAR = '+';
      private static final String PLUS_SIGN_STRING = "+";
      private static final String NANP_IDP_STRING = "011";
      private static final int NANP_LENGTH = 10;
  
      /**
       * This function checks if there is a plus sign (+) in the passed-in dialing number.
       * If there is, it processes the plus sign based on the default telephone
       * numbering plan of the system when the phone is activated and the current
       * telephone numbering plan of the system that the phone is camped on.
       * Currently, we only support the case that the default and current telephone
       * numbering plans are North American Numbering Plan(NANP).
       *
       * The passed-in dialStr should only contain the valid format as described below,
       * 1) the 1st character in the dialStr should be one of the really dialable
       *    characters listed below
       *    ISO-LATIN characters 0-9, *, # , +
       * 2) the dialStr should already strip out the separator characters,
       *    every character in the dialStr should be one of the non separator characters
       *    listed below
       *    ISO-LATIN characters 0-9, *, # , +, WILD, WAIT, PAUSE
       *
       * Otherwise, this function returns the dial string passed in
       *
       * @param dialStr the original dial string
       * @return the converted dial string if the current/default countries belong to NANP,
       * and if there is the "+" in the original dial string. Otherwise, the original dial
       * string returns.
       *
       * This API is for CDMA only
       *
       * @hide TODO: pending API Council approval
       */
      @UnsupportedAppUsage
      public static String cdmaCheckAndProcessPlusCode(String dialStr) {
          if (!TextUtils.isEmpty(dialStr)) {
              if (isReallyDialable(dialStr.charAt(0)) &&
                  isNonSeparator(dialStr)) {
                  String currIso = TelephonyManager.getDefault().getNetworkCountryIso();
                  String defaultIso = TelephonyManager.getDefault().getSimCountryIso();
                  if (!TextUtils.isEmpty(currIso) && !TextUtils.isEmpty(defaultIso)) {
                      return cdmaCheckAndProcessPlusCodeByNumberFormat(dialStr,
                              getFormatTypeFromCountryCode(currIso),
                              getFormatTypeFromCountryCode(defaultIso));
                  }
              }
          }
          return dialStr;
      }
  
      /**
       * Process phone number for CDMA, converting plus code using the home network number format.
       * This is used for outgoing SMS messages.
       *
       * @param dialStr the original dial string
       * @return the converted dial string
       * @hide for internal use
       */
      public static String cdmaCheckAndProcessPlusCodeForSms(String dialStr) {
          if (!TextUtils.isEmpty(dialStr)) {
              if (isReallyDialable(dialStr.charAt(0)) && isNonSeparator(dialStr)) {
                  String defaultIso = TelephonyManager.getDefault().getSimCountryIso();
                  if (!TextUtils.isEmpty(defaultIso)) {
                      int format = getFormatTypeFromCountryCode(defaultIso);
                      return cdmaCheckAndProcessPlusCodeByNumberFormat(dialStr, format, format);
                  }
              }
          }
          return dialStr;
      }
  
      /**
       * This function should be called from checkAndProcessPlusCode only
       * And it is used for test purpose also.
       *
       * It checks the dial string by looping through the network portion,
       * post dial portion 1, post dial porting 2, etc. If there is any
       * plus sign, then process the plus sign.
       * Currently, this function supports the plus sign conversion within NANP only.
       * Specifically, it handles the plus sign in the following ways:
       * 1)+1NANP,remove +, e.g.
       *   +18475797000 is converted to 18475797000,
       * 2)+NANP or +non-NANP Numbers,replace + with the current NANP IDP, e.g,
       *   +8475797000 is converted to 0118475797000,
       *   +11875767800 is converted to 01111875767800
       * 3)+1NANP in post dial string(s), e.g.
       *   8475797000;+18475231753 is converted to 8475797000;18475231753
       *
       *
       * @param dialStr the original dial string
       * @param currFormat the numbering system of the current country that the phone is camped on
       * @param defaultFormat the numbering system of the country that the phone is activated on
       * @return the converted dial string if the current/default countries belong to NANP,
       * and if there is the "+" in the original dial string. Otherwise, the original dial
       * string returns.
       *
       * @hide
       */
      public static String
      cdmaCheckAndProcessPlusCodeByNumberFormat(String dialStr,int currFormat,int defaultFormat) {
          String retStr = dialStr;
  
          boolean useNanp = (currFormat == defaultFormat) && (currFormat == FORMAT_NANP);
  
          // Checks if the plus sign character is in the passed-in dial string
          if (dialStr != null &&
              dialStr.lastIndexOf(PLUS_SIGN_STRING) != -1) {
  
              // Handle case where default and current telephone numbering plans are NANP.
              String postDialStr = null;
              String tempDialStr = dialStr;
  
              // Sets the retStr to null since the conversion will be performed below.
              retStr = null;
              if (DBG) log("checkAndProcessPlusCode,dialStr=" + dialStr);
              // This routine is to process the plus sign in the dial string by loop through
              // the network portion, post dial portion 1, post dial portion 2... etc. if
              // applied
              do {
                  String networkDialStr;
                  // Format the string based on the rules for the country the number is from,
                  // and the current country the phone is camped
                  if (useNanp) {
                      networkDialStr = extractNetworkPortion(tempDialStr);
                  } else  {
                      networkDialStr = extractNetworkPortionAlt(tempDialStr);
  
                  }
  
                  networkDialStr = processPlusCode(networkDialStr, useNanp);
  
                  // Concatenates the string that is converted from network portion
                  if (!TextUtils.isEmpty(networkDialStr)) {
                      if (retStr == null) {
                          retStr = networkDialStr;
                      } else {
                          retStr = retStr.concat(networkDialStr);
                      }
                  } else {
                      // This should never happen since we checked the if dialStr is null
                      // and if it contains the plus sign in the beginning of this function.
                      // The plus sign is part of the network portion.
                      Rlog.e("checkAndProcessPlusCode: null newDialStr", networkDialStr);
                      return dialStr;
                  }
                  postDialStr = extractPostDialPortion(tempDialStr);
                  if (!TextUtils.isEmpty(postDialStr)) {
                      int dialableIndex = findDialableIndexFromPostDialStr(postDialStr);
  
                      // dialableIndex should always be greater than 0
                      if (dialableIndex >= 1) {
                          retStr = appendPwCharBackToOrigDialStr(dialableIndex,
                                   retStr,postDialStr);
                          // Skips the P/W character, extracts the dialable portion
                          tempDialStr = postDialStr.substring(dialableIndex);
                      } else {
                          // Non-dialable character such as P/W should not be at the end of
                          // the dial string after P/W processing in GsmCdmaConnection.java
                          // Set the postDialStr to "" to break out of the loop
                          if (dialableIndex < 0) {
                              postDialStr = "";
                          }
                          Rlog.e("wrong postDialStr=", postDialStr);
                      }
                  }
                  if (DBG) log("checkAndProcessPlusCode,postDialStr=" + postDialStr);
              } while (!TextUtils.isEmpty(postDialStr) && !TextUtils.isEmpty(tempDialStr));
          }
          return retStr;
      }
  
      /**
       * Wrap the supplied {@code CharSequence} with a {@code TtsSpan}, annotating it as
       * containing a phone number in its entirety.
       *
       * @param phoneNumber A {@code CharSequence} the entirety of which represents a phone number.
       * @return A {@code CharSequence} with appropriate annotations.
       */
      public static CharSequence createTtsSpannable(CharSequence phoneNumber) {
          if (phoneNumber == null) {
              return null;
          }
          Spannable spannable = Spannable.Factory.getInstance().newSpannable(phoneNumber);
          PhoneNumberUtils.addTtsSpan(spannable, 0, spannable.length());
          return spannable;
      }
  
      /**
       * Attach a {@link TtsSpan} to the supplied {@code Spannable} at the indicated location,
       * annotating that location as containing a phone number.
       *
       * @param s A {@code Spannable} to annotate.
       * @param start The starting character position of the phone number in {@code s}.
       * @param endExclusive The position after the ending character in the phone number {@code s}.
       */
      public static void addTtsSpan(Spannable s, int start, int endExclusive) {
          s.setSpan(createTtsSpan(s.subSequence(start, endExclusive).toString()),
                  start,
                  endExclusive,
                  Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
      }
  
      /**
       * Wrap the supplied {@code CharSequence} with a {@code TtsSpan}, annotating it as
       * containing a phone number in its entirety.
       *
       * @param phoneNumber A {@code CharSequence} the entirety of which represents a phone number.
       * @return A {@code CharSequence} with appropriate annotations.
       * @deprecated Renamed {@link #createTtsSpannable}.
       *
       * @hide
       */
      @Deprecated
      @UnsupportedAppUsage
      public static CharSequence ttsSpanAsPhoneNumber(CharSequence phoneNumber) {
          return createTtsSpannable(phoneNumber);
      }
  
      /**
       * Attach a {@link TtsSpan} to the supplied {@code Spannable} at the indicated location,
       * annotating that location as containing a phone number.
       *
       * @param s A {@code Spannable} to annotate.
       * @param start The starting character position of the phone number in {@code s}.
       * @param end The ending character position of the phone number in {@code s}.
       *
       * @deprecated Renamed {@link #addTtsSpan}.
       *
       * @hide
       */
      @Deprecated
      public static void ttsSpanAsPhoneNumber(Spannable s, int start, int end) {
          addTtsSpan(s, start, end);
      }
  
      /**
       * Create a {@code TtsSpan} for the supplied {@code String}.
       *
       * @param phoneNumberString A {@code String} the entirety of which represents a phone number.
       * @return A {@code TtsSpan} for {@param phoneNumberString}.
       */
      public static TtsSpan createTtsSpan(String phoneNumberString) {
          if (phoneNumberString == null) {
              return null;
          }
  
          // Parse the phone number
          final PhoneNumberUtil phoneNumberUtil = PhoneNumberUtil.getInstance();
          PhoneNumber phoneNumber = null;
          try {
              // Don't supply a defaultRegion so this fails for non-international numbers because
              // we don't want to TalkBalk to read a country code (e.g. +1) if it is not already
              // present
              phoneNumber = phoneNumberUtil.parse(phoneNumberString, /* defaultRegion */ null);
          } catch (NumberParseException ignored) {
          }
  
          // Build a telephone tts span
          final TtsSpan.TelephoneBuilder builder = new TtsSpan.TelephoneBuilder();
          if (phoneNumber == null) {
              // Strip separators otherwise TalkBack will be silent
              // (this behavior was observed with TalkBalk 4.0.2 from their alpha channel)
              builder.setNumberParts(splitAtNonNumerics(phoneNumberString));
          } else {
              if (phoneNumber.hasCountryCode()) {
                  builder.setCountryCode(Integer.toString(phoneNumber.getCountryCode()));
              }
              builder.setNumberParts(Long.toString(phoneNumber.getNationalNumber()));
          }
          return builder.build();
      }
  
      // Split a phone number like "+20(123)-456#" using spaces, ignoring anything that is not
      // a digit or the characters * and #, to produce a result like "20 123 456#".
      private static String splitAtNonNumerics(CharSequence number) {
          StringBuilder sb = new StringBuilder(number.length());
          for (int i = 0; i < number.length(); i++) {
              sb.append(PhoneNumberUtils.is12Key(number.charAt(i))
                      ? number.charAt(i)
                      : " ");
          }
          // It is very important to remove extra spaces. At time of writing, any leading or trailing
          // spaces, or any sequence of more than one space, will confuse TalkBack and cause the TTS
          // span to be non-functional!
          return sb.toString().replaceAll(" +", " ").trim();
      }
  
      private static String getCurrentIdp(boolean useNanp) {
          String ps = null;
          if (useNanp) {
              ps = NANP_IDP_STRING;
          } else {
              // in case, there is no IDD is found, we shouldn't convert it.
              ps = SystemProperties.get(PROPERTY_OPERATOR_IDP_STRING, PLUS_SIGN_STRING);
          }
          return ps;
      }
  
      private static boolean isTwoToNine (char c) {
          if (c >= '2' && c <= '9') {
              return true;
          } else {
              return false;
          }
      }
  
      private static int getFormatTypeFromCountryCode (String country) {
          // Check for the NANP countries
          int length = NANP_COUNTRIES.length;
          for (int i = 0; i < length; i++) {
              if (NANP_COUNTRIES[i].compareToIgnoreCase(country) == 0) {
                  return FORMAT_NANP;
              }
          }
          if ("jp".compareToIgnoreCase(country) == 0) {
              return FORMAT_JAPAN;
          }
          return FORMAT_UNKNOWN;
      }
  
      /**
       * This function checks if the passed in string conforms to the NANP format
       * i.e. NXX-NXX-XXXX, N is any digit 2-9 and X is any digit 0-9
       * @hide
       */
      @UnsupportedAppUsage
      public static boolean isNanp (String dialStr) {
          boolean retVal = false;
          if (dialStr != null) {
              if (dialStr.length() == NANP_LENGTH) {
                  if (isTwoToNine(dialStr.charAt(0)) &&
                      isTwoToNine(dialStr.charAt(3))) {
                      retVal = true;
                      for (int i=1; i<NANP_LENGTH; i++ ) {
                          char c=dialStr.charAt(i);
                          if (!PhoneNumberUtils.isISODigit(c)) {
                              retVal = false;
                              break;
                          }
                      }
                  }
              }
          } else {
              Rlog.e("isNanp: null dialStr passed in", dialStr);
          }
          return retVal;
      }
  
     /**
      * This function checks if the passed in string conforms to 1-NANP format
      */
      private static boolean isOneNanp(String dialStr) {
          boolean retVal = false;
          if (dialStr != null) {
              String newDialStr = dialStr.substring(1);
              if ((dialStr.charAt(0) == '1') && isNanp(newDialStr)) {
                  retVal = true;
              }
          } else {
              Rlog.e("isOneNanp: null dialStr passed in", dialStr);
          }
          return retVal;
      }
  
      /**
       * Determines if the specified number is actually a URI
       * (i.e. a SIP address) rather than a regular PSTN phone number,
       * based on whether or not the number contains an "@" character.
       *
       * @hide
       * @param number
       * @return true if number contains @
       */
      @UnsupportedAppUsage
      public static boolean isUriNumber(String number) {
          // Note we allow either "@" or "%40" to indicate a URI, in case
          // the passed-in string is URI-escaped.  (Neither "@" nor "%40"
          // will ever be found in a legal PSTN number.)
          return number != null && (number.contains("@") || number.contains("%40"));
      }
  
      /**
       * @return the "username" part of the specified SIP address,
       *         i.e. the part before the "@" character (or "%40").
       *
       * @param number SIP address of the form "username@domainname"
       *               (or the URI-escaped equivalent "username%40domainname")
       * @see #isUriNumber
       *
       * @hide
       */
      @UnsupportedAppUsage
      public static String getUsernameFromUriNumber(String number) {
          // The delimiter between username and domain name can be
          // either "@" or "%40" (the URI-escaped equivalent.)
          int delimiterIndex = number.indexOf('@');
          if (delimiterIndex < 0) {
              delimiterIndex = number.indexOf("%40");
          }
          if (delimiterIndex < 0) {
              Rlog.w(LOG_TAG,
                    "getUsernameFromUriNumber: no delimiter found in SIP addr '" + number + "'");
              delimiterIndex = number.length();
          }
          return number.substring(0, delimiterIndex);
      }
  
      /**
       * Given a {@link Uri} with a {@code sip} scheme, attempts to build an equivalent {@code tel}
       * scheme {@link Uri}.  If the source {@link Uri} does not contain a valid number, or is not
       * using the {@code sip} scheme, the original {@link Uri} is returned.
       *
       * @param source The {@link Uri} to convert.
       * @return The equivalent {@code tel} scheme {@link Uri}.
       *
       * @hide
       */
      public static Uri convertSipUriToTelUri(Uri source) {
          // A valid SIP uri has the format: sip:user:password@host:port;uri-parameters?headers
          // Per RFC3261, the "user" can be a telephone number.
          // For example: sip:1650555121;phone-context=blah.com@host.com
          // In this case, the phone number is in the user field of the URI, and the parameters can be
          // ignored.
          //
          // A SIP URI can also specify a phone number in a format similar to:
          // sip:+1-212-555-1212@something.com;user=phone
          // In this case, the phone number is again in user field and the parameters can be ignored.
          // We can get the user field in these instances by splitting the string on the @, ;, or :
          // and looking at the first found item.
  
          String scheme = source.getScheme();
  
          if (!PhoneAccount.SCHEME_SIP.equals(scheme)) {
              // Not a sip URI, bail.
              return source;
          }
  
          String number = source.getSchemeSpecificPart();
          String numberParts[] = number.split("[@;:]");
  
          if (numberParts.length == 0) {
              // Number not found, bail.
              return source;
          }
          number = numberParts[0];
  
          return Uri.fromParts(PhoneAccount.SCHEME_TEL, number, null);
      }
  
      /**
       * This function handles the plus code conversion
       * If the number format is
       * 1)+1NANP,remove +,
       * 2)other than +1NANP, any + numbers,replace + with the current IDP
       */
      private static String processPlusCode(String networkDialStr, boolean useNanp) {
          String retStr = networkDialStr;
  
          if (DBG) log("processPlusCode, networkDialStr = " + networkDialStr
                  + "for NANP = " + useNanp);
          // If there is a plus sign at the beginning of the dial string,
          // Convert the plus sign to the default IDP since it's an international number
          if (networkDialStr != null &&
              networkDialStr.charAt(0) == PLUS_SIGN_CHAR &&
              networkDialStr.length() > 1) {
              String newStr = networkDialStr.substring(1);
              // TODO: for nonNanp, should the '+' be removed if following number is country code
              if (useNanp && isOneNanp(newStr)) {
                  // Remove the leading plus sign
                  retStr = newStr;
              } else {
                  // Replaces the plus sign with the default IDP
                  retStr = networkDialStr.replaceFirst("[+]", getCurrentIdp(useNanp));
              }
          }
          if (DBG) log("processPlusCode, retStr=" + retStr);
          return retStr;
      }
  
      // This function finds the index of the dialable character(s)
      // in the post dial string
      private static int findDialableIndexFromPostDialStr(String postDialStr) {
          for (int index = 0;index < postDialStr.length();index++) {
               char c = postDialStr.charAt(index);
               if (isReallyDialable(c)) {
                  return index;
               }
          }
          return -1;
      }
  
      // This function appends the non-dialable P/W character to the original
      // dial string based on the dialable index passed in
      private static String
      appendPwCharBackToOrigDialStr(int dialableIndex,String origStr, String dialStr) {
          String retStr;
  
          // There is only 1 P/W character before the dialable characters
          if (dialableIndex == 1) {
              StringBuilder ret = new StringBuilder(origStr);
              ret = ret.append(dialStr.charAt(0));
              retStr = ret.toString();
          } else {
              // It means more than 1 P/W characters in the post dial string,
              // appends to retStr
              String nonDigitStr = dialStr.substring(0,dialableIndex);
              retStr = origStr.concat(nonDigitStr);
          }
          return retStr;
      }
  
      //===== Beginning of utility methods used in compareLoosely() =====
  
      /**
       * Phone numbers are stored in "lookup" form in the database
       * as reversed strings to allow for caller ID lookup
       *
       * This method takes a phone number and makes a valid SQL "LIKE"
       * string that will match the lookup form
       *
       */
      /** all of a up to len must be an international prefix or
       *  separators/non-dialing digits
       */
      private static boolean
      matchIntlPrefix(String a, int len) {
          /* '([^0-9*#+pwn]\+[^0-9*#+pwn] | [^0-9*#+pwn]0(0|11)[^0-9*#+pwn] )$' */
          /*        0       1                           2 3 45               */
  
          int state = 0;
          for (int i = 0 ; i < len ; i++) {
              char c = a.charAt(i);
  
              switch (state) {
                  case 0:
                      if      (c == '+') state = 1;
                      else if (c == '0') state = 2;
                      else if (isNonSeparator(c)) return false;
                  break;
  
                  case 2:
                      if      (c == '0') state = 3;
                      else if (c == '1') state = 4;
                      else if (isNonSeparator(c)) return false;
                  break;
  
                  case 4:
                      if      (c == '1') state = 5;
                      else if (isNonSeparator(c)) return false;
                  break;
  
                  default:
                      if (isNonSeparator(c)) return false;
                  break;
  
              }
          }
  
          return state == 1 || state == 3 || state == 5;
      }
  
      /** all of 'a' up to len must be a (+|00|011)country code)
       *  We're fast and loose with the country code. Any \d{1,3} matches */
      private static boolean
      matchIntlPrefixAndCC(String a, int len) {
          /*  [^0-9*#+pwn]*(\+|0(0|11)\d\d?\d? [^0-9*#+pwn] $ */
          /*      0          1 2 3 45  6 7  8                 */
  
          int state = 0;
          for (int i = 0 ; i < len ; i++ ) {
              char c = a.charAt(i);
  
              switch (state) {
                  case 0:
                      if      (c == '+') state = 1;
                      else if (c == '0') state = 2;
                      else if (isNonSeparator(c)) return false;
                  break;
  
                  case 2:
                      if      (c == '0') state = 3;
                      else if (c == '1') state = 4;
                      else if (isNonSeparator(c)) return false;
                  break;
  
                  case 4:
                      if      (c == '1') state = 5;
                      else if (isNonSeparator(c)) return false;
                  break;
  
                  case 1:
                  case 3:
                  case 5:
                      if      (isISODigit(c)) state = 6;
                      else if (isNonSeparator(c)) return false;
                  break;
  
                  case 6:
                  case 7:
                      if      (isISODigit(c)) state++;
                      else if (isNonSeparator(c)) return false;
                  break;
  
                  default:
                      if (isNonSeparator(c)) return false;
              }
          }
  
          return state == 6 || state == 7 || state == 8;
      }
  
      /** all of 'a' up to len must match non-US trunk prefix ('0') */
      private static boolean
      matchTrunkPrefix(String a, int len) {
          boolean found;
  
          found = false;
  
          for (int i = 0 ; i < len ; i++) {
              char c = a.charAt(i);
  
              if (c == '0' && !found) {
                  found = true;
              } else if (isNonSeparator(c)) {
                  return false;
              }
          }
  
          return found;
      }
  
      //===== End of utility methods used only in compareLoosely() =====
  
      //===== Beginning of utility methods used only in compareStrictly() ====
  
      /*
       * If true, the number is country calling code.
       */
      private static final boolean COUNTRY_CALLING_CALL[] = {
          true, true, false, false, false, false, false, true, false, false,
          false, false, false, false, false, false, false, false, false, false,
          true, false, false, false, false, false, false, true, true, false,
          true, true, true, true, true, false, true, false, false, true,
          true, false, false, true, true, true, true, true, true, true,
          false, true, true, true, true, true, true, true, true, false,
          true, true, true, true, true, true, true, false, false, false,
          false, false, false, false, false, false, false, false, false, false,
          false, true, true, true, true, false, true, false, false, true,
          true, true, true, true, true, true, false, false, true, false,
      };
      private static final int CCC_LENGTH = COUNTRY_CALLING_CALL.length;
  
      /**
       * @return true when input is valid Country Calling Code.
       */
      private static boolean isCountryCallingCode(int countryCallingCodeCandidate) {
          return countryCallingCodeCandidate > 0 && countryCallingCodeCandidate < CCC_LENGTH &&
                  COUNTRY_CALLING_CALL[countryCallingCodeCandidate];
      }
  
      /**
       * Returns integer corresponding to the input if input "ch" is
       * ISO-LATIN characters 0-9.
       * Returns -1 otherwise
       */
      private static int tryGetISODigit(char ch) {
          if ('0' <= ch && ch <= '9') {
              return ch - '0';
          } else {
              return -1;
          }
      }
  
      private static class CountryCallingCodeAndNewIndex {
          public final int countryCallingCode;
          public final int newIndex;
          public CountryCallingCodeAndNewIndex(int countryCode, int newIndex) {
              this.countryCallingCode = countryCode;
              this.newIndex = newIndex;
          }
      }
  
      /*
       * Note that this function does not strictly care the country calling code with
       * 3 length (like Morocco: +212), assuming it is enough to use the first two
       * digit to compare two phone numbers.
       */
      private static CountryCallingCodeAndNewIndex tryGetCountryCallingCodeAndNewIndex(
          String str, boolean acceptThailandCase) {
          // Rough regexp:
          //  ^[^0-9*#+]*((\+|0(0|11)\d\d?|166) [^0-9*#+] $
          //         0        1 2 3 45  6 7  89
          //
          // In all the states, this function ignores separator characters.
          // "166" is the special case for the call from Thailand to the US. Uguu!
          int state = 0;
          int ccc = 0;
          final int length = str.length();
          for (int i = 0 ; i < length ; i++ ) {
              char ch = str.charAt(i);
              switch (state) {
                  case 0:
                      if      (ch == '+') state = 1;
                      else if (ch == '0') state = 2;
                      else if (ch == '1') {
                          if (acceptThailandCase) {
                              state = 8;
                          } else {
                              return null;
                          }
                      } else if (isDialable(ch)) {
                          return null;
                      }
                  break;
  
                  case 2:
                      if      (ch == '0') state = 3;
                      else if (ch == '1') state = 4;
                      else if (isDialable(ch)) {
                          return null;
                      }
                  break;
  
                  case 4:
                      if      (ch == '1') state = 5;
                      else if (isDialable(ch)) {
                          return null;
                      }
                  break;
  
                  case 1:
                  case 3:
                  case 5:
                  case 6:
                  case 7:
                      {
                          int ret = tryGetISODigit(ch);
                          if (ret > 0) {
                              ccc = ccc * 10 + ret;
                              if (ccc >= 100 || isCountryCallingCode(ccc)) {
                                  return new CountryCallingCodeAndNewIndex(ccc, i + 1);
                              }
                              if (state == 1 || state == 3 || state == 5) {
                                  state = 6;
                              } else {
                                  state++;
                              }
                          } else if (isDialable(ch)) {
                              return null;
                          }
                      }
                      break;
                  case 8:
                      if (ch == '6') state = 9;
                      else if (isDialable(ch)) {
                          return null;
                      }
                      break;
                  case 9:
                      if (ch == '6') {
                          return new CountryCallingCodeAndNewIndex(66, i + 1);
                      } else {
                          return null;
                      }
                  default:
                      return null;
              }
          }
  
          return null;
      }
  
      /**
       * Currently this function simply ignore the first digit assuming it is
       * trunk prefix. Actually trunk prefix is different in each country.
       *
       * e.g.
       * "+79161234567" equals "89161234567" (Russian trunk digit is 8)
       * "+33123456789" equals "0123456789" (French trunk digit is 0)
       *
       */
      private static int tryGetTrunkPrefixOmittedIndex(String str, int currentIndex) {
          int length = str.length();
          for (int i = currentIndex ; i < length ; i++) {
              final char ch = str.charAt(i);
              if (tryGetISODigit(ch) >= 0) {
                  return i + 1;
              } else if (isDialable(ch)) {
                  return -1;
              }
          }
          return -1;
      }
  
      /**
       * Return true if the prefix of "str" is "ignorable". Here, "ignorable" means
       * that "str" has only one digit and separator characters. The one digit is
       * assumed to be trunk prefix.
       */
      private static boolean checkPrefixIsIgnorable(final String str,
              int forwardIndex, int backwardIndex) {
          boolean trunk_prefix_was_read = false;
          while (backwardIndex >= forwardIndex) {
              if (tryGetISODigit(str.charAt(backwardIndex)) >= 0) {
                  if (trunk_prefix_was_read) {
                      // More than one digit appeared, meaning that "a" and "b"
                      // is different.
                      return false;
                  } else {
                      // Ignore just one digit, assuming it is trunk prefix.
                      trunk_prefix_was_read = true;
                  }
              } else if (isDialable(str.charAt(backwardIndex))) {
                  // Trunk prefix is a digit, not "*", "#"...
                  return false;
              }
              backwardIndex--;
          }
  
          return true;
      }
  
      /**
       * Returns Default voice subscription Id.
       */
      private static int getDefaultVoiceSubId() {
          return SubscriptionManager.getDefaultVoiceSubscriptionId();
      }
      //==== End of utility methods used only in compareStrictly() =====
  
  
      /*
       * The config held calling number conversion map, expected to convert to emergency number.
       */
      private static String[] sConvertToEmergencyMap = null;
  
      /**
       * Converts to emergency number based on the conversion map.
       * The conversion map is declared as config_convert_to_emergency_number_map.
       *
       * @param context a context to use for accessing resources
       * @return The converted emergency number if the number matches conversion map,
       * otherwise original number.
       *
       * @hide
       */
      public static String convertToEmergencyNumber(Context context, String number) {
          if (context == null || TextUtils.isEmpty(number)) {
              return number;
          }
  
          String normalizedNumber = normalizeNumber(number);
  
          // The number is already emergency number. Skip conversion.
          if (isEmergencyNumber(normalizedNumber)) {
              return number;
          }
  
          if (sConvertToEmergencyMap == null) {
              sConvertToEmergencyMap = context.getResources().getStringArray(
                      com.android.internal.R.array.config_convert_to_emergency_number_map);
          }
  
          // The conversion map is not defined (this is default). Skip conversion.
          if (sConvertToEmergencyMap == null || sConvertToEmergencyMap.length == 0 ) {
              return number;
          }
  
          for (String convertMap : sConvertToEmergencyMap) {
              if (DBG) log("convertToEmergencyNumber: " + convertMap);
              String[] entry = null;
              String[] filterNumbers = null;
              String convertedNumber = null;
              if (!TextUtils.isEmpty(convertMap)) {
                  entry = convertMap.split(":");
              }
              if (entry != null && entry.length == 2) {
                  convertedNumber = entry[1];
                  if (!TextUtils.isEmpty(entry[0])) {
                      filterNumbers = entry[0].split(",");
                  }
              }
              // Skip if the format of entry is invalid
              if (TextUtils.isEmpty(convertedNumber) || filterNumbers == null
                      || filterNumbers.length == 0) {
                  continue;
              }
  
              for (String filterNumber : filterNumbers) {
                  if (DBG) log("convertToEmergencyNumber: filterNumber = " + filterNumber
                          + ", convertedNumber = " + convertedNumber);
                  if (!TextUtils.isEmpty(filterNumber) && filterNumber.equals(normalizedNumber)) {
                      if (DBG) log("convertToEmergencyNumber: Matched. Successfully converted to: "
                              + convertedNumber);
                      return convertedNumber;
                  }
              }
          }
          return number;
      }
  }
  