PhoneNumberUtils.java revision fae6ec9e9673e96085fdbde69866a41b5491fa0d
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.google.i18n.phonenumbers.NumberParseException;
20import com.google.i18n.phonenumbers.PhoneNumberUtil;
21import com.google.i18n.phonenumbers.PhoneNumberUtil.PhoneNumberFormat;
22import com.google.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")) {
145            return uri.getSchemeSpecificPart();
146        }
147
148        // TODO: We don't check for SecurityException here (requires
149        // READ_PHONE_STATE permission).
150        if (scheme.equals("voicemail")) {
151            return TelephonyManager.getDefault().getVoiceMailNumber();
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.parse(phoneNumber, defaultCountryIso);
1378            result = util.formatInOriginalFormat(pn, defaultCountryIso);
1379        } catch (NumberParseException e) {
1380        }
1381        return result;
1382    }
1383
1384    /**
1385     * Normalize a phone number by removing the characters other than digits. If
1386     * the given number has keypad letters, the letters will be converted to
1387     * digits first.
1388     *
1389     * @param phoneNumber
1390     *            the number to be normalized.
1391     * @return the normalized number.
1392     *
1393     * @hide
1394     */
1395    public static String normalizeNumber(String phoneNumber) {
1396        StringBuilder sb = new StringBuilder();
1397        int len = phoneNumber.length();
1398        for (int i = 0; i < len; i++) {
1399            char c = phoneNumber.charAt(i);
1400            if (PhoneNumberUtils.isISODigit(c)) {
1401                sb.append(c);
1402            } else if (c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z') {
1403                return normalizeNumber(PhoneNumberUtils.convertKeypadLettersToDigits(phoneNumber));
1404            }
1405        }
1406        return sb.toString();
1407    }
1408
1409    // Three and four digit phone numbers for either special services,
1410    // or 3-6 digit addresses from the network (eg carrier-originated SMS messages) should
1411    // not match.
1412    //
1413    // This constant used to be 5, but SMS short codes has increased in length and
1414    // can be easily 6 digits now days. Most countries have SMS short code length between
1415    // 3 to 6 digits. The exceptions are
1416    //
1417    // Australia: Short codes are six or eight digits in length, starting with the prefix "19"
1418    //            followed by an additional four or six digits and two.
1419    // Czech Republic: Codes are seven digits in length for MO and five (not billed) or
1420    //            eight (billed) for MT direction
1421    //
1422    // see http://en.wikipedia.org/wiki/Short_code#Regional_differences for reference
1423    //
1424    // However, in order to loose match 650-555-1212 and 555-1212, we need to set the min match
1425    // to 7.
1426    static final int MIN_MATCH = 7;
1427
1428    /**
1429     * isEmergencyNumber: checks a given number against the list of
1430     *   emergency numbers provided by the RIL and SIM card.
1431     *
1432     * @param number the number to look up.
1433     * @return if the number is in the list of emergency numbers
1434     * listed in the ril / sim, then return true, otherwise false.
1435     */
1436    public static boolean isEmergencyNumber(String number) {
1437        // If the number passed in is null, just return false:
1438        if (number == null) return false;
1439
1440        // Strip the separators from the number before comparing it
1441        // to the list.
1442        number = extractNetworkPortionAlt(number);
1443
1444        // retrieve the list of emergency numbers
1445        // check read-write ecclist property first
1446        String numbers = SystemProperties.get("ril.ecclist");
1447        if (TextUtils.isEmpty(numbers)) {
1448            // then read-only ecclist property since old RIL only uses this
1449            numbers = SystemProperties.get("ro.ril.ecclist");
1450        }
1451
1452        if (!TextUtils.isEmpty(numbers)) {
1453            // searches through the comma-separated list for a match,
1454            // return true if one is found.
1455            for (String emergencyNum : numbers.split(",")) {
1456                if (emergencyNum.equals(number)) {
1457                    return true;
1458                }
1459            }
1460            // no matches found against the list!
1461            return false;
1462        }
1463
1464        //no ecclist system property, so use our own list.
1465        return (number.equals("112") || number.equals("911"));
1466    }
1467
1468    /**
1469     * isVoiceMailNumber: checks a given number against the voicemail
1470     *   number provided by the RIL and SIM card. The caller must have
1471     *   the READ_PHONE_STATE credential.
1472     *
1473     * @param number the number to look up.
1474     * @return true if the number is in the list of voicemail. False
1475     * otherwise, including if the caller does not have the permission
1476     * to read the VM number.
1477     * @hide TODO: pending API Council approval
1478     */
1479    public static boolean isVoiceMailNumber(String number) {
1480        String vmNumber;
1481
1482        try {
1483            vmNumber = TelephonyManager.getDefault().getVoiceMailNumber();
1484        } catch (SecurityException ex) {
1485            return false;
1486        }
1487
1488        // Strip the separators from the number before comparing it
1489        // to the list.
1490        number = extractNetworkPortionAlt(number);
1491
1492        // compare tolerates null so we need to make sure that we
1493        // don't return true when both are null.
1494        return !TextUtils.isEmpty(number) && compare(number, vmNumber);
1495    }
1496
1497    /**
1498     * Translates any alphabetic letters (i.e. [A-Za-z]) in the
1499     * specified phone number into the equivalent numeric digits,
1500     * according to the phone keypad letter mapping described in
1501     * ITU E.161 and ISO/IEC 9995-8.
1502     *
1503     * @return the input string, with alpha letters converted to numeric
1504     *         digits using the phone keypad letter mapping.  For example,
1505     *         an input of "1-800-GOOG-411" will return "1-800-4664-411".
1506     */
1507    public static String convertKeypadLettersToDigits(String input) {
1508        if (input == null) {
1509            return input;
1510        }
1511        int len = input.length();
1512        if (len == 0) {
1513            return input;
1514        }
1515
1516        char[] out = input.toCharArray();
1517
1518        for (int i = 0; i < len; i++) {
1519            char c = out[i];
1520            // If this char isn't in KEYPAD_MAP at all, just leave it alone.
1521            out[i] = (char) KEYPAD_MAP.get(c, c);
1522        }
1523
1524        return new String(out);
1525    }
1526
1527    /**
1528     * The phone keypad letter mapping (see ITU E.161 or ISO/IEC 9995-8.)
1529     * TODO: This should come from a resource.
1530     */
1531    private static final SparseIntArray KEYPAD_MAP = new SparseIntArray();
1532    static {
1533        KEYPAD_MAP.put('a', '2'); KEYPAD_MAP.put('b', '2'); KEYPAD_MAP.put('c', '2');
1534        KEYPAD_MAP.put('A', '2'); KEYPAD_MAP.put('B', '2'); KEYPAD_MAP.put('C', '2');
1535
1536        KEYPAD_MAP.put('d', '3'); KEYPAD_MAP.put('e', '3'); KEYPAD_MAP.put('f', '3');
1537        KEYPAD_MAP.put('D', '3'); KEYPAD_MAP.put('E', '3'); KEYPAD_MAP.put('F', '3');
1538
1539        KEYPAD_MAP.put('g', '4'); KEYPAD_MAP.put('h', '4'); KEYPAD_MAP.put('i', '4');
1540        KEYPAD_MAP.put('G', '4'); KEYPAD_MAP.put('H', '4'); KEYPAD_MAP.put('I', '4');
1541
1542        KEYPAD_MAP.put('j', '5'); KEYPAD_MAP.put('k', '5'); KEYPAD_MAP.put('l', '5');
1543        KEYPAD_MAP.put('J', '5'); KEYPAD_MAP.put('K', '5'); KEYPAD_MAP.put('L', '5');
1544
1545        KEYPAD_MAP.put('m', '6'); KEYPAD_MAP.put('n', '6'); KEYPAD_MAP.put('o', '6');
1546        KEYPAD_MAP.put('M', '6'); KEYPAD_MAP.put('N', '6'); KEYPAD_MAP.put('O', '6');
1547
1548        KEYPAD_MAP.put('p', '7'); KEYPAD_MAP.put('q', '7'); KEYPAD_MAP.put('r', '7'); KEYPAD_MAP.put('s', '7');
1549        KEYPAD_MAP.put('P', '7'); KEYPAD_MAP.put('Q', '7'); KEYPAD_MAP.put('R', '7'); KEYPAD_MAP.put('S', '7');
1550
1551        KEYPAD_MAP.put('t', '8'); KEYPAD_MAP.put('u', '8'); KEYPAD_MAP.put('v', '8');
1552        KEYPAD_MAP.put('T', '8'); KEYPAD_MAP.put('U', '8'); KEYPAD_MAP.put('V', '8');
1553
1554        KEYPAD_MAP.put('w', '9'); KEYPAD_MAP.put('x', '9'); KEYPAD_MAP.put('y', '9'); KEYPAD_MAP.put('z', '9');
1555        KEYPAD_MAP.put('W', '9'); KEYPAD_MAP.put('X', '9'); KEYPAD_MAP.put('Y', '9'); KEYPAD_MAP.put('Z', '9');
1556    }
1557
1558    //================ Plus Code formatting =========================
1559    private static final char PLUS_SIGN_CHAR = '+';
1560    private static final String PLUS_SIGN_STRING = "+";
1561    private static final String NANP_IDP_STRING = "011";
1562    private static final int NANP_LENGTH = 10;
1563
1564    /**
1565     * This function checks if there is a plus sign (+) in the passed-in dialing number.
1566     * If there is, it processes the plus sign based on the default telephone
1567     * numbering plan of the system when the phone is activated and the current
1568     * telephone numbering plan of the system that the phone is camped on.
1569     * Currently, we only support the case that the default and current telephone
1570     * numbering plans are North American Numbering Plan(NANP).
1571     *
1572     * The passed-in dialStr should only contain the valid format as described below,
1573     * 1) the 1st character in the dialStr should be one of the really dialable
1574     *    characters listed below
1575     *    ISO-LATIN characters 0-9, *, # , +
1576     * 2) the dialStr should already strip out the separator characters,
1577     *    every character in the dialStr should be one of the non separator characters
1578     *    listed below
1579     *    ISO-LATIN characters 0-9, *, # , +, WILD, WAIT, PAUSE
1580     *
1581     * Otherwise, this function returns the dial string passed in
1582     *
1583     * @param dialStr the original dial string
1584     * @return the converted dial string if the current/default countries belong to NANP,
1585     * and if there is the "+" in the original dial string. Otherwise, the original dial
1586     * string returns.
1587     *
1588     * This API is for CDMA only
1589     *
1590     * @hide TODO: pending API Council approval
1591     */
1592    public static String cdmaCheckAndProcessPlusCode(String dialStr) {
1593        if (!TextUtils.isEmpty(dialStr)) {
1594            if (isReallyDialable(dialStr.charAt(0)) &&
1595                isNonSeparator(dialStr)) {
1596                String currIso = SystemProperties.get(PROPERTY_OPERATOR_ISO_COUNTRY, "");
1597                String defaultIso = SystemProperties.get(PROPERTY_ICC_OPERATOR_ISO_COUNTRY, "");
1598                if (!TextUtils.isEmpty(currIso) && !TextUtils.isEmpty(defaultIso)) {
1599                    return cdmaCheckAndProcessPlusCodeByNumberFormat(dialStr,
1600                            getFormatTypeFromCountryCode(currIso),
1601                            getFormatTypeFromCountryCode(defaultIso));
1602                }
1603            }
1604        }
1605        return dialStr;
1606    }
1607
1608    /**
1609     * This function should be called from checkAndProcessPlusCode only
1610     * And it is used for test purpose also.
1611     *
1612     * It checks the dial string by looping through the network portion,
1613     * post dial portion 1, post dial porting 2, etc. If there is any
1614     * plus sign, then process the plus sign.
1615     * Currently, this function supports the plus sign conversion within NANP only.
1616     * Specifically, it handles the plus sign in the following ways:
1617     * 1)+1NANP,remove +, e.g.
1618     *   +18475797000 is converted to 18475797000,
1619     * 2)+NANP or +non-NANP Numbers,replace + with the current NANP IDP, e.g,
1620     *   +8475797000 is converted to 0118475797000,
1621     *   +11875767800 is converted to 01111875767800
1622     * 3)+1NANP in post dial string(s), e.g.
1623     *   8475797000;+18475231753 is converted to 8475797000;18475231753
1624     *
1625     *
1626     * @param dialStr the original dial string
1627     * @param currFormat the numbering system of the current country that the phone is camped on
1628     * @param defaultFormat the numbering system of the country that the phone is activated on
1629     * @return the converted dial string if the current/default countries belong to NANP,
1630     * and if there is the "+" in the original dial string. Otherwise, the original dial
1631     * string returns.
1632     *
1633     * @hide
1634     */
1635    public static String
1636    cdmaCheckAndProcessPlusCodeByNumberFormat(String dialStr,int currFormat,int defaultFormat) {
1637        String retStr = dialStr;
1638
1639        // Checks if the plus sign character is in the passed-in dial string
1640        if (dialStr != null &&
1641            dialStr.lastIndexOf(PLUS_SIGN_STRING) != -1) {
1642            // Format the string based on the rules for the country the number is from,
1643            // and the current country the phone is camped on.
1644            if ((currFormat == defaultFormat) && (currFormat == FORMAT_NANP)) {
1645                // Handle case where default and current telephone numbering plans are NANP.
1646                String postDialStr = null;
1647                String tempDialStr = dialStr;
1648
1649                // Sets the retStr to null since the conversion will be performed below.
1650                retStr = null;
1651                if (DBG) log("checkAndProcessPlusCode,dialStr=" + dialStr);
1652                // This routine is to process the plus sign in the dial string by loop through
1653                // the network portion, post dial portion 1, post dial portion 2... etc. if
1654                // applied
1655                do {
1656                    String networkDialStr;
1657                    networkDialStr = extractNetworkPortion(tempDialStr);
1658                    // Handles the conversion within NANP
1659                    networkDialStr = processPlusCodeWithinNanp(networkDialStr);
1660
1661                    // Concatenates the string that is converted from network portion
1662                    if (!TextUtils.isEmpty(networkDialStr)) {
1663                        if (retStr == null) {
1664                            retStr = networkDialStr;
1665                        } else {
1666                            retStr = retStr.concat(networkDialStr);
1667                        }
1668                    } else {
1669                        // This should never happen since we checked the if dialStr is null
1670                        // and if it contains the plus sign in the beginning of this function.
1671                        // The plus sign is part of the network portion.
1672                        Log.e("checkAndProcessPlusCode: null newDialStr", networkDialStr);
1673                        return dialStr;
1674                    }
1675                    postDialStr = extractPostDialPortion(tempDialStr);
1676                    if (!TextUtils.isEmpty(postDialStr)) {
1677                        int dialableIndex = findDialableIndexFromPostDialStr(postDialStr);
1678
1679                        // dialableIndex should always be greater than 0
1680                        if (dialableIndex >= 1) {
1681                            retStr = appendPwCharBackToOrigDialStr(dialableIndex,
1682                                     retStr,postDialStr);
1683                            // Skips the P/W character, extracts the dialable portion
1684                            tempDialStr = postDialStr.substring(dialableIndex);
1685                        } else {
1686                            // Non-dialable character such as P/W should not be at the end of
1687                            // the dial string after P/W processing in CdmaConnection.java
1688                            // Set the postDialStr to "" to break out of the loop
1689                            if (dialableIndex < 0) {
1690                                postDialStr = "";
1691                            }
1692                            Log.e("wrong postDialStr=", postDialStr);
1693                        }
1694                    }
1695                    if (DBG) log("checkAndProcessPlusCode,postDialStr=" + postDialStr);
1696                } while (!TextUtils.isEmpty(postDialStr) && !TextUtils.isEmpty(tempDialStr));
1697            } else {
1698                // TODO: Support NANP international conversion and other telephone numbering plans.
1699                // Currently the phone is never used in non-NANP system, so return the original
1700                // dial string.
1701                Log.e("checkAndProcessPlusCode:non-NANP not supported", dialStr);
1702            }
1703        }
1704        return retStr;
1705     }
1706
1707    // This function gets the default international dialing prefix
1708    private static String getDefaultIdp( ) {
1709        String ps = null;
1710        SystemProperties.get(PROPERTY_IDP_STRING, ps);
1711        if (TextUtils.isEmpty(ps)) {
1712            ps = NANP_IDP_STRING;
1713        }
1714        return ps;
1715    }
1716
1717    private static boolean isTwoToNine (char c) {
1718        if (c >= '2' && c <= '9') {
1719            return true;
1720        } else {
1721            return false;
1722        }
1723    }
1724
1725    private static int getFormatTypeFromCountryCode (String country) {
1726        // Check for the NANP countries
1727        int length = NANP_COUNTRIES.length;
1728        for (int i = 0; i < length; i++) {
1729            if (NANP_COUNTRIES[i].compareToIgnoreCase(country) == 0) {
1730                return FORMAT_NANP;
1731            }
1732        }
1733        if ("jp".compareToIgnoreCase(country) == 0) {
1734            return FORMAT_JAPAN;
1735        }
1736        return FORMAT_UNKNOWN;
1737    }
1738
1739    /**
1740     * This function checks if the passed in string conforms to the NANP format
1741     * i.e. NXX-NXX-XXXX, N is any digit 2-9 and X is any digit 0-9
1742     */
1743    private static boolean isNanp (String dialStr) {
1744        boolean retVal = false;
1745        if (dialStr != null) {
1746            if (dialStr.length() == NANP_LENGTH) {
1747                if (isTwoToNine(dialStr.charAt(0)) &&
1748                    isTwoToNine(dialStr.charAt(3))) {
1749                    retVal = true;
1750                    for (int i=1; i<NANP_LENGTH; i++ ) {
1751                        char c=dialStr.charAt(i);
1752                        if (!PhoneNumberUtils.isISODigit(c)) {
1753                            retVal = false;
1754                            break;
1755                        }
1756                    }
1757                }
1758            }
1759        } else {
1760            Log.e("isNanp: null dialStr passed in", dialStr);
1761        }
1762        return retVal;
1763    }
1764
1765   /**
1766    * This function checks if the passed in string conforms to 1-NANP format
1767    */
1768    private static boolean isOneNanp(String dialStr) {
1769        boolean retVal = false;
1770        if (dialStr != null) {
1771            String newDialStr = dialStr.substring(1);
1772            if ((dialStr.charAt(0) == '1') && isNanp(newDialStr)) {
1773                retVal = true;
1774            }
1775        } else {
1776            Log.e("isOneNanp: null dialStr passed in", dialStr);
1777        }
1778        return retVal;
1779    }
1780
1781    /**
1782     * This function handles the plus code conversion within NANP CDMA network
1783     * If the number format is
1784     * 1)+1NANP,remove +,
1785     * 2)other than +1NANP, any + numbers,replace + with the current IDP
1786     */
1787    private static String processPlusCodeWithinNanp(String networkDialStr) {
1788        String retStr = networkDialStr;
1789
1790        if (DBG) log("processPlusCodeWithinNanp,networkDialStr=" + networkDialStr);
1791        // If there is a plus sign at the beginning of the dial string,
1792        // Convert the plus sign to the default IDP since it's an international number
1793        if (networkDialStr != null &&
1794            networkDialStr.charAt(0) == PLUS_SIGN_CHAR &&
1795            networkDialStr.length() > 1) {
1796            String newStr = networkDialStr.substring(1);
1797            if (isOneNanp(newStr)) {
1798                // Remove the leading plus sign
1799                retStr = newStr;
1800             } else {
1801                 String idpStr = getDefaultIdp();
1802                 // Replaces the plus sign with the default IDP
1803                 retStr = networkDialStr.replaceFirst("[+]", idpStr);
1804            }
1805        }
1806        if (DBG) log("processPlusCodeWithinNanp,retStr=" + retStr);
1807        return retStr;
1808    }
1809
1810    // This function finds the index of the dialable character(s)
1811    // in the post dial string
1812    private static int findDialableIndexFromPostDialStr(String postDialStr) {
1813        for (int index = 0;index < postDialStr.length();index++) {
1814             char c = postDialStr.charAt(index);
1815             if (isReallyDialable(c)) {
1816                return index;
1817             }
1818        }
1819        return -1;
1820    }
1821
1822    // This function appends the non-dialable P/W character to the original
1823    // dial string based on the dialable index passed in
1824    private static String
1825    appendPwCharBackToOrigDialStr(int dialableIndex,String origStr, String dialStr) {
1826        String retStr;
1827
1828        // There is only 1 P/W character before the dialable characters
1829        if (dialableIndex == 1) {
1830            StringBuilder ret = new StringBuilder(origStr);
1831            ret = ret.append(dialStr.charAt(0));
1832            retStr = ret.toString();
1833        } else {
1834            // It means more than 1 P/W characters in the post dial string,
1835            // appends to retStr
1836            String nonDigitStr = dialStr.substring(0,dialableIndex);
1837            retStr = origStr.concat(nonDigitStr);
1838        }
1839        return retStr;
1840    }
1841
1842    //===== Beginning of utility methods used in compareLoosely() =====
1843
1844    /**
1845     * Phone numbers are stored in "lookup" form in the database
1846     * as reversed strings to allow for caller ID lookup
1847     *
1848     * This method takes a phone number and makes a valid SQL "LIKE"
1849     * string that will match the lookup form
1850     *
1851     */
1852    /** all of a up to len must be an international prefix or
1853     *  separators/non-dialing digits
1854     */
1855    private static boolean
1856    matchIntlPrefix(String a, int len) {
1857        /* '([^0-9*#+pwn]\+[^0-9*#+pwn] | [^0-9*#+pwn]0(0|11)[^0-9*#+pwn] )$' */
1858        /*        0       1                           2 3 45               */
1859
1860        int state = 0;
1861        for (int i = 0 ; i < len ; i++) {
1862            char c = a.charAt(i);
1863
1864            switch (state) {
1865                case 0:
1866                    if      (c == '+') state = 1;
1867                    else if (c == '0') state = 2;
1868                    else if (isNonSeparator(c)) return false;
1869                break;
1870
1871                case 2:
1872                    if      (c == '0') state = 3;
1873                    else if (c == '1') state = 4;
1874                    else if (isNonSeparator(c)) return false;
1875                break;
1876
1877                case 4:
1878                    if      (c == '1') state = 5;
1879                    else if (isNonSeparator(c)) return false;
1880                break;
1881
1882                default:
1883                    if (isNonSeparator(c)) return false;
1884                break;
1885
1886            }
1887        }
1888
1889        return state == 1 || state == 3 || state == 5;
1890    }
1891
1892    /** all of 'a' up to len must be a (+|00|011)country code)
1893     *  We're fast and loose with the country code. Any \d{1,3} matches */
1894    private static boolean
1895    matchIntlPrefixAndCC(String a, int len) {
1896        /*  [^0-9*#+pwn]*(\+|0(0|11)\d\d?\d? [^0-9*#+pwn] $ */
1897        /*      0          1 2 3 45  6 7  8                 */
1898
1899        int state = 0;
1900        for (int i = 0 ; i < len ; i++ ) {
1901            char c = a.charAt(i);
1902
1903            switch (state) {
1904                case 0:
1905                    if      (c == '+') state = 1;
1906                    else if (c == '0') state = 2;
1907                    else if (isNonSeparator(c)) return false;
1908                break;
1909
1910                case 2:
1911                    if      (c == '0') state = 3;
1912                    else if (c == '1') state = 4;
1913                    else if (isNonSeparator(c)) return false;
1914                break;
1915
1916                case 4:
1917                    if      (c == '1') state = 5;
1918                    else if (isNonSeparator(c)) return false;
1919                break;
1920
1921                case 1:
1922                case 3:
1923                case 5:
1924                    if      (isISODigit(c)) state = 6;
1925                    else if (isNonSeparator(c)) return false;
1926                break;
1927
1928                case 6:
1929                case 7:
1930                    if      (isISODigit(c)) state++;
1931                    else if (isNonSeparator(c)) return false;
1932                break;
1933
1934                default:
1935                    if (isNonSeparator(c)) return false;
1936            }
1937        }
1938
1939        return state == 6 || state == 7 || state == 8;
1940    }
1941
1942    /** all of 'a' up to len must match non-US trunk prefix ('0') */
1943    private static boolean
1944    matchTrunkPrefix(String a, int len) {
1945        boolean found;
1946
1947        found = false;
1948
1949        for (int i = 0 ; i < len ; i++) {
1950            char c = a.charAt(i);
1951
1952            if (c == '0' && !found) {
1953                found = true;
1954            } else if (isNonSeparator(c)) {
1955                return false;
1956            }
1957        }
1958
1959        return found;
1960    }
1961
1962    //===== End of utility methods used only in compareLoosely() =====
1963
1964    //===== Beginning of utility methods used only in compareStrictly() ====
1965
1966    /*
1967     * If true, the number is country calling code.
1968     */
1969    private static final boolean COUNTRY_CALLING_CALL[] = {
1970        true, true, false, false, false, false, false, true, false, false,
1971        false, false, false, false, false, false, false, false, false, false,
1972        true, false, false, false, false, false, false, true, true, false,
1973        true, true, true, true, true, false, true, false, false, true,
1974        true, false, false, true, true, true, true, true, true, true,
1975        false, true, true, true, true, true, true, true, true, false,
1976        true, true, true, true, true, true, true, false, false, false,
1977        false, false, false, false, false, false, false, false, false, false,
1978        false, true, true, true, true, false, true, false, false, true,
1979        true, true, true, true, true, true, false, false, true, false,
1980    };
1981    private static final int CCC_LENGTH = COUNTRY_CALLING_CALL.length;
1982
1983    /**
1984     * @return true when input is valid Country Calling Code.
1985     */
1986    private static boolean isCountryCallingCode(int countryCallingCodeCandidate) {
1987        return countryCallingCodeCandidate > 0 && countryCallingCodeCandidate < CCC_LENGTH &&
1988                COUNTRY_CALLING_CALL[countryCallingCodeCandidate];
1989    }
1990
1991    /**
1992     * Returns integer corresponding to the input if input "ch" is
1993     * ISO-LATIN characters 0-9.
1994     * Returns -1 otherwise
1995     */
1996    private static int tryGetISODigit(char ch) {
1997        if ('0' <= ch && ch <= '9') {
1998            return ch - '0';
1999        } else {
2000            return -1;
2001        }
2002    }
2003
2004    private static class CountryCallingCodeAndNewIndex {
2005        public final int countryCallingCode;
2006        public final int newIndex;
2007        public CountryCallingCodeAndNewIndex(int countryCode, int newIndex) {
2008            this.countryCallingCode = countryCode;
2009            this.newIndex = newIndex;
2010        }
2011    }
2012
2013    /*
2014     * Note that this function does not strictly care the country calling code with
2015     * 3 length (like Morocco: +212), assuming it is enough to use the first two
2016     * digit to compare two phone numbers.
2017     */
2018    private static CountryCallingCodeAndNewIndex tryGetCountryCallingCodeAndNewIndex(
2019        String str, boolean acceptThailandCase) {
2020        // Rough regexp:
2021        //  ^[^0-9*#+]*((\+|0(0|11)\d\d?|166) [^0-9*#+] $
2022        //         0        1 2 3 45  6 7  89
2023        //
2024        // In all the states, this function ignores separator characters.
2025        // "166" is the special case for the call from Thailand to the US. Uguu!
2026        int state = 0;
2027        int ccc = 0;
2028        final int length = str.length();
2029        for (int i = 0 ; i < length ; i++ ) {
2030            char ch = str.charAt(i);
2031            switch (state) {
2032                case 0:
2033                    if      (ch == '+') state = 1;
2034                    else if (ch == '0') state = 2;
2035                    else if (ch == '1') {
2036                        if (acceptThailandCase) {
2037                            state = 8;
2038                        } else {
2039                            return null;
2040                        }
2041                    } else if (isDialable(ch)) {
2042                        return null;
2043                    }
2044                break;
2045
2046                case 2:
2047                    if      (ch == '0') state = 3;
2048                    else if (ch == '1') state = 4;
2049                    else if (isDialable(ch)) {
2050                        return null;
2051                    }
2052                break;
2053
2054                case 4:
2055                    if      (ch == '1') state = 5;
2056                    else if (isDialable(ch)) {
2057                        return null;
2058                    }
2059                break;
2060
2061                case 1:
2062                case 3:
2063                case 5:
2064                case 6:
2065                case 7:
2066                    {
2067                        int ret = tryGetISODigit(ch);
2068                        if (ret > 0) {
2069                            ccc = ccc * 10 + ret;
2070                            if (ccc >= 100 || isCountryCallingCode(ccc)) {
2071                                return new CountryCallingCodeAndNewIndex(ccc, i + 1);
2072                            }
2073                            if (state == 1 || state == 3 || state == 5) {
2074                                state = 6;
2075                            } else {
2076                                state++;
2077                            }
2078                        } else if (isDialable(ch)) {
2079                            return null;
2080                        }
2081                    }
2082                    break;
2083                case 8:
2084                    if (ch == '6') state = 9;
2085                    else if (isDialable(ch)) {
2086                        return null;
2087                    }
2088                    break;
2089                case 9:
2090                    if (ch == '6') {
2091                        return new CountryCallingCodeAndNewIndex(66, i + 1);
2092                    } else {
2093                        return null;
2094                    }
2095                default:
2096                    return null;
2097            }
2098        }
2099
2100        return null;
2101    }
2102
2103    /**
2104     * Currently this function simply ignore the first digit assuming it is
2105     * trunk prefix. Actually trunk prefix is different in each country.
2106     *
2107     * e.g.
2108     * "+79161234567" equals "89161234567" (Russian trunk digit is 8)
2109     * "+33123456789" equals "0123456789" (French trunk digit is 0)
2110     *
2111     */
2112    private static int tryGetTrunkPrefixOmittedIndex(String str, int currentIndex) {
2113        int length = str.length();
2114        for (int i = currentIndex ; i < length ; i++) {
2115            final char ch = str.charAt(i);
2116            if (tryGetISODigit(ch) >= 0) {
2117                return i + 1;
2118            } else if (isDialable(ch)) {
2119                return -1;
2120            }
2121        }
2122        return -1;
2123    }
2124
2125    /**
2126     * Return true if the prefix of "str" is "ignorable". Here, "ignorable" means
2127     * that "str" has only one digit and separator characters. The one digit is
2128     * assumed to be trunk prefix.
2129     */
2130    private static boolean checkPrefixIsIgnorable(final String str,
2131            int forwardIndex, int backwardIndex) {
2132        boolean trunk_prefix_was_read = false;
2133        while (backwardIndex >= forwardIndex) {
2134            if (tryGetISODigit(str.charAt(backwardIndex)) >= 0) {
2135                if (trunk_prefix_was_read) {
2136                    // More than one digit appeared, meaning that "a" and "b"
2137                    // is different.
2138                    return false;
2139                } else {
2140                    // Ignore just one digit, assuming it is trunk prefix.
2141                    trunk_prefix_was_read = true;
2142                }
2143            } else if (isDialable(str.charAt(backwardIndex))) {
2144                // Trunk prefix is a digit, not "*", "#"...
2145                return false;
2146            }
2147            backwardIndex--;
2148        }
2149
2150        return true;
2151    }
2152
2153    //==== End of utility methods used only in compareStrictly() =====
2154}
2155