PhoneNumberUtils.java revision 60d45f0f0320801a16db2ad038453c098e98966c
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    /** Extracts the phone number from an Intent.
115     *
116     * @param intent the intent to get the number of
117     * @param context a context to use for database access
118     *
119     * @return the phone number that would be called by the intent, or
120     *         <code>null</code> if the number cannot be found.
121     */
122    public static String getNumberFromIntent(Intent intent, Context context) {
123        String number = null;
124
125        Uri uri = intent.getData();
126        String scheme = uri.getScheme();
127
128        if (scheme.equals("tel")) {
129            return uri.getSchemeSpecificPart();
130        }
131
132        // TODO: We don't check for SecurityException here (requires
133        // READ_PHONE_STATE permission).
134        if (scheme.equals("voicemail")) {
135            return TelephonyManager.getDefault().getVoiceMailNumber();
136        }
137
138        if (context == null) {
139            return null;
140        }
141
142        String type = intent.resolveType(context);
143        String phoneColumn = null;
144
145        // Correctly read out the phone entry based on requested provider
146        final String authority = uri.getAuthority();
147        if (Contacts.AUTHORITY.equals(authority)) {
148            phoneColumn = Contacts.People.Phones.NUMBER;
149        } else if (ContactsContract.AUTHORITY.equals(authority)) {
150            phoneColumn = ContactsContract.CommonDataKinds.Phone.NUMBER;
151        }
152
153        final Cursor c = context.getContentResolver().query(uri, new String[] {
154            phoneColumn
155        }, null, null, null);
156        if (c != null) {
157            try {
158                if (c.moveToFirst()) {
159                    number = c.getString(c.getColumnIndex(phoneColumn));
160                }
161            } finally {
162                c.close();
163            }
164        }
165
166        return number;
167    }
168
169    /** Extracts the network address portion and canonicalizes
170     *  (filters out separators.)
171     *  Network address portion is everything up to DTMF control digit
172     *  separators (pause or wait), but without non-dialable characters.
173     *
174     *  Please note that the GSM wild character is allowed in the result.
175     *  This must be resolved before dialing.
176     *
177     *  Allows + only in the first  position in the result string.
178     *
179     *  Returns null if phoneNumber == null
180     */
181    public static String
182    extractNetworkPortion(String phoneNumber) {
183        if (phoneNumber == null) {
184            return null;
185        }
186
187        int len = phoneNumber.length();
188        StringBuilder ret = new StringBuilder(len);
189        boolean firstCharAdded = false;
190
191        for (int i = 0; i < len; i++) {
192            char c = phoneNumber.charAt(i);
193            if (isDialable(c) && (c != '+' || !firstCharAdded)) {
194                firstCharAdded = true;
195                ret.append(c);
196            } else if (isStartsPostDial (c)) {
197                break;
198            }
199        }
200
201        return ret.toString();
202    }
203
204    /**
205     * Strips separators from a phone number string.
206     * @param phoneNumber phone number to strip.
207     * @return phone string stripped of separators.
208     */
209    public static String stripSeparators(String phoneNumber) {
210        if (phoneNumber == null) {
211            return null;
212        }
213        int len = phoneNumber.length();
214        StringBuilder ret = new StringBuilder(len);
215
216        for (int i = 0; i < len; i++) {
217            char c = phoneNumber.charAt(i);
218            if (isNonSeparator(c)) {
219                ret.append(c);
220            }
221        }
222
223        return ret.toString();
224    }
225
226    /** or -1 if both are negative */
227    static private int
228    minPositive (int a, int b) {
229        if (a >= 0 && b >= 0) {
230            return (a < b) ? a : b;
231        } else if (a >= 0) { /* && b < 0 */
232            return a;
233        } else if (b >= 0) { /* && a < 0 */
234            return b;
235        } else { /* a < 0 && b < 0 */
236            return -1;
237        }
238    }
239
240    private static void log(String msg) {
241        Log.d(LOG_TAG, msg);
242    }
243    /** index of the last character of the network portion
244     *  (eg anything after is a post-dial string)
245     */
246    static private int
247    indexOfLastNetworkChar(String a) {
248        int pIndex, wIndex;
249        int origLength;
250        int trimIndex;
251
252        origLength = a.length();
253
254        pIndex = a.indexOf(PAUSE);
255        wIndex = a.indexOf(WAIT);
256
257        trimIndex = minPositive(pIndex, wIndex);
258
259        if (trimIndex < 0) {
260            return origLength - 1;
261        } else {
262            return trimIndex - 1;
263        }
264    }
265
266    /**
267     * Extracts the post-dial sequence of DTMF control digits, pauses, and
268     * waits. Strips separators. This string may be empty, but will not be null
269     * unless phoneNumber == null.
270     *
271     * Returns null if phoneNumber == null
272     */
273
274    public static String
275    extractPostDialPortion(String phoneNumber) {
276        if (phoneNumber == null) return null;
277
278        int trimIndex;
279        StringBuilder ret = new StringBuilder();
280
281        trimIndex = indexOfLastNetworkChar (phoneNumber);
282
283        for (int i = trimIndex + 1, s = phoneNumber.length()
284                ; i < s; i++
285        ) {
286            char c = phoneNumber.charAt(i);
287            if (isNonSeparator(c)) {
288                ret.append(c);
289            }
290        }
291
292        return ret.toString();
293    }
294
295    /**
296     * Compare phone numbers a and b, return true if they're identical
297     * enough for caller ID purposes.
298     *
299     * - Compares from right to left
300     * - requires MIN_MATCH (5) characters to match
301     * - handles common trunk prefixes and international prefixes
302     *   (basically, everything except the Russian trunk prefix)
303     *
304     * Tolerates nulls
305     */
306    public static boolean
307    compare(String a, String b) {
308        int ia, ib;
309        int matched;
310
311        if (a == null || b == null) return a == b;
312
313        if (a.length() == 0 || b.length() == 0) {
314            return false;
315        }
316
317        ia = indexOfLastNetworkChar (a);
318        ib = indexOfLastNetworkChar (b);
319        matched = 0;
320
321        while (ia >= 0 && ib >=0) {
322            char ca, cb;
323            boolean skipCmp = false;
324
325            ca = a.charAt(ia);
326
327            if (!isDialable(ca)) {
328                ia--;
329                skipCmp = true;
330            }
331
332            cb = b.charAt(ib);
333
334            if (!isDialable(cb)) {
335                ib--;
336                skipCmp = true;
337            }
338
339            if (!skipCmp) {
340                if (cb != ca && ca != WILD && cb != WILD) {
341                    break;
342                }
343                ia--; ib--; matched++;
344            }
345        }
346
347        if (matched < MIN_MATCH) {
348            int aLen = a.length();
349
350            // if the input strings match, but their lengths < MIN_MATCH,
351            // treat them as equal.
352            if (aLen == b.length() && aLen == matched) {
353                return true;
354            }
355            return false;
356        }
357
358        // At least one string has matched completely;
359        if (matched >= MIN_MATCH && (ia < 0 || ib < 0)) {
360            return true;
361        }
362
363        /*
364         * Now, what remains must be one of the following for a
365         * match:
366         *
367         *  - a '+' on one and a '00' or a '011' on the other
368         *  - a '0' on one and a (+,00)<country code> on the other
369         *     (for this, a '0' and a '00' prefix would have succeeded above)
370         */
371
372        if (matchIntlPrefix(a, ia + 1)
373            && matchIntlPrefix (b, ib +1)
374        ) {
375            return true;
376        }
377
378        if (matchTrunkPrefix(a, ia + 1)
379            && matchIntlPrefixAndCC(b, ib +1)
380        ) {
381            return true;
382        }
383
384        if (matchTrunkPrefix(b, ib + 1)
385            && matchIntlPrefixAndCC(a, ia +1)
386        ) {
387            return true;
388        }
389
390        return false;
391    }
392
393    /**
394     * Returns the rightmost MIN_MATCH (5) characters in the network portion
395     * in *reversed* order
396     *
397     * This can be used to do a database lookup against the column
398     * that stores getStrippedReversed()
399     *
400     * Returns null if phoneNumber == null
401     */
402    public static String
403    toCallerIDMinMatch(String phoneNumber) {
404        String np = extractNetworkPortion(phoneNumber);
405        return internalGetStrippedReversed(np, MIN_MATCH);
406    }
407
408    /**
409     * Returns the network portion reversed.
410     * This string is intended to go into an index column for a
411     * database lookup.
412     *
413     * Returns null if phoneNumber == null
414     */
415    public static String
416    getStrippedReversed(String phoneNumber) {
417        String np = extractNetworkPortion(phoneNumber);
418
419        if (np == null) return null;
420
421        return internalGetStrippedReversed(np, np.length());
422    }
423
424    /**
425     * Returns the last numDigits of the reversed phone number
426     * Returns null if np == null
427     */
428    private static String
429    internalGetStrippedReversed(String np, int numDigits) {
430        if (np == null) return null;
431
432        StringBuilder ret = new StringBuilder(numDigits);
433        int length = np.length();
434
435        for (int i = length - 1, s = length
436            ; i >= 0 && (s - i) <= numDigits ; i--
437        ) {
438            char c = np.charAt(i);
439
440            ret.append(c);
441        }
442
443        return ret.toString();
444    }
445
446    /**
447     * Basically: makes sure there's a + in front of a
448     * TOA_International number
449     *
450     * Returns null if s == null
451     */
452    public static String
453    stringFromStringAndTOA(String s, int TOA) {
454        if (s == null) return null;
455
456        if (TOA == TOA_International && s.length() > 0 && s.charAt(0) != '+') {
457            return "+" + s;
458        }
459
460        return s;
461    }
462
463    /**
464     * Returns the TOA for the given dial string
465     * Basically, returns TOA_International if there's a + prefix
466     */
467
468    public static int
469    toaFromString(String s) {
470        if (s != null && s.length() > 0 && s.charAt(0) == '+') {
471            return TOA_International;
472        }
473
474        return TOA_Unknown;
475    }
476
477    /**
478     * Phone numbers are stored in "lookup" form in the database
479     * as reversed strings to allow for caller ID lookup
480     *
481     * This method takes a phone number and makes a valid SQL "LIKE"
482     * string that will match the lookup form
483     *
484     */
485    /** all of a up to len must be an international prefix or
486     *  separators/non-dialing digits
487     */
488    private static boolean
489    matchIntlPrefix(String a, int len) {
490        /* '([^0-9*#+pwn]\+[^0-9*#+pwn] | [^0-9*#+pwn]0(0|11)[^0-9*#+pwn] )$' */
491        /*        0       1                           2 3 45               */
492
493        int state = 0;
494        for (int i = 0 ; i < len ; i++) {
495            char c = a.charAt(i);
496
497            switch (state) {
498                case 0:
499                    if      (c == '+') state = 1;
500                    else if (c == '0') state = 2;
501                    else if (isNonSeparator(c)) return false;
502                break;
503
504                case 2:
505                    if      (c == '0') state = 3;
506                    else if (c == '1') state = 4;
507                    else if (isNonSeparator(c)) return false;
508                break;
509
510                case 4:
511                    if      (c == '1') state = 5;
512                    else if (isNonSeparator(c)) return false;
513                break;
514
515                default:
516                    if (isNonSeparator(c)) return false;
517                break;
518
519            }
520        }
521
522        return state == 1 || state == 3 || state == 5;
523    }
524
525    /**
526     *  3GPP TS 24.008 10.5.4.7
527     *  Called Party BCD Number
528     *
529     *  See Also TS 51.011 10.5.1 "dialing number/ssc string"
530     *  and TS 11.11 "10.3.1 EF adn (Abbreviated dialing numbers)"
531     *
532     * @param bytes the data buffer
533     * @param offset should point to the TOA (aka. TON/NPI) octet after the length byte
534     * @param length is the number of bytes including TOA byte
535     *                and must be at least 2
536     *
537     * @return partial string on invalid decode
538     *
539     * FIXME(mkf) support alphanumeric address type
540     *  currently implemented in SMSMessage.getAddress()
541     */
542    public static String
543    calledPartyBCDToString (byte[] bytes, int offset, int length) {
544        boolean prependPlus = false;
545        StringBuilder ret = new StringBuilder(1 + length * 2);
546
547        if (length < 2) {
548            return "";
549        }
550
551        if ((bytes[offset] & 0xff) == TOA_International) {
552            prependPlus = true;
553        }
554
555        internalCalledPartyBCDFragmentToString(
556                ret, bytes, offset + 1, length - 1);
557
558        if (prependPlus && ret.length() == 0) {
559            // If the only thing there is a prepended plus, return ""
560            return "";
561        }
562
563        if (prependPlus) {
564            // This is an "international number" and should have
565            // a plus prepended to the dialing number. But there
566            // can also be Gsm MMI codes as defined in TS 22.030 6.5.2
567            // so we need to handle those also.
568            //
569            // http://web.telia.com/~u47904776/gsmkode.htm is a
570            // has a nice list of some of these GSM codes.
571            //
572            // Examples are:
573            //   **21*+886988171479#
574            //   **21*8311234567#
575            //   *21#
576            //   #21#
577            //   *#21#
578            //   *31#+11234567890
579            //   #31#+18311234567
580            //   #31#8311234567
581            //   18311234567
582            //   +18311234567#
583            //   +18311234567
584            // Odd ball cases that some phones handled
585            // where there is no dialing number so they
586            // append the "+"
587            //   *21#+
588            //   **21#+
589            String retString = ret.toString();
590            Pattern p = Pattern.compile("(^[#*])(.*)([#*])(.*)(#)$");
591            Matcher m = p.matcher(retString);
592            if (m.matches()) {
593                if ("".equals(m.group(2))) {
594                    // Started with two [#*] ends with #
595                    // So no dialing number and we'll just
596                    // append a +, this handles **21#+
597                    ret = new StringBuilder();
598                    ret.append(m.group(1));
599                    ret.append(m.group(3));
600                    ret.append(m.group(4));
601                    ret.append(m.group(5));
602                    ret.append("+");
603                } else {
604                    // Starts with [#*] and ends with #
605                    // Assume group 4 is a dialing number
606                    // such as *21*+1234554#
607                    ret = new StringBuilder();
608                    ret.append(m.group(1));
609                    ret.append(m.group(2));
610                    ret.append(m.group(3));
611                    ret.append("+");
612                    ret.append(m.group(4));
613                    ret.append(m.group(5));
614                }
615            } else {
616                p = Pattern.compile("(^[#*])(.*)([#*])(.*)");
617                m = p.matcher(retString);
618                if (m.matches()) {
619                    // Starts with [#*] and only one other [#*]
620                    // Assume the data after last [#*] is dialing
621                    // number (i.e. group 4) such as *31#+11234567890.
622                    // This also includes the odd ball *21#+
623                    ret = new StringBuilder();
624                    ret.append(m.group(1));
625                    ret.append(m.group(2));
626                    ret.append(m.group(3));
627                    ret.append("+");
628                    ret.append(m.group(4));
629                } else {
630                    // Does NOT start with [#*] just prepend '+'
631                    ret = new StringBuilder();
632                    ret.append('+');
633                    ret.append(retString);
634                }
635            }
636        }
637
638        return ret.toString();
639    }
640
641    private static void
642    internalCalledPartyBCDFragmentToString(
643        StringBuilder sb, byte [] bytes, int offset, int length) {
644        for (int i = offset ; i < length + offset ; i++) {
645            byte b;
646            char c;
647
648            c = bcdToChar((byte)(bytes[i] & 0xf));
649
650            if (c == 0) {
651                return;
652            }
653            sb.append(c);
654
655            // FIXME(mkf) TS 23.040 9.1.2.3 says
656            // "if a mobile receives 1111 in a position prior to
657            // the last semi-octet then processing shall commense with
658            // the next semi-octet and the intervening
659            // semi-octet shall be ignored"
660            // How does this jive with 24,008 10.5.4.7
661
662            b = (byte)((bytes[i] >> 4) & 0xf);
663
664            if (b == 0xf && i + 1 == length + offset) {
665                //ignore final 0xf
666                break;
667            }
668
669            c = bcdToChar(b);
670            if (c == 0) {
671                return;
672            }
673
674            sb.append(c);
675        }
676
677    }
678
679    /**
680     * Like calledPartyBCDToString, but field does not start with a
681     * TOA byte. For example: SIM ADN extension fields
682     */
683
684    public static String
685    calledPartyBCDFragmentToString(byte [] bytes, int offset, int length) {
686        StringBuilder ret = new StringBuilder(length * 2);
687
688        internalCalledPartyBCDFragmentToString(ret, bytes, offset, length);
689
690        return ret.toString();
691    }
692
693    /** returns 0 on invalid value */
694    private static char
695    bcdToChar(byte b) {
696        if (b < 0xa) {
697            return (char)('0' + b);
698        } else switch (b) {
699            case 0xa: return '*';
700            case 0xb: return '#';
701            case 0xc: return PAUSE;
702            case 0xd: return WILD;
703
704            default: return 0;
705        }
706    }
707
708    private static int
709    charToBCD(char c) {
710        if (c >= '0' && c <= '9') {
711            return c - '0';
712        } else if (c == '*') {
713            return 0xa;
714        } else if (c == '#') {
715            return 0xb;
716        } else if (c == PAUSE) {
717            return 0xc;
718        } else if (c == WILD) {
719            return 0xd;
720        } else {
721            throw new RuntimeException ("invalid char for BCD " + c);
722        }
723    }
724
725    /**
726     * Return true iff the network portion of <code>address</code> is,
727     * as far as we can tell on the device, suitable for use as an SMS
728     * destination address.
729     */
730    public static boolean isWellFormedSmsAddress(String address) {
731        String networkPortion =
732                PhoneNumberUtils.extractNetworkPortion(address);
733
734        return (!(networkPortion.equals("+")
735                  || TextUtils.isEmpty(networkPortion)))
736               && isDialable(networkPortion);
737    }
738
739    public static boolean isGlobalPhoneNumber(String phoneNumber) {
740        if (TextUtils.isEmpty(phoneNumber)) {
741            return false;
742        }
743
744        Matcher match = GLOBAL_PHONE_NUMBER_PATTERN.matcher(phoneNumber);
745        return match.matches();
746    }
747
748    private static boolean isDialable(String address) {
749        for (int i = 0, count = address.length(); i < count; i++) {
750            if (!isDialable(address.charAt(i))) {
751                return false;
752            }
753        }
754        return true;
755    }
756
757    private static boolean isNonSeparator(String address) {
758        for (int i = 0, count = address.length(); i < count; i++) {
759            if (!isNonSeparator(address.charAt(i))) {
760                return false;
761            }
762        }
763        return true;
764    }
765    /**
766     * Note: calls extractNetworkPortion(), so do not use for
767     * SIM EF[ADN] style records
768     *
769     * Returns null if network portion is empty.
770     */
771    public static byte[]
772    networkPortionToCalledPartyBCD(String s) {
773        String networkPortion = extractNetworkPortion(s);
774        return numberToCalledPartyBCDHelper(networkPortion, false);
775    }
776
777    /**
778     * Same as {@link #networkPortionToCalledPartyBCD}, but includes a
779     * one-byte length prefix.
780     */
781    public static byte[]
782    networkPortionToCalledPartyBCDWithLength(String s) {
783        String networkPortion = extractNetworkPortion(s);
784        return numberToCalledPartyBCDHelper(networkPortion, true);
785    }
786
787    /**
788     * Convert a dialing number to BCD byte array
789     *
790     * @param number dialing number string
791     *        if the dialing number starts with '+', set to internationl TOA
792     * @return BCD byte array
793     */
794    public static byte[]
795    numberToCalledPartyBCD(String number) {
796        return numberToCalledPartyBCDHelper(number, false);
797    }
798
799    /**
800     * If includeLength is true, prepend a one-byte length value to
801     * the return array.
802     */
803    private static byte[]
804    numberToCalledPartyBCDHelper(String number, boolean includeLength) {
805        int numberLenReal = number.length();
806        int numberLenEffective = numberLenReal;
807        boolean hasPlus = number.indexOf('+') != -1;
808        if (hasPlus) numberLenEffective--;
809
810        if (numberLenEffective == 0) return null;
811
812        int resultLen = (numberLenEffective + 1) / 2;  // Encoded numbers require only 4 bits each.
813        int extraBytes = 1;                            // Prepended TOA byte.
814        if (includeLength) extraBytes++;               // Optional prepended length byte.
815        resultLen += extraBytes;
816
817        byte[] result = new byte[resultLen];
818
819        int digitCount = 0;
820        for (int i = 0; i < numberLenReal; i++) {
821            char c = number.charAt(i);
822            if (c == '+') continue;
823            int shift = ((digitCount & 0x01) == 1) ? 4 : 0;
824            result[extraBytes + (digitCount >> 1)] |= (byte)((charToBCD(c) & 0x0F) << shift);
825            digitCount++;
826        }
827
828        // 1-fill any trailing odd nibble/quartet.
829        if ((digitCount & 0x01) == 1) result[extraBytes + (digitCount >> 1)] |= 0xF0;
830
831        int offset = 0;
832        if (includeLength) result[offset++] = (byte)(resultLen - 1);
833        result[offset] = (byte)(hasPlus ? TOA_International : TOA_Unknown);
834
835        return result;
836    }
837
838    /** all of 'a' up to len must match non-US trunk prefix ('0') */
839    private static boolean
840    matchTrunkPrefix(String a, int len) {
841        boolean found;
842
843        found = false;
844
845        for (int i = 0 ; i < len ; i++) {
846            char c = a.charAt(i);
847
848            if (c == '0' && !found) {
849                found = true;
850            } else if (isNonSeparator(c)) {
851                return false;
852            }
853        }
854
855        return found;
856    }
857
858    /** all of 'a' up to len must be a (+|00|011)country code)
859     *  We're fast and loose with the country code. Any \d{1,3} matches */
860    private static boolean
861    matchIntlPrefixAndCC(String a, int len) {
862        /*  [^0-9*#+pwn]*(\+|0(0|11)\d\d?\d? [^0-9*#+pwn] $ */
863        /*      0          1 2 3 45  6 7  8                 */
864
865        int state = 0;
866        for (int i = 0 ; i < len ; i++ ) {
867            char c = a.charAt(i);
868
869            switch (state) {
870                case 0:
871                    if      (c == '+') state = 1;
872                    else if (c == '0') state = 2;
873                    else if (isNonSeparator(c)) return false;
874                break;
875
876                case 2:
877                    if      (c == '0') state = 3;
878                    else if (c == '1') state = 4;
879                    else if (isNonSeparator(c)) return false;
880                break;
881
882                case 4:
883                    if      (c == '1') state = 5;
884                    else if (isNonSeparator(c)) return false;
885                break;
886
887                case 1:
888                case 3:
889                case 5:
890                    if      (isISODigit(c)) state = 6;
891                    else if (isNonSeparator(c)) return false;
892                break;
893
894                case 6:
895                case 7:
896                    if      (isISODigit(c)) state++;
897                    else if (isNonSeparator(c)) return false;
898                break;
899
900                default:
901                    if (isNonSeparator(c)) return false;
902            }
903        }
904
905        return state == 6 || state == 7 || state == 8;
906    }
907
908    //================ Number formatting =========================
909
910    /** The current locale is unknown, look for a country code or don't format */
911    public static final int FORMAT_UNKNOWN = 0;
912    /** NANP formatting */
913    public static final int FORMAT_NANP = 1;
914    /** Japanese formatting */
915    public static final int FORMAT_JAPAN = 2;
916
917    /** List of country codes for countries that use the NANP */
918    private static final String[] NANP_COUNTRIES = new String[] {
919        "US", // United States
920        "CA", // Canada
921        "AS", // American Samoa
922        "AI", // Anguilla
923        "AG", // Antigua and Barbuda
924        "BS", // Bahamas
925        "BB", // Barbados
926        "BM", // Bermuda
927        "VG", // British Virgin Islands
928        "KY", // Cayman Islands
929        "DM", // Dominica
930        "DO", // Dominican Republic
931        "GD", // Grenada
932        "GU", // Guam
933        "JM", // Jamaica
934        "PR", // Puerto Rico
935        "MS", // Montserrat
936        "MP", // Northern Mariana Islands
937        "KN", // Saint Kitts and Nevis
938        "LC", // Saint Lucia
939        "VC", // Saint Vincent and the Grenadines
940        "TT", // Trinidad and Tobago
941        "TC", // Turks and Caicos Islands
942        "VI", // U.S. Virgin Islands
943    };
944
945    /**
946     * Breaks the given number down and formats it according to the rules
947     * for the country the number is from.
948     *
949     * @param source the phone number to format
950     * @return a locally acceptable formatting of the input, or the raw input if
951     *  formatting rules aren't known for the number
952     */
953    public static String formatNumber(String source) {
954        SpannableStringBuilder text = new SpannableStringBuilder(source);
955        formatNumber(text, getFormatTypeForLocale(Locale.getDefault()));
956        return text.toString();
957    }
958
959    /**
960     * Returns the phone number formatting type for the given locale.
961     *
962     * @param locale The locale of interest, usually {@link Locale#getDefault()}
963     * @return the formatting type for the given locale, or FORMAT_UNKNOWN if the formatting
964     * rules are not known for the given locale
965     */
966    public static int getFormatTypeForLocale(Locale locale) {
967        String country = locale.getCountry();
968
969        return getFormatTypeFromCountryCode(country);
970    }
971
972    /**
973     * Formats a phone number in-place. Currently only supports NANP formatting.
974     *
975     * @param text The number to be formatted, will be modified with the formatting
976     * @param defaultFormattingType The default formatting rules to apply if the number does
977     * not begin with +<country_code>
978     */
979    public static void formatNumber(Editable text, int defaultFormattingType) {
980        int formatType = defaultFormattingType;
981
982        if (text.length() > 2 && text.charAt(0) == '+') {
983            if (text.charAt(1) == '1') {
984                formatType = FORMAT_NANP;
985            } else if (text.length() >= 3 && text.charAt(1) == '8'
986                && text.charAt(2) == '1') {
987                formatType = FORMAT_JAPAN;
988            } else {
989                return;
990            }
991        }
992
993        switch (formatType) {
994            case FORMAT_NANP:
995                formatNanpNumber(text);
996                return;
997            case FORMAT_JAPAN:
998                formatJapaneseNumber(text);
999                return;
1000        }
1001    }
1002
1003    private static final int NANP_STATE_DIGIT = 1;
1004    private static final int NANP_STATE_PLUS = 2;
1005    private static final int NANP_STATE_ONE = 3;
1006    private static final int NANP_STATE_DASH = 4;
1007
1008    /**
1009     * Formats a phone number in-place using the NANP formatting rules. Numbers will be formatted
1010     * as:
1011     *
1012     * <p><code>
1013     * xxxxx
1014     * xxx-xxxx
1015     * xxx-xxx-xxxx
1016     * 1-xxx-xxx-xxxx
1017     * +1-xxx-xxx-xxxx
1018     * </code></p>
1019     *
1020     * @param text the number to be formatted, will be modified with the formatting
1021     */
1022    public static void formatNanpNumber(Editable text) {
1023        int length = text.length();
1024        if (length > "+1-nnn-nnn-nnnn".length()) {
1025            // The string is too long to be formatted
1026            return;
1027        } else if (length <= 5) {
1028            // The string is either a shortcode or too short to be formatted
1029            return;
1030        }
1031
1032        CharSequence saved = text.subSequence(0, length);
1033
1034        // Strip the dashes first, as we're going to add them back
1035        int p = 0;
1036        while (p < text.length()) {
1037            if (text.charAt(p) == '-') {
1038                text.delete(p, p + 1);
1039            } else {
1040                p++;
1041            }
1042        }
1043        length = text.length();
1044
1045        // When scanning the number we record where dashes need to be added,
1046        // if they're non-0 at the end of the scan the dashes will be added in
1047        // the proper places.
1048        int dashPositions[] = new int[3];
1049        int numDashes = 0;
1050
1051        int state = NANP_STATE_DIGIT;
1052        int numDigits = 0;
1053        for (int i = 0; i < length; i++) {
1054            char c = text.charAt(i);
1055            switch (c) {
1056                case '1':
1057                    if (numDigits == 0 || state == NANP_STATE_PLUS) {
1058                        state = NANP_STATE_ONE;
1059                        break;
1060                    }
1061                    // fall through
1062                case '2':
1063                case '3':
1064                case '4':
1065                case '5':
1066                case '6':
1067                case '7':
1068                case '8':
1069                case '9':
1070                case '0':
1071                    if (state == NANP_STATE_PLUS) {
1072                        // Only NANP number supported for now
1073                        text.replace(0, length, saved);
1074                        return;
1075                    } else if (state == NANP_STATE_ONE) {
1076                        // Found either +1 or 1, follow it up with a dash
1077                        dashPositions[numDashes++] = i;
1078                    } else if (state != NANP_STATE_DASH && (numDigits == 3 || numDigits == 6)) {
1079                        // Found a digit that should be after a dash that isn't
1080                        dashPositions[numDashes++] = i;
1081                    }
1082                    state = NANP_STATE_DIGIT;
1083                    numDigits++;
1084                    break;
1085
1086                case '-':
1087                    state = NANP_STATE_DASH;
1088                    break;
1089
1090                case '+':
1091                    if (i == 0) {
1092                        // Plus is only allowed as the first character
1093                        state = NANP_STATE_PLUS;
1094                        break;
1095                    }
1096                    // Fall through
1097                default:
1098                    // Unknown character, bail on formatting
1099                    text.replace(0, length, saved);
1100                    return;
1101            }
1102        }
1103
1104        if (numDigits == 7) {
1105            // With 7 digits we want xxx-xxxx, not xxx-xxx-x
1106            numDashes--;
1107        }
1108
1109        // Actually put the dashes in place
1110        for (int i = 0; i < numDashes; i++) {
1111            int pos = dashPositions[i];
1112            text.replace(pos + i, pos + i, "-");
1113        }
1114
1115        // Remove trailing dashes
1116        int len = text.length();
1117        while (len > 0) {
1118            if (text.charAt(len - 1) == '-') {
1119                text.delete(len - 1, len);
1120                len--;
1121            } else {
1122                break;
1123            }
1124        }
1125    }
1126
1127    /**
1128     * Formats a phone number in-place using the Japanese formatting rules.
1129     * Numbers will be formatted as:
1130     *
1131     * <p><code>
1132     * 03-xxxx-xxxx
1133     * 090-xxxx-xxxx
1134     * 0120-xxx-xxx
1135     * +81-3-xxxx-xxxx
1136     * +81-90-xxxx-xxxx
1137     * </code></p>
1138     *
1139     * @param text the number to be formatted, will be modified with
1140     * the formatting
1141     */
1142    public static void formatJapaneseNumber(Editable text) {
1143        JapanesePhoneNumberFormatter.format(text);
1144    }
1145
1146    // Three and four digit phone numbers for either special services
1147    // or from the network (eg carrier-originated SMS messages) should
1148    // not match
1149    static final int MIN_MATCH = 5;
1150
1151    /**
1152     * isEmergencyNumber: checks a given number against the list of
1153     *   emergency numbers provided by the RIL and SIM card.
1154     *
1155     * @param number the number to look up.
1156     * @return if the number is in the list of emergency numbers
1157     * listed in the ril / sim, then return true, otherwise false.
1158     */
1159    public static boolean isEmergencyNumber(String number) {
1160        // Strip the separators from the number before comparing it
1161        // to the list.
1162        number = extractNetworkPortion(number);
1163
1164        // retrieve the list of emergency numbers
1165        String numbers = SystemProperties.get("ro.ril.ecclist");
1166
1167        if (!TextUtils.isEmpty(numbers)) {
1168            // searches through the comma-separated list for a match,
1169            // return true if one is found.
1170            for (String emergencyNum : numbers.split(",")) {
1171                if (emergencyNum.equals(number)) {
1172                    return true;
1173                }
1174            }
1175            // no matches found against the list!
1176            return false;
1177        }
1178
1179        //no ecclist system property, so use our own list.
1180        return (number.equals("112") || number.equals("911"));
1181    }
1182
1183    /**
1184     * isVoiceMailNumber: checks a given number against the voicemail
1185     *   number provided by the RIL and SIM card. The caller must have
1186     *   the READ_PHONE_STATE credential.
1187     *
1188     * @param number the number to look up.
1189     * @return true if the number is in the list of voicemail. False
1190     * otherwise, including if the caller does not have the permission
1191     * to read the VM number.
1192     * @hide TODO: pending API Council approval
1193     */
1194    public static boolean isVoiceMailNumber(String number) {
1195        String vmNumber;
1196
1197        try {
1198            vmNumber = TelephonyManager.getDefault().getVoiceMailNumber();
1199        } catch (SecurityException ex) {
1200            return false;
1201        }
1202
1203        // Strip the separators from the number before comparing it
1204        // to the list.
1205        number = extractNetworkPortion(number);
1206
1207        // compare tolerates null so we need to make sure that we
1208        // don't return true when both are null.
1209        return !TextUtils.isEmpty(number) && compare(number, vmNumber);
1210    }
1211
1212    /**
1213     * Translates any alphabetic letters (i.e. [A-Za-z]) in the
1214     * specified phone number into the equivalent numeric digits,
1215     * according to the phone keypad letter mapping described in
1216     * ITU E.161 and ISO/IEC 9995-8.
1217     *
1218     * @return the input string, with alpha letters converted to numeric
1219     *         digits using the phone keypad letter mapping.  For example,
1220     *         an input of "1-800-GOOG-411" will return "1-800-4664-411".
1221     */
1222    public static String convertKeypadLettersToDigits(String input) {
1223        if (input == null) {
1224            return input;
1225        }
1226        int len = input.length();
1227        if (len == 0) {
1228            return input;
1229        }
1230
1231        char[] out = input.toCharArray();
1232
1233        for (int i = 0; i < len; i++) {
1234            char c = out[i];
1235            // If this char isn't in KEYPAD_MAP at all, just leave it alone.
1236            out[i] = (char) KEYPAD_MAP.get(c, c);
1237        }
1238
1239        return new String(out);
1240    }
1241
1242    /**
1243     * The phone keypad letter mapping (see ITU E.161 or ISO/IEC 9995-8.)
1244     * TODO: This should come from a resource.
1245     */
1246    private static final SparseIntArray KEYPAD_MAP = new SparseIntArray();
1247    static {
1248        KEYPAD_MAP.put('a', '2'); KEYPAD_MAP.put('b', '2'); KEYPAD_MAP.put('c', '2');
1249        KEYPAD_MAP.put('A', '2'); KEYPAD_MAP.put('B', '2'); KEYPAD_MAP.put('C', '2');
1250
1251        KEYPAD_MAP.put('d', '3'); KEYPAD_MAP.put('e', '3'); KEYPAD_MAP.put('f', '3');
1252        KEYPAD_MAP.put('D', '3'); KEYPAD_MAP.put('E', '3'); KEYPAD_MAP.put('F', '3');
1253
1254        KEYPAD_MAP.put('g', '4'); KEYPAD_MAP.put('h', '4'); KEYPAD_MAP.put('i', '4');
1255        KEYPAD_MAP.put('G', '4'); KEYPAD_MAP.put('H', '4'); KEYPAD_MAP.put('I', '4');
1256
1257        KEYPAD_MAP.put('j', '5'); KEYPAD_MAP.put('k', '5'); KEYPAD_MAP.put('l', '5');
1258        KEYPAD_MAP.put('J', '5'); KEYPAD_MAP.put('K', '5'); KEYPAD_MAP.put('L', '5');
1259
1260        KEYPAD_MAP.put('m', '6'); KEYPAD_MAP.put('n', '6'); KEYPAD_MAP.put('o', '6');
1261        KEYPAD_MAP.put('M', '6'); KEYPAD_MAP.put('N', '6'); KEYPAD_MAP.put('O', '6');
1262
1263        KEYPAD_MAP.put('p', '7'); KEYPAD_MAP.put('q', '7'); KEYPAD_MAP.put('r', '7'); KEYPAD_MAP.put('s', '7');
1264        KEYPAD_MAP.put('P', '7'); KEYPAD_MAP.put('Q', '7'); KEYPAD_MAP.put('R', '7'); KEYPAD_MAP.put('S', '7');
1265
1266        KEYPAD_MAP.put('t', '8'); KEYPAD_MAP.put('u', '8'); KEYPAD_MAP.put('v', '8');
1267        KEYPAD_MAP.put('T', '8'); KEYPAD_MAP.put('U', '8'); KEYPAD_MAP.put('V', '8');
1268
1269        KEYPAD_MAP.put('w', '9'); KEYPAD_MAP.put('x', '9'); KEYPAD_MAP.put('y', '9'); KEYPAD_MAP.put('z', '9');
1270        KEYPAD_MAP.put('W', '9'); KEYPAD_MAP.put('X', '9'); KEYPAD_MAP.put('Y', '9'); KEYPAD_MAP.put('Z', '9');
1271    }
1272
1273    //================ Plus Code formatting =========================
1274    private static final char PLUS_SIGN_CHAR = '+';
1275    private static final String PLUS_SIGN_STRING = "+";
1276    private static final String NANP_IDP_STRING = "011";
1277    private static final int NANP_LENGTH = 10;
1278
1279    /**
1280     * This function checks if there is a plus sign (+) in the passed-in dialing number.
1281     * If there is, it processes the plus sign based on the default telephone
1282     * numbering plan of the system when the phone is activated and the current
1283     * telephone numbering plan of the system that the phone is camped on.
1284     * Currently, we only support the case that the default and current telephone
1285     * numbering plans are North American Numbering Plan(NANP).
1286     *
1287     * The passed-in dialStr should only contain the valid format as described below,
1288     * 1) the 1st character in the dialStr should be one of the really dialable
1289     *    characters listed below
1290     *    ISO-LATIN characters 0-9, *, # , +
1291     * 2) the dialStr should already strip out the separator characters,
1292     *    every character in the dialStr should be one of the non separator characters
1293     *    listed below
1294     *    ISO-LATIN characters 0-9, *, # , +, WILD, WAIT, PAUSE
1295     *
1296     * Otherwise, this function returns the dial string passed in
1297     *
1298     * @param dialStr the original dial string
1299     * @return the converted dial string if the current/default countries belong to NANP,
1300     * and if there is the "+" in the original dial string. Otherwise, the original dial
1301     * string returns.
1302     *
1303     * This API is for CDMA only
1304     *
1305     * @hide TODO: pending API Council approval
1306     */
1307    public static String cdmaCheckAndProcessPlusCode(String dialStr) {
1308        if (!TextUtils.isEmpty(dialStr)) {
1309            if (isReallyDialable(dialStr.charAt(0)) &&
1310                isNonSeparator(dialStr)) {
1311                String currIso = SystemProperties.get(PROPERTY_OPERATOR_ISO_COUNTRY, "");
1312                String defaultIso = SystemProperties.get(PROPERTY_ICC_OPERATOR_ISO_COUNTRY, "");
1313                if (!TextUtils.isEmpty(currIso) && !TextUtils.isEmpty(defaultIso)) {
1314                    return cdmaCheckAndProcessPlusCodeByNumberFormat(dialStr,
1315                            getFormatTypeFromCountryCode(currIso),
1316                            getFormatTypeFromCountryCode(defaultIso));
1317                }
1318            }
1319        }
1320        return dialStr;
1321    }
1322
1323    /**
1324     * This function should be called from checkAndProcessPlusCode only
1325     * And it is used for test purpose also.
1326     *
1327     * It checks the dial string by looping through the network portion,
1328     * post dial portion 1, post dial porting 2, etc. If there is any
1329     * plus sign, then process the plus sign.
1330     * Currently, this function supports the plus sign conversion within NANP only.
1331     * Specifically, it handles the plus sign in the following ways:
1332     * 1)+1NANP,remove +, e.g.
1333     *   +18475797000 is converted to 18475797000,
1334     * 2)+NANP or +non-NANP Numbers,replace + with the current NANP IDP, e.g,
1335     *   +8475797000 is converted to 0118475797000,
1336     *   +11875767800 is converted to 01111875767800
1337     * 3)+1NANP in post dial string(s), e.g.
1338     *   8475797000;+18475231753 is converted to 8475797000;18475231753
1339     *
1340     *
1341     * @param dialStr the original dial string
1342     * @param currFormat the numbering system of the current country that the phone is camped on
1343     * @param defaultFormat the numbering system of the country that the phone is activated on
1344     * @return the converted dial string if the current/default countries belong to NANP,
1345     * and if there is the "+" in the original dial string. Otherwise, the original dial
1346     * string returns.
1347     *
1348     * @hide
1349     */
1350    public static String
1351    cdmaCheckAndProcessPlusCodeByNumberFormat(String dialStr,int currFormat,int defaultFormt) {
1352        String retStr = dialStr;
1353
1354        // Checks if the plus sign character is in the passed-in dial string
1355        if (dialStr != null &&
1356            dialStr.lastIndexOf(PLUS_SIGN_STRING) != -1) {
1357            // Format the string based on the rules for the country the number is from,
1358            // and the current country the phone is camped on.
1359            if ((currFormat == defaultFormt) && (currFormat == FORMAT_NANP)) {
1360                // Handle case where default and current telephone numbering plans are NANP.
1361                String postDialStr = null;
1362                String tempDialStr = dialStr;
1363
1364                // Sets the retStr to null since the conversion will be performed below.
1365                retStr = null;
1366                if (DBG) log("checkAndProcessPlusCode,dialStr=" + dialStr);
1367                // This routine is to process the plus sign in the dial string by loop through
1368                // the network portion, post dial portion 1, post dial portion 2... etc. if
1369                // applied
1370                do {
1371                    String networkDialStr;
1372                    networkDialStr = extractNetworkPortion(tempDialStr);
1373                    // Handles the conversion within NANP
1374                    networkDialStr = processPlusCodeWithinNanp(networkDialStr);
1375
1376                    // Concatenates the string that is converted from network portion
1377                    if (!TextUtils.isEmpty(networkDialStr)) {
1378                        if (retStr == null) {
1379                            retStr = networkDialStr;
1380                        } else {
1381                            retStr = retStr.concat(networkDialStr);
1382                        }
1383                    } else {
1384                        // This should never happen since we checked the if dialStr is null
1385                        // and if it contains the plus sign in the beginning of this function.
1386                        // The plus sign is part of the network portion.
1387                        Log.e("checkAndProcessPlusCode: null newDialStr", networkDialStr);
1388                        return dialStr;
1389                    }
1390                    postDialStr = extractPostDialPortion(tempDialStr);
1391                    if (!TextUtils.isEmpty(postDialStr)) {
1392                        int dialableIndex = findDialableIndexFromPostDialStr(postDialStr);
1393
1394                        // dialableIndex should always be greater than 0
1395                        if (dialableIndex >= 1) {
1396                            retStr = appendPwCharBackToOrigDialStr(dialableIndex,
1397                                     retStr,postDialStr);
1398                            // Skips the P/W character, extracts the dialable portion
1399                            tempDialStr = postDialStr.substring(dialableIndex);
1400                        } else {
1401                            // Non-dialable character such as P/W should not be at the end of
1402                            // the dial string after P/W processing in CdmaConnection.java
1403                            // Set the postDialStr to "" to break out of the loop
1404                            if (dialableIndex < 0) {
1405                                postDialStr = "";
1406                            }
1407                            Log.e("wrong postDialStr=", postDialStr);
1408                        }
1409                    }
1410                    if (DBG) log("checkAndProcessPlusCode,postDialStr=" + postDialStr);
1411                } while (!TextUtils.isEmpty(postDialStr) && !TextUtils.isEmpty(tempDialStr));
1412            } else {
1413                // TODO: Support NANP international conversion and other telephone numbering plans.
1414                // Currently the phone is never used in non-NANP system, so return the original
1415                // dial string.
1416                Log.e("checkAndProcessPlusCode:non-NANP not supported", dialStr);
1417            }
1418        }
1419        return retStr;
1420     }
1421
1422    // This function gets the default international dialing prefix
1423    private static String getDefaultIdp( ) {
1424        String ps = null;
1425        SystemProperties.get(PROPERTY_IDP_STRING, ps);
1426        if (TextUtils.isEmpty(ps)) {
1427            ps = NANP_IDP_STRING;
1428        }
1429        return ps;
1430    }
1431
1432    private static boolean isTwoToNine (char c) {
1433        if (c >= '2' && c <= '9') {
1434            return true;
1435        } else {
1436            return false;
1437        }
1438    }
1439
1440    private static int getFormatTypeFromCountryCode (String country) {
1441        // Check for the NANP countries
1442        int length = NANP_COUNTRIES.length;
1443        for (int i = 0; i < length; i++) {
1444            if (NANP_COUNTRIES[i].compareToIgnoreCase(country) == 0) {
1445                return FORMAT_NANP;
1446            }
1447        }
1448        if ("jp".compareToIgnoreCase(country) == 0) {
1449            return FORMAT_JAPAN;
1450        }
1451        return FORMAT_UNKNOWN;
1452    }
1453
1454    /**
1455     * This function checks if the passed in string conforms to the NANP format
1456     * i.e. NXX-NXX-XXXX, N is any digit 2-9 and X is any digit 0-9
1457     */
1458    private static boolean isNanp (String dialStr) {
1459        boolean retVal = false;
1460        if (dialStr != null) {
1461            if (dialStr.length() == NANP_LENGTH) {
1462                if (isTwoToNine(dialStr.charAt(0)) &&
1463                    isTwoToNine(dialStr.charAt(3))) {
1464                    retVal = true;
1465                    for (int i=1; i<NANP_LENGTH; i++ ) {
1466                        char c=dialStr.charAt(i);
1467                        if (!PhoneNumberUtils.isISODigit(c)) {
1468                            retVal = false;
1469                            break;
1470                        }
1471                    }
1472                }
1473            }
1474        } else {
1475            Log.e("isNanp: null dialStr passed in", dialStr);
1476        }
1477        return retVal;
1478    }
1479
1480   /**
1481    * This function checks if the passed in string conforms to 1-NANP format
1482    */
1483    private static boolean isOneNanp(String dialStr) {
1484        boolean retVal = false;
1485        if (dialStr != null) {
1486            String newDialStr = dialStr.substring(1);
1487            if ((dialStr.charAt(0) == '1') && isNanp(newDialStr)) {
1488                retVal = true;
1489            }
1490        } else {
1491            Log.e("isOneNanp: null dialStr passed in", dialStr);
1492        }
1493        return retVal;
1494    }
1495
1496    /**
1497     * This function handles the plus code conversion within NANP CDMA network
1498     * If the number format is
1499     * 1)+1NANP,remove +,
1500     * 2)other than +1NANP, any + numbers,replace + with the current IDP
1501     */
1502    private static String processPlusCodeWithinNanp(String networkDialStr) {
1503        String retStr = networkDialStr;
1504
1505        if (DBG) log("processPlusCodeWithinNanp,networkDialStr=" + networkDialStr);
1506        // If there is a plus sign at the beginning of the dial string,
1507        // Convert the plus sign to the default IDP since it's an international number
1508        if (networkDialStr != null &
1509            networkDialStr.charAt(0) == PLUS_SIGN_CHAR &&
1510            networkDialStr.length() > 1) {
1511            String newStr = networkDialStr.substring(1);
1512            if (isOneNanp(newStr)) {
1513                // Remove the leading plus sign
1514                retStr = newStr;
1515             } else {
1516                 String idpStr = getDefaultIdp();
1517                 // Replaces the plus sign with the default IDP
1518                 retStr = networkDialStr.replaceFirst("[+]", idpStr);
1519            }
1520        }
1521        if (DBG) log("processPlusCodeWithinNanp,retStr=" + retStr);
1522        return retStr;
1523    }
1524
1525    // This function finds the index of the dialable character(s)
1526    // in the post dial string
1527    private static int findDialableIndexFromPostDialStr(String postDialStr) {
1528        for (int index = 0;index < postDialStr.length();index++) {
1529             char c = postDialStr.charAt(index);
1530             if (isReallyDialable(c)) {
1531                return index;
1532             }
1533        }
1534        return -1;
1535    }
1536
1537    // This function appends the non-diablable P/W character to the original
1538    // dial string based on the dialable index passed in
1539    private static String
1540    appendPwCharBackToOrigDialStr(int dialableIndex,String origStr, String dialStr) {
1541        String retStr;
1542
1543        // There is only 1 P/W character before the dialable characters
1544        if (dialableIndex == 1) {
1545            StringBuilder ret = new StringBuilder(origStr);
1546            ret = ret.append(dialStr.charAt(0));
1547            retStr = ret.toString();
1548        } else {
1549            // It means more than 1 P/W characters in the post dial string,
1550            // appends to retStr
1551            String nonDigitStr = dialStr.substring(0,dialableIndex);
1552            retStr = origStr.concat(nonDigitStr);
1553        }
1554        return retStr;
1555    }
1556}
1557