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