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