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