PhoneNumberUtils.java revision 694bd8bf733aee372705e00d13964a20e5b16209
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        // If the number passed in is null, just return false:
1234        if (number == null) return false;
1235
1236        // Strip the separators from the number before comparing it
1237        // to the list.
1238        number = extractNetworkPortion(number);
1239
1240        // retrieve the list of emergency numbers
1241        String numbers = SystemProperties.get("ro.ril.ecclist");
1242
1243        if (!TextUtils.isEmpty(numbers)) {
1244            // searches through the comma-separated list for a match,
1245            // return true if one is found.
1246            for (String emergencyNum : numbers.split(",")) {
1247                if (emergencyNum.equals(number)) {
1248                    return true;
1249                }
1250            }
1251            // no matches found against the list!
1252            return false;
1253        }
1254
1255        //no ecclist system property, so use our own list.
1256        return (number.equals("112") || number.equals("911"));
1257    }
1258
1259    /**
1260     * isVoiceMailNumber: checks a given number against the voicemail
1261     *   number provided by the RIL and SIM card. The caller must have
1262     *   the READ_PHONE_STATE credential.
1263     *
1264     * @param number the number to look up.
1265     * @return true if the number is in the list of voicemail. False
1266     * otherwise, including if the caller does not have the permission
1267     * to read the VM number.
1268     * @hide TODO: pending API Council approval
1269     */
1270    public static boolean isVoiceMailNumber(String number) {
1271        String vmNumber;
1272
1273        try {
1274            vmNumber = TelephonyManager.getDefault().getVoiceMailNumber();
1275        } catch (SecurityException ex) {
1276            return false;
1277        }
1278
1279        // Strip the separators from the number before comparing it
1280        // to the list.
1281        number = extractNetworkPortion(number);
1282
1283        // compare tolerates null so we need to make sure that we
1284        // don't return true when both are null.
1285        return !TextUtils.isEmpty(number) && compare(number, vmNumber);
1286    }
1287
1288    /**
1289     * Translates any alphabetic letters (i.e. [A-Za-z]) in the
1290     * specified phone number into the equivalent numeric digits,
1291     * according to the phone keypad letter mapping described in
1292     * ITU E.161 and ISO/IEC 9995-8.
1293     *
1294     * @return the input string, with alpha letters converted to numeric
1295     *         digits using the phone keypad letter mapping.  For example,
1296     *         an input of "1-800-GOOG-411" will return "1-800-4664-411".
1297     */
1298    public static String convertKeypadLettersToDigits(String input) {
1299        if (input == null) {
1300            return input;
1301        }
1302        int len = input.length();
1303        if (len == 0) {
1304            return input;
1305        }
1306
1307        char[] out = input.toCharArray();
1308
1309        for (int i = 0; i < len; i++) {
1310            char c = out[i];
1311            // If this char isn't in KEYPAD_MAP at all, just leave it alone.
1312            out[i] = (char) KEYPAD_MAP.get(c, c);
1313        }
1314
1315        return new String(out);
1316    }
1317
1318    /**
1319     * The phone keypad letter mapping (see ITU E.161 or ISO/IEC 9995-8.)
1320     * TODO: This should come from a resource.
1321     */
1322    private static final SparseIntArray KEYPAD_MAP = new SparseIntArray();
1323    static {
1324        KEYPAD_MAP.put('a', '2'); KEYPAD_MAP.put('b', '2'); KEYPAD_MAP.put('c', '2');
1325        KEYPAD_MAP.put('A', '2'); KEYPAD_MAP.put('B', '2'); KEYPAD_MAP.put('C', '2');
1326
1327        KEYPAD_MAP.put('d', '3'); KEYPAD_MAP.put('e', '3'); KEYPAD_MAP.put('f', '3');
1328        KEYPAD_MAP.put('D', '3'); KEYPAD_MAP.put('E', '3'); KEYPAD_MAP.put('F', '3');
1329
1330        KEYPAD_MAP.put('g', '4'); KEYPAD_MAP.put('h', '4'); KEYPAD_MAP.put('i', '4');
1331        KEYPAD_MAP.put('G', '4'); KEYPAD_MAP.put('H', '4'); KEYPAD_MAP.put('I', '4');
1332
1333        KEYPAD_MAP.put('j', '5'); KEYPAD_MAP.put('k', '5'); KEYPAD_MAP.put('l', '5');
1334        KEYPAD_MAP.put('J', '5'); KEYPAD_MAP.put('K', '5'); KEYPAD_MAP.put('L', '5');
1335
1336        KEYPAD_MAP.put('m', '6'); KEYPAD_MAP.put('n', '6'); KEYPAD_MAP.put('o', '6');
1337        KEYPAD_MAP.put('M', '6'); KEYPAD_MAP.put('N', '6'); KEYPAD_MAP.put('O', '6');
1338
1339        KEYPAD_MAP.put('p', '7'); KEYPAD_MAP.put('q', '7'); KEYPAD_MAP.put('r', '7'); KEYPAD_MAP.put('s', '7');
1340        KEYPAD_MAP.put('P', '7'); KEYPAD_MAP.put('Q', '7'); KEYPAD_MAP.put('R', '7'); KEYPAD_MAP.put('S', '7');
1341
1342        KEYPAD_MAP.put('t', '8'); KEYPAD_MAP.put('u', '8'); KEYPAD_MAP.put('v', '8');
1343        KEYPAD_MAP.put('T', '8'); KEYPAD_MAP.put('U', '8'); KEYPAD_MAP.put('V', '8');
1344
1345        KEYPAD_MAP.put('w', '9'); KEYPAD_MAP.put('x', '9'); KEYPAD_MAP.put('y', '9'); KEYPAD_MAP.put('z', '9');
1346        KEYPAD_MAP.put('W', '9'); KEYPAD_MAP.put('X', '9'); KEYPAD_MAP.put('Y', '9'); KEYPAD_MAP.put('Z', '9');
1347    }
1348
1349    //================ Plus Code formatting =========================
1350    private static final char PLUS_SIGN_CHAR = '+';
1351    private static final String PLUS_SIGN_STRING = "+";
1352    private static final String NANP_IDP_STRING = "011";
1353    private static final int NANP_LENGTH = 10;
1354
1355    /**
1356     * This function checks if there is a plus sign (+) in the passed-in dialing number.
1357     * If there is, it processes the plus sign based on the default telephone
1358     * numbering plan of the system when the phone is activated and the current
1359     * telephone numbering plan of the system that the phone is camped on.
1360     * Currently, we only support the case that the default and current telephone
1361     * numbering plans are North American Numbering Plan(NANP).
1362     *
1363     * The passed-in dialStr should only contain the valid format as described below,
1364     * 1) the 1st character in the dialStr should be one of the really dialable
1365     *    characters listed below
1366     *    ISO-LATIN characters 0-9, *, # , +
1367     * 2) the dialStr should already strip out the separator characters,
1368     *    every character in the dialStr should be one of the non separator characters
1369     *    listed below
1370     *    ISO-LATIN characters 0-9, *, # , +, WILD, WAIT, PAUSE
1371     *
1372     * Otherwise, this function returns the dial string passed in
1373     *
1374     * @param dialStr the original dial string
1375     * @return the converted dial string if the current/default countries belong to NANP,
1376     * and if there is the "+" in the original dial string. Otherwise, the original dial
1377     * string returns.
1378     *
1379     * This API is for CDMA only
1380     *
1381     * @hide TODO: pending API Council approval
1382     */
1383    public static String cdmaCheckAndProcessPlusCode(String dialStr) {
1384        if (!TextUtils.isEmpty(dialStr)) {
1385            if (isReallyDialable(dialStr.charAt(0)) &&
1386                isNonSeparator(dialStr)) {
1387                String currIso = SystemProperties.get(PROPERTY_OPERATOR_ISO_COUNTRY, "");
1388                String defaultIso = SystemProperties.get(PROPERTY_ICC_OPERATOR_ISO_COUNTRY, "");
1389                if (!TextUtils.isEmpty(currIso) && !TextUtils.isEmpty(defaultIso)) {
1390                    return cdmaCheckAndProcessPlusCodeByNumberFormat(dialStr,
1391                            getFormatTypeFromCountryCode(currIso),
1392                            getFormatTypeFromCountryCode(defaultIso));
1393                }
1394            }
1395        }
1396        return dialStr;
1397    }
1398
1399    /**
1400     * This function should be called from checkAndProcessPlusCode only
1401     * And it is used for test purpose also.
1402     *
1403     * It checks the dial string by looping through the network portion,
1404     * post dial portion 1, post dial porting 2, etc. If there is any
1405     * plus sign, then process the plus sign.
1406     * Currently, this function supports the plus sign conversion within NANP only.
1407     * Specifically, it handles the plus sign in the following ways:
1408     * 1)+1NANP,remove +, e.g.
1409     *   +18475797000 is converted to 18475797000,
1410     * 2)+NANP or +non-NANP Numbers,replace + with the current NANP IDP, e.g,
1411     *   +8475797000 is converted to 0118475797000,
1412     *   +11875767800 is converted to 01111875767800
1413     * 3)+1NANP in post dial string(s), e.g.
1414     *   8475797000;+18475231753 is converted to 8475797000;18475231753
1415     *
1416     *
1417     * @param dialStr the original dial string
1418     * @param currFormat the numbering system of the current country that the phone is camped on
1419     * @param defaultFormat the numbering system of the country that the phone is activated on
1420     * @return the converted dial string if the current/default countries belong to NANP,
1421     * and if there is the "+" in the original dial string. Otherwise, the original dial
1422     * string returns.
1423     *
1424     * @hide
1425     */
1426    public static String
1427    cdmaCheckAndProcessPlusCodeByNumberFormat(String dialStr,int currFormat,int defaultFormt) {
1428        String retStr = dialStr;
1429
1430        // Checks if the plus sign character is in the passed-in dial string
1431        if (dialStr != null &&
1432            dialStr.lastIndexOf(PLUS_SIGN_STRING) != -1) {
1433            // Format the string based on the rules for the country the number is from,
1434            // and the current country the phone is camped on.
1435            if ((currFormat == defaultFormt) && (currFormat == FORMAT_NANP)) {
1436                // Handle case where default and current telephone numbering plans are NANP.
1437                String postDialStr = null;
1438                String tempDialStr = dialStr;
1439
1440                // Sets the retStr to null since the conversion will be performed below.
1441                retStr = null;
1442                if (DBG) log("checkAndProcessPlusCode,dialStr=" + dialStr);
1443                // This routine is to process the plus sign in the dial string by loop through
1444                // the network portion, post dial portion 1, post dial portion 2... etc. if
1445                // applied
1446                do {
1447                    String networkDialStr;
1448                    networkDialStr = extractNetworkPortion(tempDialStr);
1449                    // Handles the conversion within NANP
1450                    networkDialStr = processPlusCodeWithinNanp(networkDialStr);
1451
1452                    // Concatenates the string that is converted from network portion
1453                    if (!TextUtils.isEmpty(networkDialStr)) {
1454                        if (retStr == null) {
1455                            retStr = networkDialStr;
1456                        } else {
1457                            retStr = retStr.concat(networkDialStr);
1458                        }
1459                    } else {
1460                        // This should never happen since we checked the if dialStr is null
1461                        // and if it contains the plus sign in the beginning of this function.
1462                        // The plus sign is part of the network portion.
1463                        Log.e("checkAndProcessPlusCode: null newDialStr", networkDialStr);
1464                        return dialStr;
1465                    }
1466                    postDialStr = extractPostDialPortion(tempDialStr);
1467                    if (!TextUtils.isEmpty(postDialStr)) {
1468                        int dialableIndex = findDialableIndexFromPostDialStr(postDialStr);
1469
1470                        // dialableIndex should always be greater than 0
1471                        if (dialableIndex >= 1) {
1472                            retStr = appendPwCharBackToOrigDialStr(dialableIndex,
1473                                     retStr,postDialStr);
1474                            // Skips the P/W character, extracts the dialable portion
1475                            tempDialStr = postDialStr.substring(dialableIndex);
1476                        } else {
1477                            // Non-dialable character such as P/W should not be at the end of
1478                            // the dial string after P/W processing in CdmaConnection.java
1479                            // Set the postDialStr to "" to break out of the loop
1480                            if (dialableIndex < 0) {
1481                                postDialStr = "";
1482                            }
1483                            Log.e("wrong postDialStr=", postDialStr);
1484                        }
1485                    }
1486                    if (DBG) log("checkAndProcessPlusCode,postDialStr=" + postDialStr);
1487                } while (!TextUtils.isEmpty(postDialStr) && !TextUtils.isEmpty(tempDialStr));
1488            } else {
1489                // TODO: Support NANP international conversion and other telephone numbering plans.
1490                // Currently the phone is never used in non-NANP system, so return the original
1491                // dial string.
1492                Log.e("checkAndProcessPlusCode:non-NANP not supported", dialStr);
1493            }
1494        }
1495        return retStr;
1496     }
1497
1498    // This function gets the default international dialing prefix
1499    private static String getDefaultIdp( ) {
1500        String ps = null;
1501        SystemProperties.get(PROPERTY_IDP_STRING, ps);
1502        if (TextUtils.isEmpty(ps)) {
1503            ps = NANP_IDP_STRING;
1504        }
1505        return ps;
1506    }
1507
1508    private static boolean isTwoToNine (char c) {
1509        if (c >= '2' && c <= '9') {
1510            return true;
1511        } else {
1512            return false;
1513        }
1514    }
1515
1516    private static int getFormatTypeFromCountryCode (String country) {
1517        // Check for the NANP countries
1518        int length = NANP_COUNTRIES.length;
1519        for (int i = 0; i < length; i++) {
1520            if (NANP_COUNTRIES[i].compareToIgnoreCase(country) == 0) {
1521                return FORMAT_NANP;
1522            }
1523        }
1524        if ("jp".compareToIgnoreCase(country) == 0) {
1525            return FORMAT_JAPAN;
1526        }
1527        return FORMAT_UNKNOWN;
1528    }
1529
1530    /**
1531     * This function checks if the passed in string conforms to the NANP format
1532     * i.e. NXX-NXX-XXXX, N is any digit 2-9 and X is any digit 0-9
1533     */
1534    private static boolean isNanp (String dialStr) {
1535        boolean retVal = false;
1536        if (dialStr != null) {
1537            if (dialStr.length() == NANP_LENGTH) {
1538                if (isTwoToNine(dialStr.charAt(0)) &&
1539                    isTwoToNine(dialStr.charAt(3))) {
1540                    retVal = true;
1541                    for (int i=1; i<NANP_LENGTH; i++ ) {
1542                        char c=dialStr.charAt(i);
1543                        if (!PhoneNumberUtils.isISODigit(c)) {
1544                            retVal = false;
1545                            break;
1546                        }
1547                    }
1548                }
1549            }
1550        } else {
1551            Log.e("isNanp: null dialStr passed in", dialStr);
1552        }
1553        return retVal;
1554    }
1555
1556   /**
1557    * This function checks if the passed in string conforms to 1-NANP format
1558    */
1559    private static boolean isOneNanp(String dialStr) {
1560        boolean retVal = false;
1561        if (dialStr != null) {
1562            String newDialStr = dialStr.substring(1);
1563            if ((dialStr.charAt(0) == '1') && isNanp(newDialStr)) {
1564                retVal = true;
1565            }
1566        } else {
1567            Log.e("isOneNanp: null dialStr passed in", dialStr);
1568        }
1569        return retVal;
1570    }
1571
1572    /**
1573     * This function handles the plus code conversion within NANP CDMA network
1574     * If the number format is
1575     * 1)+1NANP,remove +,
1576     * 2)other than +1NANP, any + numbers,replace + with the current IDP
1577     */
1578    private static String processPlusCodeWithinNanp(String networkDialStr) {
1579        String retStr = networkDialStr;
1580
1581        if (DBG) log("processPlusCodeWithinNanp,networkDialStr=" + networkDialStr);
1582        // If there is a plus sign at the beginning of the dial string,
1583        // Convert the plus sign to the default IDP since it's an international number
1584        if (networkDialStr != null &
1585            networkDialStr.charAt(0) == PLUS_SIGN_CHAR &&
1586            networkDialStr.length() > 1) {
1587            String newStr = networkDialStr.substring(1);
1588            if (isOneNanp(newStr)) {
1589                // Remove the leading plus sign
1590                retStr = newStr;
1591             } else {
1592                 String idpStr = getDefaultIdp();
1593                 // Replaces the plus sign with the default IDP
1594                 retStr = networkDialStr.replaceFirst("[+]", idpStr);
1595            }
1596        }
1597        if (DBG) log("processPlusCodeWithinNanp,retStr=" + retStr);
1598        return retStr;
1599    }
1600
1601    // This function finds the index of the dialable character(s)
1602    // in the post dial string
1603    private static int findDialableIndexFromPostDialStr(String postDialStr) {
1604        for (int index = 0;index < postDialStr.length();index++) {
1605             char c = postDialStr.charAt(index);
1606             if (isReallyDialable(c)) {
1607                return index;
1608             }
1609        }
1610        return -1;
1611    }
1612
1613    // This function appends the non-diablable P/W character to the original
1614    // dial string based on the dialable index passed in
1615    private static String
1616    appendPwCharBackToOrigDialStr(int dialableIndex,String origStr, String dialStr) {
1617        String retStr;
1618
1619        // There is only 1 P/W character before the dialable characters
1620        if (dialableIndex == 1) {
1621            StringBuilder ret = new StringBuilder(origStr);
1622            ret = ret.append(dialStr.charAt(0));
1623            retStr = ret.toString();
1624        } else {
1625            // It means more than 1 P/W characters in the post dial string,
1626            // appends to retStr
1627            String nonDigitStr = dialStr.substring(0,dialableIndex);
1628            retStr = origStr.concat(nonDigitStr);
1629        }
1630        return retStr;
1631    }
1632
1633    //===== Begining of utility methods used in compareLoosely() =====
1634
1635    /**
1636     * Phone numbers are stored in "lookup" form in the database
1637     * as reversed strings to allow for caller ID lookup
1638     *
1639     * This method takes a phone number and makes a valid SQL "LIKE"
1640     * string that will match the lookup form
1641     *
1642     */
1643    /** all of a up to len must be an international prefix or
1644     *  separators/non-dialing digits
1645     */
1646    private static boolean
1647    matchIntlPrefix(String a, int len) {
1648        /* '([^0-9*#+pwn]\+[^0-9*#+pwn] | [^0-9*#+pwn]0(0|11)[^0-9*#+pwn] )$' */
1649        /*        0       1                           2 3 45               */
1650
1651        int state = 0;
1652        for (int i = 0 ; i < len ; i++) {
1653            char c = a.charAt(i);
1654
1655            switch (state) {
1656                case 0:
1657                    if      (c == '+') state = 1;
1658                    else if (c == '0') state = 2;
1659                    else if (isNonSeparator(c)) return false;
1660                break;
1661
1662                case 2:
1663                    if      (c == '0') state = 3;
1664                    else if (c == '1') state = 4;
1665                    else if (isNonSeparator(c)) return false;
1666                break;
1667
1668                case 4:
1669                    if      (c == '1') state = 5;
1670                    else if (isNonSeparator(c)) return false;
1671                break;
1672
1673                default:
1674                    if (isNonSeparator(c)) return false;
1675                break;
1676
1677            }
1678        }
1679
1680        return state == 1 || state == 3 || state == 5;
1681    }
1682
1683    /** all of 'a' up to len must be a (+|00|011)country code)
1684     *  We're fast and loose with the country code. Any \d{1,3} matches */
1685    private static boolean
1686    matchIntlPrefixAndCC(String a, int len) {
1687        /*  [^0-9*#+pwn]*(\+|0(0|11)\d\d?\d? [^0-9*#+pwn] $ */
1688        /*      0          1 2 3 45  6 7  8                 */
1689
1690        int state = 0;
1691        for (int i = 0 ; i < len ; i++ ) {
1692            char c = a.charAt(i);
1693
1694            switch (state) {
1695                case 0:
1696                    if      (c == '+') state = 1;
1697                    else if (c == '0') state = 2;
1698                    else if (isNonSeparator(c)) return false;
1699                break;
1700
1701                case 2:
1702                    if      (c == '0') state = 3;
1703                    else if (c == '1') state = 4;
1704                    else if (isNonSeparator(c)) return false;
1705                break;
1706
1707                case 4:
1708                    if      (c == '1') state = 5;
1709                    else if (isNonSeparator(c)) return false;
1710                break;
1711
1712                case 1:
1713                case 3:
1714                case 5:
1715                    if      (isISODigit(c)) state = 6;
1716                    else if (isNonSeparator(c)) return false;
1717                break;
1718
1719                case 6:
1720                case 7:
1721                    if      (isISODigit(c)) state++;
1722                    else if (isNonSeparator(c)) return false;
1723                break;
1724
1725                default:
1726                    if (isNonSeparator(c)) return false;
1727            }
1728        }
1729
1730        return state == 6 || state == 7 || state == 8;
1731    }
1732
1733    /** all of 'a' up to len must match non-US trunk prefix ('0') */
1734    private static boolean
1735    matchTrunkPrefix(String a, int len) {
1736        boolean found;
1737
1738        found = false;
1739
1740        for (int i = 0 ; i < len ; i++) {
1741            char c = a.charAt(i);
1742
1743            if (c == '0' && !found) {
1744                found = true;
1745            } else if (isNonSeparator(c)) {
1746                return false;
1747            }
1748        }
1749
1750        return found;
1751    }
1752
1753    //===== End of utility methods used only in compareLoosely() =====
1754
1755    //===== Beggining of utility methods used only in compareStrictly() ====
1756
1757    /*
1758     * If true, the number is country calling code.
1759     */
1760    private static final boolean COUNTLY_CALLING_CALL[] = {
1761        true, true, false, false, false, false, false, true, false, false,
1762        false, false, false, false, false, false, false, false, false, false,
1763        true, false, false, false, false, false, false, true, true, false,
1764        true, true, true, true, true, false, true, false, false, true,
1765        true, false, false, true, true, true, true, true, true, true,
1766        false, true, true, true, true, true, true, true, true, false,
1767        true, true, true, true, true, true, true, false, false, false,
1768        false, false, false, false, false, false, false, false, false, false,
1769        false, true, true, true, true, false, true, false, false, true,
1770        true, true, true, true, true, true, false, false, true, false,
1771    };
1772    private static final int CCC_LENGTH = COUNTLY_CALLING_CALL.length;
1773
1774    /**
1775     * @return true when input is valid Country Calling Code.
1776     */
1777    private static boolean isCountryCallingCode(int countryCallingCodeCandidate) {
1778        return countryCallingCodeCandidate > 0 && countryCallingCodeCandidate < CCC_LENGTH &&
1779                COUNTLY_CALLING_CALL[countryCallingCodeCandidate];
1780    }
1781
1782    /**
1783     * Returns interger corresponding to the input if input "ch" is
1784     * ISO-LATIN characters 0-9.
1785     * Returns -1 otherwise
1786     */
1787    private static int tryGetISODigit(char ch) {
1788        if ('0' <= ch && ch <= '9') {
1789            return ch - '0';
1790        } else {
1791            return -1;
1792        }
1793    }
1794
1795    private static class CountryCallingCodeAndNewIndex {
1796        public final int countryCallingCode;
1797        public final int newIndex;
1798        public CountryCallingCodeAndNewIndex(int countryCode, int newIndex) {
1799            this.countryCallingCode = countryCode;
1800            this.newIndex = newIndex;
1801        }
1802    }
1803
1804    /*
1805     * Note that this function does not strictly care the country calling code with
1806     * 3 length (like Morocco: +212), assuming it is enough to use the first two
1807     * digit to compare two phone numbers.
1808     */
1809    private static CountryCallingCodeAndNewIndex tryGetCountryCallingCodeAndNewIndex(
1810        String str, boolean acceptThailandCase) {
1811        // Rough regexp:
1812        //  ^[^0-9*#+]*((\+|0(0|11)\d\d?|166) [^0-9*#+] $
1813        //         0        1 2 3 45  6 7  89
1814        //
1815        // In all the states, this function ignores separator characters.
1816        // "166" is the special case for the call from Thailand to the US. Uguu!
1817        int state = 0;
1818        int ccc = 0;
1819        final int length = str.length();
1820        for (int i = 0 ; i < length ; i++ ) {
1821            char ch = str.charAt(i);
1822            switch (state) {
1823                case 0:
1824                    if      (ch == '+') state = 1;
1825                    else if (ch == '0') state = 2;
1826                    else if (ch == '1') {
1827                        if (acceptThailandCase) {
1828                            state = 8;
1829                        } else {
1830                            return null;
1831                        }
1832                    } else if (isDialable(ch)) {
1833                        return null;
1834                    }
1835                break;
1836
1837                case 2:
1838                    if      (ch == '0') state = 3;
1839                    else if (ch == '1') state = 4;
1840                    else if (isDialable(ch)) {
1841                        return null;
1842                    }
1843                break;
1844
1845                case 4:
1846                    if      (ch == '1') state = 5;
1847                    else if (isDialable(ch)) {
1848                        return null;
1849                    }
1850                break;
1851
1852                case 1:
1853                case 3:
1854                case 5:
1855                case 6:
1856                case 7:
1857                    {
1858                        int ret = tryGetISODigit(ch);
1859                        if (ret > 0) {
1860                            ccc = ccc * 10 + ret;
1861                            if (ccc >= 100 || isCountryCallingCode(ccc)) {
1862                                return new CountryCallingCodeAndNewIndex(ccc, i + 1);
1863                            }
1864                            if (state == 1 || state == 3 || state == 5) {
1865                                state = 6;
1866                            } else {
1867                                state++;
1868                            }
1869                        } else if (isDialable(ch)) {
1870                            return null;
1871                        }
1872                    }
1873                    break;
1874                case 8:
1875                    if (ch == '6') state = 9;
1876                    else if (isDialable(ch)) {
1877                        return null;
1878                    }
1879                    break;
1880                case 9:
1881                    if (ch == '6') {
1882                        return new CountryCallingCodeAndNewIndex(66, i + 1);
1883                    } else {
1884                        return null;
1885                    }
1886                default:
1887                    return null;
1888            }
1889        }
1890
1891        return null;
1892    }
1893
1894    /**
1895     * Currently this function simply ignore the first digit assuming it is
1896     * trunk prefix. Actually trunk prefix is different in each country.
1897     *
1898     * e.g.
1899     * "+79161234567" equals "89161234567" (Russian trunk digit is 8)
1900     * "+33123456789" equals "0123456789" (French trunk digit is 0)
1901     *
1902     */
1903    private static int tryGetTrunkPrefixOmittedIndex(String str, int currentIndex) {
1904        int length = str.length();
1905        for (int i = currentIndex ; i < length ; i++) {
1906            final char ch = str.charAt(i);
1907            if (tryGetISODigit(ch) >= 0) {
1908                return i + 1;
1909            } else if (isDialable(ch)) {
1910                return -1;
1911            }
1912        }
1913        return -1;
1914    }
1915
1916    /**
1917     * Return true if the prefix of "str" is "ignorable". Here, "ignorable" means
1918     * that "str" has only one digit and separater characters. The one digit is
1919     * assumed to be trunk prefix.
1920     */
1921    private static boolean checkPrefixIsIgnorable(final String str,
1922            int forwardIndex, int backwardIndex) {
1923        boolean trunk_prefix_was_read = false;
1924        while (backwardIndex >= forwardIndex) {
1925            if (tryGetISODigit(str.charAt(backwardIndex)) >= 0) {
1926                if (trunk_prefix_was_read) {
1927                    // More than one digit appeared, meaning that "a" and "b"
1928                    // is different.
1929                    return false;
1930                } else {
1931                    // Ignore just one digit, assuming it is trunk prefix.
1932                    trunk_prefix_was_read = true;
1933                }
1934            } else if (isDialable(str.charAt(backwardIndex))) {
1935                // Trunk prefix is a digit, not "*", "#"...
1936                return false;
1937            }
1938            backwardIndex--;
1939        }
1940
1941        return true;
1942    }
1943
1944    //==== End of utility methods used only in compareStrictly() =====
1945}
1946