PhoneNumberUtils.java revision fd7b4f1d12779a0363e98a37e7f5a7ccacba5ee8
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     * Returns the phone number formatting type for the given locale.
1075     *
1076     * @param locale The locale of interest, usually {@link Locale#getDefault()}
1077     * @return the formatting type for the given locale, or FORMAT_UNKNOWN if the formatting
1078     * rules are not known for the given locale
1079     */
1080    public static int getFormatTypeForLocale(Locale locale) {
1081        String country = locale.getCountry();
1082
1083        return getFormatTypeFromCountryCode(country);
1084    }
1085
1086    /**
1087     * Formats a phone number in-place. Currently only supports NANP formatting.
1088     *
1089     * @param text The number to be formatted, will be modified with the formatting
1090     * @param defaultFormattingType The default formatting rules to apply if the number does
1091     * not begin with +<country_code>
1092     */
1093    public static void formatNumber(Editable text, int defaultFormattingType) {
1094        int formatType = defaultFormattingType;
1095
1096        if (text.length() > 2 && text.charAt(0) == '+') {
1097            if (text.charAt(1) == '1') {
1098                formatType = FORMAT_NANP;
1099            } else if (text.length() >= 3 && text.charAt(1) == '8'
1100                && text.charAt(2) == '1') {
1101                formatType = FORMAT_JAPAN;
1102            } else {
1103                return;
1104            }
1105        }
1106
1107        switch (formatType) {
1108            case FORMAT_NANP:
1109                formatNanpNumber(text);
1110                return;
1111            case FORMAT_JAPAN:
1112                formatJapaneseNumber(text);
1113                return;
1114        }
1115    }
1116
1117    private static final int NANP_STATE_DIGIT = 1;
1118    private static final int NANP_STATE_PLUS = 2;
1119    private static final int NANP_STATE_ONE = 3;
1120    private static final int NANP_STATE_DASH = 4;
1121
1122    /**
1123     * Formats a phone number in-place using the NANP formatting rules. Numbers will be formatted
1124     * as:
1125     *
1126     * <p><code>
1127     * xxxxx
1128     * xxx-xxxx
1129     * xxx-xxx-xxxx
1130     * 1-xxx-xxx-xxxx
1131     * +1-xxx-xxx-xxxx
1132     * </code></p>
1133     *
1134     * @param text the number to be formatted, will be modified with the formatting
1135     */
1136    public static void formatNanpNumber(Editable text) {
1137        int length = text.length();
1138        if (length > "+1-nnn-nnn-nnnn".length()) {
1139            // The string is too long to be formatted
1140            return;
1141        } else if (length <= 5) {
1142            // The string is either a shortcode or too short to be formatted
1143            return;
1144        }
1145
1146        CharSequence saved = text.subSequence(0, length);
1147
1148        // Strip the dashes first, as we're going to add them back
1149        int p = 0;
1150        while (p < text.length()) {
1151            if (text.charAt(p) == '-') {
1152                text.delete(p, p + 1);
1153            } else {
1154                p++;
1155            }
1156        }
1157        length = text.length();
1158
1159        // When scanning the number we record where dashes need to be added,
1160        // if they're non-0 at the end of the scan the dashes will be added in
1161        // the proper places.
1162        int dashPositions[] = new int[3];
1163        int numDashes = 0;
1164
1165        int state = NANP_STATE_DIGIT;
1166        int numDigits = 0;
1167        for (int i = 0; i < length; i++) {
1168            char c = text.charAt(i);
1169            switch (c) {
1170                case '1':
1171                    if (numDigits == 0 || state == NANP_STATE_PLUS) {
1172                        state = NANP_STATE_ONE;
1173                        break;
1174                    }
1175                    // fall through
1176                case '2':
1177                case '3':
1178                case '4':
1179                case '5':
1180                case '6':
1181                case '7':
1182                case '8':
1183                case '9':
1184                case '0':
1185                    if (state == NANP_STATE_PLUS) {
1186                        // Only NANP number supported for now
1187                        text.replace(0, length, saved);
1188                        return;
1189                    } else if (state == NANP_STATE_ONE) {
1190                        // Found either +1 or 1, follow it up with a dash
1191                        dashPositions[numDashes++] = i;
1192                    } else if (state != NANP_STATE_DASH && (numDigits == 3 || numDigits == 6)) {
1193                        // Found a digit that should be after a dash that isn't
1194                        dashPositions[numDashes++] = i;
1195                    }
1196                    state = NANP_STATE_DIGIT;
1197                    numDigits++;
1198                    break;
1199
1200                case '-':
1201                    state = NANP_STATE_DASH;
1202                    break;
1203
1204                case '+':
1205                    if (i == 0) {
1206                        // Plus is only allowed as the first character
1207                        state = NANP_STATE_PLUS;
1208                        break;
1209                    }
1210                    // Fall through
1211                default:
1212                    // Unknown character, bail on formatting
1213                    text.replace(0, length, saved);
1214                    return;
1215            }
1216        }
1217
1218        if (numDigits == 7) {
1219            // With 7 digits we want xxx-xxxx, not xxx-xxx-x
1220            numDashes--;
1221        }
1222
1223        // Actually put the dashes in place
1224        for (int i = 0; i < numDashes; i++) {
1225            int pos = dashPositions[i];
1226            text.replace(pos + i, pos + i, "-");
1227        }
1228
1229        // Remove trailing dashes
1230        int len = text.length();
1231        while (len > 0) {
1232            if (text.charAt(len - 1) == '-') {
1233                text.delete(len - 1, len);
1234                len--;
1235            } else {
1236                break;
1237            }
1238        }
1239    }
1240
1241    /**
1242     * Formats a phone number in-place using the Japanese formatting rules.
1243     * Numbers will be formatted as:
1244     *
1245     * <p><code>
1246     * 03-xxxx-xxxx
1247     * 090-xxxx-xxxx
1248     * 0120-xxx-xxx
1249     * +81-3-xxxx-xxxx
1250     * +81-90-xxxx-xxxx
1251     * </code></p>
1252     *
1253     * @param text the number to be formatted, will be modified with
1254     * the formatting
1255     */
1256    public static void formatJapaneseNumber(Editable text) {
1257        JapanesePhoneNumberFormatter.format(text);
1258    }
1259
1260    // Three and four digit phone numbers for either special services,
1261    // or 3-6 digit addresses from the network (eg carrier-originated SMS messages) should
1262    // not match.
1263    //
1264    // This constant used to be 5, but SMS short codes has increased in length and
1265    // can be easily 6 digits now days. Most countries have SMS short code length between
1266    // 3 to 6 digits. The exceptions are
1267    //
1268    // Australia: Short codes are six or eight digits in length, starting with the prefix "19"
1269    //            followed by an additional four or six digits and two.
1270    // Czech Republic: Codes are seven digits in length for MO and five (not billed) or
1271    //            eight (billed) for MT direction
1272    //
1273    // see http://en.wikipedia.org/wiki/Short_code#Regional_differences for reference
1274    //
1275    // However, in order to loose match 650-555-1212 and 555-1212, we need to set the min match
1276    // to 7.
1277    static final int MIN_MATCH = 7;
1278
1279    /**
1280     * isEmergencyNumber: checks a given number against the list of
1281     *   emergency numbers provided by the RIL and SIM card.
1282     *
1283     * @param number the number to look up.
1284     * @return if the number is in the list of emergency numbers
1285     * listed in the ril / sim, then return true, otherwise false.
1286     */
1287    public static boolean isEmergencyNumber(String number) {
1288        // If the number passed in is null, just return false:
1289        if (number == null) return false;
1290
1291        // Strip the separators from the number before comparing it
1292        // to the list.
1293        number = extractNetworkPortionAlt(number);
1294
1295        // retrieve the list of emergency numbers
1296        String numbers = SystemProperties.get("ro.ril.ecclist");
1297
1298        if (!TextUtils.isEmpty(numbers)) {
1299            // searches through the comma-separated list for a match,
1300            // return true if one is found.
1301            for (String emergencyNum : numbers.split(",")) {
1302                if (emergencyNum.equals(number)) {
1303                    return true;
1304                }
1305            }
1306            // no matches found against the list!
1307            return false;
1308        }
1309
1310        //no ecclist system property, so use our own list.
1311        return (number.equals("112") || number.equals("911"));
1312    }
1313
1314    /**
1315     * isVoiceMailNumber: checks a given number against the voicemail
1316     *   number provided by the RIL and SIM card. The caller must have
1317     *   the READ_PHONE_STATE credential.
1318     *
1319     * @param number the number to look up.
1320     * @return true if the number is in the list of voicemail. False
1321     * otherwise, including if the caller does not have the permission
1322     * to read the VM number.
1323     * @hide TODO: pending API Council approval
1324     */
1325    public static boolean isVoiceMailNumber(String number) {
1326        String vmNumber;
1327
1328        try {
1329            vmNumber = TelephonyManager.getDefault().getVoiceMailNumber();
1330        } catch (SecurityException ex) {
1331            return false;
1332        }
1333
1334        // Strip the separators from the number before comparing it
1335        // to the list.
1336        number = extractNetworkPortionAlt(number);
1337
1338        // compare tolerates null so we need to make sure that we
1339        // don't return true when both are null.
1340        return !TextUtils.isEmpty(number) && compare(number, vmNumber);
1341    }
1342
1343    /**
1344     * Translates any alphabetic letters (i.e. [A-Za-z]) in the
1345     * specified phone number into the equivalent numeric digits,
1346     * according to the phone keypad letter mapping described in
1347     * ITU E.161 and ISO/IEC 9995-8.
1348     *
1349     * @return the input string, with alpha letters converted to numeric
1350     *         digits using the phone keypad letter mapping.  For example,
1351     *         an input of "1-800-GOOG-411" will return "1-800-4664-411".
1352     */
1353    public static String convertKeypadLettersToDigits(String input) {
1354        if (input == null) {
1355            return input;
1356        }
1357        int len = input.length();
1358        if (len == 0) {
1359            return input;
1360        }
1361
1362        char[] out = input.toCharArray();
1363
1364        for (int i = 0; i < len; i++) {
1365            char c = out[i];
1366            // If this char isn't in KEYPAD_MAP at all, just leave it alone.
1367            out[i] = (char) KEYPAD_MAP.get(c, c);
1368        }
1369
1370        return new String(out);
1371    }
1372
1373    /**
1374     * The phone keypad letter mapping (see ITU E.161 or ISO/IEC 9995-8.)
1375     * TODO: This should come from a resource.
1376     */
1377    private static final SparseIntArray KEYPAD_MAP = new SparseIntArray();
1378    static {
1379        KEYPAD_MAP.put('a', '2'); KEYPAD_MAP.put('b', '2'); KEYPAD_MAP.put('c', '2');
1380        KEYPAD_MAP.put('A', '2'); KEYPAD_MAP.put('B', '2'); KEYPAD_MAP.put('C', '2');
1381
1382        KEYPAD_MAP.put('d', '3'); KEYPAD_MAP.put('e', '3'); KEYPAD_MAP.put('f', '3');
1383        KEYPAD_MAP.put('D', '3'); KEYPAD_MAP.put('E', '3'); KEYPAD_MAP.put('F', '3');
1384
1385        KEYPAD_MAP.put('g', '4'); KEYPAD_MAP.put('h', '4'); KEYPAD_MAP.put('i', '4');
1386        KEYPAD_MAP.put('G', '4'); KEYPAD_MAP.put('H', '4'); KEYPAD_MAP.put('I', '4');
1387
1388        KEYPAD_MAP.put('j', '5'); KEYPAD_MAP.put('k', '5'); KEYPAD_MAP.put('l', '5');
1389        KEYPAD_MAP.put('J', '5'); KEYPAD_MAP.put('K', '5'); KEYPAD_MAP.put('L', '5');
1390
1391        KEYPAD_MAP.put('m', '6'); KEYPAD_MAP.put('n', '6'); KEYPAD_MAP.put('o', '6');
1392        KEYPAD_MAP.put('M', '6'); KEYPAD_MAP.put('N', '6'); KEYPAD_MAP.put('O', '6');
1393
1394        KEYPAD_MAP.put('p', '7'); KEYPAD_MAP.put('q', '7'); KEYPAD_MAP.put('r', '7'); KEYPAD_MAP.put('s', '7');
1395        KEYPAD_MAP.put('P', '7'); KEYPAD_MAP.put('Q', '7'); KEYPAD_MAP.put('R', '7'); KEYPAD_MAP.put('S', '7');
1396
1397        KEYPAD_MAP.put('t', '8'); KEYPAD_MAP.put('u', '8'); KEYPAD_MAP.put('v', '8');
1398        KEYPAD_MAP.put('T', '8'); KEYPAD_MAP.put('U', '8'); KEYPAD_MAP.put('V', '8');
1399
1400        KEYPAD_MAP.put('w', '9'); KEYPAD_MAP.put('x', '9'); KEYPAD_MAP.put('y', '9'); KEYPAD_MAP.put('z', '9');
1401        KEYPAD_MAP.put('W', '9'); KEYPAD_MAP.put('X', '9'); KEYPAD_MAP.put('Y', '9'); KEYPAD_MAP.put('Z', '9');
1402    }
1403
1404    //================ Plus Code formatting =========================
1405    private static final char PLUS_SIGN_CHAR = '+';
1406    private static final String PLUS_SIGN_STRING = "+";
1407    private static final String NANP_IDP_STRING = "011";
1408    private static final int NANP_LENGTH = 10;
1409
1410    /**
1411     * This function checks if there is a plus sign (+) in the passed-in dialing number.
1412     * If there is, it processes the plus sign based on the default telephone
1413     * numbering plan of the system when the phone is activated and the current
1414     * telephone numbering plan of the system that the phone is camped on.
1415     * Currently, we only support the case that the default and current telephone
1416     * numbering plans are North American Numbering Plan(NANP).
1417     *
1418     * The passed-in dialStr should only contain the valid format as described below,
1419     * 1) the 1st character in the dialStr should be one of the really dialable
1420     *    characters listed below
1421     *    ISO-LATIN characters 0-9, *, # , +
1422     * 2) the dialStr should already strip out the separator characters,
1423     *    every character in the dialStr should be one of the non separator characters
1424     *    listed below
1425     *    ISO-LATIN characters 0-9, *, # , +, WILD, WAIT, PAUSE
1426     *
1427     * Otherwise, this function returns the dial string passed in
1428     *
1429     * @param dialStr the original dial string
1430     * @return the converted dial string if the current/default countries belong to NANP,
1431     * and if there is the "+" in the original dial string. Otherwise, the original dial
1432     * string returns.
1433     *
1434     * This API is for CDMA only
1435     *
1436     * @hide TODO: pending API Council approval
1437     */
1438    public static String cdmaCheckAndProcessPlusCode(String dialStr) {
1439        if (!TextUtils.isEmpty(dialStr)) {
1440            if (isReallyDialable(dialStr.charAt(0)) &&
1441                isNonSeparator(dialStr)) {
1442                String currIso = SystemProperties.get(PROPERTY_OPERATOR_ISO_COUNTRY, "");
1443                String defaultIso = SystemProperties.get(PROPERTY_ICC_OPERATOR_ISO_COUNTRY, "");
1444                if (!TextUtils.isEmpty(currIso) && !TextUtils.isEmpty(defaultIso)) {
1445                    return cdmaCheckAndProcessPlusCodeByNumberFormat(dialStr,
1446                            getFormatTypeFromCountryCode(currIso),
1447                            getFormatTypeFromCountryCode(defaultIso));
1448                }
1449            }
1450        }
1451        return dialStr;
1452    }
1453
1454    /**
1455     * This function should be called from checkAndProcessPlusCode only
1456     * And it is used for test purpose also.
1457     *
1458     * It checks the dial string by looping through the network portion,
1459     * post dial portion 1, post dial porting 2, etc. If there is any
1460     * plus sign, then process the plus sign.
1461     * Currently, this function supports the plus sign conversion within NANP only.
1462     * Specifically, it handles the plus sign in the following ways:
1463     * 1)+1NANP,remove +, e.g.
1464     *   +18475797000 is converted to 18475797000,
1465     * 2)+NANP or +non-NANP Numbers,replace + with the current NANP IDP, e.g,
1466     *   +8475797000 is converted to 0118475797000,
1467     *   +11875767800 is converted to 01111875767800
1468     * 3)+1NANP in post dial string(s), e.g.
1469     *   8475797000;+18475231753 is converted to 8475797000;18475231753
1470     *
1471     *
1472     * @param dialStr the original dial string
1473     * @param currFormat the numbering system of the current country that the phone is camped on
1474     * @param defaultFormat the numbering system of the country that the phone is activated on
1475     * @return the converted dial string if the current/default countries belong to NANP,
1476     * and if there is the "+" in the original dial string. Otherwise, the original dial
1477     * string returns.
1478     *
1479     * @hide
1480     */
1481    public static String
1482    cdmaCheckAndProcessPlusCodeByNumberFormat(String dialStr,int currFormat,int defaultFormt) {
1483        String retStr = dialStr;
1484
1485        // Checks if the plus sign character is in the passed-in dial string
1486        if (dialStr != null &&
1487            dialStr.lastIndexOf(PLUS_SIGN_STRING) != -1) {
1488            // Format the string based on the rules for the country the number is from,
1489            // and the current country the phone is camped on.
1490            if ((currFormat == defaultFormt) && (currFormat == FORMAT_NANP)) {
1491                // Handle case where default and current telephone numbering plans are NANP.
1492                String postDialStr = null;
1493                String tempDialStr = dialStr;
1494
1495                // Sets the retStr to null since the conversion will be performed below.
1496                retStr = null;
1497                if (DBG) log("checkAndProcessPlusCode,dialStr=" + dialStr);
1498                // This routine is to process the plus sign in the dial string by loop through
1499                // the network portion, post dial portion 1, post dial portion 2... etc. if
1500                // applied
1501                do {
1502                    String networkDialStr;
1503                    networkDialStr = extractNetworkPortion(tempDialStr);
1504                    // Handles the conversion within NANP
1505                    networkDialStr = processPlusCodeWithinNanp(networkDialStr);
1506
1507                    // Concatenates the string that is converted from network portion
1508                    if (!TextUtils.isEmpty(networkDialStr)) {
1509                        if (retStr == null) {
1510                            retStr = networkDialStr;
1511                        } else {
1512                            retStr = retStr.concat(networkDialStr);
1513                        }
1514                    } else {
1515                        // This should never happen since we checked the if dialStr is null
1516                        // and if it contains the plus sign in the beginning of this function.
1517                        // The plus sign is part of the network portion.
1518                        Log.e("checkAndProcessPlusCode: null newDialStr", networkDialStr);
1519                        return dialStr;
1520                    }
1521                    postDialStr = extractPostDialPortion(tempDialStr);
1522                    if (!TextUtils.isEmpty(postDialStr)) {
1523                        int dialableIndex = findDialableIndexFromPostDialStr(postDialStr);
1524
1525                        // dialableIndex should always be greater than 0
1526                        if (dialableIndex >= 1) {
1527                            retStr = appendPwCharBackToOrigDialStr(dialableIndex,
1528                                     retStr,postDialStr);
1529                            // Skips the P/W character, extracts the dialable portion
1530                            tempDialStr = postDialStr.substring(dialableIndex);
1531                        } else {
1532                            // Non-dialable character such as P/W should not be at the end of
1533                            // the dial string after P/W processing in CdmaConnection.java
1534                            // Set the postDialStr to "" to break out of the loop
1535                            if (dialableIndex < 0) {
1536                                postDialStr = "";
1537                            }
1538                            Log.e("wrong postDialStr=", postDialStr);
1539                        }
1540                    }
1541                    if (DBG) log("checkAndProcessPlusCode,postDialStr=" + postDialStr);
1542                } while (!TextUtils.isEmpty(postDialStr) && !TextUtils.isEmpty(tempDialStr));
1543            } else {
1544                // TODO: Support NANP international conversion and other telephone numbering plans.
1545                // Currently the phone is never used in non-NANP system, so return the original
1546                // dial string.
1547                Log.e("checkAndProcessPlusCode:non-NANP not supported", dialStr);
1548            }
1549        }
1550        return retStr;
1551     }
1552
1553    // This function gets the default international dialing prefix
1554    private static String getDefaultIdp( ) {
1555        String ps = null;
1556        SystemProperties.get(PROPERTY_IDP_STRING, ps);
1557        if (TextUtils.isEmpty(ps)) {
1558            ps = NANP_IDP_STRING;
1559        }
1560        return ps;
1561    }
1562
1563    private static boolean isTwoToNine (char c) {
1564        if (c >= '2' && c <= '9') {
1565            return true;
1566        } else {
1567            return false;
1568        }
1569    }
1570
1571    private static int getFormatTypeFromCountryCode (String country) {
1572        // Check for the NANP countries
1573        int length = NANP_COUNTRIES.length;
1574        for (int i = 0; i < length; i++) {
1575            if (NANP_COUNTRIES[i].compareToIgnoreCase(country) == 0) {
1576                return FORMAT_NANP;
1577            }
1578        }
1579        if ("jp".compareToIgnoreCase(country) == 0) {
1580            return FORMAT_JAPAN;
1581        }
1582        return FORMAT_UNKNOWN;
1583    }
1584
1585    /**
1586     * This function checks if the passed in string conforms to the NANP format
1587     * i.e. NXX-NXX-XXXX, N is any digit 2-9 and X is any digit 0-9
1588     */
1589    private static boolean isNanp (String dialStr) {
1590        boolean retVal = false;
1591        if (dialStr != null) {
1592            if (dialStr.length() == NANP_LENGTH) {
1593                if (isTwoToNine(dialStr.charAt(0)) &&
1594                    isTwoToNine(dialStr.charAt(3))) {
1595                    retVal = true;
1596                    for (int i=1; i<NANP_LENGTH; i++ ) {
1597                        char c=dialStr.charAt(i);
1598                        if (!PhoneNumberUtils.isISODigit(c)) {
1599                            retVal = false;
1600                            break;
1601                        }
1602                    }
1603                }
1604            }
1605        } else {
1606            Log.e("isNanp: null dialStr passed in", dialStr);
1607        }
1608        return retVal;
1609    }
1610
1611   /**
1612    * This function checks if the passed in string conforms to 1-NANP format
1613    */
1614    private static boolean isOneNanp(String dialStr) {
1615        boolean retVal = false;
1616        if (dialStr != null) {
1617            String newDialStr = dialStr.substring(1);
1618            if ((dialStr.charAt(0) == '1') && isNanp(newDialStr)) {
1619                retVal = true;
1620            }
1621        } else {
1622            Log.e("isOneNanp: null dialStr passed in", dialStr);
1623        }
1624        return retVal;
1625    }
1626
1627    /**
1628     * This function handles the plus code conversion within NANP CDMA network
1629     * If the number format is
1630     * 1)+1NANP,remove +,
1631     * 2)other than +1NANP, any + numbers,replace + with the current IDP
1632     */
1633    private static String processPlusCodeWithinNanp(String networkDialStr) {
1634        String retStr = networkDialStr;
1635
1636        if (DBG) log("processPlusCodeWithinNanp,networkDialStr=" + networkDialStr);
1637        // If there is a plus sign at the beginning of the dial string,
1638        // Convert the plus sign to the default IDP since it's an international number
1639        if (networkDialStr != null &
1640            networkDialStr.charAt(0) == PLUS_SIGN_CHAR &&
1641            networkDialStr.length() > 1) {
1642            String newStr = networkDialStr.substring(1);
1643            if (isOneNanp(newStr)) {
1644                // Remove the leading plus sign
1645                retStr = newStr;
1646             } else {
1647                 String idpStr = getDefaultIdp();
1648                 // Replaces the plus sign with the default IDP
1649                 retStr = networkDialStr.replaceFirst("[+]", idpStr);
1650            }
1651        }
1652        if (DBG) log("processPlusCodeWithinNanp,retStr=" + retStr);
1653        return retStr;
1654    }
1655
1656    // This function finds the index of the dialable character(s)
1657    // in the post dial string
1658    private static int findDialableIndexFromPostDialStr(String postDialStr) {
1659        for (int index = 0;index < postDialStr.length();index++) {
1660             char c = postDialStr.charAt(index);
1661             if (isReallyDialable(c)) {
1662                return index;
1663             }
1664        }
1665        return -1;
1666    }
1667
1668    // This function appends the non-diablable P/W character to the original
1669    // dial string based on the dialable index passed in
1670    private static String
1671    appendPwCharBackToOrigDialStr(int dialableIndex,String origStr, String dialStr) {
1672        String retStr;
1673
1674        // There is only 1 P/W character before the dialable characters
1675        if (dialableIndex == 1) {
1676            StringBuilder ret = new StringBuilder(origStr);
1677            ret = ret.append(dialStr.charAt(0));
1678            retStr = ret.toString();
1679        } else {
1680            // It means more than 1 P/W characters in the post dial string,
1681            // appends to retStr
1682            String nonDigitStr = dialStr.substring(0,dialableIndex);
1683            retStr = origStr.concat(nonDigitStr);
1684        }
1685        return retStr;
1686    }
1687
1688    //===== Begining of utility methods used in compareLoosely() =====
1689
1690    /**
1691     * Phone numbers are stored in "lookup" form in the database
1692     * as reversed strings to allow for caller ID lookup
1693     *
1694     * This method takes a phone number and makes a valid SQL "LIKE"
1695     * string that will match the lookup form
1696     *
1697     */
1698    /** all of a up to len must be an international prefix or
1699     *  separators/non-dialing digits
1700     */
1701    private static boolean
1702    matchIntlPrefix(String a, int len) {
1703        /* '([^0-9*#+pwn]\+[^0-9*#+pwn] | [^0-9*#+pwn]0(0|11)[^0-9*#+pwn] )$' */
1704        /*        0       1                           2 3 45               */
1705
1706        int state = 0;
1707        for (int i = 0 ; i < len ; i++) {
1708            char c = a.charAt(i);
1709
1710            switch (state) {
1711                case 0:
1712                    if      (c == '+') state = 1;
1713                    else if (c == '0') state = 2;
1714                    else if (isNonSeparator(c)) return false;
1715                break;
1716
1717                case 2:
1718                    if      (c == '0') state = 3;
1719                    else if (c == '1') state = 4;
1720                    else if (isNonSeparator(c)) return false;
1721                break;
1722
1723                case 4:
1724                    if      (c == '1') state = 5;
1725                    else if (isNonSeparator(c)) return false;
1726                break;
1727
1728                default:
1729                    if (isNonSeparator(c)) return false;
1730                break;
1731
1732            }
1733        }
1734
1735        return state == 1 || state == 3 || state == 5;
1736    }
1737
1738    /** all of 'a' up to len must be a (+|00|011)country code)
1739     *  We're fast and loose with the country code. Any \d{1,3} matches */
1740    private static boolean
1741    matchIntlPrefixAndCC(String a, int len) {
1742        /*  [^0-9*#+pwn]*(\+|0(0|11)\d\d?\d? [^0-9*#+pwn] $ */
1743        /*      0          1 2 3 45  6 7  8                 */
1744
1745        int state = 0;
1746        for (int i = 0 ; i < len ; i++ ) {
1747            char c = a.charAt(i);
1748
1749            switch (state) {
1750                case 0:
1751                    if      (c == '+') state = 1;
1752                    else if (c == '0') state = 2;
1753                    else if (isNonSeparator(c)) return false;
1754                break;
1755
1756                case 2:
1757                    if      (c == '0') state = 3;
1758                    else if (c == '1') state = 4;
1759                    else if (isNonSeparator(c)) return false;
1760                break;
1761
1762                case 4:
1763                    if      (c == '1') state = 5;
1764                    else if (isNonSeparator(c)) return false;
1765                break;
1766
1767                case 1:
1768                case 3:
1769                case 5:
1770                    if      (isISODigit(c)) state = 6;
1771                    else if (isNonSeparator(c)) return false;
1772                break;
1773
1774                case 6:
1775                case 7:
1776                    if      (isISODigit(c)) state++;
1777                    else if (isNonSeparator(c)) return false;
1778                break;
1779
1780                default:
1781                    if (isNonSeparator(c)) return false;
1782            }
1783        }
1784
1785        return state == 6 || state == 7 || state == 8;
1786    }
1787
1788    /** all of 'a' up to len must match non-US trunk prefix ('0') */
1789    private static boolean
1790    matchTrunkPrefix(String a, int len) {
1791        boolean found;
1792
1793        found = false;
1794
1795        for (int i = 0 ; i < len ; i++) {
1796            char c = a.charAt(i);
1797
1798            if (c == '0' && !found) {
1799                found = true;
1800            } else if (isNonSeparator(c)) {
1801                return false;
1802            }
1803        }
1804
1805        return found;
1806    }
1807
1808    //===== End of utility methods used only in compareLoosely() =====
1809
1810    //===== Beggining of utility methods used only in compareStrictly() ====
1811
1812    /*
1813     * If true, the number is country calling code.
1814     */
1815    private static final boolean COUNTLY_CALLING_CALL[] = {
1816        true, true, false, false, false, false, false, true, false, false,
1817        false, false, false, false, false, false, false, false, false, false,
1818        true, false, false, false, false, false, false, true, true, false,
1819        true, true, true, true, true, false, true, false, false, true,
1820        true, false, false, true, true, true, true, true, true, true,
1821        false, true, true, true, true, true, true, true, true, false,
1822        true, true, true, true, true, true, true, false, false, false,
1823        false, false, false, false, false, false, false, false, false, false,
1824        false, true, true, true, true, false, true, false, false, true,
1825        true, true, true, true, true, true, false, false, true, false,
1826    };
1827    private static final int CCC_LENGTH = COUNTLY_CALLING_CALL.length;
1828
1829    /**
1830     * @return true when input is valid Country Calling Code.
1831     */
1832    private static boolean isCountryCallingCode(int countryCallingCodeCandidate) {
1833        return countryCallingCodeCandidate > 0 && countryCallingCodeCandidate < CCC_LENGTH &&
1834                COUNTLY_CALLING_CALL[countryCallingCodeCandidate];
1835    }
1836
1837    /**
1838     * Returns interger corresponding to the input if input "ch" is
1839     * ISO-LATIN characters 0-9.
1840     * Returns -1 otherwise
1841     */
1842    private static int tryGetISODigit(char ch) {
1843        if ('0' <= ch && ch <= '9') {
1844            return ch - '0';
1845        } else {
1846            return -1;
1847        }
1848    }
1849
1850    private static class CountryCallingCodeAndNewIndex {
1851        public final int countryCallingCode;
1852        public final int newIndex;
1853        public CountryCallingCodeAndNewIndex(int countryCode, int newIndex) {
1854            this.countryCallingCode = countryCode;
1855            this.newIndex = newIndex;
1856        }
1857    }
1858
1859    /*
1860     * Note that this function does not strictly care the country calling code with
1861     * 3 length (like Morocco: +212), assuming it is enough to use the first two
1862     * digit to compare two phone numbers.
1863     */
1864    private static CountryCallingCodeAndNewIndex tryGetCountryCallingCodeAndNewIndex(
1865        String str, boolean acceptThailandCase) {
1866        // Rough regexp:
1867        //  ^[^0-9*#+]*((\+|0(0|11)\d\d?|166) [^0-9*#+] $
1868        //         0        1 2 3 45  6 7  89
1869        //
1870        // In all the states, this function ignores separator characters.
1871        // "166" is the special case for the call from Thailand to the US. Uguu!
1872        int state = 0;
1873        int ccc = 0;
1874        final int length = str.length();
1875        for (int i = 0 ; i < length ; i++ ) {
1876            char ch = str.charAt(i);
1877            switch (state) {
1878                case 0:
1879                    if      (ch == '+') state = 1;
1880                    else if (ch == '0') state = 2;
1881                    else if (ch == '1') {
1882                        if (acceptThailandCase) {
1883                            state = 8;
1884                        } else {
1885                            return null;
1886                        }
1887                    } else if (isDialable(ch)) {
1888                        return null;
1889                    }
1890                break;
1891
1892                case 2:
1893                    if      (ch == '0') state = 3;
1894                    else if (ch == '1') state = 4;
1895                    else if (isDialable(ch)) {
1896                        return null;
1897                    }
1898                break;
1899
1900                case 4:
1901                    if      (ch == '1') state = 5;
1902                    else if (isDialable(ch)) {
1903                        return null;
1904                    }
1905                break;
1906
1907                case 1:
1908                case 3:
1909                case 5:
1910                case 6:
1911                case 7:
1912                    {
1913                        int ret = tryGetISODigit(ch);
1914                        if (ret > 0) {
1915                            ccc = ccc * 10 + ret;
1916                            if (ccc >= 100 || isCountryCallingCode(ccc)) {
1917                                return new CountryCallingCodeAndNewIndex(ccc, i + 1);
1918                            }
1919                            if (state == 1 || state == 3 || state == 5) {
1920                                state = 6;
1921                            } else {
1922                                state++;
1923                            }
1924                        } else if (isDialable(ch)) {
1925                            return null;
1926                        }
1927                    }
1928                    break;
1929                case 8:
1930                    if (ch == '6') state = 9;
1931                    else if (isDialable(ch)) {
1932                        return null;
1933                    }
1934                    break;
1935                case 9:
1936                    if (ch == '6') {
1937                        return new CountryCallingCodeAndNewIndex(66, i + 1);
1938                    } else {
1939                        return null;
1940                    }
1941                default:
1942                    return null;
1943            }
1944        }
1945
1946        return null;
1947    }
1948
1949    /**
1950     * Currently this function simply ignore the first digit assuming it is
1951     * trunk prefix. Actually trunk prefix is different in each country.
1952     *
1953     * e.g.
1954     * "+79161234567" equals "89161234567" (Russian trunk digit is 8)
1955     * "+33123456789" equals "0123456789" (French trunk digit is 0)
1956     *
1957     */
1958    private static int tryGetTrunkPrefixOmittedIndex(String str, int currentIndex) {
1959        int length = str.length();
1960        for (int i = currentIndex ; i < length ; i++) {
1961            final char ch = str.charAt(i);
1962            if (tryGetISODigit(ch) >= 0) {
1963                return i + 1;
1964            } else if (isDialable(ch)) {
1965                return -1;
1966            }
1967        }
1968        return -1;
1969    }
1970
1971    /**
1972     * Return true if the prefix of "str" is "ignorable". Here, "ignorable" means
1973     * that "str" has only one digit and separater characters. The one digit is
1974     * assumed to be trunk prefix.
1975     */
1976    private static boolean checkPrefixIsIgnorable(final String str,
1977            int forwardIndex, int backwardIndex) {
1978        boolean trunk_prefix_was_read = false;
1979        while (backwardIndex >= forwardIndex) {
1980            if (tryGetISODigit(str.charAt(backwardIndex)) >= 0) {
1981                if (trunk_prefix_was_read) {
1982                    // More than one digit appeared, meaning that "a" and "b"
1983                    // is different.
1984                    return false;
1985                } else {
1986                    // Ignore just one digit, assuming it is trunk prefix.
1987                    trunk_prefix_was_read = true;
1988                }
1989            } else if (isDialable(str.charAt(backwardIndex))) {
1990                // Trunk prefix is a digit, not "*", "#"...
1991                return false;
1992            }
1993            backwardIndex--;
1994        }
1995
1996        return true;
1997    }
1998
1999    //==== End of utility methods used only in compareStrictly() =====
2000}
2001