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