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