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