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