PhoneNumberUtils.java revision fb40dd4d00bd3361b2535bc866e6c21eadc52558
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 {
988            throw new RuntimeException ("invalid char for BCD " + c);
989        }
990    }
991
992    /**
993     * Return true iff the network portion of <code>address</code> is,
994     * as far as we can tell on the device, suitable for use as an SMS
995     * destination address.
996     */
997    public static boolean isWellFormedSmsAddress(String address) {
998        String networkPortion =
999                PhoneNumberUtils.extractNetworkPortion(address);
1000
1001        return (!(networkPortion.equals("+")
1002                  || TextUtils.isEmpty(networkPortion)))
1003               && isDialable(networkPortion);
1004    }
1005
1006    public static boolean isGlobalPhoneNumber(String phoneNumber) {
1007        if (TextUtils.isEmpty(phoneNumber)) {
1008            return false;
1009        }
1010
1011        Matcher match = GLOBAL_PHONE_NUMBER_PATTERN.matcher(phoneNumber);
1012        return match.matches();
1013    }
1014
1015    private static boolean isDialable(String address) {
1016        for (int i = 0, count = address.length(); i < count; i++) {
1017            if (!isDialable(address.charAt(i))) {
1018                return false;
1019            }
1020        }
1021        return true;
1022    }
1023
1024    private static boolean isNonSeparator(String address) {
1025        for (int i = 0, count = address.length(); i < count; i++) {
1026            if (!isNonSeparator(address.charAt(i))) {
1027                return false;
1028            }
1029        }
1030        return true;
1031    }
1032    /**
1033     * Note: calls extractNetworkPortion(), so do not use for
1034     * SIM EF[ADN] style records
1035     *
1036     * Returns null if network portion is empty.
1037     */
1038    public static byte[]
1039    networkPortionToCalledPartyBCD(String s) {
1040        String networkPortion = extractNetworkPortion(s);
1041        return numberToCalledPartyBCDHelper(networkPortion, false);
1042    }
1043
1044    /**
1045     * Same as {@link #networkPortionToCalledPartyBCD}, but includes a
1046     * one-byte length prefix.
1047     */
1048    public static byte[]
1049    networkPortionToCalledPartyBCDWithLength(String s) {
1050        String networkPortion = extractNetworkPortion(s);
1051        return numberToCalledPartyBCDHelper(networkPortion, true);
1052    }
1053
1054    /**
1055     * Convert a dialing number to BCD byte array
1056     *
1057     * @param number dialing number string
1058     *        if the dialing number starts with '+', set to international TOA
1059     * @return BCD byte array
1060     */
1061    public static byte[]
1062    numberToCalledPartyBCD(String number) {
1063        return numberToCalledPartyBCDHelper(number, false);
1064    }
1065
1066    /**
1067     * If includeLength is true, prepend a one-byte length value to
1068     * the return array.
1069     */
1070    private static byte[]
1071    numberToCalledPartyBCDHelper(String number, boolean includeLength) {
1072        int numberLenReal = number.length();
1073        int numberLenEffective = numberLenReal;
1074        boolean hasPlus = number.indexOf('+') != -1;
1075        if (hasPlus) numberLenEffective--;
1076
1077        if (numberLenEffective == 0) return null;
1078
1079        int resultLen = (numberLenEffective + 1) / 2;  // Encoded numbers require only 4 bits each.
1080        int extraBytes = 1;                            // Prepended TOA byte.
1081        if (includeLength) extraBytes++;               // Optional prepended length byte.
1082        resultLen += extraBytes;
1083
1084        byte[] result = new byte[resultLen];
1085
1086        int digitCount = 0;
1087        for (int i = 0; i < numberLenReal; i++) {
1088            char c = number.charAt(i);
1089            if (c == '+') continue;
1090            int shift = ((digitCount & 0x01) == 1) ? 4 : 0;
1091            result[extraBytes + (digitCount >> 1)] |= (byte)((charToBCD(c) & 0x0F) << shift);
1092            digitCount++;
1093        }
1094
1095        // 1-fill any trailing odd nibble/quartet.
1096        if ((digitCount & 0x01) == 1) result[extraBytes + (digitCount >> 1)] |= 0xF0;
1097
1098        int offset = 0;
1099        if (includeLength) result[offset++] = (byte)(resultLen - 1);
1100        result[offset] = (byte)(hasPlus ? TOA_International : TOA_Unknown);
1101
1102        return result;
1103    }
1104
1105    //================ Number formatting =========================
1106
1107    /** The current locale is unknown, look for a country code or don't format */
1108    public static final int FORMAT_UNKNOWN = 0;
1109    /** NANP formatting */
1110    public static final int FORMAT_NANP = 1;
1111    /** Japanese formatting */
1112    public static final int FORMAT_JAPAN = 2;
1113
1114    /** List of country codes for countries that use the NANP */
1115    private static final String[] NANP_COUNTRIES = new String[] {
1116        "US", // United States
1117        "CA", // Canada
1118        "AS", // American Samoa
1119        "AI", // Anguilla
1120        "AG", // Antigua and Barbuda
1121        "BS", // Bahamas
1122        "BB", // Barbados
1123        "BM", // Bermuda
1124        "VG", // British Virgin Islands
1125        "KY", // Cayman Islands
1126        "DM", // Dominica
1127        "DO", // Dominican Republic
1128        "GD", // Grenada
1129        "GU", // Guam
1130        "JM", // Jamaica
1131        "PR", // Puerto Rico
1132        "MS", // Montserrat
1133        "MP", // Northern Mariana Islands
1134        "KN", // Saint Kitts and Nevis
1135        "LC", // Saint Lucia
1136        "VC", // Saint Vincent and the Grenadines
1137        "TT", // Trinidad and Tobago
1138        "TC", // Turks and Caicos Islands
1139        "VI", // U.S. Virgin Islands
1140    };
1141
1142    /**
1143     * Breaks the given number down and formats it according to the rules
1144     * for the country the number is from.
1145     *
1146     * @param source The phone number to format
1147     * @return A locally acceptable formatting of the input, or the raw input if
1148     *  formatting rules aren't known for the number
1149     *
1150     * @deprecated Use link #formatNumber(String phoneNumber, String defaultCountryIso) instead
1151     */
1152    public static String formatNumber(String source) {
1153        SpannableStringBuilder text = new SpannableStringBuilder(source);
1154        formatNumber(text, getFormatTypeForLocale(Locale.getDefault()));
1155        return text.toString();
1156    }
1157
1158    /**
1159     * Formats the given number with the given formatting type. Currently
1160     * {@link #FORMAT_NANP} and {@link #FORMAT_JAPAN} are supported as a formating type.
1161     *
1162     * @param source the phone number to format
1163     * @param defaultFormattingType The default formatting rules to apply if the number does
1164     * not begin with +[country_code]
1165     * @return The phone number formatted with the given formatting type.
1166     *
1167     * @hide
1168     * @deprecated Use link #formatNumber(String phoneNumber, String defaultCountryIso) instead
1169     */
1170    public static String formatNumber(String source, int defaultFormattingType) {
1171        SpannableStringBuilder text = new SpannableStringBuilder(source);
1172        formatNumber(text, defaultFormattingType);
1173        return text.toString();
1174    }
1175
1176    /**
1177     * Returns the phone number formatting type for the given locale.
1178     *
1179     * @param locale The locale of interest, usually {@link Locale#getDefault()}
1180     * @return The formatting type for the given locale, or FORMAT_UNKNOWN if the formatting
1181     * rules are not known for the given locale
1182     *
1183     * @deprecated Use link #formatNumber(String phoneNumber, String defaultCountryIso) instead
1184     */
1185    public static int getFormatTypeForLocale(Locale locale) {
1186        String country = locale.getCountry();
1187
1188        return getFormatTypeFromCountryCode(country);
1189    }
1190
1191    /**
1192     * Formats a phone number in-place. Currently {@link #FORMAT_JAPAN} and {@link #FORMAT_NANP}
1193     * is supported as a second argument.
1194     *
1195     * @param text The number to be formatted, will be modified with the formatting
1196     * @param defaultFormattingType The default formatting rules to apply if the number does
1197     * not begin with +[country_code]
1198     *
1199     * @deprecated Use link #formatNumber(String phoneNumber, String defaultCountryIso) instead
1200     */
1201    public static void formatNumber(Editable text, int defaultFormattingType) {
1202        int formatType = defaultFormattingType;
1203
1204        if (text.length() > 2 && text.charAt(0) == '+') {
1205            if (text.charAt(1) == '1') {
1206                formatType = FORMAT_NANP;
1207            } else if (text.length() >= 3 && text.charAt(1) == '8'
1208                && text.charAt(2) == '1') {
1209                formatType = FORMAT_JAPAN;
1210            } else {
1211                formatType = FORMAT_UNKNOWN;
1212            }
1213        }
1214
1215        switch (formatType) {
1216            case FORMAT_NANP:
1217                formatNanpNumber(text);
1218                return;
1219            case FORMAT_JAPAN:
1220                formatJapaneseNumber(text);
1221                return;
1222            case FORMAT_UNKNOWN:
1223                removeDashes(text);
1224                return;
1225        }
1226    }
1227
1228    private static final int NANP_STATE_DIGIT = 1;
1229    private static final int NANP_STATE_PLUS = 2;
1230    private static final int NANP_STATE_ONE = 3;
1231    private static final int NANP_STATE_DASH = 4;
1232
1233    /**
1234     * Formats a phone number in-place using the NANP formatting rules. Numbers will be formatted
1235     * as:
1236     *
1237     * <p><code>
1238     * xxxxx
1239     * xxx-xxxx
1240     * xxx-xxx-xxxx
1241     * 1-xxx-xxx-xxxx
1242     * +1-xxx-xxx-xxxx
1243     * </code></p>
1244     *
1245     * @param text the number to be formatted, will be modified with the formatting
1246     *
1247     * @deprecated Use link #formatNumber(String phoneNumber, String defaultCountryIso) instead
1248     */
1249    public static void formatNanpNumber(Editable text) {
1250        int length = text.length();
1251        if (length > "+1-nnn-nnn-nnnn".length()) {
1252            // The string is too long to be formatted
1253            return;
1254        } else if (length <= 5) {
1255            // The string is either a shortcode or too short to be formatted
1256            return;
1257        }
1258
1259        CharSequence saved = text.subSequence(0, length);
1260
1261        // Strip the dashes first, as we're going to add them back
1262        removeDashes(text);
1263        length = text.length();
1264
1265        // When scanning the number we record where dashes need to be added,
1266        // if they're non-0 at the end of the scan the dashes will be added in
1267        // the proper places.
1268        int dashPositions[] = new int[3];
1269        int numDashes = 0;
1270
1271        int state = NANP_STATE_DIGIT;
1272        int numDigits = 0;
1273        for (int i = 0; i < length; i++) {
1274            char c = text.charAt(i);
1275            switch (c) {
1276                case '1':
1277                    if (numDigits == 0 || state == NANP_STATE_PLUS) {
1278                        state = NANP_STATE_ONE;
1279                        break;
1280                    }
1281                    // fall through
1282                case '2':
1283                case '3':
1284                case '4':
1285                case '5':
1286                case '6':
1287                case '7':
1288                case '8':
1289                case '9':
1290                case '0':
1291                    if (state == NANP_STATE_PLUS) {
1292                        // Only NANP number supported for now
1293                        text.replace(0, length, saved);
1294                        return;
1295                    } else if (state == NANP_STATE_ONE) {
1296                        // Found either +1 or 1, follow it up with a dash
1297                        dashPositions[numDashes++] = i;
1298                    } else if (state != NANP_STATE_DASH && (numDigits == 3 || numDigits == 6)) {
1299                        // Found a digit that should be after a dash that isn't
1300                        dashPositions[numDashes++] = i;
1301                    }
1302                    state = NANP_STATE_DIGIT;
1303                    numDigits++;
1304                    break;
1305
1306                case '-':
1307                    state = NANP_STATE_DASH;
1308                    break;
1309
1310                case '+':
1311                    if (i == 0) {
1312                        // Plus is only allowed as the first character
1313                        state = NANP_STATE_PLUS;
1314                        break;
1315                    }
1316                    // Fall through
1317                default:
1318                    // Unknown character, bail on formatting
1319                    text.replace(0, length, saved);
1320                    return;
1321            }
1322        }
1323
1324        if (numDigits == 7) {
1325            // With 7 digits we want xxx-xxxx, not xxx-xxx-x
1326            numDashes--;
1327        }
1328
1329        // Actually put the dashes in place
1330        for (int i = 0; i < numDashes; i++) {
1331            int pos = dashPositions[i];
1332            text.replace(pos + i, pos + i, "-");
1333        }
1334
1335        // Remove trailing dashes
1336        int len = text.length();
1337        while (len > 0) {
1338            if (text.charAt(len - 1) == '-') {
1339                text.delete(len - 1, len);
1340                len--;
1341            } else {
1342                break;
1343            }
1344        }
1345    }
1346
1347    /**
1348     * Formats a phone number in-place using the Japanese formatting rules.
1349     * Numbers will be formatted as:
1350     *
1351     * <p><code>
1352     * 03-xxxx-xxxx
1353     * 090-xxxx-xxxx
1354     * 0120-xxx-xxx
1355     * +81-3-xxxx-xxxx
1356     * +81-90-xxxx-xxxx
1357     * </code></p>
1358     *
1359     * @param text the number to be formatted, will be modified with
1360     * the formatting
1361     *
1362     * @deprecated Use link #formatNumber(String phoneNumber, String defaultCountryIso) instead
1363     */
1364    public static void formatJapaneseNumber(Editable text) {
1365        JapanesePhoneNumberFormatter.format(text);
1366    }
1367
1368    /**
1369     * Removes all dashes from the number.
1370     *
1371     * @param text the number to clear from dashes
1372     */
1373    private static void removeDashes(Editable text) {
1374        int p = 0;
1375        while (p < text.length()) {
1376            if (text.charAt(p) == '-') {
1377                text.delete(p, p + 1);
1378           } else {
1379                p++;
1380           }
1381        }
1382    }
1383
1384    /**
1385     * Format the given phoneNumber to the E.164 representation.
1386     * <p>
1387     * The given phone number must have an area code and could have a country
1388     * code.
1389     * <p>
1390     * The defaultCountryIso is used to validate the given number and generate
1391     * the E.164 phone number if the given number doesn't have a country code.
1392     *
1393     * @param phoneNumber
1394     *            the phone number to format
1395     * @param defaultCountryIso
1396     *            the ISO 3166-1 two letters country code
1397     * @return the E.164 representation, or null if the given phone number is
1398     *         not valid.
1399     */
1400    public static String formatNumberToE164(String phoneNumber, String defaultCountryIso) {
1401        PhoneNumberUtil util = PhoneNumberUtil.getInstance();
1402        String result = null;
1403        try {
1404            PhoneNumber pn = util.parse(phoneNumber, defaultCountryIso);
1405            if (util.isValidNumber(pn)) {
1406                result = util.format(pn, PhoneNumberFormat.E164);
1407            }
1408        } catch (NumberParseException e) {
1409        }
1410        return result;
1411    }
1412
1413    /**
1414     * Format a phone number.
1415     * <p>
1416     * If the given number doesn't have the country code, the phone will be
1417     * formatted to the default country's convention.
1418     *
1419     * @param phoneNumber
1420     *            the number to be formatted.
1421     * @param defaultCountryIso
1422     *            the ISO 3166-1 two letters country code whose convention will
1423     *            be used if the given number doesn't have the country code.
1424     * @return the formatted number, or null if the given number is not valid.
1425     */
1426    public static String formatNumber(String phoneNumber, String defaultCountryIso) {
1427        // Do not attempt to format numbers that start with a hash or star symbol.
1428        if (phoneNumber.startsWith("#") || phoneNumber.startsWith("*")) {
1429            return phoneNumber;
1430        }
1431
1432        PhoneNumberUtil util = PhoneNumberUtil.getInstance();
1433        String result = null;
1434        try {
1435            PhoneNumber pn = util.parseAndKeepRawInput(phoneNumber, defaultCountryIso);
1436            result = util.formatInOriginalFormat(pn, defaultCountryIso);
1437        } catch (NumberParseException e) {
1438        }
1439        return result;
1440    }
1441
1442    /**
1443     * Format the phone number only if the given number hasn't been formatted.
1444     * <p>
1445     * The number which has only dailable character is treated as not being
1446     * formatted.
1447     *
1448     * @param phoneNumber
1449     *            the number to be formatted.
1450     * @param phoneNumberE164
1451     *            the E164 format number whose country code is used if the given
1452     *            phoneNumber doesn't have the country code.
1453     * @param defaultCountryIso
1454     *            the ISO 3166-1 two letters country code whose convention will
1455     *            be used if the phoneNumberE164 is null or invalid, or if phoneNumber
1456     *            contains IDD.
1457     * @return the formatted number if the given number has been formatted,
1458     *            otherwise, return the given number.
1459     */
1460    public static String formatNumber(
1461            String phoneNumber, String phoneNumberE164, String defaultCountryIso) {
1462        int len = phoneNumber.length();
1463        for (int i = 0; i < len; i++) {
1464            if (!isDialable(phoneNumber.charAt(i))) {
1465                return phoneNumber;
1466            }
1467        }
1468        PhoneNumberUtil util = PhoneNumberUtil.getInstance();
1469        // Get the country code from phoneNumberE164
1470        if (phoneNumberE164 != null && phoneNumberE164.length() >= 2
1471                && phoneNumberE164.charAt(0) == '+') {
1472            try {
1473                // The number to be parsed is in E164 format, so the default region used doesn't
1474                // matter.
1475                PhoneNumber pn = util.parse(phoneNumberE164, "ZZ");
1476                String regionCode = util.getRegionCodeForNumber(pn);
1477                if (!TextUtils.isEmpty(regionCode) &&
1478                    // This makes sure phoneNumber doesn't contain an IDD
1479                    normalizeNumber(phoneNumber).indexOf(phoneNumberE164.substring(1)) <= 0) {
1480                    defaultCountryIso = regionCode;
1481                }
1482            } catch (NumberParseException e) {
1483            }
1484        }
1485        String result = formatNumber(phoneNumber, defaultCountryIso);
1486        return result != null ? result : phoneNumber;
1487    }
1488
1489    /**
1490     * Normalize a phone number by removing the characters other than digits. If
1491     * the given number has keypad letters, the letters will be converted to
1492     * digits first.
1493     *
1494     * @param phoneNumber the number to be normalized.
1495     * @return the normalized number.
1496     */
1497    public static String normalizeNumber(String phoneNumber) {
1498        if (TextUtils.isEmpty(phoneNumber)) {
1499            return "";
1500        }
1501
1502        StringBuilder sb = new StringBuilder();
1503        int len = phoneNumber.length();
1504        for (int i = 0; i < len; i++) {
1505            char c = phoneNumber.charAt(i);
1506            // Character.digit() supports ASCII and Unicode digits (fullwidth, Arabic-Indic, etc.)
1507            int digit = Character.digit(c, 10);
1508            if (digit != -1) {
1509                sb.append(digit);
1510            } else if (i == 0 && c == '+') {
1511                sb.append(c);
1512            } else if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) {
1513                return normalizeNumber(PhoneNumberUtils.convertKeypadLettersToDigits(phoneNumber));
1514            }
1515        }
1516        return sb.toString();
1517    }
1518
1519    /**
1520     * Replaces all unicode(e.g. Arabic, Persian) digits with their decimal digit equivalents.
1521     *
1522     * @param number the number to perform the replacement on.
1523     * @return the replaced number.
1524     */
1525    public static String replaceUnicodeDigits(String number) {
1526        StringBuilder normalizedDigits = new StringBuilder(number.length());
1527        for (char c : number.toCharArray()) {
1528            int digit = Character.digit(c, 10);
1529            if (digit != -1) {
1530                normalizedDigits.append(digit);
1531            } else {
1532                normalizedDigits.append(c);
1533            }
1534        }
1535        return normalizedDigits.toString();
1536    }
1537
1538    // Three and four digit phone numbers for either special services,
1539    // or 3-6 digit addresses from the network (eg carrier-originated SMS messages) should
1540    // not match.
1541    //
1542    // This constant used to be 5, but SMS short codes has increased in length and
1543    // can be easily 6 digits now days. Most countries have SMS short code length between
1544    // 3 to 6 digits. The exceptions are
1545    //
1546    // Australia: Short codes are six or eight digits in length, starting with the prefix "19"
1547    //            followed by an additional four or six digits and two.
1548    // Czech Republic: Codes are seven digits in length for MO and five (not billed) or
1549    //            eight (billed) for MT direction
1550    //
1551    // see http://en.wikipedia.org/wiki/Short_code#Regional_differences for reference
1552    //
1553    // However, in order to loose match 650-555-1212 and 555-1212, we need to set the min match
1554    // to 7.
1555    static final int MIN_MATCH = 7;
1556
1557    /**
1558     * Checks a given number against the list of
1559     * emergency numbers provided by the RIL and SIM card.
1560     *
1561     * @param number the number to look up.
1562     * @return true if the number is in the list of emergency numbers
1563     *         listed in the RIL / SIM, otherwise return false.
1564     */
1565    public static boolean isEmergencyNumber(String number) {
1566        return isEmergencyNumber(getDefaultVoiceSubId(), number);
1567    }
1568
1569    /**
1570     * Checks a given number against the list of
1571     * emergency numbers provided by the RIL and SIM card.
1572     *
1573     * @param subId the subscription id of the SIM.
1574     * @param number the number to look up.
1575     * @return true if the number is in the list of emergency numbers
1576     *         listed in the RIL / SIM, otherwise return false.
1577     * @hide
1578     */
1579    public static boolean isEmergencyNumber(long subId, String number) {
1580        // Return true only if the specified number *exactly* matches
1581        // one of the emergency numbers listed by the RIL / SIM.
1582        return isEmergencyNumberInternal(subId, number, true /* useExactMatch */);
1583    }
1584
1585    /**
1586     * Checks if given number might *potentially* result in
1587     * a call to an emergency service on the current network.
1588     *
1589     * Specifically, this method will return true if the specified number
1590     * is an emergency number according to the list managed by the RIL or
1591     * SIM, *or* if the specified number simply starts with the same
1592     * digits as any of the emergency numbers listed in the RIL / SIM.
1593     *
1594     * This method is intended for internal use by the phone app when
1595     * deciding whether to allow ACTION_CALL intents from 3rd party apps
1596     * (where we're required to *not* allow emergency calls to be placed.)
1597     *
1598     * @param number the number to look up.
1599     * @return true if the number is in the list of emergency numbers
1600     *         listed in the RIL / SIM, *or* if the number starts with the
1601     *         same digits as any of those emergency numbers.
1602     *
1603     * @hide
1604     */
1605    public static boolean isPotentialEmergencyNumber(String number) {
1606        return isPotentialEmergencyNumber(getDefaultVoiceSubId(), number);
1607    }
1608
1609    /**
1610     * Checks if given number might *potentially* result in
1611     * a call to an emergency service on the current network.
1612     *
1613     * Specifically, this method will return true if the specified number
1614     * is an emergency number according to the list managed by the RIL or
1615     * SIM, *or* if the specified number simply starts with the same
1616     * digits as any of the emergency numbers listed in the RIL / SIM.
1617     *
1618     * This method is intended for internal use by the phone app when
1619     * deciding whether to allow ACTION_CALL intents from 3rd party apps
1620     * (where we're required to *not* allow emergency calls to be placed.)
1621     *
1622     * @param subId the subscription id of the SIM.
1623     * @param number the number to look up.
1624     * @return true if the number is in the list of emergency numbers
1625     *         listed in the RIL / SIM, *or* if the number starts with the
1626     *         same digits as any of those emergency numbers.
1627     * @hide
1628     */
1629    public static boolean isPotentialEmergencyNumber(long subId, String number) {
1630        // Check against the emergency numbers listed by the RIL / SIM,
1631        // and *don't* require an exact match.
1632        return isEmergencyNumberInternal(subId, number, false /* useExactMatch */);
1633    }
1634
1635    /**
1636     * Helper function for isEmergencyNumber(String) and
1637     * isPotentialEmergencyNumber(String).
1638     *
1639     * @param number the number to look up.
1640     *
1641     * @param useExactMatch if true, consider a number to be an emergency
1642     *           number only if it *exactly* matches a number listed in
1643     *           the RIL / SIM.  If false, a number is considered to be an
1644     *           emergency number if it simply starts with the same digits
1645     *           as any of the emergency numbers listed in the RIL / SIM.
1646     *           (Setting useExactMatch to false allows you to identify
1647     *           number that could *potentially* result in emergency calls
1648     *           since many networks will actually ignore trailing digits
1649     *           after a valid emergency number.)
1650     *
1651     * @return true if the number is in the list of emergency numbers
1652     *         listed in the RIL / sim, otherwise return false.
1653     */
1654    private static boolean isEmergencyNumberInternal(String number, boolean useExactMatch) {
1655        return isEmergencyNumberInternal(getDefaultVoiceSubId(), number, useExactMatch);
1656    }
1657
1658    /**
1659     * Helper function for isEmergencyNumber(String) and
1660     * isPotentialEmergencyNumber(String).
1661     *
1662     * @param subId the subscription id of the SIM.
1663     * @param number the number to look up.
1664     *
1665     * @param useExactMatch if true, consider a number to be an emergency
1666     *           number only if it *exactly* matches a number listed in
1667     *           the RIL / SIM.  If false, a number is considered to be an
1668     *           emergency number if it simply starts with the same digits
1669     *           as any of the emergency numbers listed in the RIL / SIM.
1670     *           (Setting useExactMatch to false allows you to identify
1671     *           number that could *potentially* result in emergency calls
1672     *           since many networks will actually ignore trailing digits
1673     *           after a valid emergency number.)
1674     *
1675     * @return true if the number is in the list of emergency numbers
1676     *         listed in the RIL / sim, otherwise return false.
1677     */
1678    private static boolean isEmergencyNumberInternal(long subId, String number,
1679            boolean useExactMatch) {
1680        return isEmergencyNumberInternal(subId, number, null, useExactMatch);
1681    }
1682
1683    /**
1684     * Checks if a given number is an emergency number for a specific country.
1685     *
1686     * @param number the number to look up.
1687     * @param defaultCountryIso the specific country which the number should be checked against
1688     * @return if the number is an emergency number for the specific country, then return true,
1689     * otherwise false
1690     *
1691     * @hide
1692     */
1693    public static boolean isEmergencyNumber(String number, String defaultCountryIso) {
1694            return isEmergencyNumber(getDefaultVoiceSubId(), number, defaultCountryIso);
1695    }
1696
1697    /**
1698     * Checks if a given number is an emergency number for a specific country.
1699     *
1700     * @param subId the subscription id of the SIM.
1701     * @param number the number to look up.
1702     * @param defaultCountryIso the specific country which the number should be checked against
1703     * @return if the number is an emergency number for the specific country, then return true,
1704     * otherwise false
1705     * @hide
1706     */
1707    public static boolean isEmergencyNumber(long subId, String number, String defaultCountryIso) {
1708        return isEmergencyNumberInternal(subId, number,
1709                                         defaultCountryIso,
1710                                         true /* useExactMatch */);
1711    }
1712
1713    /**
1714     * Checks if a given number might *potentially* result in a call to an
1715     * emergency service, for a specific country.
1716     *
1717     * Specifically, this method will return true if the specified number
1718     * is an emergency number in the specified country, *or* if the number
1719     * simply starts with the same digits as any emergency number for that
1720     * country.
1721     *
1722     * This method is intended for internal use by the phone app when
1723     * deciding whether to allow ACTION_CALL intents from 3rd party apps
1724     * (where we're required to *not* allow emergency calls to be placed.)
1725     *
1726     * @param number the number to look up.
1727     * @param defaultCountryIso the specific country which the number should be checked against
1728     * @return true if the number is an emergency number for the specific
1729     *         country, *or* if the number starts with the same digits as
1730     *         any of those emergency numbers.
1731     *
1732     * @hide
1733     */
1734    public static boolean isPotentialEmergencyNumber(String number, String defaultCountryIso) {
1735        return isPotentialEmergencyNumber(getDefaultVoiceSubId(), number, defaultCountryIso);
1736    }
1737
1738    /**
1739     * Checks if a given number might *potentially* result in a call to an
1740     * emergency service, for a specific country.
1741     *
1742     * Specifically, this method will return true if the specified number
1743     * is an emergency number in the specified country, *or* if the number
1744     * simply starts with the same digits as any emergency number for that
1745     * country.
1746     *
1747     * This method is intended for internal use by the phone app when
1748     * deciding whether to allow ACTION_CALL intents from 3rd party apps
1749     * (where we're required to *not* allow emergency calls to be placed.)
1750     *
1751     * @param subId the subscription id of the SIM.
1752     * @param number the number to look up.
1753     * @param defaultCountryIso the specific country which the number should be checked against
1754     * @return true if the number is an emergency number for the specific
1755     *         country, *or* if the number starts with the same digits as
1756     *         any of those emergency numbers.
1757     * @hide
1758     */
1759    public static boolean isPotentialEmergencyNumber(long subId, String number,
1760            String defaultCountryIso) {
1761        return isEmergencyNumberInternal(subId, number,
1762                                         defaultCountryIso,
1763                                         false /* useExactMatch */);
1764    }
1765
1766    /**
1767     * Helper function for isEmergencyNumber(String, String) and
1768     * isPotentialEmergencyNumber(String, String).
1769     *
1770     * @param number the number to look up.
1771     * @param defaultCountryIso the specific country which the number should be checked against
1772     * @param useExactMatch if true, consider a number to be an emergency
1773     *           number only if it *exactly* matches a number listed in
1774     *           the RIL / SIM.  If false, a number is considered to be an
1775     *           emergency number if it simply starts with the same digits
1776     *           as any of the emergency numbers listed in the RIL / SIM.
1777     *
1778     * @return true if the number is an emergency number for the specified country.
1779     */
1780    private static boolean isEmergencyNumberInternal(String number,
1781                                                     String defaultCountryIso,
1782                                                     boolean useExactMatch) {
1783        return isEmergencyNumberInternal(getDefaultVoiceSubId(), number, defaultCountryIso,
1784                useExactMatch);
1785    }
1786
1787    /**
1788     * Helper function for isEmergencyNumber(String, String) and
1789     * isPotentialEmergencyNumber(String, String).
1790     *
1791     * @param subId the subscription id of the SIM.
1792     * @param number the number to look up.
1793     * @param defaultCountryIso the specific country which the number should be checked against
1794     * @param useExactMatch if true, consider a number to be an emergency
1795     *           number only if it *exactly* matches a number listed in
1796     *           the RIL / SIM.  If false, a number is considered to be an
1797     *           emergency number if it simply starts with the same digits
1798     *           as any of the emergency numbers listed in the RIL / SIM.
1799     *
1800     * @return true if the number is an emergency number for the specified country.
1801     * @hide
1802     */
1803    private static boolean isEmergencyNumberInternal(long subId, String number,
1804                                                     String defaultCountryIso,
1805                                                     boolean useExactMatch) {
1806        // If the number passed in is null, just return false:
1807        if (number == null) return false;
1808
1809        // If the number passed in is a SIP address, return false, since the
1810        // concept of "emergency numbers" is only meaningful for calls placed
1811        // over the cell network.
1812        // (Be sure to do this check *before* calling extractNetworkPortionAlt(),
1813        // since the whole point of extractNetworkPortionAlt() is to filter out
1814        // any non-dialable characters (which would turn 'abc911def@example.com'
1815        // into '911', for example.))
1816        if (isUriNumber(number)) {
1817            return false;
1818        }
1819
1820        // Strip the separators from the number before comparing it
1821        // to the list.
1822        number = extractNetworkPortionAlt(number);
1823
1824        String numbers = "";
1825        int slotId = SubscriptionManager.getSlotId(subId);
1826        // retrieve the list of emergency numbers
1827        // check read-write ecclist property first
1828        String ecclist = (slotId == 0) ? "ril.ecclist" : ("ril.ecclist" + slotId);
1829
1830        numbers = SystemProperties.get(ecclist);
1831
1832        if (TextUtils.isEmpty(numbers)) {
1833            // then read-only ecclist property since old RIL only uses this
1834            numbers = SystemProperties.get("ro.ril.ecclist");
1835        }
1836
1837        if (!TextUtils.isEmpty(numbers)) {
1838            // searches through the comma-separated list for a match,
1839            // return true if one is found.
1840            for (String emergencyNum : numbers.split(",")) {
1841                // It is not possible to append additional digits to an emergency number to dial
1842                // the number in Brazil - it won't connect.
1843                if (useExactMatch || "BR".equalsIgnoreCase(defaultCountryIso)) {
1844                    if (number.equals(emergencyNum)) {
1845                        return true;
1846                    }
1847                } else {
1848                    if (number.startsWith(emergencyNum)) {
1849                        return true;
1850                    }
1851                }
1852            }
1853            // no matches found against the list!
1854            return false;
1855        }
1856
1857        Rlog.d(LOG_TAG, "System property doesn't provide any emergency numbers."
1858                + " Use embedded logic for determining ones.");
1859
1860        // No ecclist system property, so use our own list.
1861        if (defaultCountryIso != null) {
1862            ShortNumberUtil util = new ShortNumberUtil();
1863            if (useExactMatch) {
1864                return util.isEmergencyNumber(number, defaultCountryIso);
1865            } else {
1866                return util.connectsToEmergencyNumber(number, defaultCountryIso);
1867            }
1868        } else {
1869            if (useExactMatch) {
1870                return (number.equals("112") || number.equals("911"));
1871            } else {
1872                return (number.startsWith("112") || number.startsWith("911"));
1873            }
1874        }
1875    }
1876
1877    /**
1878     * Checks if a given number is an emergency number for the country that the user is in.
1879     *
1880     * @param number the number to look up.
1881     * @param context the specific context which the number should be checked against
1882     * @return true if the specified number is an emergency number for the country the user
1883     * is currently in.
1884     */
1885    public static boolean isLocalEmergencyNumber(Context context, String number) {
1886        return isLocalEmergencyNumber(context, getDefaultVoiceSubId(), number);
1887    }
1888
1889    /**
1890     * Checks if a given number is an emergency number for the country that the user is in.
1891     *
1892     * @param subId the subscription id of the SIM.
1893     * @param number the number to look up.
1894     * @param context the specific context which the number should be checked against
1895     * @return true if the specified number is an emergency number for the country the user
1896     * is currently in.
1897     * @hide
1898     */
1899    public static boolean isLocalEmergencyNumber(Context context, long subId, String number) {
1900        return isLocalEmergencyNumberInternal(subId, number,
1901                                              context,
1902                                              true /* useExactMatch */);
1903    }
1904
1905    /**
1906     * Checks if a given number might *potentially* result in a call to an
1907     * emergency service, for the country that the user is in. The current
1908     * country is determined using the CountryDetector.
1909     *
1910     * Specifically, this method will return true if the specified number
1911     * is an emergency number in the current country, *or* if the number
1912     * simply starts with the same digits as any emergency number for the
1913     * current country.
1914     *
1915     * This method is intended for internal use by the phone app when
1916     * deciding whether to allow ACTION_CALL intents from 3rd party apps
1917     * (where we're required to *not* allow emergency calls to be placed.)
1918     *
1919     * @param number the number to look up.
1920     * @param context the specific context which the number should be checked against
1921     * @return true if the specified number is an emergency number for a local country, based on the
1922     *              CountryDetector.
1923     *
1924     * @see android.location.CountryDetector
1925     * @hide
1926     */
1927    public static boolean isPotentialLocalEmergencyNumber(Context context, String number) {
1928        return isPotentialLocalEmergencyNumber(context, getDefaultVoiceSubId(), number);
1929    }
1930
1931    /**
1932     * Checks if a given number might *potentially* result in a call to an
1933     * emergency service, for the country that the user is in. The current
1934     * country is determined using the CountryDetector.
1935     *
1936     * Specifically, this method will return true if the specified number
1937     * is an emergency number in the current country, *or* if the number
1938     * simply starts with the same digits as any emergency number for the
1939     * current country.
1940     *
1941     * This method is intended for internal use by the phone app when
1942     * deciding whether to allow ACTION_CALL intents from 3rd party apps
1943     * (where we're required to *not* allow emergency calls to be placed.)
1944     *
1945     * @param subId the subscription id of the SIM.
1946     * @param number the number to look up.
1947     * @param context the specific context which the number should be checked against
1948     * @return true if the specified number is an emergency number for a local country, based on the
1949     *              CountryDetector.
1950     *
1951     * @hide
1952     */
1953    public static boolean isPotentialLocalEmergencyNumber(Context context, long subId,
1954            String number) {
1955        return isLocalEmergencyNumberInternal(subId, number,
1956                                              context,
1957                                              false /* useExactMatch */);
1958    }
1959
1960    /**
1961     * Helper function for isLocalEmergencyNumber() and
1962     * isPotentialLocalEmergencyNumber().
1963     *
1964     * @param number the number to look up.
1965     * @param context the specific context which the number should be checked against
1966     * @param useExactMatch if true, consider a number to be an emergency
1967     *           number only if it *exactly* matches a number listed in
1968     *           the RIL / SIM.  If false, a number is considered to be an
1969     *           emergency number if it simply starts with the same digits
1970     *           as any of the emergency numbers listed in the RIL / SIM.
1971     *
1972     * @return true if the specified number is an emergency number for a
1973     *              local country, based on the CountryDetector.
1974     *
1975     * @see android.location.CountryDetector
1976     * @hide
1977     */
1978    private static boolean isLocalEmergencyNumberInternal(String number,
1979                                                          Context context,
1980                                                          boolean useExactMatch) {
1981        return isLocalEmergencyNumberInternal(getDefaultVoiceSubId(), number, context,
1982                useExactMatch);
1983    }
1984
1985    /**
1986     * Helper function for isLocalEmergencyNumber() and
1987     * isPotentialLocalEmergencyNumber().
1988     *
1989     * @param subId the subscription id of the SIM.
1990     * @param number the number to look up.
1991     * @param context the specific context which the number should be checked against
1992     * @param useExactMatch if true, consider a number to be an emergency
1993     *           number only if it *exactly* matches a number listed in
1994     *           the RIL / SIM.  If false, a number is considered to be an
1995     *           emergency number if it simply starts with the same digits
1996     *           as any of the emergency numbers listed in the RIL / SIM.
1997     *
1998     * @return true if the specified number is an emergency number for a
1999     *              local country, based on the CountryDetector.
2000     * @hide
2001     */
2002    private static boolean isLocalEmergencyNumberInternal(long subId, String number,
2003                                                          Context context,
2004                                                          boolean useExactMatch) {
2005        String countryIso;
2006        CountryDetector detector = (CountryDetector) context.getSystemService(
2007                Context.COUNTRY_DETECTOR);
2008        if (detector != null && detector.detectCountry() != null) {
2009            countryIso = detector.detectCountry().getCountryIso();
2010        } else {
2011            Locale locale = context.getResources().getConfiguration().locale;
2012            countryIso = locale.getCountry();
2013            Rlog.w(LOG_TAG, "No CountryDetector; falling back to countryIso based on locale: "
2014                    + countryIso);
2015        }
2016        return isEmergencyNumberInternal(subId, number, countryIso, useExactMatch);
2017    }
2018
2019    /**
2020     * isVoiceMailNumber: checks a given number against the voicemail
2021     *   number provided by the RIL and SIM card. The caller must have
2022     *   the READ_PHONE_STATE credential.
2023     *
2024     * @param number the number to look up.
2025     * @return true if the number is in the list of voicemail. False
2026     * otherwise, including if the caller does not have the permission
2027     * to read the VM number.
2028     */
2029    public static boolean isVoiceMailNumber(String number) {
2030        return isVoiceMailNumber(SubscriptionManager.getDefaultSubId(), number);
2031    }
2032
2033    /**
2034     * isVoiceMailNumber: checks a given number against the voicemail
2035     *   number provided by the RIL and SIM card. The caller must have
2036     *   the READ_PHONE_STATE credential.
2037     *
2038     * @param subId the subscription id of the SIM.
2039     * @param number the number to look up.
2040     * @return true if the number is in the list of voicemail. False
2041     * otherwise, including if the caller does not have the permission
2042     * to read the VM number.
2043     * @hide
2044     */
2045    public static boolean isVoiceMailNumber(long subId, String number) {
2046        String vmNumber;
2047
2048        try {
2049            vmNumber = TelephonyManager.getDefault().getVoiceMailNumber(subId);
2050        } catch (SecurityException ex) {
2051            return false;
2052        }
2053
2054        // Strip the separators from the number before comparing it
2055        // to the list.
2056        number = extractNetworkPortionAlt(number);
2057
2058        // compare tolerates null so we need to make sure that we
2059        // don't return true when both are null.
2060        return !TextUtils.isEmpty(number) && compare(number, vmNumber);
2061    }
2062
2063    /**
2064     * Translates any alphabetic letters (i.e. [A-Za-z]) in the
2065     * specified phone number into the equivalent numeric digits,
2066     * according to the phone keypad letter mapping described in
2067     * ITU E.161 and ISO/IEC 9995-8.
2068     *
2069     * @return the input string, with alpha letters converted to numeric
2070     *         digits using the phone keypad letter mapping.  For example,
2071     *         an input of "1-800-GOOG-411" will return "1-800-4664-411".
2072     */
2073    public static String convertKeypadLettersToDigits(String input) {
2074        if (input == null) {
2075            return input;
2076        }
2077        int len = input.length();
2078        if (len == 0) {
2079            return input;
2080        }
2081
2082        char[] out = input.toCharArray();
2083
2084        for (int i = 0; i < len; i++) {
2085            char c = out[i];
2086            // If this char isn't in KEYPAD_MAP at all, just leave it alone.
2087            out[i] = (char) KEYPAD_MAP.get(c, c);
2088        }
2089
2090        return new String(out);
2091    }
2092
2093    /**
2094     * The phone keypad letter mapping (see ITU E.161 or ISO/IEC 9995-8.)
2095     * TODO: This should come from a resource.
2096     */
2097    private static final SparseIntArray KEYPAD_MAP = new SparseIntArray();
2098    static {
2099        KEYPAD_MAP.put('a', '2'); KEYPAD_MAP.put('b', '2'); KEYPAD_MAP.put('c', '2');
2100        KEYPAD_MAP.put('A', '2'); KEYPAD_MAP.put('B', '2'); KEYPAD_MAP.put('C', '2');
2101
2102        KEYPAD_MAP.put('d', '3'); KEYPAD_MAP.put('e', '3'); KEYPAD_MAP.put('f', '3');
2103        KEYPAD_MAP.put('D', '3'); KEYPAD_MAP.put('E', '3'); KEYPAD_MAP.put('F', '3');
2104
2105        KEYPAD_MAP.put('g', '4'); KEYPAD_MAP.put('h', '4'); KEYPAD_MAP.put('i', '4');
2106        KEYPAD_MAP.put('G', '4'); KEYPAD_MAP.put('H', '4'); KEYPAD_MAP.put('I', '4');
2107
2108        KEYPAD_MAP.put('j', '5'); KEYPAD_MAP.put('k', '5'); KEYPAD_MAP.put('l', '5');
2109        KEYPAD_MAP.put('J', '5'); KEYPAD_MAP.put('K', '5'); KEYPAD_MAP.put('L', '5');
2110
2111        KEYPAD_MAP.put('m', '6'); KEYPAD_MAP.put('n', '6'); KEYPAD_MAP.put('o', '6');
2112        KEYPAD_MAP.put('M', '6'); KEYPAD_MAP.put('N', '6'); KEYPAD_MAP.put('O', '6');
2113
2114        KEYPAD_MAP.put('p', '7'); KEYPAD_MAP.put('q', '7'); KEYPAD_MAP.put('r', '7'); KEYPAD_MAP.put('s', '7');
2115        KEYPAD_MAP.put('P', '7'); KEYPAD_MAP.put('Q', '7'); KEYPAD_MAP.put('R', '7'); KEYPAD_MAP.put('S', '7');
2116
2117        KEYPAD_MAP.put('t', '8'); KEYPAD_MAP.put('u', '8'); KEYPAD_MAP.put('v', '8');
2118        KEYPAD_MAP.put('T', '8'); KEYPAD_MAP.put('U', '8'); KEYPAD_MAP.put('V', '8');
2119
2120        KEYPAD_MAP.put('w', '9'); KEYPAD_MAP.put('x', '9'); KEYPAD_MAP.put('y', '9'); KEYPAD_MAP.put('z', '9');
2121        KEYPAD_MAP.put('W', '9'); KEYPAD_MAP.put('X', '9'); KEYPAD_MAP.put('Y', '9'); KEYPAD_MAP.put('Z', '9');
2122    }
2123
2124    //================ Plus Code formatting =========================
2125    private static final char PLUS_SIGN_CHAR = '+';
2126    private static final String PLUS_SIGN_STRING = "+";
2127    private static final String NANP_IDP_STRING = "011";
2128    private static final int NANP_LENGTH = 10;
2129
2130    /**
2131     * This function checks if there is a plus sign (+) in the passed-in dialing number.
2132     * If there is, it processes the plus sign based on the default telephone
2133     * numbering plan of the system when the phone is activated and the current
2134     * telephone numbering plan of the system that the phone is camped on.
2135     * Currently, we only support the case that the default and current telephone
2136     * numbering plans are North American Numbering Plan(NANP).
2137     *
2138     * The passed-in dialStr should only contain the valid format as described below,
2139     * 1) the 1st character in the dialStr should be one of the really dialable
2140     *    characters listed below
2141     *    ISO-LATIN characters 0-9, *, # , +
2142     * 2) the dialStr should already strip out the separator characters,
2143     *    every character in the dialStr should be one of the non separator characters
2144     *    listed below
2145     *    ISO-LATIN characters 0-9, *, # , +, WILD, WAIT, PAUSE
2146     *
2147     * Otherwise, this function returns the dial string passed in
2148     *
2149     * @param dialStr the original dial string
2150     * @return the converted dial string if the current/default countries belong to NANP,
2151     * and if there is the "+" in the original dial string. Otherwise, the original dial
2152     * string returns.
2153     *
2154     * This API is for CDMA only
2155     *
2156     * @hide TODO: pending API Council approval
2157     */
2158    public static String cdmaCheckAndProcessPlusCode(String dialStr) {
2159        if (!TextUtils.isEmpty(dialStr)) {
2160            if (isReallyDialable(dialStr.charAt(0)) &&
2161                isNonSeparator(dialStr)) {
2162                String currIso = SystemProperties.get(PROPERTY_OPERATOR_ISO_COUNTRY, "");
2163                String defaultIso = SystemProperties.get(PROPERTY_ICC_OPERATOR_ISO_COUNTRY, "");
2164                if (!TextUtils.isEmpty(currIso) && !TextUtils.isEmpty(defaultIso)) {
2165                    return cdmaCheckAndProcessPlusCodeByNumberFormat(dialStr,
2166                            getFormatTypeFromCountryCode(currIso),
2167                            getFormatTypeFromCountryCode(defaultIso));
2168                }
2169            }
2170        }
2171        return dialStr;
2172    }
2173
2174    /**
2175     * Process phone number for CDMA, converting plus code using the home network number format.
2176     * This is used for outgoing SMS messages.
2177     *
2178     * @param dialStr the original dial string
2179     * @return the converted dial string
2180     * @hide for internal use
2181     */
2182    public static String cdmaCheckAndProcessPlusCodeForSms(String dialStr) {
2183        if (!TextUtils.isEmpty(dialStr)) {
2184            if (isReallyDialable(dialStr.charAt(0)) && isNonSeparator(dialStr)) {
2185                String defaultIso = SystemProperties.get(PROPERTY_ICC_OPERATOR_ISO_COUNTRY, "");
2186                if (!TextUtils.isEmpty(defaultIso)) {
2187                    int format = getFormatTypeFromCountryCode(defaultIso);
2188                    return cdmaCheckAndProcessPlusCodeByNumberFormat(dialStr, format, format);
2189                }
2190            }
2191        }
2192        return dialStr;
2193    }
2194
2195    /**
2196     * This function should be called from checkAndProcessPlusCode only
2197     * And it is used for test purpose also.
2198     *
2199     * It checks the dial string by looping through the network portion,
2200     * post dial portion 1, post dial porting 2, etc. If there is any
2201     * plus sign, then process the plus sign.
2202     * Currently, this function supports the plus sign conversion within NANP only.
2203     * Specifically, it handles the plus sign in the following ways:
2204     * 1)+1NANP,remove +, e.g.
2205     *   +18475797000 is converted to 18475797000,
2206     * 2)+NANP or +non-NANP Numbers,replace + with the current NANP IDP, e.g,
2207     *   +8475797000 is converted to 0118475797000,
2208     *   +11875767800 is converted to 01111875767800
2209     * 3)+1NANP in post dial string(s), e.g.
2210     *   8475797000;+18475231753 is converted to 8475797000;18475231753
2211     *
2212     *
2213     * @param dialStr the original dial string
2214     * @param currFormat the numbering system of the current country that the phone is camped on
2215     * @param defaultFormat the numbering system of the country that the phone is activated on
2216     * @return the converted dial string if the current/default countries belong to NANP,
2217     * and if there is the "+" in the original dial string. Otherwise, the original dial
2218     * string returns.
2219     *
2220     * @hide
2221     */
2222    public static String
2223    cdmaCheckAndProcessPlusCodeByNumberFormat(String dialStr,int currFormat,int defaultFormat) {
2224        String retStr = dialStr;
2225
2226        // Checks if the plus sign character is in the passed-in dial string
2227        if (dialStr != null &&
2228            dialStr.lastIndexOf(PLUS_SIGN_STRING) != -1) {
2229            // Format the string based on the rules for the country the number is from,
2230            // and the current country the phone is camped on.
2231            if ((currFormat == defaultFormat) && (currFormat == FORMAT_NANP)) {
2232                // Handle case where default and current telephone numbering plans are NANP.
2233                String postDialStr = null;
2234                String tempDialStr = dialStr;
2235
2236                // Sets the retStr to null since the conversion will be performed below.
2237                retStr = null;
2238                if (DBG) log("checkAndProcessPlusCode,dialStr=" + dialStr);
2239                // This routine is to process the plus sign in the dial string by loop through
2240                // the network portion, post dial portion 1, post dial portion 2... etc. if
2241                // applied
2242                do {
2243                    String networkDialStr;
2244                    networkDialStr = extractNetworkPortion(tempDialStr);
2245                    // Handles the conversion within NANP
2246                    networkDialStr = processPlusCodeWithinNanp(networkDialStr);
2247
2248                    // Concatenates the string that is converted from network portion
2249                    if (!TextUtils.isEmpty(networkDialStr)) {
2250                        if (retStr == null) {
2251                            retStr = networkDialStr;
2252                        } else {
2253                            retStr = retStr.concat(networkDialStr);
2254                        }
2255                    } else {
2256                        // This should never happen since we checked the if dialStr is null
2257                        // and if it contains the plus sign in the beginning of this function.
2258                        // The plus sign is part of the network portion.
2259                        Rlog.e("checkAndProcessPlusCode: null newDialStr", networkDialStr);
2260                        return dialStr;
2261                    }
2262                    postDialStr = extractPostDialPortion(tempDialStr);
2263                    if (!TextUtils.isEmpty(postDialStr)) {
2264                        int dialableIndex = findDialableIndexFromPostDialStr(postDialStr);
2265
2266                        // dialableIndex should always be greater than 0
2267                        if (dialableIndex >= 1) {
2268                            retStr = appendPwCharBackToOrigDialStr(dialableIndex,
2269                                     retStr,postDialStr);
2270                            // Skips the P/W character, extracts the dialable portion
2271                            tempDialStr = postDialStr.substring(dialableIndex);
2272                        } else {
2273                            // Non-dialable character such as P/W should not be at the end of
2274                            // the dial string after P/W processing in CdmaConnection.java
2275                            // Set the postDialStr to "" to break out of the loop
2276                            if (dialableIndex < 0) {
2277                                postDialStr = "";
2278                            }
2279                            Rlog.e("wrong postDialStr=", postDialStr);
2280                        }
2281                    }
2282                    if (DBG) log("checkAndProcessPlusCode,postDialStr=" + postDialStr);
2283                } while (!TextUtils.isEmpty(postDialStr) && !TextUtils.isEmpty(tempDialStr));
2284            } else {
2285                // TODO: Support NANP international conversion and other telephone numbering plans.
2286                // Currently the phone is never used in non-NANP system, so return the original
2287                // dial string.
2288                Rlog.e("checkAndProcessPlusCode:non-NANP not supported", dialStr);
2289            }
2290        }
2291        return retStr;
2292     }
2293
2294    // This function gets the default international dialing prefix
2295    private static String getDefaultIdp( ) {
2296        String ps = null;
2297        SystemProperties.get(PROPERTY_IDP_STRING, ps);
2298        if (TextUtils.isEmpty(ps)) {
2299            ps = NANP_IDP_STRING;
2300        }
2301        return ps;
2302    }
2303
2304    private static boolean isTwoToNine (char c) {
2305        if (c >= '2' && c <= '9') {
2306            return true;
2307        } else {
2308            return false;
2309        }
2310    }
2311
2312    private static int getFormatTypeFromCountryCode (String country) {
2313        // Check for the NANP countries
2314        int length = NANP_COUNTRIES.length;
2315        for (int i = 0; i < length; i++) {
2316            if (NANP_COUNTRIES[i].compareToIgnoreCase(country) == 0) {
2317                return FORMAT_NANP;
2318            }
2319        }
2320        if ("jp".compareToIgnoreCase(country) == 0) {
2321            return FORMAT_JAPAN;
2322        }
2323        return FORMAT_UNKNOWN;
2324    }
2325
2326    /**
2327     * This function checks if the passed in string conforms to the NANP format
2328     * i.e. NXX-NXX-XXXX, N is any digit 2-9 and X is any digit 0-9
2329     */
2330    private static boolean isNanp (String dialStr) {
2331        boolean retVal = false;
2332        if (dialStr != null) {
2333            if (dialStr.length() == NANP_LENGTH) {
2334                if (isTwoToNine(dialStr.charAt(0)) &&
2335                    isTwoToNine(dialStr.charAt(3))) {
2336                    retVal = true;
2337                    for (int i=1; i<NANP_LENGTH; i++ ) {
2338                        char c=dialStr.charAt(i);
2339                        if (!PhoneNumberUtils.isISODigit(c)) {
2340                            retVal = false;
2341                            break;
2342                        }
2343                    }
2344                }
2345            }
2346        } else {
2347            Rlog.e("isNanp: null dialStr passed in", dialStr);
2348        }
2349        return retVal;
2350    }
2351
2352   /**
2353    * This function checks if the passed in string conforms to 1-NANP format
2354    */
2355    private static boolean isOneNanp(String dialStr) {
2356        boolean retVal = false;
2357        if (dialStr != null) {
2358            String newDialStr = dialStr.substring(1);
2359            if ((dialStr.charAt(0) == '1') && isNanp(newDialStr)) {
2360                retVal = true;
2361            }
2362        } else {
2363            Rlog.e("isOneNanp: null dialStr passed in", dialStr);
2364        }
2365        return retVal;
2366    }
2367
2368    /**
2369     * Determines if the specified number is actually a URI
2370     * (i.e. a SIP address) rather than a regular PSTN phone number,
2371     * based on whether or not the number contains an "@" character.
2372     *
2373     * @hide
2374     * @param number
2375     * @return true if number contains @
2376     */
2377    public static boolean isUriNumber(String number) {
2378        // Note we allow either "@" or "%40" to indicate a URI, in case
2379        // the passed-in string is URI-escaped.  (Neither "@" nor "%40"
2380        // will ever be found in a legal PSTN number.)
2381        return number != null && (number.contains("@") || number.contains("%40"));
2382    }
2383
2384    /**
2385     * @return the "username" part of the specified SIP address,
2386     *         i.e. the part before the "@" character (or "%40").
2387     *
2388     * @param number SIP address of the form "username@domainname"
2389     *               (or the URI-escaped equivalent "username%40domainname")
2390     * @see isUriNumber
2391     *
2392     * @hide
2393     */
2394    public static String getUsernameFromUriNumber(String number) {
2395        // The delimiter between username and domain name can be
2396        // either "@" or "%40" (the URI-escaped equivalent.)
2397        int delimiterIndex = number.indexOf('@');
2398        if (delimiterIndex < 0) {
2399            delimiterIndex = number.indexOf("%40");
2400        }
2401        if (delimiterIndex < 0) {
2402            Rlog.w(LOG_TAG,
2403                  "getUsernameFromUriNumber: no delimiter found in SIP addr '" + number + "'");
2404            delimiterIndex = number.length();
2405        }
2406        return number.substring(0, delimiterIndex);
2407    }
2408
2409    /**
2410     * This function handles the plus code conversion within NANP CDMA network
2411     * If the number format is
2412     * 1)+1NANP,remove +,
2413     * 2)other than +1NANP, any + numbers,replace + with the current IDP
2414     */
2415    private static String processPlusCodeWithinNanp(String networkDialStr) {
2416        String retStr = networkDialStr;
2417
2418        if (DBG) log("processPlusCodeWithinNanp,networkDialStr=" + networkDialStr);
2419        // If there is a plus sign at the beginning of the dial string,
2420        // Convert the plus sign to the default IDP since it's an international number
2421        if (networkDialStr != null &&
2422            networkDialStr.charAt(0) == PLUS_SIGN_CHAR &&
2423            networkDialStr.length() > 1) {
2424            String newStr = networkDialStr.substring(1);
2425            if (isOneNanp(newStr)) {
2426                // Remove the leading plus sign
2427                retStr = newStr;
2428             } else {
2429                 String idpStr = getDefaultIdp();
2430                 // Replaces the plus sign with the default IDP
2431                 retStr = networkDialStr.replaceFirst("[+]", idpStr);
2432            }
2433        }
2434        if (DBG) log("processPlusCodeWithinNanp,retStr=" + retStr);
2435        return retStr;
2436    }
2437
2438    // This function finds the index of the dialable character(s)
2439    // in the post dial string
2440    private static int findDialableIndexFromPostDialStr(String postDialStr) {
2441        for (int index = 0;index < postDialStr.length();index++) {
2442             char c = postDialStr.charAt(index);
2443             if (isReallyDialable(c)) {
2444                return index;
2445             }
2446        }
2447        return -1;
2448    }
2449
2450    // This function appends the non-dialable P/W character to the original
2451    // dial string based on the dialable index passed in
2452    private static String
2453    appendPwCharBackToOrigDialStr(int dialableIndex,String origStr, String dialStr) {
2454        String retStr;
2455
2456        // There is only 1 P/W character before the dialable characters
2457        if (dialableIndex == 1) {
2458            StringBuilder ret = new StringBuilder(origStr);
2459            ret = ret.append(dialStr.charAt(0));
2460            retStr = ret.toString();
2461        } else {
2462            // It means more than 1 P/W characters in the post dial string,
2463            // appends to retStr
2464            String nonDigitStr = dialStr.substring(0,dialableIndex);
2465            retStr = origStr.concat(nonDigitStr);
2466        }
2467        return retStr;
2468    }
2469
2470    //===== Beginning of utility methods used in compareLoosely() =====
2471
2472    /**
2473     * Phone numbers are stored in "lookup" form in the database
2474     * as reversed strings to allow for caller ID lookup
2475     *
2476     * This method takes a phone number and makes a valid SQL "LIKE"
2477     * string that will match the lookup form
2478     *
2479     */
2480    /** all of a up to len must be an international prefix or
2481     *  separators/non-dialing digits
2482     */
2483    private static boolean
2484    matchIntlPrefix(String a, int len) {
2485        /* '([^0-9*#+pwn]\+[^0-9*#+pwn] | [^0-9*#+pwn]0(0|11)[^0-9*#+pwn] )$' */
2486        /*        0       1                           2 3 45               */
2487
2488        int state = 0;
2489        for (int i = 0 ; i < len ; i++) {
2490            char c = a.charAt(i);
2491
2492            switch (state) {
2493                case 0:
2494                    if      (c == '+') state = 1;
2495                    else if (c == '0') state = 2;
2496                    else if (isNonSeparator(c)) return false;
2497                break;
2498
2499                case 2:
2500                    if      (c == '0') state = 3;
2501                    else if (c == '1') state = 4;
2502                    else if (isNonSeparator(c)) return false;
2503                break;
2504
2505                case 4:
2506                    if      (c == '1') state = 5;
2507                    else if (isNonSeparator(c)) return false;
2508                break;
2509
2510                default:
2511                    if (isNonSeparator(c)) return false;
2512                break;
2513
2514            }
2515        }
2516
2517        return state == 1 || state == 3 || state == 5;
2518    }
2519
2520    /** all of 'a' up to len must be a (+|00|011)country code)
2521     *  We're fast and loose with the country code. Any \d{1,3} matches */
2522    private static boolean
2523    matchIntlPrefixAndCC(String a, int len) {
2524        /*  [^0-9*#+pwn]*(\+|0(0|11)\d\d?\d? [^0-9*#+pwn] $ */
2525        /*      0          1 2 3 45  6 7  8                 */
2526
2527        int state = 0;
2528        for (int i = 0 ; i < len ; i++ ) {
2529            char c = a.charAt(i);
2530
2531            switch (state) {
2532                case 0:
2533                    if      (c == '+') state = 1;
2534                    else if (c == '0') state = 2;
2535                    else if (isNonSeparator(c)) return false;
2536                break;
2537
2538                case 2:
2539                    if      (c == '0') state = 3;
2540                    else if (c == '1') state = 4;
2541                    else if (isNonSeparator(c)) return false;
2542                break;
2543
2544                case 4:
2545                    if      (c == '1') state = 5;
2546                    else if (isNonSeparator(c)) return false;
2547                break;
2548
2549                case 1:
2550                case 3:
2551                case 5:
2552                    if      (isISODigit(c)) state = 6;
2553                    else if (isNonSeparator(c)) return false;
2554                break;
2555
2556                case 6:
2557                case 7:
2558                    if      (isISODigit(c)) state++;
2559                    else if (isNonSeparator(c)) return false;
2560                break;
2561
2562                default:
2563                    if (isNonSeparator(c)) return false;
2564            }
2565        }
2566
2567        return state == 6 || state == 7 || state == 8;
2568    }
2569
2570    /** all of 'a' up to len must match non-US trunk prefix ('0') */
2571    private static boolean
2572    matchTrunkPrefix(String a, int len) {
2573        boolean found;
2574
2575        found = false;
2576
2577        for (int i = 0 ; i < len ; i++) {
2578            char c = a.charAt(i);
2579
2580            if (c == '0' && !found) {
2581                found = true;
2582            } else if (isNonSeparator(c)) {
2583                return false;
2584            }
2585        }
2586
2587        return found;
2588    }
2589
2590    //===== End of utility methods used only in compareLoosely() =====
2591
2592    //===== Beginning of utility methods used only in compareStrictly() ====
2593
2594    /*
2595     * If true, the number is country calling code.
2596     */
2597    private static final boolean COUNTRY_CALLING_CALL[] = {
2598        true, true, false, false, false, false, false, true, false, false,
2599        false, false, false, false, false, false, false, false, false, false,
2600        true, false, false, false, false, false, false, true, true, false,
2601        true, true, true, true, true, false, true, false, false, true,
2602        true, false, false, true, true, true, true, true, true, true,
2603        false, true, true, true, true, true, true, true, true, false,
2604        true, true, true, true, true, true, true, false, false, false,
2605        false, false, false, false, false, false, false, false, false, false,
2606        false, true, true, true, true, false, true, false, false, true,
2607        true, true, true, true, true, true, false, false, true, false,
2608    };
2609    private static final int CCC_LENGTH = COUNTRY_CALLING_CALL.length;
2610
2611    /**
2612     * @return true when input is valid Country Calling Code.
2613     */
2614    private static boolean isCountryCallingCode(int countryCallingCodeCandidate) {
2615        return countryCallingCodeCandidate > 0 && countryCallingCodeCandidate < CCC_LENGTH &&
2616                COUNTRY_CALLING_CALL[countryCallingCodeCandidate];
2617    }
2618
2619    /**
2620     * Returns integer corresponding to the input if input "ch" is
2621     * ISO-LATIN characters 0-9.
2622     * Returns -1 otherwise
2623     */
2624    private static int tryGetISODigit(char ch) {
2625        if ('0' <= ch && ch <= '9') {
2626            return ch - '0';
2627        } else {
2628            return -1;
2629        }
2630    }
2631
2632    private static class CountryCallingCodeAndNewIndex {
2633        public final int countryCallingCode;
2634        public final int newIndex;
2635        public CountryCallingCodeAndNewIndex(int countryCode, int newIndex) {
2636            this.countryCallingCode = countryCode;
2637            this.newIndex = newIndex;
2638        }
2639    }
2640
2641    /*
2642     * Note that this function does not strictly care the country calling code with
2643     * 3 length (like Morocco: +212), assuming it is enough to use the first two
2644     * digit to compare two phone numbers.
2645     */
2646    private static CountryCallingCodeAndNewIndex tryGetCountryCallingCodeAndNewIndex(
2647        String str, boolean acceptThailandCase) {
2648        // Rough regexp:
2649        //  ^[^0-9*#+]*((\+|0(0|11)\d\d?|166) [^0-9*#+] $
2650        //         0        1 2 3 45  6 7  89
2651        //
2652        // In all the states, this function ignores separator characters.
2653        // "166" is the special case for the call from Thailand to the US. Uguu!
2654        int state = 0;
2655        int ccc = 0;
2656        final int length = str.length();
2657        for (int i = 0 ; i < length ; i++ ) {
2658            char ch = str.charAt(i);
2659            switch (state) {
2660                case 0:
2661                    if      (ch == '+') state = 1;
2662                    else if (ch == '0') state = 2;
2663                    else if (ch == '1') {
2664                        if (acceptThailandCase) {
2665                            state = 8;
2666                        } else {
2667                            return null;
2668                        }
2669                    } else if (isDialable(ch)) {
2670                        return null;
2671                    }
2672                break;
2673
2674                case 2:
2675                    if      (ch == '0') state = 3;
2676                    else if (ch == '1') state = 4;
2677                    else if (isDialable(ch)) {
2678                        return null;
2679                    }
2680                break;
2681
2682                case 4:
2683                    if      (ch == '1') state = 5;
2684                    else if (isDialable(ch)) {
2685                        return null;
2686                    }
2687                break;
2688
2689                case 1:
2690                case 3:
2691                case 5:
2692                case 6:
2693                case 7:
2694                    {
2695                        int ret = tryGetISODigit(ch);
2696                        if (ret > 0) {
2697                            ccc = ccc * 10 + ret;
2698                            if (ccc >= 100 || isCountryCallingCode(ccc)) {
2699                                return new CountryCallingCodeAndNewIndex(ccc, i + 1);
2700                            }
2701                            if (state == 1 || state == 3 || state == 5) {
2702                                state = 6;
2703                            } else {
2704                                state++;
2705                            }
2706                        } else if (isDialable(ch)) {
2707                            return null;
2708                        }
2709                    }
2710                    break;
2711                case 8:
2712                    if (ch == '6') state = 9;
2713                    else if (isDialable(ch)) {
2714                        return null;
2715                    }
2716                    break;
2717                case 9:
2718                    if (ch == '6') {
2719                        return new CountryCallingCodeAndNewIndex(66, i + 1);
2720                    } else {
2721                        return null;
2722                    }
2723                default:
2724                    return null;
2725            }
2726        }
2727
2728        return null;
2729    }
2730
2731    /**
2732     * Currently this function simply ignore the first digit assuming it is
2733     * trunk prefix. Actually trunk prefix is different in each country.
2734     *
2735     * e.g.
2736     * "+79161234567" equals "89161234567" (Russian trunk digit is 8)
2737     * "+33123456789" equals "0123456789" (French trunk digit is 0)
2738     *
2739     */
2740    private static int tryGetTrunkPrefixOmittedIndex(String str, int currentIndex) {
2741        int length = str.length();
2742        for (int i = currentIndex ; i < length ; i++) {
2743            final char ch = str.charAt(i);
2744            if (tryGetISODigit(ch) >= 0) {
2745                return i + 1;
2746            } else if (isDialable(ch)) {
2747                return -1;
2748            }
2749        }
2750        return -1;
2751    }
2752
2753    /**
2754     * Return true if the prefix of "str" is "ignorable". Here, "ignorable" means
2755     * that "str" has only one digit and separator characters. The one digit is
2756     * assumed to be trunk prefix.
2757     */
2758    private static boolean checkPrefixIsIgnorable(final String str,
2759            int forwardIndex, int backwardIndex) {
2760        boolean trunk_prefix_was_read = false;
2761        while (backwardIndex >= forwardIndex) {
2762            if (tryGetISODigit(str.charAt(backwardIndex)) >= 0) {
2763                if (trunk_prefix_was_read) {
2764                    // More than one digit appeared, meaning that "a" and "b"
2765                    // is different.
2766                    return false;
2767                } else {
2768                    // Ignore just one digit, assuming it is trunk prefix.
2769                    trunk_prefix_was_read = true;
2770                }
2771            } else if (isDialable(str.charAt(backwardIndex))) {
2772                // Trunk prefix is a digit, not "*", "#"...
2773                return false;
2774            }
2775            backwardIndex--;
2776        }
2777
2778        return true;
2779    }
2780
2781    /**
2782     * Returns Default voice subscription Id.
2783     */
2784    private static long getDefaultVoiceSubId() {
2785        return SubscriptionManager.getDefaultVoiceSubId();
2786    }
2787    //==== End of utility methods used only in compareStrictly() =====
2788}
2789