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