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