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