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