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