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