PhoneNumberUtils.java revision 47633f86279bc1854660d95a0b02bfa5ff14ad04
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 android.content.Context;
20import android.content.Intent;
21import android.database.Cursor;
22import android.net.Uri;
23import android.os.SystemProperties;
24import android.provider.Contacts;
25import android.provider.ContactsContract;
26import android.text.Editable;
27import android.text.SpannableStringBuilder;
28import android.text.TextUtils;
29import android.util.Log;
30import android.util.SparseIntArray;
31
32import static com.android.internal.telephony.TelephonyProperties.PROPERTY_ICC_OPERATOR_ISO_COUNTRY;
33import static com.android.internal.telephony.TelephonyProperties.PROPERTY_IDP_STRING;
34import static com.android.internal.telephony.TelephonyProperties.PROPERTY_OPERATOR_ISO_COUNTRY;
35
36import java.util.Locale;
37import java.util.regex.Matcher;
38import java.util.regex.Pattern;
39
40/**
41 * Various utilities for dealing with phone number strings.
42 */
43public class PhoneNumberUtils
44{
45    /*
46     * Special characters
47     *
48     * (See "What is a phone number?" doc)
49     * 'p' --- GSM pause character, same as comma
50     * 'n' --- GSM wild character
51     * 'w' --- GSM wait character
52     */
53    public static final char PAUSE = ',';
54    public static final char WAIT = ';';
55    public static final char WILD = 'N';
56
57    /*
58     * TOA = TON + NPI
59     * See TS 24.008 section 10.5.4.7 for details.
60     * These are the only really useful TOA values
61     */
62    public static final int TOA_International = 0x91;
63    public static final int TOA_Unknown = 0x81;
64
65    static final String LOG_TAG = "PhoneNumberUtils";
66    private static final boolean DBG = false;
67
68    /*
69     * global-phone-number = ["+"] 1*( DIGIT / written-sep )
70     * written-sep         = ("-"/".")
71     */
72    private static final Pattern GLOBAL_PHONE_NUMBER_PATTERN =
73            Pattern.compile("[\\+]?[0-9.-]+");
74
75    /** True if c is ISO-LATIN characters 0-9 */
76    public static boolean
77    isISODigit (char c) {
78        return c >= '0' && c <= '9';
79    }
80
81    /** True if c is ISO-LATIN characters 0-9, *, # */
82    public final static boolean
83    is12Key(char c) {
84        return (c >= '0' && c <= '9') || c == '*' || c == '#';
85    }
86
87    /** True if c is ISO-LATIN characters 0-9, *, # , +, WILD  */
88    public final static boolean
89    isDialable(char c) {
90        return (c >= '0' && c <= '9') || c == '*' || c == '#' || c == '+' || c == WILD;
91    }
92
93    /** True if c is ISO-LATIN characters 0-9, *, # , + (no WILD)  */
94    public final static boolean
95    isReallyDialable(char c) {
96        return (c >= '0' && c <= '9') || c == '*' || c == '#' || c == '+';
97    }
98
99    /** True if c is ISO-LATIN characters 0-9, *, # , +, WILD, WAIT, PAUSE   */
100    public final static boolean
101    isNonSeparator(char c) {
102        return (c >= '0' && c <= '9') || c == '*' || c == '#' || c == '+'
103                || c == WILD || c == WAIT || c == PAUSE;
104    }
105
106    /** This any anything to the right of this char is part of the
107     *  post-dial string (eg this is PAUSE or WAIT)
108     */
109    public final static boolean
110    isStartsPostDial (char c) {
111        return c == PAUSE || c == WAIT;
112    }
113
114    /** Returns true if ch is not dialable or alpha char */
115    private static boolean isSeparator(char ch) {
116        return !isDialable(ch) && !(('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z'));
117    }
118
119    /** Extracts the phone number from an Intent.
120     *
121     * @param intent the intent to get the number of
122     * @param context a context to use for database access
123     *
124     * @return the phone number that would be called by the intent, or
125     *         <code>null</code> if the number cannot be found.
126     */
127    public static String getNumberFromIntent(Intent intent, Context context) {
128        String number = null;
129
130        Uri uri = intent.getData();
131        String scheme = uri.getScheme();
132
133        if (scheme.equals("tel")) {
134            return uri.getSchemeSpecificPart();
135        }
136
137        // TODO: We don't check for SecurityException here (requires
138        // READ_PHONE_STATE permission).
139        if (scheme.equals("voicemail")) {
140            return TelephonyManager.getDefault().getVoiceMailNumber();
141        }
142
143        if (context == null) {
144            return null;
145        }
146
147        String type = intent.resolveType(context);
148        String phoneColumn = null;
149
150        // Correctly read out the phone entry based on requested provider
151        final String authority = uri.getAuthority();
152        if (Contacts.AUTHORITY.equals(authority)) {
153            phoneColumn = Contacts.People.Phones.NUMBER;
154        } else if (ContactsContract.AUTHORITY.equals(authority)) {
155            phoneColumn = ContactsContract.CommonDataKinds.Phone.NUMBER;
156        }
157
158        final Cursor c = context.getContentResolver().query(uri, new String[] {
159            phoneColumn
160        }, null, null, null);
161        if (c != null) {
162            try {
163                if (c.moveToFirst()) {
164                    number = c.getString(c.getColumnIndex(phoneColumn));
165                }
166            } finally {
167                c.close();
168            }
169        }
170
171        return number;
172    }
173
174    /** Extracts the network address portion and canonicalizes
175     *  (filters out separators.)
176     *  Network address portion is everything up to DTMF control digit
177     *  separators (pause or wait), but without non-dialable characters.
178     *
179     *  Please note that the GSM wild character is allowed in the result.
180     *  This must be resolved before dialing.
181     *
182     *  Allows + only in the first  position in the result string.
183     *
184     *  Returns null if phoneNumber == null
185     */
186    public static String
187    extractNetworkPortion(String phoneNumber) {
188        if (phoneNumber == null) {
189            return null;
190        }
191
192        int len = phoneNumber.length();
193        StringBuilder ret = new StringBuilder(len);
194        boolean firstCharAdded = false;
195
196        for (int i = 0; i < len; i++) {
197            char c = phoneNumber.charAt(i);
198            if (isDialable(c) && (c != '+' || !firstCharAdded)) {
199                firstCharAdded = true;
200                ret.append(c);
201            } else if (isStartsPostDial (c)) {
202                break;
203            }
204        }
205
206        return ret.toString();
207    }
208
209    /**
210     * Strips separators from a phone number string.
211     * @param phoneNumber phone number to strip.
212     * @return phone string stripped of separators.
213     */
214    public static String stripSeparators(String phoneNumber) {
215        if (phoneNumber == null) {
216            return null;
217        }
218        int len = phoneNumber.length();
219        StringBuilder ret = new StringBuilder(len);
220
221        for (int i = 0; i < len; i++) {
222            char c = phoneNumber.charAt(i);
223            if (isNonSeparator(c)) {
224                ret.append(c);
225            }
226        }
227
228        return ret.toString();
229    }
230
231    /** or -1 if both are negative */
232    static private int
233    minPositive (int a, int b) {
234        if (a >= 0 && b >= 0) {
235            return (a < b) ? a : b;
236        } else if (a >= 0) { /* && b < 0 */
237            return a;
238        } else if (b >= 0) { /* && a < 0 */
239            return b;
240        } else { /* a < 0 && b < 0 */
241            return -1;
242        }
243    }
244
245    private static void log(String msg) {
246        Log.d(LOG_TAG, msg);
247    }
248    /** index of the last character of the network portion
249     *  (eg anything after is a post-dial string)
250     */
251    static private int
252    indexOfLastNetworkChar(String a) {
253        int pIndex, wIndex;
254        int origLength;
255        int trimIndex;
256
257        origLength = a.length();
258
259        pIndex = a.indexOf(PAUSE);
260        wIndex = a.indexOf(WAIT);
261
262        trimIndex = minPositive(pIndex, wIndex);
263
264        if (trimIndex < 0) {
265            return origLength - 1;
266        } else {
267            return trimIndex - 1;
268        }
269    }
270
271    /**
272     * Extracts the post-dial sequence of DTMF control digits, pauses, and
273     * waits. Strips separators. This string may be empty, but will not be null
274     * unless phoneNumber == null.
275     *
276     * Returns null if phoneNumber == null
277     */
278
279    public static String
280    extractPostDialPortion(String phoneNumber) {
281        if (phoneNumber == null) return null;
282
283        int trimIndex;
284        StringBuilder ret = new StringBuilder();
285
286        trimIndex = indexOfLastNetworkChar (phoneNumber);
287
288        for (int i = trimIndex + 1, s = phoneNumber.length()
289                ; i < s; i++
290        ) {
291            char c = phoneNumber.charAt(i);
292            if (isNonSeparator(c)) {
293                ret.append(c);
294            }
295        }
296
297        return ret.toString();
298    }
299
300    /**
301     * Compare phone numbers a and b, return true if they're identical enough for caller ID purposes.
302     */
303    public static boolean compare(String a, String b) {
304        // We've used loose comparation at least Eclair, which may change in the future.
305
306        return compare(a, b, false);
307    }
308
309    /**
310     * Compare phone numbers a and b, and return true if they're identical
311     * enough for caller ID purposes. Checks a resource to determine whether
312     * to use a strict or loose comparison algorithm.
313     */
314    public static boolean compare(Context context, String a, String b) {
315        boolean useStrict = context.getResources().getBoolean(
316               com.android.internal.R.bool.config_use_strict_phone_number_comparation);
317        return compare(a, b, useStrict);
318    }
319
320    /**
321     * @hide only for testing.
322     */
323    public static boolean compare(String a, String b, boolean useStrictComparation) {
324        return (useStrictComparation ? compareStrictly(a, b) : compareLoosely(a, b));
325    }
326
327    /**
328     * Compare phone numbers a and b, return true if they're identical
329     * enough for caller ID purposes.
330     *
331     * - Compares from right to left
332     * - requires MIN_MATCH (7) characters to match
333     * - handles common trunk prefixes and international prefixes
334     *   (basically, everything except the Russian trunk prefix)
335     *
336     * Note that this method does not return false even when the two phone numbers
337     * are not exactly same; rather; we can call this method "similar()", not "equals()".
338     *
339     * @hide
340     */
341    public static boolean
342    compareLoosely(String a, String b) {
343        int ia, ib;
344        int matched;
345
346        if (a == null || b == null) return a == b;
347
348        if (a.length() == 0 || b.length() == 0) {
349            return false;
350        }
351
352        ia = indexOfLastNetworkChar (a);
353        ib = indexOfLastNetworkChar (b);
354        matched = 0;
355
356        while (ia >= 0 && ib >=0) {
357            char ca, cb;
358            boolean skipCmp = false;
359
360            ca = a.charAt(ia);
361
362            if (!isDialable(ca)) {
363                ia--;
364                skipCmp = true;
365            }
366
367            cb = b.charAt(ib);
368
369            if (!isDialable(cb)) {
370                ib--;
371                skipCmp = true;
372            }
373
374            if (!skipCmp) {
375                if (cb != ca && ca != WILD && cb != WILD) {
376                    break;
377                }
378                ia--; ib--; matched++;
379            }
380        }
381
382        if (matched < MIN_MATCH) {
383            int aLen = a.length();
384
385            // if the input strings match, but their lengths < MIN_MATCH,
386            // treat them as equal.
387            if (aLen == b.length() && aLen == matched) {
388                return true;
389            }
390            return false;
391        }
392
393        // At least one string has matched completely;
394        if (matched >= MIN_MATCH && (ia < 0 || ib < 0)) {
395            return true;
396        }
397
398        /*
399         * Now, what remains must be one of the following for a
400         * match:
401         *
402         *  - a '+' on one and a '00' or a '011' on the other
403         *  - a '0' on one and a (+,00)<country code> on the other
404         *     (for this, a '0' and a '00' prefix would have succeeded above)
405         */
406
407        if (matchIntlPrefix(a, ia + 1)
408            && matchIntlPrefix (b, ib +1)
409        ) {
410            return true;
411        }
412
413        if (matchTrunkPrefix(a, ia + 1)
414            && matchIntlPrefixAndCC(b, ib +1)
415        ) {
416            return true;
417        }
418
419        if (matchTrunkPrefix(b, ib + 1)
420            && matchIntlPrefixAndCC(a, ia +1)
421        ) {
422            return true;
423        }
424
425        return false;
426    }
427
428    /**
429     * @hide
430     */
431    public static boolean
432    compareStrictly(String a, String b) {
433        return compareStrictly(a, b, true);
434    }
435
436    /**
437     * @hide
438     */
439    public static boolean
440    compareStrictly(String a, String b, boolean acceptInvalidCCCPrefix) {
441        if (a == null || b == null) {
442            return a == b;
443        } else if (a.length() == 0 && b.length() == 0) {
444            return false;
445        }
446
447        int forwardIndexA = 0;
448        int forwardIndexB = 0;
449
450        CountryCallingCodeAndNewIndex cccA =
451            tryGetCountryCallingCodeAndNewIndex(a, acceptInvalidCCCPrefix);
452        CountryCallingCodeAndNewIndex cccB =
453            tryGetCountryCallingCodeAndNewIndex(b, acceptInvalidCCCPrefix);
454        boolean bothHasCountryCallingCode = false;
455        boolean okToIgnorePrefix = true;
456        boolean trunkPrefixIsOmittedA = false;
457        boolean trunkPrefixIsOmittedB = false;
458        if (cccA != null && cccB != null) {
459            if (cccA.countryCallingCode != cccB.countryCallingCode) {
460                // Different Country Calling Code. Must be different phone number.
461                return false;
462            }
463            // When both have ccc, do not ignore trunk prefix. Without this,
464            // "+81123123" becomes same as "+810123123" (+81 == Japan)
465            okToIgnorePrefix = false;
466            bothHasCountryCallingCode = true;
467            forwardIndexA = cccA.newIndex;
468            forwardIndexB = cccB.newIndex;
469        } else if (cccA == null && cccB == null) {
470            // When both do not have ccc, do not ignore trunk prefix. Without this,
471            // "123123" becomes same as "0123123"
472            okToIgnorePrefix = false;
473        } else {
474            if (cccA != null) {
475                forwardIndexA = cccA.newIndex;
476            } else {
477                int tmp = tryGetTrunkPrefixOmittedIndex(b, 0);
478                if (tmp >= 0) {
479                    forwardIndexA = tmp;
480                    trunkPrefixIsOmittedA = true;
481                }
482            }
483            if (cccB != null) {
484                forwardIndexB = cccB.newIndex;
485            } else {
486                int tmp = tryGetTrunkPrefixOmittedIndex(b, 0);
487                if (tmp >= 0) {
488                    forwardIndexB = tmp;
489                    trunkPrefixIsOmittedB = true;
490                }
491            }
492        }
493
494        int backwardIndexA = a.length() - 1;
495        int backwardIndexB = b.length() - 1;
496        while (backwardIndexA >= forwardIndexA && backwardIndexB >= forwardIndexB) {
497            boolean skip_compare = false;
498            final char chA = a.charAt(backwardIndexA);
499            final char chB = b.charAt(backwardIndexB);
500            if (isSeparator(chA)) {
501                backwardIndexA--;
502                skip_compare = true;
503            }
504            if (isSeparator(chB)) {
505                backwardIndexB--;
506                skip_compare = true;
507            }
508
509            if (!skip_compare) {
510                if (chA != chB) {
511                    return false;
512                }
513                backwardIndexA--;
514                backwardIndexB--;
515            }
516        }
517
518        if (okToIgnorePrefix) {
519            if ((trunkPrefixIsOmittedA && forwardIndexA <= backwardIndexA) ||
520                !checkPrefixIsIgnorable(a, forwardIndexA, backwardIndexA)) {
521                if (acceptInvalidCCCPrefix) {
522                    // Maybe the code handling the special case for Thailand makes the
523                    // result garbled, so disable the code and try again.
524                    // e.g. "16610001234" must equal to "6610001234", but with
525                    //      Thailand-case handling code, they become equal to each other.
526                    //
527                    // Note: we select simplicity rather than adding some complicated
528                    //       logic here for performance(like "checking whether remaining
529                    //       numbers are just 66 or not"), assuming inputs are small
530                    //       enough.
531                    return compare(a, b, false);
532                } else {
533                    return false;
534                }
535            }
536            if ((trunkPrefixIsOmittedB && forwardIndexB <= backwardIndexB) ||
537                !checkPrefixIsIgnorable(b, forwardIndexA, backwardIndexB)) {
538                if (acceptInvalidCCCPrefix) {
539                    return compare(a, b, false);
540                } else {
541                    return false;
542                }
543            }
544        } else {
545            // In the US, 1-650-555-1234 must be equal to 650-555-1234,
546            // while 090-1234-1234 must not be equalt to 90-1234-1234 in Japan.
547            // This request exists just in US (with 1 trunk (NDD) prefix).
548            // In addition, "011 11 7005554141" must not equal to "+17005554141",
549            // while "011 1 7005554141" must equal to "+17005554141"
550            //
551            // In this comparison, we ignore the prefix '1' just once, when
552            // - at least either does not have CCC, or
553            // - the remaining non-separator number is 1
554            boolean maybeNamp = !bothHasCountryCallingCode;
555            while (backwardIndexA >= forwardIndexA) {
556                final char chA = a.charAt(backwardIndexA);
557                if (isDialable(chA)) {
558                    if (maybeNamp && tryGetISODigit(chA) == 1) {
559                        maybeNamp = false;
560                    } else {
561                        return false;
562                    }
563                }
564                backwardIndexA--;
565            }
566            while (backwardIndexB >= forwardIndexB) {
567                final char chB = b.charAt(backwardIndexB);
568                if (isDialable(chB)) {
569                    if (maybeNamp && tryGetISODigit(chB) == 1) {
570                        maybeNamp = false;
571                    } else {
572                        return false;
573                    }
574                }
575                backwardIndexB--;
576            }
577        }
578
579        return true;
580    }
581
582    /**
583     * Returns the rightmost MIN_MATCH (5) characters in the network portion
584     * in *reversed* order
585     *
586     * This can be used to do a database lookup against the column
587     * that stores getStrippedReversed()
588     *
589     * Returns null if phoneNumber == null
590     */
591    public static String
592    toCallerIDMinMatch(String phoneNumber) {
593        String np = extractNetworkPortion(phoneNumber);
594        return internalGetStrippedReversed(np, MIN_MATCH);
595    }
596
597    /**
598     * Returns the network portion reversed.
599     * This string is intended to go into an index column for a
600     * database lookup.
601     *
602     * Returns null if phoneNumber == null
603     */
604    public static String
605    getStrippedReversed(String phoneNumber) {
606        String np = extractNetworkPortion(phoneNumber);
607
608        if (np == null) return null;
609
610        return internalGetStrippedReversed(np, np.length());
611    }
612
613    /**
614     * Returns the last numDigits of the reversed phone number
615     * Returns null if np == null
616     */
617    private static String
618    internalGetStrippedReversed(String np, int numDigits) {
619        if (np == null) return null;
620
621        StringBuilder ret = new StringBuilder(numDigits);
622        int length = np.length();
623
624        for (int i = length - 1, s = length
625            ; i >= 0 && (s - i) <= numDigits ; i--
626        ) {
627            char c = np.charAt(i);
628
629            ret.append(c);
630        }
631
632        return ret.toString();
633    }
634
635    /**
636     * Basically: makes sure there's a + in front of a
637     * TOA_International number
638     *
639     * Returns null if s == null
640     */
641    public static String
642    stringFromStringAndTOA(String s, int TOA) {
643        if (s == null) return null;
644
645        if (TOA == TOA_International && s.length() > 0 && s.charAt(0) != '+') {
646            return "+" + s;
647        }
648
649        return s;
650    }
651
652    /**
653     * Returns the TOA for the given dial string
654     * Basically, returns TOA_International if there's a + prefix
655     */
656
657    public static int
658    toaFromString(String s) {
659        if (s != null && s.length() > 0 && s.charAt(0) == '+') {
660            return TOA_International;
661        }
662
663        return TOA_Unknown;
664    }
665
666    /**
667     *  3GPP TS 24.008 10.5.4.7
668     *  Called Party BCD Number
669     *
670     *  See Also TS 51.011 10.5.1 "dialing number/ssc string"
671     *  and TS 11.11 "10.3.1 EF adn (Abbreviated dialing numbers)"
672     *
673     * @param bytes the data buffer
674     * @param offset should point to the TOA (aka. TON/NPI) octet after the length byte
675     * @param length is the number of bytes including TOA byte
676     *                and must be at least 2
677     *
678     * @return partial string on invalid decode
679     *
680     * FIXME(mkf) support alphanumeric address type
681     *  currently implemented in SMSMessage.getAddress()
682     */
683    public static String
684    calledPartyBCDToString (byte[] bytes, int offset, int length) {
685        boolean prependPlus = false;
686        StringBuilder ret = new StringBuilder(1 + length * 2);
687
688        if (length < 2) {
689            return "";
690        }
691
692        //Only TON field should be taken in concideration
693        if ((bytes[offset] & 0xf0) == (TOA_International & 0xf0)) {
694            prependPlus = true;
695        }
696
697        internalCalledPartyBCDFragmentToString(
698                ret, bytes, offset + 1, length - 1);
699
700        if (prependPlus && ret.length() == 0) {
701            // If the only thing there is a prepended plus, return ""
702            return "";
703        }
704
705        if (prependPlus) {
706            // This is an "international number" and should have
707            // a plus prepended to the dialing number. But there
708            // can also be Gsm MMI codes as defined in TS 22.030 6.5.2
709            // so we need to handle those also.
710            //
711            // http://web.telia.com/~u47904776/gsmkode.htm is a
712            // has a nice list of some of these GSM codes.
713            //
714            // Examples are:
715            //   **21*+886988171479#
716            //   **21*8311234567#
717            //   *21#
718            //   #21#
719            //   *#21#
720            //   *31#+11234567890
721            //   #31#+18311234567
722            //   #31#8311234567
723            //   18311234567
724            //   +18311234567#
725            //   +18311234567
726            // Odd ball cases that some phones handled
727            // where there is no dialing number so they
728            // append the "+"
729            //   *21#+
730            //   **21#+
731            String retString = ret.toString();
732            Pattern p = Pattern.compile("(^[#*])(.*)([#*])(.*)(#)$");
733            Matcher m = p.matcher(retString);
734            if (m.matches()) {
735                if ("".equals(m.group(2))) {
736                    // Started with two [#*] ends with #
737                    // So no dialing number and we'll just
738                    // append a +, this handles **21#+
739                    ret = new StringBuilder();
740                    ret.append(m.group(1));
741                    ret.append(m.group(3));
742                    ret.append(m.group(4));
743                    ret.append(m.group(5));
744                    ret.append("+");
745                } else {
746                    // Starts with [#*] and ends with #
747                    // Assume group 4 is a dialing number
748                    // such as *21*+1234554#
749                    ret = new StringBuilder();
750                    ret.append(m.group(1));
751                    ret.append(m.group(2));
752                    ret.append(m.group(3));
753                    ret.append("+");
754                    ret.append(m.group(4));
755                    ret.append(m.group(5));
756                }
757            } else {
758                p = Pattern.compile("(^[#*])(.*)([#*])(.*)");
759                m = p.matcher(retString);
760                if (m.matches()) {
761                    // Starts with [#*] and only one other [#*]
762                    // Assume the data after last [#*] is dialing
763                    // number (i.e. group 4) such as *31#+11234567890.
764                    // This also includes the odd ball *21#+
765                    ret = new StringBuilder();
766                    ret.append(m.group(1));
767                    ret.append(m.group(2));
768                    ret.append(m.group(3));
769                    ret.append("+");
770                    ret.append(m.group(4));
771                } else {
772                    // Does NOT start with [#*] just prepend '+'
773                    ret = new StringBuilder();
774                    ret.append('+');
775                    ret.append(retString);
776                }
777            }
778        }
779
780        return ret.toString();
781    }
782
783    private static void
784    internalCalledPartyBCDFragmentToString(
785        StringBuilder sb, byte [] bytes, int offset, int length) {
786        for (int i = offset ; i < length + offset ; i++) {
787            byte b;
788            char c;
789
790            c = bcdToChar((byte)(bytes[i] & 0xf));
791
792            if (c == 0) {
793                return;
794            }
795            sb.append(c);
796
797            // FIXME(mkf) TS 23.040 9.1.2.3 says
798            // "if a mobile receives 1111 in a position prior to
799            // the last semi-octet then processing shall commense with
800            // the next semi-octet and the intervening
801            // semi-octet shall be ignored"
802            // How does this jive with 24,008 10.5.4.7
803
804            b = (byte)((bytes[i] >> 4) & 0xf);
805
806            if (b == 0xf && i + 1 == length + offset) {
807                //ignore final 0xf
808                break;
809            }
810
811            c = bcdToChar(b);
812            if (c == 0) {
813                return;
814            }
815
816            sb.append(c);
817        }
818
819    }
820
821    /**
822     * Like calledPartyBCDToString, but field does not start with a
823     * TOA byte. For example: SIM ADN extension fields
824     */
825
826    public static String
827    calledPartyBCDFragmentToString(byte [] bytes, int offset, int length) {
828        StringBuilder ret = new StringBuilder(length * 2);
829
830        internalCalledPartyBCDFragmentToString(ret, bytes, offset, length);
831
832        return ret.toString();
833    }
834
835    /** returns 0 on invalid value */
836    private static char
837    bcdToChar(byte b) {
838        if (b < 0xa) {
839            return (char)('0' + b);
840        } else switch (b) {
841            case 0xa: return '*';
842            case 0xb: return '#';
843            case 0xc: return PAUSE;
844            case 0xd: return WILD;
845
846            default: return 0;
847        }
848    }
849
850    private static int
851    charToBCD(char c) {
852        if (c >= '0' && c <= '9') {
853            return c - '0';
854        } else if (c == '*') {
855            return 0xa;
856        } else if (c == '#') {
857            return 0xb;
858        } else if (c == PAUSE) {
859            return 0xc;
860        } else if (c == WILD) {
861            return 0xd;
862        } else {
863            throw new RuntimeException ("invalid char for BCD " + c);
864        }
865    }
866
867    /**
868     * Return true iff the network portion of <code>address</code> is,
869     * as far as we can tell on the device, suitable for use as an SMS
870     * destination address.
871     */
872    public static boolean isWellFormedSmsAddress(String address) {
873        String networkPortion =
874                PhoneNumberUtils.extractNetworkPortion(address);
875
876        return (!(networkPortion.equals("+")
877                  || TextUtils.isEmpty(networkPortion)))
878               && isDialable(networkPortion);
879    }
880
881    public static boolean isGlobalPhoneNumber(String phoneNumber) {
882        if (TextUtils.isEmpty(phoneNumber)) {
883            return false;
884        }
885
886        Matcher match = GLOBAL_PHONE_NUMBER_PATTERN.matcher(phoneNumber);
887        return match.matches();
888    }
889
890    private static boolean isDialable(String address) {
891        for (int i = 0, count = address.length(); i < count; i++) {
892            if (!isDialable(address.charAt(i))) {
893                return false;
894            }
895        }
896        return true;
897    }
898
899    private static boolean isNonSeparator(String address) {
900        for (int i = 0, count = address.length(); i < count; i++) {
901            if (!isNonSeparator(address.charAt(i))) {
902                return false;
903            }
904        }
905        return true;
906    }
907    /**
908     * Note: calls extractNetworkPortion(), so do not use for
909     * SIM EF[ADN] style records
910     *
911     * Returns null if network portion is empty.
912     */
913    public static byte[]
914    networkPortionToCalledPartyBCD(String s) {
915        String networkPortion = extractNetworkPortion(s);
916        return numberToCalledPartyBCDHelper(networkPortion, false);
917    }
918
919    /**
920     * Same as {@link #networkPortionToCalledPartyBCD}, but includes a
921     * one-byte length prefix.
922     */
923    public static byte[]
924    networkPortionToCalledPartyBCDWithLength(String s) {
925        String networkPortion = extractNetworkPortion(s);
926        return numberToCalledPartyBCDHelper(networkPortion, true);
927    }
928
929    /**
930     * Convert a dialing number to BCD byte array
931     *
932     * @param number dialing number string
933     *        if the dialing number starts with '+', set to internationl TOA
934     * @return BCD byte array
935     */
936    public static byte[]
937    numberToCalledPartyBCD(String number) {
938        return numberToCalledPartyBCDHelper(number, false);
939    }
940
941    /**
942     * If includeLength is true, prepend a one-byte length value to
943     * the return array.
944     */
945    private static byte[]
946    numberToCalledPartyBCDHelper(String number, boolean includeLength) {
947        int numberLenReal = number.length();
948        int numberLenEffective = numberLenReal;
949        boolean hasPlus = number.indexOf('+') != -1;
950        if (hasPlus) numberLenEffective--;
951
952        if (numberLenEffective == 0) return null;
953
954        int resultLen = (numberLenEffective + 1) / 2;  // Encoded numbers require only 4 bits each.
955        int extraBytes = 1;                            // Prepended TOA byte.
956        if (includeLength) extraBytes++;               // Optional prepended length byte.
957        resultLen += extraBytes;
958
959        byte[] result = new byte[resultLen];
960
961        int digitCount = 0;
962        for (int i = 0; i < numberLenReal; i++) {
963            char c = number.charAt(i);
964            if (c == '+') continue;
965            int shift = ((digitCount & 0x01) == 1) ? 4 : 0;
966            result[extraBytes + (digitCount >> 1)] |= (byte)((charToBCD(c) & 0x0F) << shift);
967            digitCount++;
968        }
969
970        // 1-fill any trailing odd nibble/quartet.
971        if ((digitCount & 0x01) == 1) result[extraBytes + (digitCount >> 1)] |= 0xF0;
972
973        int offset = 0;
974        if (includeLength) result[offset++] = (byte)(resultLen - 1);
975        result[offset] = (byte)(hasPlus ? TOA_International : TOA_Unknown);
976
977        return result;
978    }
979
980    //================ Number formatting =========================
981
982    /** The current locale is unknown, look for a country code or don't format */
983    public static final int FORMAT_UNKNOWN = 0;
984    /** NANP formatting */
985    public static final int FORMAT_NANP = 1;
986    /** Japanese formatting */
987    public static final int FORMAT_JAPAN = 2;
988
989    /** List of country codes for countries that use the NANP */
990    private static final String[] NANP_COUNTRIES = new String[] {
991        "US", // United States
992        "CA", // Canada
993        "AS", // American Samoa
994        "AI", // Anguilla
995        "AG", // Antigua and Barbuda
996        "BS", // Bahamas
997        "BB", // Barbados
998        "BM", // Bermuda
999        "VG", // British Virgin Islands
1000        "KY", // Cayman Islands
1001        "DM", // Dominica
1002        "DO", // Dominican Republic
1003        "GD", // Grenada
1004        "GU", // Guam
1005        "JM", // Jamaica
1006        "PR", // Puerto Rico
1007        "MS", // Montserrat
1008        "MP", // Northern Mariana Islands
1009        "KN", // Saint Kitts and Nevis
1010        "LC", // Saint Lucia
1011        "VC", // Saint Vincent and the Grenadines
1012        "TT", // Trinidad and Tobago
1013        "TC", // Turks and Caicos Islands
1014        "VI", // U.S. Virgin Islands
1015    };
1016
1017    /**
1018     * Breaks the given number down and formats it according to the rules
1019     * for the country the number is from.
1020     *
1021     * @param source the phone number to format
1022     * @return a locally acceptable formatting of the input, or the raw input if
1023     *  formatting rules aren't known for the number
1024     */
1025    public static String formatNumber(String source) {
1026        SpannableStringBuilder text = new SpannableStringBuilder(source);
1027        formatNumber(text, getFormatTypeForLocale(Locale.getDefault()));
1028        return text.toString();
1029    }
1030
1031    /**
1032     * Returns the phone number formatting type for the given locale.
1033     *
1034     * @param locale The locale of interest, usually {@link Locale#getDefault()}
1035     * @return the formatting type for the given locale, or FORMAT_UNKNOWN if the formatting
1036     * rules are not known for the given locale
1037     */
1038    public static int getFormatTypeForLocale(Locale locale) {
1039        String country = locale.getCountry();
1040
1041        return getFormatTypeFromCountryCode(country);
1042    }
1043
1044    /**
1045     * Formats a phone number in-place. Currently only supports NANP formatting.
1046     *
1047     * @param text The number to be formatted, will be modified with the formatting
1048     * @param defaultFormattingType The default formatting rules to apply if the number does
1049     * not begin with +<country_code>
1050     */
1051    public static void formatNumber(Editable text, int defaultFormattingType) {
1052        int formatType = defaultFormattingType;
1053
1054        if (text.length() > 2 && text.charAt(0) == '+') {
1055            if (text.charAt(1) == '1') {
1056                formatType = FORMAT_NANP;
1057            } else if (text.length() >= 3 && text.charAt(1) == '8'
1058                && text.charAt(2) == '1') {
1059                formatType = FORMAT_JAPAN;
1060            } else {
1061                formatType = FORMAT_UNKNOWN;
1062            }
1063        }
1064
1065        switch (formatType) {
1066            case FORMAT_NANP:
1067                formatNanpNumber(text);
1068                return;
1069            case FORMAT_JAPAN:
1070                formatJapaneseNumber(text);
1071                return;
1072            case FORMAT_UNKNOWN:
1073                removeDashes(text);
1074                return;
1075        }
1076    }
1077
1078    private static final int NANP_STATE_DIGIT = 1;
1079    private static final int NANP_STATE_PLUS = 2;
1080    private static final int NANP_STATE_ONE = 3;
1081    private static final int NANP_STATE_DASH = 4;
1082
1083    /**
1084     * Formats a phone number in-place using the NANP formatting rules. Numbers will be formatted
1085     * as:
1086     *
1087     * <p><code>
1088     * xxxxx
1089     * xxx-xxxx
1090     * xxx-xxx-xxxx
1091     * 1-xxx-xxx-xxxx
1092     * +1-xxx-xxx-xxxx
1093     * </code></p>
1094     *
1095     * @param text the number to be formatted, will be modified with the formatting
1096     */
1097    public static void formatNanpNumber(Editable text) {
1098        int length = text.length();
1099        if (length > "+1-nnn-nnn-nnnn".length()) {
1100            // The string is too long to be formatted
1101            return;
1102        } else if (length <= 5) {
1103            // The string is either a shortcode or too short to be formatted
1104            return;
1105        }
1106
1107        CharSequence saved = text.subSequence(0, length);
1108
1109        // Strip the dashes first, as we're going to add them back
1110        removeDashes(text);
1111        length = text.length();
1112
1113        // When scanning the number we record where dashes need to be added,
1114        // if they're non-0 at the end of the scan the dashes will be added in
1115        // the proper places.
1116        int dashPositions[] = new int[3];
1117        int numDashes = 0;
1118
1119        int state = NANP_STATE_DIGIT;
1120        int numDigits = 0;
1121        for (int i = 0; i < length; i++) {
1122            char c = text.charAt(i);
1123            switch (c) {
1124                case '1':
1125                    if (numDigits == 0 || state == NANP_STATE_PLUS) {
1126                        state = NANP_STATE_ONE;
1127                        break;
1128                    }
1129                    // fall through
1130                case '2':
1131                case '3':
1132                case '4':
1133                case '5':
1134                case '6':
1135                case '7':
1136                case '8':
1137                case '9':
1138                case '0':
1139                    if (state == NANP_STATE_PLUS) {
1140                        // Only NANP number supported for now
1141                        text.replace(0, length, saved);
1142                        return;
1143                    } else if (state == NANP_STATE_ONE) {
1144                        // Found either +1 or 1, follow it up with a dash
1145                        dashPositions[numDashes++] = i;
1146                    } else if (state != NANP_STATE_DASH && (numDigits == 3 || numDigits == 6)) {
1147                        // Found a digit that should be after a dash that isn't
1148                        dashPositions[numDashes++] = i;
1149                    }
1150                    state = NANP_STATE_DIGIT;
1151                    numDigits++;
1152                    break;
1153
1154                case '-':
1155                    state = NANP_STATE_DASH;
1156                    break;
1157
1158                case '+':
1159                    if (i == 0) {
1160                        // Plus is only allowed as the first character
1161                        state = NANP_STATE_PLUS;
1162                        break;
1163                    }
1164                    // Fall through
1165                default:
1166                    // Unknown character, bail on formatting
1167                    text.replace(0, length, saved);
1168                    return;
1169            }
1170        }
1171
1172        if (numDigits == 7) {
1173            // With 7 digits we want xxx-xxxx, not xxx-xxx-x
1174            numDashes--;
1175        }
1176
1177        // Actually put the dashes in place
1178        for (int i = 0; i < numDashes; i++) {
1179            int pos = dashPositions[i];
1180            text.replace(pos + i, pos + i, "-");
1181        }
1182
1183        // Remove trailing dashes
1184        int len = text.length();
1185        while (len > 0) {
1186            if (text.charAt(len - 1) == '-') {
1187                text.delete(len - 1, len);
1188                len--;
1189            } else {
1190                break;
1191            }
1192        }
1193    }
1194
1195    /**
1196     * Formats a phone number in-place using the Japanese formatting rules.
1197     * Numbers will be formatted as:
1198     *
1199     * <p><code>
1200     * 03-xxxx-xxxx
1201     * 090-xxxx-xxxx
1202     * 0120-xxx-xxx
1203     * +81-3-xxxx-xxxx
1204     * +81-90-xxxx-xxxx
1205     * </code></p>
1206     *
1207     * @param text the number to be formatted, will be modified with
1208     * the formatting
1209     */
1210    public static void formatJapaneseNumber(Editable text) {
1211        JapanesePhoneNumberFormatter.format(text);
1212    }
1213
1214    /**
1215     * Removes all dashes from the number.
1216     *
1217     * @param text the number to clear from dashes
1218     */
1219    private static void removeDashes(Editable text) {
1220        int p = 0;
1221        while (p < text.length()) {
1222            if (text.charAt(p) == '-') {
1223                text.delete(p, p + 1);
1224           } else {
1225                p++;
1226           }
1227        }
1228    }
1229
1230    // Three and four digit phone numbers for either special services,
1231    // or 3-6 digit addresses from the network (eg carrier-originated SMS messages) should
1232    // not match.
1233    //
1234    // This constant used to be 5, but SMS short codes has increased in length and
1235    // can be easily 6 digits now days. Most countries have SMS short code length between
1236    // 3 to 6 digits. The exceptions are
1237    //
1238    // Australia: Short codes are six or eight digits in length, starting with the prefix "19"
1239    //            followed by an additional four or six digits and two.
1240    // Czech Republic: Codes are seven digits in length for MO and five (not billed) or
1241    //            eight (billed) for MT direction
1242    //
1243    // see http://en.wikipedia.org/wiki/Short_code#Regional_differences for reference
1244    //
1245    // However, in order to loose match 650-555-1212 and 555-1212, we need to set the min match
1246    // to 7.
1247    static final int MIN_MATCH = 7;
1248
1249    /**
1250     * isEmergencyNumber: checks a given number against the list of
1251     *   emergency numbers provided by the RIL and SIM card.
1252     *
1253     * @param number the number to look up.
1254     * @return if the number is in the list of emergency numbers
1255     * listed in the ril / sim, then return true, otherwise false.
1256     */
1257    public static boolean isEmergencyNumber(String number) {
1258        // If the number passed in is null, just return false:
1259        if (number == null) return false;
1260
1261        // Strip the separators from the number before comparing it
1262        // to the list.
1263        number = extractNetworkPortion(number);
1264
1265        // retrieve the list of emergency numbers
1266        String numbers = SystemProperties.get("ro.ril.ecclist");
1267
1268        if (!TextUtils.isEmpty(numbers)) {
1269            // searches through the comma-separated list for a match,
1270            // return true if one is found.
1271            for (String emergencyNum : numbers.split(",")) {
1272                if (emergencyNum.equals(number)) {
1273                    return true;
1274                }
1275            }
1276            // no matches found against the list!
1277            return false;
1278        }
1279
1280        //no ecclist system property, so use our own list.
1281        return (number.equals("112") || number.equals("911"));
1282    }
1283
1284    /**
1285     * isVoiceMailNumber: checks a given number against the voicemail
1286     *   number provided by the RIL and SIM card. The caller must have
1287     *   the READ_PHONE_STATE credential.
1288     *
1289     * @param number the number to look up.
1290     * @return true if the number is in the list of voicemail. False
1291     * otherwise, including if the caller does not have the permission
1292     * to read the VM number.
1293     * @hide TODO: pending API Council approval
1294     */
1295    public static boolean isVoiceMailNumber(String number) {
1296        String vmNumber;
1297
1298        try {
1299            vmNumber = TelephonyManager.getDefault().getVoiceMailNumber();
1300        } catch (SecurityException ex) {
1301            return false;
1302        }
1303
1304        // Strip the separators from the number before comparing it
1305        // to the list.
1306        number = extractNetworkPortion(number);
1307
1308        // compare tolerates null so we need to make sure that we
1309        // don't return true when both are null.
1310        return !TextUtils.isEmpty(number) && compare(number, vmNumber);
1311    }
1312
1313    /**
1314     * Translates any alphabetic letters (i.e. [A-Za-z]) in the
1315     * specified phone number into the equivalent numeric digits,
1316     * according to the phone keypad letter mapping described in
1317     * ITU E.161 and ISO/IEC 9995-8.
1318     *
1319     * @return the input string, with alpha letters converted to numeric
1320     *         digits using the phone keypad letter mapping.  For example,
1321     *         an input of "1-800-GOOG-411" will return "1-800-4664-411".
1322     */
1323    public static String convertKeypadLettersToDigits(String input) {
1324        if (input == null) {
1325            return input;
1326        }
1327        int len = input.length();
1328        if (len == 0) {
1329            return input;
1330        }
1331
1332        char[] out = input.toCharArray();
1333
1334        for (int i = 0; i < len; i++) {
1335            char c = out[i];
1336            // If this char isn't in KEYPAD_MAP at all, just leave it alone.
1337            out[i] = (char) KEYPAD_MAP.get(c, c);
1338        }
1339
1340        return new String(out);
1341    }
1342
1343    /**
1344     * The phone keypad letter mapping (see ITU E.161 or ISO/IEC 9995-8.)
1345     * TODO: This should come from a resource.
1346     */
1347    private static final SparseIntArray KEYPAD_MAP = new SparseIntArray();
1348    static {
1349        KEYPAD_MAP.put('a', '2'); KEYPAD_MAP.put('b', '2'); KEYPAD_MAP.put('c', '2');
1350        KEYPAD_MAP.put('A', '2'); KEYPAD_MAP.put('B', '2'); KEYPAD_MAP.put('C', '2');
1351
1352        KEYPAD_MAP.put('d', '3'); KEYPAD_MAP.put('e', '3'); KEYPAD_MAP.put('f', '3');
1353        KEYPAD_MAP.put('D', '3'); KEYPAD_MAP.put('E', '3'); KEYPAD_MAP.put('F', '3');
1354
1355        KEYPAD_MAP.put('g', '4'); KEYPAD_MAP.put('h', '4'); KEYPAD_MAP.put('i', '4');
1356        KEYPAD_MAP.put('G', '4'); KEYPAD_MAP.put('H', '4'); KEYPAD_MAP.put('I', '4');
1357
1358        KEYPAD_MAP.put('j', '5'); KEYPAD_MAP.put('k', '5'); KEYPAD_MAP.put('l', '5');
1359        KEYPAD_MAP.put('J', '5'); KEYPAD_MAP.put('K', '5'); KEYPAD_MAP.put('L', '5');
1360
1361        KEYPAD_MAP.put('m', '6'); KEYPAD_MAP.put('n', '6'); KEYPAD_MAP.put('o', '6');
1362        KEYPAD_MAP.put('M', '6'); KEYPAD_MAP.put('N', '6'); KEYPAD_MAP.put('O', '6');
1363
1364        KEYPAD_MAP.put('p', '7'); KEYPAD_MAP.put('q', '7'); KEYPAD_MAP.put('r', '7'); KEYPAD_MAP.put('s', '7');
1365        KEYPAD_MAP.put('P', '7'); KEYPAD_MAP.put('Q', '7'); KEYPAD_MAP.put('R', '7'); KEYPAD_MAP.put('S', '7');
1366
1367        KEYPAD_MAP.put('t', '8'); KEYPAD_MAP.put('u', '8'); KEYPAD_MAP.put('v', '8');
1368        KEYPAD_MAP.put('T', '8'); KEYPAD_MAP.put('U', '8'); KEYPAD_MAP.put('V', '8');
1369
1370        KEYPAD_MAP.put('w', '9'); KEYPAD_MAP.put('x', '9'); KEYPAD_MAP.put('y', '9'); KEYPAD_MAP.put('z', '9');
1371        KEYPAD_MAP.put('W', '9'); KEYPAD_MAP.put('X', '9'); KEYPAD_MAP.put('Y', '9'); KEYPAD_MAP.put('Z', '9');
1372    }
1373
1374    //================ Plus Code formatting =========================
1375    private static final char PLUS_SIGN_CHAR = '+';
1376    private static final String PLUS_SIGN_STRING = "+";
1377    private static final String NANP_IDP_STRING = "011";
1378    private static final int NANP_LENGTH = 10;
1379
1380    /**
1381     * This function checks if there is a plus sign (+) in the passed-in dialing number.
1382     * If there is, it processes the plus sign based on the default telephone
1383     * numbering plan of the system when the phone is activated and the current
1384     * telephone numbering plan of the system that the phone is camped on.
1385     * Currently, we only support the case that the default and current telephone
1386     * numbering plans are North American Numbering Plan(NANP).
1387     *
1388     * The passed-in dialStr should only contain the valid format as described below,
1389     * 1) the 1st character in the dialStr should be one of the really dialable
1390     *    characters listed below
1391     *    ISO-LATIN characters 0-9, *, # , +
1392     * 2) the dialStr should already strip out the separator characters,
1393     *    every character in the dialStr should be one of the non separator characters
1394     *    listed below
1395     *    ISO-LATIN characters 0-9, *, # , +, WILD, WAIT, PAUSE
1396     *
1397     * Otherwise, this function returns the dial string passed in
1398     *
1399     * @param dialStr the original dial string
1400     * @return the converted dial string if the current/default countries belong to NANP,
1401     * and if there is the "+" in the original dial string. Otherwise, the original dial
1402     * string returns.
1403     *
1404     * This API is for CDMA only
1405     *
1406     * @hide TODO: pending API Council approval
1407     */
1408    public static String cdmaCheckAndProcessPlusCode(String dialStr) {
1409        if (!TextUtils.isEmpty(dialStr)) {
1410            if (isReallyDialable(dialStr.charAt(0)) &&
1411                isNonSeparator(dialStr)) {
1412                String currIso = SystemProperties.get(PROPERTY_OPERATOR_ISO_COUNTRY, "");
1413                String defaultIso = SystemProperties.get(PROPERTY_ICC_OPERATOR_ISO_COUNTRY, "");
1414                if (!TextUtils.isEmpty(currIso) && !TextUtils.isEmpty(defaultIso)) {
1415                    return cdmaCheckAndProcessPlusCodeByNumberFormat(dialStr,
1416                            getFormatTypeFromCountryCode(currIso),
1417                            getFormatTypeFromCountryCode(defaultIso));
1418                }
1419            }
1420        }
1421        return dialStr;
1422    }
1423
1424    /**
1425     * This function should be called from checkAndProcessPlusCode only
1426     * And it is used for test purpose also.
1427     *
1428     * It checks the dial string by looping through the network portion,
1429     * post dial portion 1, post dial porting 2, etc. If there is any
1430     * plus sign, then process the plus sign.
1431     * Currently, this function supports the plus sign conversion within NANP only.
1432     * Specifically, it handles the plus sign in the following ways:
1433     * 1)+1NANP,remove +, e.g.
1434     *   +18475797000 is converted to 18475797000,
1435     * 2)+NANP or +non-NANP Numbers,replace + with the current NANP IDP, e.g,
1436     *   +8475797000 is converted to 0118475797000,
1437     *   +11875767800 is converted to 01111875767800
1438     * 3)+1NANP in post dial string(s), e.g.
1439     *   8475797000;+18475231753 is converted to 8475797000;18475231753
1440     *
1441     *
1442     * @param dialStr the original dial string
1443     * @param currFormat the numbering system of the current country that the phone is camped on
1444     * @param defaultFormat the numbering system of the country that the phone is activated on
1445     * @return the converted dial string if the current/default countries belong to NANP,
1446     * and if there is the "+" in the original dial string. Otherwise, the original dial
1447     * string returns.
1448     *
1449     * @hide
1450     */
1451    public static String
1452    cdmaCheckAndProcessPlusCodeByNumberFormat(String dialStr,int currFormat,int defaultFormt) {
1453        String retStr = dialStr;
1454
1455        // Checks if the plus sign character is in the passed-in dial string
1456        if (dialStr != null &&
1457            dialStr.lastIndexOf(PLUS_SIGN_STRING) != -1) {
1458            // Format the string based on the rules for the country the number is from,
1459            // and the current country the phone is camped on.
1460            if ((currFormat == defaultFormt) && (currFormat == FORMAT_NANP)) {
1461                // Handle case where default and current telephone numbering plans are NANP.
1462                String postDialStr = null;
1463                String tempDialStr = dialStr;
1464
1465                // Sets the retStr to null since the conversion will be performed below.
1466                retStr = null;
1467                if (DBG) log("checkAndProcessPlusCode,dialStr=" + dialStr);
1468                // This routine is to process the plus sign in the dial string by loop through
1469                // the network portion, post dial portion 1, post dial portion 2... etc. if
1470                // applied
1471                do {
1472                    String networkDialStr;
1473                    networkDialStr = extractNetworkPortion(tempDialStr);
1474                    // Handles the conversion within NANP
1475                    networkDialStr = processPlusCodeWithinNanp(networkDialStr);
1476
1477                    // Concatenates the string that is converted from network portion
1478                    if (!TextUtils.isEmpty(networkDialStr)) {
1479                        if (retStr == null) {
1480                            retStr = networkDialStr;
1481                        } else {
1482                            retStr = retStr.concat(networkDialStr);
1483                        }
1484                    } else {
1485                        // This should never happen since we checked the if dialStr is null
1486                        // and if it contains the plus sign in the beginning of this function.
1487                        // The plus sign is part of the network portion.
1488                        Log.e("checkAndProcessPlusCode: null newDialStr", networkDialStr);
1489                        return dialStr;
1490                    }
1491                    postDialStr = extractPostDialPortion(tempDialStr);
1492                    if (!TextUtils.isEmpty(postDialStr)) {
1493                        int dialableIndex = findDialableIndexFromPostDialStr(postDialStr);
1494
1495                        // dialableIndex should always be greater than 0
1496                        if (dialableIndex >= 1) {
1497                            retStr = appendPwCharBackToOrigDialStr(dialableIndex,
1498                                     retStr,postDialStr);
1499                            // Skips the P/W character, extracts the dialable portion
1500                            tempDialStr = postDialStr.substring(dialableIndex);
1501                        } else {
1502                            // Non-dialable character such as P/W should not be at the end of
1503                            // the dial string after P/W processing in CdmaConnection.java
1504                            // Set the postDialStr to "" to break out of the loop
1505                            if (dialableIndex < 0) {
1506                                postDialStr = "";
1507                            }
1508                            Log.e("wrong postDialStr=", postDialStr);
1509                        }
1510                    }
1511                    if (DBG) log("checkAndProcessPlusCode,postDialStr=" + postDialStr);
1512                } while (!TextUtils.isEmpty(postDialStr) && !TextUtils.isEmpty(tempDialStr));
1513            } else {
1514                // TODO: Support NANP international conversion and other telephone numbering plans.
1515                // Currently the phone is never used in non-NANP system, so return the original
1516                // dial string.
1517                Log.e("checkAndProcessPlusCode:non-NANP not supported", dialStr);
1518            }
1519        }
1520        return retStr;
1521     }
1522
1523    // This function gets the default international dialing prefix
1524    private static String getDefaultIdp( ) {
1525        String ps = null;
1526        SystemProperties.get(PROPERTY_IDP_STRING, ps);
1527        if (TextUtils.isEmpty(ps)) {
1528            ps = NANP_IDP_STRING;
1529        }
1530        return ps;
1531    }
1532
1533    private static boolean isTwoToNine (char c) {
1534        if (c >= '2' && c <= '9') {
1535            return true;
1536        } else {
1537            return false;
1538        }
1539    }
1540
1541    private static int getFormatTypeFromCountryCode (String country) {
1542        // Check for the NANP countries
1543        int length = NANP_COUNTRIES.length;
1544        for (int i = 0; i < length; i++) {
1545            if (NANP_COUNTRIES[i].compareToIgnoreCase(country) == 0) {
1546                return FORMAT_NANP;
1547            }
1548        }
1549        if ("jp".compareToIgnoreCase(country) == 0) {
1550            return FORMAT_JAPAN;
1551        }
1552        return FORMAT_UNKNOWN;
1553    }
1554
1555    /**
1556     * This function checks if the passed in string conforms to the NANP format
1557     * i.e. NXX-NXX-XXXX, N is any digit 2-9 and X is any digit 0-9
1558     */
1559    private static boolean isNanp (String dialStr) {
1560        boolean retVal = false;
1561        if (dialStr != null) {
1562            if (dialStr.length() == NANP_LENGTH) {
1563                if (isTwoToNine(dialStr.charAt(0)) &&
1564                    isTwoToNine(dialStr.charAt(3))) {
1565                    retVal = true;
1566                    for (int i=1; i<NANP_LENGTH; i++ ) {
1567                        char c=dialStr.charAt(i);
1568                        if (!PhoneNumberUtils.isISODigit(c)) {
1569                            retVal = false;
1570                            break;
1571                        }
1572                    }
1573                }
1574            }
1575        } else {
1576            Log.e("isNanp: null dialStr passed in", dialStr);
1577        }
1578        return retVal;
1579    }
1580
1581   /**
1582    * This function checks if the passed in string conforms to 1-NANP format
1583    */
1584    private static boolean isOneNanp(String dialStr) {
1585        boolean retVal = false;
1586        if (dialStr != null) {
1587            String newDialStr = dialStr.substring(1);
1588            if ((dialStr.charAt(0) == '1') && isNanp(newDialStr)) {
1589                retVal = true;
1590            }
1591        } else {
1592            Log.e("isOneNanp: null dialStr passed in", dialStr);
1593        }
1594        return retVal;
1595    }
1596
1597    /**
1598     * This function handles the plus code conversion within NANP CDMA network
1599     * If the number format is
1600     * 1)+1NANP,remove +,
1601     * 2)other than +1NANP, any + numbers,replace + with the current IDP
1602     */
1603    private static String processPlusCodeWithinNanp(String networkDialStr) {
1604        String retStr = networkDialStr;
1605
1606        if (DBG) log("processPlusCodeWithinNanp,networkDialStr=" + networkDialStr);
1607        // If there is a plus sign at the beginning of the dial string,
1608        // Convert the plus sign to the default IDP since it's an international number
1609        if (networkDialStr != null &
1610            networkDialStr.charAt(0) == PLUS_SIGN_CHAR &&
1611            networkDialStr.length() > 1) {
1612            String newStr = networkDialStr.substring(1);
1613            if (isOneNanp(newStr)) {
1614                // Remove the leading plus sign
1615                retStr = newStr;
1616             } else {
1617                 String idpStr = getDefaultIdp();
1618                 // Replaces the plus sign with the default IDP
1619                 retStr = networkDialStr.replaceFirst("[+]", idpStr);
1620            }
1621        }
1622        if (DBG) log("processPlusCodeWithinNanp,retStr=" + retStr);
1623        return retStr;
1624    }
1625
1626    // This function finds the index of the dialable character(s)
1627    // in the post dial string
1628    private static int findDialableIndexFromPostDialStr(String postDialStr) {
1629        for (int index = 0;index < postDialStr.length();index++) {
1630             char c = postDialStr.charAt(index);
1631             if (isReallyDialable(c)) {
1632                return index;
1633             }
1634        }
1635        return -1;
1636    }
1637
1638    // This function appends the non-diablable P/W character to the original
1639    // dial string based on the dialable index passed in
1640    private static String
1641    appendPwCharBackToOrigDialStr(int dialableIndex,String origStr, String dialStr) {
1642        String retStr;
1643
1644        // There is only 1 P/W character before the dialable characters
1645        if (dialableIndex == 1) {
1646            StringBuilder ret = new StringBuilder(origStr);
1647            ret = ret.append(dialStr.charAt(0));
1648            retStr = ret.toString();
1649        } else {
1650            // It means more than 1 P/W characters in the post dial string,
1651            // appends to retStr
1652            String nonDigitStr = dialStr.substring(0,dialableIndex);
1653            retStr = origStr.concat(nonDigitStr);
1654        }
1655        return retStr;
1656    }
1657
1658    //===== Begining of utility methods used in compareLoosely() =====
1659
1660    /**
1661     * Phone numbers are stored in "lookup" form in the database
1662     * as reversed strings to allow for caller ID lookup
1663     *
1664     * This method takes a phone number and makes a valid SQL "LIKE"
1665     * string that will match the lookup form
1666     *
1667     */
1668    /** all of a up to len must be an international prefix or
1669     *  separators/non-dialing digits
1670     */
1671    private static boolean
1672    matchIntlPrefix(String a, int len) {
1673        /* '([^0-9*#+pwn]\+[^0-9*#+pwn] | [^0-9*#+pwn]0(0|11)[^0-9*#+pwn] )$' */
1674        /*        0       1                           2 3 45               */
1675
1676        int state = 0;
1677        for (int i = 0 ; i < len ; i++) {
1678            char c = a.charAt(i);
1679
1680            switch (state) {
1681                case 0:
1682                    if      (c == '+') state = 1;
1683                    else if (c == '0') state = 2;
1684                    else if (isNonSeparator(c)) return false;
1685                break;
1686
1687                case 2:
1688                    if      (c == '0') state = 3;
1689                    else if (c == '1') state = 4;
1690                    else if (isNonSeparator(c)) return false;
1691                break;
1692
1693                case 4:
1694                    if      (c == '1') state = 5;
1695                    else if (isNonSeparator(c)) return false;
1696                break;
1697
1698                default:
1699                    if (isNonSeparator(c)) return false;
1700                break;
1701
1702            }
1703        }
1704
1705        return state == 1 || state == 3 || state == 5;
1706    }
1707
1708    /** all of 'a' up to len must be a (+|00|011)country code)
1709     *  We're fast and loose with the country code. Any \d{1,3} matches */
1710    private static boolean
1711    matchIntlPrefixAndCC(String a, int len) {
1712        /*  [^0-9*#+pwn]*(\+|0(0|11)\d\d?\d? [^0-9*#+pwn] $ */
1713        /*      0          1 2 3 45  6 7  8                 */
1714
1715        int state = 0;
1716        for (int i = 0 ; i < len ; i++ ) {
1717            char c = a.charAt(i);
1718
1719            switch (state) {
1720                case 0:
1721                    if      (c == '+') state = 1;
1722                    else if (c == '0') state = 2;
1723                    else if (isNonSeparator(c)) return false;
1724                break;
1725
1726                case 2:
1727                    if      (c == '0') state = 3;
1728                    else if (c == '1') state = 4;
1729                    else if (isNonSeparator(c)) return false;
1730                break;
1731
1732                case 4:
1733                    if      (c == '1') state = 5;
1734                    else if (isNonSeparator(c)) return false;
1735                break;
1736
1737                case 1:
1738                case 3:
1739                case 5:
1740                    if      (isISODigit(c)) state = 6;
1741                    else if (isNonSeparator(c)) return false;
1742                break;
1743
1744                case 6:
1745                case 7:
1746                    if      (isISODigit(c)) state++;
1747                    else if (isNonSeparator(c)) return false;
1748                break;
1749
1750                default:
1751                    if (isNonSeparator(c)) return false;
1752            }
1753        }
1754
1755        return state == 6 || state == 7 || state == 8;
1756    }
1757
1758    /** all of 'a' up to len must match non-US trunk prefix ('0') */
1759    private static boolean
1760    matchTrunkPrefix(String a, int len) {
1761        boolean found;
1762
1763        found = false;
1764
1765        for (int i = 0 ; i < len ; i++) {
1766            char c = a.charAt(i);
1767
1768            if (c == '0' && !found) {
1769                found = true;
1770            } else if (isNonSeparator(c)) {
1771                return false;
1772            }
1773        }
1774
1775        return found;
1776    }
1777
1778    //===== End of utility methods used only in compareLoosely() =====
1779
1780    //===== Beggining of utility methods used only in compareStrictly() ====
1781
1782    /*
1783     * If true, the number is country calling code.
1784     */
1785    private static final boolean COUNTLY_CALLING_CALL[] = {
1786        true, true, false, false, false, false, false, true, false, false,
1787        false, false, false, false, false, false, false, false, false, false,
1788        true, false, false, false, false, false, false, true, true, false,
1789        true, true, true, true, true, false, true, false, false, true,
1790        true, false, false, true, true, true, true, true, true, true,
1791        false, true, true, true, true, true, true, true, true, false,
1792        true, true, true, true, true, true, true, false, false, false,
1793        false, false, false, false, false, false, false, false, false, false,
1794        false, true, true, true, true, false, true, false, false, true,
1795        true, true, true, true, true, true, false, false, true, false,
1796    };
1797    private static final int CCC_LENGTH = COUNTLY_CALLING_CALL.length;
1798
1799    /**
1800     * @return true when input is valid Country Calling Code.
1801     */
1802    private static boolean isCountryCallingCode(int countryCallingCodeCandidate) {
1803        return countryCallingCodeCandidate > 0 && countryCallingCodeCandidate < CCC_LENGTH &&
1804                COUNTLY_CALLING_CALL[countryCallingCodeCandidate];
1805    }
1806
1807    /**
1808     * Returns interger corresponding to the input if input "ch" is
1809     * ISO-LATIN characters 0-9.
1810     * Returns -1 otherwise
1811     */
1812    private static int tryGetISODigit(char ch) {
1813        if ('0' <= ch && ch <= '9') {
1814            return ch - '0';
1815        } else {
1816            return -1;
1817        }
1818    }
1819
1820    private static class CountryCallingCodeAndNewIndex {
1821        public final int countryCallingCode;
1822        public final int newIndex;
1823        public CountryCallingCodeAndNewIndex(int countryCode, int newIndex) {
1824            this.countryCallingCode = countryCode;
1825            this.newIndex = newIndex;
1826        }
1827    }
1828
1829    /*
1830     * Note that this function does not strictly care the country calling code with
1831     * 3 length (like Morocco: +212), assuming it is enough to use the first two
1832     * digit to compare two phone numbers.
1833     */
1834    private static CountryCallingCodeAndNewIndex tryGetCountryCallingCodeAndNewIndex(
1835        String str, boolean acceptThailandCase) {
1836        // Rough regexp:
1837        //  ^[^0-9*#+]*((\+|0(0|11)\d\d?|166) [^0-9*#+] $
1838        //         0        1 2 3 45  6 7  89
1839        //
1840        // In all the states, this function ignores separator characters.
1841        // "166" is the special case for the call from Thailand to the US. Uguu!
1842        int state = 0;
1843        int ccc = 0;
1844        final int length = str.length();
1845        for (int i = 0 ; i < length ; i++ ) {
1846            char ch = str.charAt(i);
1847            switch (state) {
1848                case 0:
1849                    if      (ch == '+') state = 1;
1850                    else if (ch == '0') state = 2;
1851                    else if (ch == '1') {
1852                        if (acceptThailandCase) {
1853                            state = 8;
1854                        } else {
1855                            return null;
1856                        }
1857                    } else if (isDialable(ch)) {
1858                        return null;
1859                    }
1860                break;
1861
1862                case 2:
1863                    if      (ch == '0') state = 3;
1864                    else if (ch == '1') state = 4;
1865                    else if (isDialable(ch)) {
1866                        return null;
1867                    }
1868                break;
1869
1870                case 4:
1871                    if      (ch == '1') state = 5;
1872                    else if (isDialable(ch)) {
1873                        return null;
1874                    }
1875                break;
1876
1877                case 1:
1878                case 3:
1879                case 5:
1880                case 6:
1881                case 7:
1882                    {
1883                        int ret = tryGetISODigit(ch);
1884                        if (ret > 0) {
1885                            ccc = ccc * 10 + ret;
1886                            if (ccc >= 100 || isCountryCallingCode(ccc)) {
1887                                return new CountryCallingCodeAndNewIndex(ccc, i + 1);
1888                            }
1889                            if (state == 1 || state == 3 || state == 5) {
1890                                state = 6;
1891                            } else {
1892                                state++;
1893                            }
1894                        } else if (isDialable(ch)) {
1895                            return null;
1896                        }
1897                    }
1898                    break;
1899                case 8:
1900                    if (ch == '6') state = 9;
1901                    else if (isDialable(ch)) {
1902                        return null;
1903                    }
1904                    break;
1905                case 9:
1906                    if (ch == '6') {
1907                        return new CountryCallingCodeAndNewIndex(66, i + 1);
1908                    } else {
1909                        return null;
1910                    }
1911                default:
1912                    return null;
1913            }
1914        }
1915
1916        return null;
1917    }
1918
1919    /**
1920     * Currently this function simply ignore the first digit assuming it is
1921     * trunk prefix. Actually trunk prefix is different in each country.
1922     *
1923     * e.g.
1924     * "+79161234567" equals "89161234567" (Russian trunk digit is 8)
1925     * "+33123456789" equals "0123456789" (French trunk digit is 0)
1926     *
1927     */
1928    private static int tryGetTrunkPrefixOmittedIndex(String str, int currentIndex) {
1929        int length = str.length();
1930        for (int i = currentIndex ; i < length ; i++) {
1931            final char ch = str.charAt(i);
1932            if (tryGetISODigit(ch) >= 0) {
1933                return i + 1;
1934            } else if (isDialable(ch)) {
1935                return -1;
1936            }
1937        }
1938        return -1;
1939    }
1940
1941    /**
1942     * Return true if the prefix of "str" is "ignorable". Here, "ignorable" means
1943     * that "str" has only one digit and separater characters. The one digit is
1944     * assumed to be trunk prefix.
1945     */
1946    private static boolean checkPrefixIsIgnorable(final String str,
1947            int forwardIndex, int backwardIndex) {
1948        boolean trunk_prefix_was_read = false;
1949        while (backwardIndex >= forwardIndex) {
1950            if (tryGetISODigit(str.charAt(backwardIndex)) >= 0) {
1951                if (trunk_prefix_was_read) {
1952                    // More than one digit appeared, meaning that "a" and "b"
1953                    // is different.
1954                    return false;
1955                } else {
1956                    // Ignore just one digit, assuming it is trunk prefix.
1957                    trunk_prefix_was_read = true;
1958                }
1959            } else if (isDialable(str.charAt(backwardIndex))) {
1960                // Trunk prefix is a digit, not "*", "#"...
1961                return false;
1962            }
1963            backwardIndex--;
1964        }
1965
1966        return true;
1967    }
1968
1969    //==== End of utility methods used only in compareStrictly() =====
1970}
1971