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