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