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