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