PhoneNumberUtils.java revision c159c84699d4b739a14cdfb22c5807f419e1754f
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        // CALL_PRIVILEGED permission).
139        if (scheme.equals("voicemail")) {
140            return TelephonyManager.getDefault().getCompleteVoiceMailNumber();
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        //Only TON field should be taken in consideration
736        if ((bytes[offset] & 0xf0) == (TOA_International & 0xf0)) {
737            prependPlus = true;
738        }
739
740        internalCalledPartyBCDFragmentToString(
741                ret, bytes, offset + 1, length - 1);
742
743        if (prependPlus && ret.length() == 0) {
744            // If the only thing there is a prepended plus, return ""
745            return "";
746        }
747
748        if (prependPlus) {
749            // This is an "international number" and should have
750            // a plus prepended to the dialing number. But there
751            // can also be Gsm MMI codes as defined in TS 22.030 6.5.2
752            // so we need to handle those also.
753            //
754            // http://web.telia.com/~u47904776/gsmkode.htm is a
755            // has a nice list of some of these GSM codes.
756            //
757            // Examples are:
758            //   **21*+886988171479#
759            //   **21*8311234567#
760            //   *21#
761            //   #21#
762            //   *#21#
763            //   *31#+11234567890
764            //   #31#+18311234567
765            //   #31#8311234567
766            //   18311234567
767            //   +18311234567#
768            //   +18311234567
769            // Odd ball cases that some phones handled
770            // where there is no dialing number so they
771            // append the "+"
772            //   *21#+
773            //   **21#+
774            String retString = ret.toString();
775            Pattern p = Pattern.compile("(^[#*])(.*)([#*])(.*)(#)$");
776            Matcher m = p.matcher(retString);
777            if (m.matches()) {
778                if ("".equals(m.group(2))) {
779                    // Started with two [#*] ends with #
780                    // So no dialing number and we'll just
781                    // append a +, this handles **21#+
782                    ret = new StringBuilder();
783                    ret.append(m.group(1));
784                    ret.append(m.group(3));
785                    ret.append(m.group(4));
786                    ret.append(m.group(5));
787                    ret.append("+");
788                } else {
789                    // Starts with [#*] and ends with #
790                    // Assume group 4 is a dialing number
791                    // such as *21*+1234554#
792                    ret = new StringBuilder();
793                    ret.append(m.group(1));
794                    ret.append(m.group(2));
795                    ret.append(m.group(3));
796                    ret.append("+");
797                    ret.append(m.group(4));
798                    ret.append(m.group(5));
799                }
800            } else {
801                p = Pattern.compile("(^[#*])(.*)([#*])(.*)");
802                m = p.matcher(retString);
803                if (m.matches()) {
804                    // Starts with [#*] and only one other [#*]
805                    // Assume the data after last [#*] is dialing
806                    // number (i.e. group 4) such as *31#+11234567890.
807                    // This also includes the odd ball *21#+
808                    ret = new StringBuilder();
809                    ret.append(m.group(1));
810                    ret.append(m.group(2));
811                    ret.append(m.group(3));
812                    ret.append("+");
813                    ret.append(m.group(4));
814                } else {
815                    // Does NOT start with [#*] just prepend '+'
816                    ret = new StringBuilder();
817                    ret.append('+');
818                    ret.append(retString);
819                }
820            }
821        }
822
823        return ret.toString();
824    }
825
826    private static void
827    internalCalledPartyBCDFragmentToString(
828        StringBuilder sb, byte [] bytes, int offset, int length) {
829        for (int i = offset ; i < length + offset ; i++) {
830            byte b;
831            char c;
832
833            c = bcdToChar((byte)(bytes[i] & 0xf));
834
835            if (c == 0) {
836                return;
837            }
838            sb.append(c);
839
840            // FIXME(mkf) TS 23.040 9.1.2.3 says
841            // "if a mobile receives 1111 in a position prior to
842            // the last semi-octet then processing shall commense with
843            // the next semi-octet and the intervening
844            // semi-octet shall be ignored"
845            // How does this jive with 24,008 10.5.4.7
846
847            b = (byte)((bytes[i] >> 4) & 0xf);
848
849            if (b == 0xf && i + 1 == length + offset) {
850                //ignore final 0xf
851                break;
852            }
853
854            c = bcdToChar(b);
855            if (c == 0) {
856                return;
857            }
858
859            sb.append(c);
860        }
861
862    }
863
864    /**
865     * Like calledPartyBCDToString, but field does not start with a
866     * TOA byte. For example: SIM ADN extension fields
867     */
868
869    public static String
870    calledPartyBCDFragmentToString(byte [] bytes, int offset, int length) {
871        StringBuilder ret = new StringBuilder(length * 2);
872
873        internalCalledPartyBCDFragmentToString(ret, bytes, offset, length);
874
875        return ret.toString();
876    }
877
878    /** returns 0 on invalid value */
879    private static char
880    bcdToChar(byte b) {
881        if (b < 0xa) {
882            return (char)('0' + b);
883        } else switch (b) {
884            case 0xa: return '*';
885            case 0xb: return '#';
886            case 0xc: return PAUSE;
887            case 0xd: return WILD;
888
889            default: return 0;
890        }
891    }
892
893    private static int
894    charToBCD(char c) {
895        if (c >= '0' && c <= '9') {
896            return c - '0';
897        } else if (c == '*') {
898            return 0xa;
899        } else if (c == '#') {
900            return 0xb;
901        } else if (c == PAUSE) {
902            return 0xc;
903        } else if (c == WILD) {
904            return 0xd;
905        } else {
906            throw new RuntimeException ("invalid char for BCD " + c);
907        }
908    }
909
910    /**
911     * Return true iff the network portion of <code>address</code> is,
912     * as far as we can tell on the device, suitable for use as an SMS
913     * destination address.
914     */
915    public static boolean isWellFormedSmsAddress(String address) {
916        String networkPortion =
917                PhoneNumberUtils.extractNetworkPortion(address);
918
919        return (!(networkPortion.equals("+")
920                  || TextUtils.isEmpty(networkPortion)))
921               && isDialable(networkPortion);
922    }
923
924    public static boolean isGlobalPhoneNumber(String phoneNumber) {
925        if (TextUtils.isEmpty(phoneNumber)) {
926            return false;
927        }
928
929        Matcher match = GLOBAL_PHONE_NUMBER_PATTERN.matcher(phoneNumber);
930        return match.matches();
931    }
932
933    private static boolean isDialable(String address) {
934        for (int i = 0, count = address.length(); i < count; i++) {
935            if (!isDialable(address.charAt(i))) {
936                return false;
937            }
938        }
939        return true;
940    }
941
942    private static boolean isNonSeparator(String address) {
943        for (int i = 0, count = address.length(); i < count; i++) {
944            if (!isNonSeparator(address.charAt(i))) {
945                return false;
946            }
947        }
948        return true;
949    }
950    /**
951     * Note: calls extractNetworkPortion(), so do not use for
952     * SIM EF[ADN] style records
953     *
954     * Returns null if network portion is empty.
955     */
956    public static byte[]
957    networkPortionToCalledPartyBCD(String s) {
958        String networkPortion = extractNetworkPortion(s);
959        return numberToCalledPartyBCDHelper(networkPortion, false);
960    }
961
962    /**
963     * Same as {@link #networkPortionToCalledPartyBCD}, but includes a
964     * one-byte length prefix.
965     */
966    public static byte[]
967    networkPortionToCalledPartyBCDWithLength(String s) {
968        String networkPortion = extractNetworkPortion(s);
969        return numberToCalledPartyBCDHelper(networkPortion, true);
970    }
971
972    /**
973     * Convert a dialing number to BCD byte array
974     *
975     * @param number dialing number string
976     *        if the dialing number starts with '+', set to internationl TOA
977     * @return BCD byte array
978     */
979    public static byte[]
980    numberToCalledPartyBCD(String number) {
981        return numberToCalledPartyBCDHelper(number, false);
982    }
983
984    /**
985     * If includeLength is true, prepend a one-byte length value to
986     * the return array.
987     */
988    private static byte[]
989    numberToCalledPartyBCDHelper(String number, boolean includeLength) {
990        int numberLenReal = number.length();
991        int numberLenEffective = numberLenReal;
992        boolean hasPlus = number.indexOf('+') != -1;
993        if (hasPlus) numberLenEffective--;
994
995        if (numberLenEffective == 0) return null;
996
997        int resultLen = (numberLenEffective + 1) / 2;  // Encoded numbers require only 4 bits each.
998        int extraBytes = 1;                            // Prepended TOA byte.
999        if (includeLength) extraBytes++;               // Optional prepended length byte.
1000        resultLen += extraBytes;
1001
1002        byte[] result = new byte[resultLen];
1003
1004        int digitCount = 0;
1005        for (int i = 0; i < numberLenReal; i++) {
1006            char c = number.charAt(i);
1007            if (c == '+') continue;
1008            int shift = ((digitCount & 0x01) == 1) ? 4 : 0;
1009            result[extraBytes + (digitCount >> 1)] |= (byte)((charToBCD(c) & 0x0F) << shift);
1010            digitCount++;
1011        }
1012
1013        // 1-fill any trailing odd nibble/quartet.
1014        if ((digitCount & 0x01) == 1) result[extraBytes + (digitCount >> 1)] |= 0xF0;
1015
1016        int offset = 0;
1017        if (includeLength) result[offset++] = (byte)(resultLen - 1);
1018        result[offset] = (byte)(hasPlus ? TOA_International : TOA_Unknown);
1019
1020        return result;
1021    }
1022
1023    //================ Number formatting =========================
1024
1025    /** The current locale is unknown, look for a country code or don't format */
1026    public static final int FORMAT_UNKNOWN = 0;
1027    /** NANP formatting */
1028    public static final int FORMAT_NANP = 1;
1029    /** Japanese formatting */
1030    public static final int FORMAT_JAPAN = 2;
1031
1032    /** List of country codes for countries that use the NANP */
1033    private static final String[] NANP_COUNTRIES = new String[] {
1034        "US", // United States
1035        "CA", // Canada
1036        "AS", // American Samoa
1037        "AI", // Anguilla
1038        "AG", // Antigua and Barbuda
1039        "BS", // Bahamas
1040        "BB", // Barbados
1041        "BM", // Bermuda
1042        "VG", // British Virgin Islands
1043        "KY", // Cayman Islands
1044        "DM", // Dominica
1045        "DO", // Dominican Republic
1046        "GD", // Grenada
1047        "GU", // Guam
1048        "JM", // Jamaica
1049        "PR", // Puerto Rico
1050        "MS", // Montserrat
1051        "MP", // Northern Mariana Islands
1052        "KN", // Saint Kitts and Nevis
1053        "LC", // Saint Lucia
1054        "VC", // Saint Vincent and the Grenadines
1055        "TT", // Trinidad and Tobago
1056        "TC", // Turks and Caicos Islands
1057        "VI", // U.S. Virgin Islands
1058    };
1059
1060    /**
1061     * Breaks the given number down and formats it according to the rules
1062     * for the country the number is from.
1063     *
1064     * @param source The phone number to format
1065     * @return A locally acceptable formatting of the input, or the raw input if
1066     *  formatting rules aren't known for the number
1067     */
1068    public static String formatNumber(String source) {
1069        SpannableStringBuilder text = new SpannableStringBuilder(source);
1070        formatNumber(text, getFormatTypeForLocale(Locale.getDefault()));
1071        return text.toString();
1072    }
1073
1074    /**
1075     * Formats the given number with the given formatting type. Currently
1076     * {@link #FORMAT_NANP} and {@link #FORMAT_JAPAN} are supported as a formating type.
1077     *
1078     * @param source the phone number to format
1079     * @param defaultFormattingType The default formatting rules to apply if the number does
1080     * not begin with +<country_code>
1081     * @return The phone number formatted with the given formatting type.
1082     *
1083     * @hide TODO:Shuold be unhidden.
1084     */
1085    public static String formatNumber(String source, int defaultFormattingType) {
1086        SpannableStringBuilder text = new SpannableStringBuilder(source);
1087        formatNumber(text, defaultFormattingType);
1088        return text.toString();
1089    }
1090
1091    /**
1092     * Returns the phone number formatting type for the given locale.
1093     *
1094     * @param locale The locale of interest, usually {@link Locale#getDefault()}
1095     * @return The formatting type for the given locale, or FORMAT_UNKNOWN if the formatting
1096     * rules are not known for the given locale
1097     */
1098    public static int getFormatTypeForLocale(Locale locale) {
1099        String country = locale.getCountry();
1100
1101        return getFormatTypeFromCountryCode(country);
1102    }
1103
1104    /**
1105     * Formats a phone number in-place. Currently {@link #FORMAT_JAPAN} and {@link #FORMAT_NANP}
1106     * is supported as a second argument.
1107     *
1108     * @param text The number to be formatted, will be modified with the formatting
1109     * @param defaultFormattingType The default formatting rules to apply if the number does
1110     * not begin with +<country_code>
1111     */
1112    public static void formatNumber(Editable text, int defaultFormattingType) {
1113        int formatType = defaultFormattingType;
1114
1115        if (text.length() > 2 && text.charAt(0) == '+') {
1116            if (text.charAt(1) == '1') {
1117                formatType = FORMAT_NANP;
1118            } else if (text.length() >= 3 && text.charAt(1) == '8'
1119                && text.charAt(2) == '1') {
1120                formatType = FORMAT_JAPAN;
1121            } else {
1122                return;
1123            }
1124        }
1125
1126        switch (formatType) {
1127            case FORMAT_NANP:
1128                formatNanpNumber(text);
1129                return;
1130            case FORMAT_JAPAN:
1131                formatJapaneseNumber(text);
1132                return;
1133        }
1134    }
1135
1136    private static final int NANP_STATE_DIGIT = 1;
1137    private static final int NANP_STATE_PLUS = 2;
1138    private static final int NANP_STATE_ONE = 3;
1139    private static final int NANP_STATE_DASH = 4;
1140
1141    /**
1142     * Formats a phone number in-place using the NANP formatting rules. Numbers will be formatted
1143     * as:
1144     *
1145     * <p><code>
1146     * xxxxx
1147     * xxx-xxxx
1148     * xxx-xxx-xxxx
1149     * 1-xxx-xxx-xxxx
1150     * +1-xxx-xxx-xxxx
1151     * </code></p>
1152     *
1153     * @param text the number to be formatted, will be modified with the formatting
1154     */
1155    public static void formatNanpNumber(Editable text) {
1156        int length = text.length();
1157        if (length > "+1-nnn-nnn-nnnn".length()) {
1158            // The string is too long to be formatted
1159            return;
1160        } else if (length <= 5) {
1161            // The string is either a shortcode or too short to be formatted
1162            return;
1163        }
1164
1165        CharSequence saved = text.subSequence(0, length);
1166
1167        // Strip the dashes first, as we're going to add them back
1168        int p = 0;
1169        while (p < text.length()) {
1170            if (text.charAt(p) == '-') {
1171                text.delete(p, p + 1);
1172            } else {
1173                p++;
1174            }
1175        }
1176        length = text.length();
1177
1178        // When scanning the number we record where dashes need to be added,
1179        // if they're non-0 at the end of the scan the dashes will be added in
1180        // the proper places.
1181        int dashPositions[] = new int[3];
1182        int numDashes = 0;
1183
1184        int state = NANP_STATE_DIGIT;
1185        int numDigits = 0;
1186        for (int i = 0; i < length; i++) {
1187            char c = text.charAt(i);
1188            switch (c) {
1189                case '1':
1190                    if (numDigits == 0 || state == NANP_STATE_PLUS) {
1191                        state = NANP_STATE_ONE;
1192                        break;
1193                    }
1194                    // fall through
1195                case '2':
1196                case '3':
1197                case '4':
1198                case '5':
1199                case '6':
1200                case '7':
1201                case '8':
1202                case '9':
1203                case '0':
1204                    if (state == NANP_STATE_PLUS) {
1205                        // Only NANP number supported for now
1206                        text.replace(0, length, saved);
1207                        return;
1208                    } else if (state == NANP_STATE_ONE) {
1209                        // Found either +1 or 1, follow it up with a dash
1210                        dashPositions[numDashes++] = i;
1211                    } else if (state != NANP_STATE_DASH && (numDigits == 3 || numDigits == 6)) {
1212                        // Found a digit that should be after a dash that isn't
1213                        dashPositions[numDashes++] = i;
1214                    }
1215                    state = NANP_STATE_DIGIT;
1216                    numDigits++;
1217                    break;
1218
1219                case '-':
1220                    state = NANP_STATE_DASH;
1221                    break;
1222
1223                case '+':
1224                    if (i == 0) {
1225                        // Plus is only allowed as the first character
1226                        state = NANP_STATE_PLUS;
1227                        break;
1228                    }
1229                    // Fall through
1230                default:
1231                    // Unknown character, bail on formatting
1232                    text.replace(0, length, saved);
1233                    return;
1234            }
1235        }
1236
1237        if (numDigits == 7) {
1238            // With 7 digits we want xxx-xxxx, not xxx-xxx-x
1239            numDashes--;
1240        }
1241
1242        // Actually put the dashes in place
1243        for (int i = 0; i < numDashes; i++) {
1244            int pos = dashPositions[i];
1245            text.replace(pos + i, pos + i, "-");
1246        }
1247
1248        // Remove trailing dashes
1249        int len = text.length();
1250        while (len > 0) {
1251            if (text.charAt(len - 1) == '-') {
1252                text.delete(len - 1, len);
1253                len--;
1254            } else {
1255                break;
1256            }
1257        }
1258    }
1259
1260    /**
1261     * Formats a phone number in-place using the Japanese formatting rules.
1262     * Numbers will be formatted as:
1263     *
1264     * <p><code>
1265     * 03-xxxx-xxxx
1266     * 090-xxxx-xxxx
1267     * 0120-xxx-xxx
1268     * +81-3-xxxx-xxxx
1269     * +81-90-xxxx-xxxx
1270     * </code></p>
1271     *
1272     * @param text the number to be formatted, will be modified with
1273     * the formatting
1274     */
1275    public static void formatJapaneseNumber(Editable text) {
1276        JapanesePhoneNumberFormatter.format(text);
1277    }
1278
1279    // Three and four digit phone numbers for either special services,
1280    // or 3-6 digit addresses from the network (eg carrier-originated SMS messages) should
1281    // not match.
1282    //
1283    // This constant used to be 5, but SMS short codes has increased in length and
1284    // can be easily 6 digits now days. Most countries have SMS short code length between
1285    // 3 to 6 digits. The exceptions are
1286    //
1287    // Australia: Short codes are six or eight digits in length, starting with the prefix "19"
1288    //            followed by an additional four or six digits and two.
1289    // Czech Republic: Codes are seven digits in length for MO and five (not billed) or
1290    //            eight (billed) for MT direction
1291    //
1292    // see http://en.wikipedia.org/wiki/Short_code#Regional_differences for reference
1293    //
1294    // However, in order to loose match 650-555-1212 and 555-1212, we need to set the min match
1295    // to 7.
1296    static final int MIN_MATCH = 7;
1297
1298    /**
1299     * isEmergencyNumber: checks a given number against the list of
1300     *   emergency numbers provided by the RIL and SIM card.
1301     *
1302     * @param number the number to look up.
1303     * @return if the number is in the list of emergency numbers
1304     * listed in the ril / sim, then return true, otherwise false.
1305     */
1306    public static boolean isEmergencyNumber(String number) {
1307        // If the number passed in is null, just return false:
1308        if (number == null) return false;
1309
1310        // Strip the separators from the number before comparing it
1311        // to the list.
1312        number = extractNetworkPortionAlt(number);
1313
1314        // retrieve the list of emergency numbers
1315        // check read-write ecclist property first
1316        String numbers = SystemProperties.get("ril.ecclist");
1317        if (TextUtils.isEmpty(numbers)) {
1318            // then read-only ecclist property since old RIL only uses this
1319            numbers = SystemProperties.get("ro.ril.ecclist");
1320        }
1321
1322        if (!TextUtils.isEmpty(numbers)) {
1323            // searches through the comma-separated list for a match,
1324            // return true if one is found.
1325            for (String emergencyNum : numbers.split(",")) {
1326                if (emergencyNum.equals(number)) {
1327                    return true;
1328                }
1329            }
1330            // no matches found against the list!
1331            return false;
1332        }
1333
1334        //no ecclist system property, so use our own list.
1335        return (number.equals("112") || number.equals("911"));
1336    }
1337
1338    /**
1339     * isVoiceMailNumber: checks a given number against the voicemail
1340     *   number provided by the RIL and SIM card. The caller must have
1341     *   the READ_PHONE_STATE credential.
1342     *
1343     * @param number the number to look up.
1344     * @return true if the number is in the list of voicemail. False
1345     * otherwise, including if the caller does not have the permission
1346     * to read the VM number.
1347     * @hide TODO: pending API Council approval
1348     */
1349    public static boolean isVoiceMailNumber(String number) {
1350        String vmNumber;
1351
1352        try {
1353            vmNumber = TelephonyManager.getDefault().getVoiceMailNumber();
1354        } catch (SecurityException ex) {
1355            return false;
1356        }
1357
1358        // Strip the separators from the number before comparing it
1359        // to the list.
1360        number = extractNetworkPortionAlt(number);
1361
1362        // compare tolerates null so we need to make sure that we
1363        // don't return true when both are null.
1364        return !TextUtils.isEmpty(number) && compare(number, vmNumber);
1365    }
1366
1367    /**
1368     * Translates any alphabetic letters (i.e. [A-Za-z]) in the
1369     * specified phone number into the equivalent numeric digits,
1370     * according to the phone keypad letter mapping described in
1371     * ITU E.161 and ISO/IEC 9995-8.
1372     *
1373     * @return the input string, with alpha letters converted to numeric
1374     *         digits using the phone keypad letter mapping.  For example,
1375     *         an input of "1-800-GOOG-411" will return "1-800-4664-411".
1376     */
1377    public static String convertKeypadLettersToDigits(String input) {
1378        if (input == null) {
1379            return input;
1380        }
1381        int len = input.length();
1382        if (len == 0) {
1383            return input;
1384        }
1385
1386        char[] out = input.toCharArray();
1387
1388        for (int i = 0; i < len; i++) {
1389            char c = out[i];
1390            // If this char isn't in KEYPAD_MAP at all, just leave it alone.
1391            out[i] = (char) KEYPAD_MAP.get(c, c);
1392        }
1393
1394        return new String(out);
1395    }
1396
1397    /**
1398     * The phone keypad letter mapping (see ITU E.161 or ISO/IEC 9995-8.)
1399     * TODO: This should come from a resource.
1400     */
1401    private static final SparseIntArray KEYPAD_MAP = new SparseIntArray();
1402    static {
1403        KEYPAD_MAP.put('a', '2'); KEYPAD_MAP.put('b', '2'); KEYPAD_MAP.put('c', '2');
1404        KEYPAD_MAP.put('A', '2'); KEYPAD_MAP.put('B', '2'); KEYPAD_MAP.put('C', '2');
1405
1406        KEYPAD_MAP.put('d', '3'); KEYPAD_MAP.put('e', '3'); KEYPAD_MAP.put('f', '3');
1407        KEYPAD_MAP.put('D', '3'); KEYPAD_MAP.put('E', '3'); KEYPAD_MAP.put('F', '3');
1408
1409        KEYPAD_MAP.put('g', '4'); KEYPAD_MAP.put('h', '4'); KEYPAD_MAP.put('i', '4');
1410        KEYPAD_MAP.put('G', '4'); KEYPAD_MAP.put('H', '4'); KEYPAD_MAP.put('I', '4');
1411
1412        KEYPAD_MAP.put('j', '5'); KEYPAD_MAP.put('k', '5'); KEYPAD_MAP.put('l', '5');
1413        KEYPAD_MAP.put('J', '5'); KEYPAD_MAP.put('K', '5'); KEYPAD_MAP.put('L', '5');
1414
1415        KEYPAD_MAP.put('m', '6'); KEYPAD_MAP.put('n', '6'); KEYPAD_MAP.put('o', '6');
1416        KEYPAD_MAP.put('M', '6'); KEYPAD_MAP.put('N', '6'); KEYPAD_MAP.put('O', '6');
1417
1418        KEYPAD_MAP.put('p', '7'); KEYPAD_MAP.put('q', '7'); KEYPAD_MAP.put('r', '7'); KEYPAD_MAP.put('s', '7');
1419        KEYPAD_MAP.put('P', '7'); KEYPAD_MAP.put('Q', '7'); KEYPAD_MAP.put('R', '7'); KEYPAD_MAP.put('S', '7');
1420
1421        KEYPAD_MAP.put('t', '8'); KEYPAD_MAP.put('u', '8'); KEYPAD_MAP.put('v', '8');
1422        KEYPAD_MAP.put('T', '8'); KEYPAD_MAP.put('U', '8'); KEYPAD_MAP.put('V', '8');
1423
1424        KEYPAD_MAP.put('w', '9'); KEYPAD_MAP.put('x', '9'); KEYPAD_MAP.put('y', '9'); KEYPAD_MAP.put('z', '9');
1425        KEYPAD_MAP.put('W', '9'); KEYPAD_MAP.put('X', '9'); KEYPAD_MAP.put('Y', '9'); KEYPAD_MAP.put('Z', '9');
1426    }
1427
1428    //================ Plus Code formatting =========================
1429    private static final char PLUS_SIGN_CHAR = '+';
1430    private static final String PLUS_SIGN_STRING = "+";
1431    private static final String NANP_IDP_STRING = "011";
1432    private static final int NANP_LENGTH = 10;
1433
1434    /**
1435     * This function checks if there is a plus sign (+) in the passed-in dialing number.
1436     * If there is, it processes the plus sign based on the default telephone
1437     * numbering plan of the system when the phone is activated and the current
1438     * telephone numbering plan of the system that the phone is camped on.
1439     * Currently, we only support the case that the default and current telephone
1440     * numbering plans are North American Numbering Plan(NANP).
1441     *
1442     * The passed-in dialStr should only contain the valid format as described below,
1443     * 1) the 1st character in the dialStr should be one of the really dialable
1444     *    characters listed below
1445     *    ISO-LATIN characters 0-9, *, # , +
1446     * 2) the dialStr should already strip out the separator characters,
1447     *    every character in the dialStr should be one of the non separator characters
1448     *    listed below
1449     *    ISO-LATIN characters 0-9, *, # , +, WILD, WAIT, PAUSE
1450     *
1451     * Otherwise, this function returns the dial string passed in
1452     *
1453     * @param dialStr the original dial string
1454     * @return the converted dial string if the current/default countries belong to NANP,
1455     * and if there is the "+" in the original dial string. Otherwise, the original dial
1456     * string returns.
1457     *
1458     * This API is for CDMA only
1459     *
1460     * @hide TODO: pending API Council approval
1461     */
1462    public static String cdmaCheckAndProcessPlusCode(String dialStr) {
1463        if (!TextUtils.isEmpty(dialStr)) {
1464            if (isReallyDialable(dialStr.charAt(0)) &&
1465                isNonSeparator(dialStr)) {
1466                String currIso = SystemProperties.get(PROPERTY_OPERATOR_ISO_COUNTRY, "");
1467                String defaultIso = SystemProperties.get(PROPERTY_ICC_OPERATOR_ISO_COUNTRY, "");
1468                if (!TextUtils.isEmpty(currIso) && !TextUtils.isEmpty(defaultIso)) {
1469                    return cdmaCheckAndProcessPlusCodeByNumberFormat(dialStr,
1470                            getFormatTypeFromCountryCode(currIso),
1471                            getFormatTypeFromCountryCode(defaultIso));
1472                }
1473            }
1474        }
1475        return dialStr;
1476    }
1477
1478    /**
1479     * This function should be called from checkAndProcessPlusCode only
1480     * And it is used for test purpose also.
1481     *
1482     * It checks the dial string by looping through the network portion,
1483     * post dial portion 1, post dial porting 2, etc. If there is any
1484     * plus sign, then process the plus sign.
1485     * Currently, this function supports the plus sign conversion within NANP only.
1486     * Specifically, it handles the plus sign in the following ways:
1487     * 1)+1NANP,remove +, e.g.
1488     *   +18475797000 is converted to 18475797000,
1489     * 2)+NANP or +non-NANP Numbers,replace + with the current NANP IDP, e.g,
1490     *   +8475797000 is converted to 0118475797000,
1491     *   +11875767800 is converted to 01111875767800
1492     * 3)+1NANP in post dial string(s), e.g.
1493     *   8475797000;+18475231753 is converted to 8475797000;18475231753
1494     *
1495     *
1496     * @param dialStr the original dial string
1497     * @param currFormat the numbering system of the current country that the phone is camped on
1498     * @param defaultFormat the numbering system of the country that the phone is activated on
1499     * @return the converted dial string if the current/default countries belong to NANP,
1500     * and if there is the "+" in the original dial string. Otherwise, the original dial
1501     * string returns.
1502     *
1503     * @hide
1504     */
1505    public static String
1506    cdmaCheckAndProcessPlusCodeByNumberFormat(String dialStr,int currFormat,int defaultFormt) {
1507        String retStr = dialStr;
1508
1509        // Checks if the plus sign character is in the passed-in dial string
1510        if (dialStr != null &&
1511            dialStr.lastIndexOf(PLUS_SIGN_STRING) != -1) {
1512            // Format the string based on the rules for the country the number is from,
1513            // and the current country the phone is camped on.
1514            if ((currFormat == defaultFormt) && (currFormat == FORMAT_NANP)) {
1515                // Handle case where default and current telephone numbering plans are NANP.
1516                String postDialStr = null;
1517                String tempDialStr = dialStr;
1518
1519                // Sets the retStr to null since the conversion will be performed below.
1520                retStr = null;
1521                if (DBG) log("checkAndProcessPlusCode,dialStr=" + dialStr);
1522                // This routine is to process the plus sign in the dial string by loop through
1523                // the network portion, post dial portion 1, post dial portion 2... etc. if
1524                // applied
1525                do {
1526                    String networkDialStr;
1527                    networkDialStr = extractNetworkPortion(tempDialStr);
1528                    // Handles the conversion within NANP
1529                    networkDialStr = processPlusCodeWithinNanp(networkDialStr);
1530
1531                    // Concatenates the string that is converted from network portion
1532                    if (!TextUtils.isEmpty(networkDialStr)) {
1533                        if (retStr == null) {
1534                            retStr = networkDialStr;
1535                        } else {
1536                            retStr = retStr.concat(networkDialStr);
1537                        }
1538                    } else {
1539                        // This should never happen since we checked the if dialStr is null
1540                        // and if it contains the plus sign in the beginning of this function.
1541                        // The plus sign is part of the network portion.
1542                        Log.e("checkAndProcessPlusCode: null newDialStr", networkDialStr);
1543                        return dialStr;
1544                    }
1545                    postDialStr = extractPostDialPortion(tempDialStr);
1546                    if (!TextUtils.isEmpty(postDialStr)) {
1547                        int dialableIndex = findDialableIndexFromPostDialStr(postDialStr);
1548
1549                        // dialableIndex should always be greater than 0
1550                        if (dialableIndex >= 1) {
1551                            retStr = appendPwCharBackToOrigDialStr(dialableIndex,
1552                                     retStr,postDialStr);
1553                            // Skips the P/W character, extracts the dialable portion
1554                            tempDialStr = postDialStr.substring(dialableIndex);
1555                        } else {
1556                            // Non-dialable character such as P/W should not be at the end of
1557                            // the dial string after P/W processing in CdmaConnection.java
1558                            // Set the postDialStr to "" to break out of the loop
1559                            if (dialableIndex < 0) {
1560                                postDialStr = "";
1561                            }
1562                            Log.e("wrong postDialStr=", postDialStr);
1563                        }
1564                    }
1565                    if (DBG) log("checkAndProcessPlusCode,postDialStr=" + postDialStr);
1566                } while (!TextUtils.isEmpty(postDialStr) && !TextUtils.isEmpty(tempDialStr));
1567            } else {
1568                // TODO: Support NANP international conversion and other telephone numbering plans.
1569                // Currently the phone is never used in non-NANP system, so return the original
1570                // dial string.
1571                Log.e("checkAndProcessPlusCode:non-NANP not supported", dialStr);
1572            }
1573        }
1574        return retStr;
1575     }
1576
1577    // This function gets the default international dialing prefix
1578    private static String getDefaultIdp( ) {
1579        String ps = null;
1580        SystemProperties.get(PROPERTY_IDP_STRING, ps);
1581        if (TextUtils.isEmpty(ps)) {
1582            ps = NANP_IDP_STRING;
1583        }
1584        return ps;
1585    }
1586
1587    private static boolean isTwoToNine (char c) {
1588        if (c >= '2' && c <= '9') {
1589            return true;
1590        } else {
1591            return false;
1592        }
1593    }
1594
1595    private static int getFormatTypeFromCountryCode (String country) {
1596        // Check for the NANP countries
1597        int length = NANP_COUNTRIES.length;
1598        for (int i = 0; i < length; i++) {
1599            if (NANP_COUNTRIES[i].compareToIgnoreCase(country) == 0) {
1600                return FORMAT_NANP;
1601            }
1602        }
1603        if ("jp".compareToIgnoreCase(country) == 0) {
1604            return FORMAT_JAPAN;
1605        }
1606        return FORMAT_UNKNOWN;
1607    }
1608
1609    /**
1610     * This function checks if the passed in string conforms to the NANP format
1611     * i.e. NXX-NXX-XXXX, N is any digit 2-9 and X is any digit 0-9
1612     */
1613    private static boolean isNanp (String dialStr) {
1614        boolean retVal = false;
1615        if (dialStr != null) {
1616            if (dialStr.length() == NANP_LENGTH) {
1617                if (isTwoToNine(dialStr.charAt(0)) &&
1618                    isTwoToNine(dialStr.charAt(3))) {
1619                    retVal = true;
1620                    for (int i=1; i<NANP_LENGTH; i++ ) {
1621                        char c=dialStr.charAt(i);
1622                        if (!PhoneNumberUtils.isISODigit(c)) {
1623                            retVal = false;
1624                            break;
1625                        }
1626                    }
1627                }
1628            }
1629        } else {
1630            Log.e("isNanp: null dialStr passed in", dialStr);
1631        }
1632        return retVal;
1633    }
1634
1635   /**
1636    * This function checks if the passed in string conforms to 1-NANP format
1637    */
1638    private static boolean isOneNanp(String dialStr) {
1639        boolean retVal = false;
1640        if (dialStr != null) {
1641            String newDialStr = dialStr.substring(1);
1642            if ((dialStr.charAt(0) == '1') && isNanp(newDialStr)) {
1643                retVal = true;
1644            }
1645        } else {
1646            Log.e("isOneNanp: null dialStr passed in", dialStr);
1647        }
1648        return retVal;
1649    }
1650
1651    /**
1652     * @hide
1653     * @param number
1654     * @return true if number contains @
1655     */
1656    public static boolean isUriNumber(String number) {
1657        return number != null && number.contains("@");
1658    }
1659
1660    /**
1661     * This function handles the plus code conversion within NANP CDMA network
1662     * If the number format is
1663     * 1)+1NANP,remove +,
1664     * 2)other than +1NANP, any + numbers,replace + with the current IDP
1665     */
1666    private static String processPlusCodeWithinNanp(String networkDialStr) {
1667        String retStr = networkDialStr;
1668
1669        if (DBG) log("processPlusCodeWithinNanp,networkDialStr=" + networkDialStr);
1670        // If there is a plus sign at the beginning of the dial string,
1671        // Convert the plus sign to the default IDP since it's an international number
1672        if (networkDialStr != null &&
1673            networkDialStr.charAt(0) == PLUS_SIGN_CHAR &&
1674            networkDialStr.length() > 1) {
1675            String newStr = networkDialStr.substring(1);
1676            if (isOneNanp(newStr)) {
1677                // Remove the leading plus sign
1678                retStr = newStr;
1679             } else {
1680                 String idpStr = getDefaultIdp();
1681                 // Replaces the plus sign with the default IDP
1682                 retStr = networkDialStr.replaceFirst("[+]", idpStr);
1683            }
1684        }
1685        if (DBG) log("processPlusCodeWithinNanp,retStr=" + retStr);
1686        return retStr;
1687    }
1688
1689    // This function finds the index of the dialable character(s)
1690    // in the post dial string
1691    private static int findDialableIndexFromPostDialStr(String postDialStr) {
1692        for (int index = 0;index < postDialStr.length();index++) {
1693             char c = postDialStr.charAt(index);
1694             if (isReallyDialable(c)) {
1695                return index;
1696             }
1697        }
1698        return -1;
1699    }
1700
1701    // This function appends the non-diablable P/W character to the original
1702    // dial string based on the dialable index passed in
1703    private static String
1704    appendPwCharBackToOrigDialStr(int dialableIndex,String origStr, String dialStr) {
1705        String retStr;
1706
1707        // There is only 1 P/W character before the dialable characters
1708        if (dialableIndex == 1) {
1709            StringBuilder ret = new StringBuilder(origStr);
1710            ret = ret.append(dialStr.charAt(0));
1711            retStr = ret.toString();
1712        } else {
1713            // It means more than 1 P/W characters in the post dial string,
1714            // appends to retStr
1715            String nonDigitStr = dialStr.substring(0,dialableIndex);
1716            retStr = origStr.concat(nonDigitStr);
1717        }
1718        return retStr;
1719    }
1720
1721    //===== Begining of utility methods used in compareLoosely() =====
1722
1723    /**
1724     * Phone numbers are stored in "lookup" form in the database
1725     * as reversed strings to allow for caller ID lookup
1726     *
1727     * This method takes a phone number and makes a valid SQL "LIKE"
1728     * string that will match the lookup form
1729     *
1730     */
1731    /** all of a up to len must be an international prefix or
1732     *  separators/non-dialing digits
1733     */
1734    private static boolean
1735    matchIntlPrefix(String a, int len) {
1736        /* '([^0-9*#+pwn]\+[^0-9*#+pwn] | [^0-9*#+pwn]0(0|11)[^0-9*#+pwn] )$' */
1737        /*        0       1                           2 3 45               */
1738
1739        int state = 0;
1740        for (int i = 0 ; i < len ; i++) {
1741            char c = a.charAt(i);
1742
1743            switch (state) {
1744                case 0:
1745                    if      (c == '+') state = 1;
1746                    else if (c == '0') state = 2;
1747                    else if (isNonSeparator(c)) return false;
1748                break;
1749
1750                case 2:
1751                    if      (c == '0') state = 3;
1752                    else if (c == '1') state = 4;
1753                    else if (isNonSeparator(c)) return false;
1754                break;
1755
1756                case 4:
1757                    if      (c == '1') state = 5;
1758                    else if (isNonSeparator(c)) return false;
1759                break;
1760
1761                default:
1762                    if (isNonSeparator(c)) return false;
1763                break;
1764
1765            }
1766        }
1767
1768        return state == 1 || state == 3 || state == 5;
1769    }
1770
1771    /** all of 'a' up to len must be a (+|00|011)country code)
1772     *  We're fast and loose with the country code. Any \d{1,3} matches */
1773    private static boolean
1774    matchIntlPrefixAndCC(String a, int len) {
1775        /*  [^0-9*#+pwn]*(\+|0(0|11)\d\d?\d? [^0-9*#+pwn] $ */
1776        /*      0          1 2 3 45  6 7  8                 */
1777
1778        int state = 0;
1779        for (int i = 0 ; i < len ; i++ ) {
1780            char c = a.charAt(i);
1781
1782            switch (state) {
1783                case 0:
1784                    if      (c == '+') state = 1;
1785                    else if (c == '0') state = 2;
1786                    else if (isNonSeparator(c)) return false;
1787                break;
1788
1789                case 2:
1790                    if      (c == '0') state = 3;
1791                    else if (c == '1') state = 4;
1792                    else if (isNonSeparator(c)) return false;
1793                break;
1794
1795                case 4:
1796                    if      (c == '1') state = 5;
1797                    else if (isNonSeparator(c)) return false;
1798                break;
1799
1800                case 1:
1801                case 3:
1802                case 5:
1803                    if      (isISODigit(c)) state = 6;
1804                    else if (isNonSeparator(c)) return false;
1805                break;
1806
1807                case 6:
1808                case 7:
1809                    if      (isISODigit(c)) state++;
1810                    else if (isNonSeparator(c)) return false;
1811                break;
1812
1813                default:
1814                    if (isNonSeparator(c)) return false;
1815            }
1816        }
1817
1818        return state == 6 || state == 7 || state == 8;
1819    }
1820
1821    /** all of 'a' up to len must match non-US trunk prefix ('0') */
1822    private static boolean
1823    matchTrunkPrefix(String a, int len) {
1824        boolean found;
1825
1826        found = false;
1827
1828        for (int i = 0 ; i < len ; i++) {
1829            char c = a.charAt(i);
1830
1831            if (c == '0' && !found) {
1832                found = true;
1833            } else if (isNonSeparator(c)) {
1834                return false;
1835            }
1836        }
1837
1838        return found;
1839    }
1840
1841    //===== End of utility methods used only in compareLoosely() =====
1842
1843    //===== Beggining of utility methods used only in compareStrictly() ====
1844
1845    /*
1846     * If true, the number is country calling code.
1847     */
1848    private static final boolean COUNTLY_CALLING_CALL[] = {
1849        true, true, false, false, false, false, false, true, false, false,
1850        false, false, false, false, false, false, false, false, false, false,
1851        true, false, false, false, false, false, false, true, true, false,
1852        true, true, true, true, true, false, true, false, false, true,
1853        true, false, false, true, true, true, true, true, true, true,
1854        false, true, true, true, true, true, true, true, true, false,
1855        true, true, true, true, true, true, true, false, false, false,
1856        false, false, false, false, false, false, false, false, false, false,
1857        false, true, true, true, true, false, true, false, false, true,
1858        true, true, true, true, true, true, false, false, true, false,
1859    };
1860    private static final int CCC_LENGTH = COUNTLY_CALLING_CALL.length;
1861
1862    /**
1863     * @return true when input is valid Country Calling Code.
1864     */
1865    private static boolean isCountryCallingCode(int countryCallingCodeCandidate) {
1866        return countryCallingCodeCandidate > 0 && countryCallingCodeCandidate < CCC_LENGTH &&
1867                COUNTLY_CALLING_CALL[countryCallingCodeCandidate];
1868    }
1869
1870    /**
1871     * Returns interger corresponding to the input if input "ch" is
1872     * ISO-LATIN characters 0-9.
1873     * Returns -1 otherwise
1874     */
1875    private static int tryGetISODigit(char ch) {
1876        if ('0' <= ch && ch <= '9') {
1877            return ch - '0';
1878        } else {
1879            return -1;
1880        }
1881    }
1882
1883    private static class CountryCallingCodeAndNewIndex {
1884        public final int countryCallingCode;
1885        public final int newIndex;
1886        public CountryCallingCodeAndNewIndex(int countryCode, int newIndex) {
1887            this.countryCallingCode = countryCode;
1888            this.newIndex = newIndex;
1889        }
1890    }
1891
1892    /*
1893     * Note that this function does not strictly care the country calling code with
1894     * 3 length (like Morocco: +212), assuming it is enough to use the first two
1895     * digit to compare two phone numbers.
1896     */
1897    private static CountryCallingCodeAndNewIndex tryGetCountryCallingCodeAndNewIndex(
1898        String str, boolean acceptThailandCase) {
1899        // Rough regexp:
1900        //  ^[^0-9*#+]*((\+|0(0|11)\d\d?|166) [^0-9*#+] $
1901        //         0        1 2 3 45  6 7  89
1902        //
1903        // In all the states, this function ignores separator characters.
1904        // "166" is the special case for the call from Thailand to the US. Uguu!
1905        int state = 0;
1906        int ccc = 0;
1907        final int length = str.length();
1908        for (int i = 0 ; i < length ; i++ ) {
1909            char ch = str.charAt(i);
1910            switch (state) {
1911                case 0:
1912                    if      (ch == '+') state = 1;
1913                    else if (ch == '0') state = 2;
1914                    else if (ch == '1') {
1915                        if (acceptThailandCase) {
1916                            state = 8;
1917                        } else {
1918                            return null;
1919                        }
1920                    } else if (isDialable(ch)) {
1921                        return null;
1922                    }
1923                break;
1924
1925                case 2:
1926                    if      (ch == '0') state = 3;
1927                    else if (ch == '1') state = 4;
1928                    else if (isDialable(ch)) {
1929                        return null;
1930                    }
1931                break;
1932
1933                case 4:
1934                    if      (ch == '1') state = 5;
1935                    else if (isDialable(ch)) {
1936                        return null;
1937                    }
1938                break;
1939
1940                case 1:
1941                case 3:
1942                case 5:
1943                case 6:
1944                case 7:
1945                    {
1946                        int ret = tryGetISODigit(ch);
1947                        if (ret > 0) {
1948                            ccc = ccc * 10 + ret;
1949                            if (ccc >= 100 || isCountryCallingCode(ccc)) {
1950                                return new CountryCallingCodeAndNewIndex(ccc, i + 1);
1951                            }
1952                            if (state == 1 || state == 3 || state == 5) {
1953                                state = 6;
1954                            } else {
1955                                state++;
1956                            }
1957                        } else if (isDialable(ch)) {
1958                            return null;
1959                        }
1960                    }
1961                    break;
1962                case 8:
1963                    if (ch == '6') state = 9;
1964                    else if (isDialable(ch)) {
1965                        return null;
1966                    }
1967                    break;
1968                case 9:
1969                    if (ch == '6') {
1970                        return new CountryCallingCodeAndNewIndex(66, i + 1);
1971                    } else {
1972                        return null;
1973                    }
1974                default:
1975                    return null;
1976            }
1977        }
1978
1979        return null;
1980    }
1981
1982    /**
1983     * Currently this function simply ignore the first digit assuming it is
1984     * trunk prefix. Actually trunk prefix is different in each country.
1985     *
1986     * e.g.
1987     * "+79161234567" equals "89161234567" (Russian trunk digit is 8)
1988     * "+33123456789" equals "0123456789" (French trunk digit is 0)
1989     *
1990     */
1991    private static int tryGetTrunkPrefixOmittedIndex(String str, int currentIndex) {
1992        int length = str.length();
1993        for (int i = currentIndex ; i < length ; i++) {
1994            final char ch = str.charAt(i);
1995            if (tryGetISODigit(ch) >= 0) {
1996                return i + 1;
1997            } else if (isDialable(ch)) {
1998                return -1;
1999            }
2000        }
2001        return -1;
2002    }
2003
2004    /**
2005     * Return true if the prefix of "str" is "ignorable". Here, "ignorable" means
2006     * that "str" has only one digit and separater characters. The one digit is
2007     * assumed to be trunk prefix.
2008     */
2009    private static boolean checkPrefixIsIgnorable(final String str,
2010            int forwardIndex, int backwardIndex) {
2011        boolean trunk_prefix_was_read = false;
2012        while (backwardIndex >= forwardIndex) {
2013            if (tryGetISODigit(str.charAt(backwardIndex)) >= 0) {
2014                if (trunk_prefix_was_read) {
2015                    // More than one digit appeared, meaning that "a" and "b"
2016                    // is different.
2017                    return false;
2018                } else {
2019                    // Ignore just one digit, assuming it is trunk prefix.
2020                    trunk_prefix_was_read = true;
2021                }
2022            } else if (isDialable(str.charAt(backwardIndex))) {
2023                // Trunk prefix is a digit, not "*", "#"...
2024                return false;
2025            }
2026            backwardIndex--;
2027        }
2028
2029        return true;
2030    }
2031
2032    //==== End of utility methods used only in compareStrictly() =====
2033}
2034