PhoneNumberUtils.java revision 9e534153264138cfee383c626db92b934fafc1af
1/*
2 * Copyright (C) 2006 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.telephony;
18
19import android.content.Context;
20import android.content.Intent;
21import android.database.Cursor;
22import android.net.Uri;
23import android.os.SystemProperties;
24import android.provider.Contacts;
25import android.provider.ContactsContract;
26import android.text.Editable;
27import android.text.SpannableStringBuilder;
28import android.text.TextUtils;
29import android.util.Log;
30import android.util.SparseIntArray;
31
32import static com.android.internal.telephony.TelephonyProperties.PROPERTY_ICC_OPERATOR_ISO_COUNTRY;
33import static com.android.internal.telephony.TelephonyProperties.PROPERTY_IDP_STRING;
34import static com.android.internal.telephony.TelephonyProperties.PROPERTY_OPERATOR_ISO_COUNTRY;
35
36import java.util.Locale;
37import java.util.regex.Matcher;
38import java.util.regex.Pattern;
39
40/**
41 * Various utilities for dealing with phone number strings.
42 */
43public class PhoneNumberUtils
44{
45    /*
46     * Special characters
47     *
48     * (See "What is a phone number?" doc)
49     * 'p' --- GSM pause character, same as comma
50     * 'n' --- GSM wild character
51     * 'w' --- GSM wait character
52     */
53    public static final char PAUSE = ',';
54    public static final char WAIT = ';';
55    public static final char WILD = 'N';
56
57    /*
58     * TOA = TON + NPI
59     * See TS 24.008 section 10.5.4.7 for details.
60     * These are the only really useful TOA values
61     */
62    public static final int TOA_International = 0x91;
63    public static final int TOA_Unknown = 0x81;
64
65    static final String LOG_TAG = "PhoneNumberUtils";
66    private static final boolean DBG = false;
67
68    /*
69     * global-phone-number = ["+"] 1*( DIGIT / written-sep )
70     * written-sep         = ("-"/".")
71     */
72    private static final Pattern GLOBAL_PHONE_NUMBER_PATTERN =
73            Pattern.compile("[\\+]?[0-9.-]+");
74
75    /** True if c is ISO-LATIN characters 0-9 */
76    public static boolean
77    isISODigit (char c) {
78        return c >= '0' && c <= '9';
79    }
80
81    /** True if c is ISO-LATIN characters 0-9, *, # */
82    public final static boolean
83    is12Key(char c) {
84        return (c >= '0' && c <= '9') || c == '*' || c == '#';
85    }
86
87    /** True if c is ISO-LATIN characters 0-9, *, # , +, WILD  */
88    public final static boolean
89    isDialable(char c) {
90        return (c >= '0' && c <= '9') || c == '*' || c == '#' || c == '+' || c == WILD;
91    }
92
93    /** True if c is ISO-LATIN characters 0-9, *, # , + (no WILD)  */
94    public final static boolean
95    isReallyDialable(char c) {
96        return (c >= '0' && c <= '9') || c == '*' || c == '#' || c == '+';
97    }
98
99    /** True if c is ISO-LATIN characters 0-9, *, # , +, WILD, WAIT, PAUSE   */
100    public final static boolean
101    isNonSeparator(char c) {
102        return (c >= '0' && c <= '9') || c == '*' || c == '#' || c == '+'
103                || c == WILD || c == WAIT || c == PAUSE;
104    }
105
106    /** This any anything to the right of this char is part of the
107     *  post-dial string (eg this is PAUSE or WAIT)
108     */
109    public final static boolean
110    isStartsPostDial (char c) {
111        return c == PAUSE || c == WAIT;
112    }
113
114    /** Returns true if ch is not dialable or alpha char */
115    private static boolean isSeparator(char ch) {
116        return !isDialable(ch) && !(('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z'));
117    }
118
119    /** Extracts the phone number from an Intent.
120     *
121     * @param intent the intent to get the number of
122     * @param context a context to use for database access
123     *
124     * @return the phone number that would be called by the intent, or
125     *         <code>null</code> if the number cannot be found.
126     */
127    public static String getNumberFromIntent(Intent intent, Context context) {
128        String number = null;
129
130        Uri uri = intent.getData();
131        String scheme = uri.getScheme();
132
133        if (scheme.equals("tel")) {
134            return uri.getSchemeSpecificPart();
135        }
136
137        // TODO: We don't check for SecurityException here (requires
138        // READ_PHONE_STATE permission).
139        if (scheme.equals("voicemail")) {
140            return TelephonyManager.getDefault().getVoiceMailNumber();
141        }
142
143        if (context == null) {
144            return null;
145        }
146
147        String type = intent.resolveType(context);
148        String phoneColumn = null;
149
150        // Correctly read out the phone entry based on requested provider
151        final String authority = uri.getAuthority();
152        if (Contacts.AUTHORITY.equals(authority)) {
153            phoneColumn = Contacts.People.Phones.NUMBER;
154        } else if (ContactsContract.AUTHORITY.equals(authority)) {
155            phoneColumn = ContactsContract.CommonDataKinds.Phone.NUMBER;
156        }
157
158        final Cursor c = context.getContentResolver().query(uri, new String[] {
159            phoneColumn
160        }, null, null, null);
161        if (c != null) {
162            try {
163                if (c.moveToFirst()) {
164                    number = c.getString(c.getColumnIndex(phoneColumn));
165                }
166            } finally {
167                c.close();
168            }
169        }
170
171        return number;
172    }
173
174    /** Extracts the network address portion and canonicalizes
175     *  (filters out separators.)
176     *  Network address portion is everything up to DTMF control digit
177     *  separators (pause or wait), but without non-dialable characters.
178     *
179     *  Please note that the GSM wild character is allowed in the result.
180     *  This must be resolved before dialing.
181     *
182     *  Allows + only in the first  position in the result string.
183     *
184     *  Returns null if phoneNumber == null
185     */
186    public static String
187    extractNetworkPortion(String phoneNumber) {
188        if (phoneNumber == null) {
189            return null;
190        }
191
192        int len = phoneNumber.length();
193        StringBuilder ret = new StringBuilder(len);
194        boolean firstCharAdded = false;
195
196        for (int i = 0; i < len; i++) {
197            char c = phoneNumber.charAt(i);
198            if (isDialable(c) && (c != '+' || !firstCharAdded)) {
199                firstCharAdded = true;
200                ret.append(c);
201            } else if (isStartsPostDial (c)) {
202                break;
203            }
204        }
205
206        return ret.toString();
207    }
208
209    /**
210     * Extracts the network address portion and canonicalize.
211     *
212     * This function is equivalent to extractNetworkPortion(), except
213     * for allowing the PLUS character to occur at arbitrary positions
214     * in the address portion, not just the first position.
215     *
216     * @hide
217     */
218    public static String extractNetworkPortionAlt(String phoneNumber) {
219        if (phoneNumber == null) {
220            return null;
221        }
222
223        int len = phoneNumber.length();
224        StringBuilder ret = new StringBuilder(len);
225        boolean haveSeenPlus = false;
226
227        for (int i = 0; i < len; i++) {
228            char c = phoneNumber.charAt(i);
229            if (c == '+') {
230                if (haveSeenPlus) {
231                    continue;
232                }
233                haveSeenPlus = true;
234            }
235            if (isDialable(c)) {
236                ret.append(c);
237            } else if (isStartsPostDial (c)) {
238                break;
239            }
240        }
241
242        return ret.toString();
243    }
244
245    /**
246     * Strips separators from a phone number string.
247     * @param phoneNumber phone number to strip.
248     * @return phone string stripped of separators.
249     */
250    public static String stripSeparators(String phoneNumber) {
251        if (phoneNumber == null) {
252            return null;
253        }
254        int len = phoneNumber.length();
255        StringBuilder ret = new StringBuilder(len);
256
257        for (int i = 0; i < len; i++) {
258            char c = phoneNumber.charAt(i);
259            if (isNonSeparator(c)) {
260                ret.append(c);
261            }
262        }
263
264        return ret.toString();
265    }
266
267    /** or -1 if both are negative */
268    static private int
269    minPositive (int a, int b) {
270        if (a >= 0 && b >= 0) {
271            return (a < b) ? a : b;
272        } else if (a >= 0) { /* && b < 0 */
273            return a;
274        } else if (b >= 0) { /* && a < 0 */
275            return b;
276        } else { /* a < 0 && b < 0 */
277            return -1;
278        }
279    }
280
281    private static void log(String msg) {
282        Log.d(LOG_TAG, msg);
283    }
284    /** index of the last character of the network portion
285     *  (eg anything after is a post-dial string)
286     */
287    static private int
288    indexOfLastNetworkChar(String a) {
289        int pIndex, wIndex;
290        int origLength;
291        int trimIndex;
292
293        origLength = a.length();
294
295        pIndex = a.indexOf(PAUSE);
296        wIndex = a.indexOf(WAIT);
297
298        trimIndex = minPositive(pIndex, wIndex);
299
300        if (trimIndex < 0) {
301            return origLength - 1;
302        } else {
303            return trimIndex - 1;
304        }
305    }
306
307    /**
308     * Extracts the post-dial sequence of DTMF control digits, pauses, and
309     * waits. Strips separators. This string may be empty, but will not be null
310     * unless phoneNumber == null.
311     *
312     * Returns null if phoneNumber == null
313     */
314
315    public static String
316    extractPostDialPortion(String phoneNumber) {
317        if (phoneNumber == null) return null;
318
319        int trimIndex;
320        StringBuilder ret = new StringBuilder();
321
322        trimIndex = indexOfLastNetworkChar (phoneNumber);
323
324        for (int i = trimIndex + 1, s = phoneNumber.length()
325                ; i < s; i++
326        ) {
327            char c = phoneNumber.charAt(i);
328            if (isNonSeparator(c)) {
329                ret.append(c);
330            }
331        }
332
333        return ret.toString();
334    }
335
336    /**
337     * Compare phone numbers a and b, return true if they're identical enough for caller ID purposes.
338     */
339    public static boolean compare(String a, String b) {
340        // We've used loose comparation at least Eclair, which may change in the future.
341
342        return compare(a, b, false);
343    }
344
345    /**
346     * Compare phone numbers a and b, and return true if they're identical
347     * enough for caller ID purposes. Checks a resource to determine whether
348     * to use a strict or loose comparison algorithm.
349     */
350    public static boolean compare(Context context, String a, String b) {
351        boolean useStrict = context.getResources().getBoolean(
352               com.android.internal.R.bool.config_use_strict_phone_number_comparation);
353        return compare(a, b, useStrict);
354    }
355
356    /**
357     * @hide only for testing.
358     */
359    public static boolean compare(String a, String b, boolean useStrictComparation) {
360        return (useStrictComparation ? compareStrictly(a, b) : compareLoosely(a, b));
361    }
362
363    /**
364     * Compare phone numbers a and b, return true if they're identical
365     * enough for caller ID purposes.
366     *
367     * - Compares from right to left
368     * - requires MIN_MATCH (7) characters to match
369     * - handles common trunk prefixes and international prefixes
370     *   (basically, everything except the Russian trunk prefix)
371     *
372     * Note that this method does not return false even when the two phone numbers
373     * are not exactly same; rather; we can call this method "similar()", not "equals()".
374     *
375     * @hide
376     */
377    public static boolean
378    compareLoosely(String a, String b) {
379        int ia, ib;
380        int matched;
381
382        if (a == null || b == null) return a == b;
383
384        if (a.length() == 0 || b.length() == 0) {
385            return false;
386        }
387
388        ia = indexOfLastNetworkChar (a);
389        ib = indexOfLastNetworkChar (b);
390        matched = 0;
391
392        while (ia >= 0 && ib >=0) {
393            char ca, cb;
394            boolean skipCmp = false;
395
396            ca = a.charAt(ia);
397
398            if (!isDialable(ca)) {
399                ia--;
400                skipCmp = true;
401            }
402
403            cb = b.charAt(ib);
404
405            if (!isDialable(cb)) {
406                ib--;
407                skipCmp = true;
408            }
409
410            if (!skipCmp) {
411                if (cb != ca && ca != WILD && cb != WILD) {
412                    break;
413                }
414                ia--; ib--; matched++;
415            }
416        }
417
418        if (matched < MIN_MATCH) {
419            int aLen = a.length();
420
421            // if the input strings match, but their lengths < MIN_MATCH,
422            // treat them as equal.
423            if (aLen == b.length() && aLen == matched) {
424                return true;
425            }
426            return false;
427        }
428
429        // At least one string has matched completely;
430        if (matched >= MIN_MATCH && (ia < 0 || ib < 0)) {
431            return true;
432        }
433
434        /*
435         * Now, what remains must be one of the following for a
436         * match:
437         *
438         *  - a '+' on one and a '00' or a '011' on the other
439         *  - a '0' on one and a (+,00)<country code> on the other
440         *     (for this, a '0' and a '00' prefix would have succeeded above)
441         */
442
443        if (matchIntlPrefix(a, ia + 1)
444            && matchIntlPrefix (b, ib +1)
445        ) {
446            return true;
447        }
448
449        if (matchTrunkPrefix(a, ia + 1)
450            && matchIntlPrefixAndCC(b, ib +1)
451        ) {
452            return true;
453        }
454
455        if (matchTrunkPrefix(b, ib + 1)
456            && matchIntlPrefixAndCC(a, ia +1)
457        ) {
458            return true;
459        }
460
461        return false;
462    }
463
464    /**
465     * @hide
466     */
467    public static boolean
468    compareStrictly(String a, String b) {
469        return compareStrictly(a, b, true);
470    }
471
472    /**
473     * @hide
474     */
475    public static boolean
476    compareStrictly(String a, String b, boolean acceptInvalidCCCPrefix) {
477        if (a == null || b == null) {
478            return a == b;
479        } else if (a.length() == 0 && b.length() == 0) {
480            return false;
481        }
482
483        int forwardIndexA = 0;
484        int forwardIndexB = 0;
485
486        CountryCallingCodeAndNewIndex cccA =
487            tryGetCountryCallingCodeAndNewIndex(a, acceptInvalidCCCPrefix);
488        CountryCallingCodeAndNewIndex cccB =
489            tryGetCountryCallingCodeAndNewIndex(b, acceptInvalidCCCPrefix);
490        boolean bothHasCountryCallingCode = false;
491        boolean okToIgnorePrefix = true;
492        boolean trunkPrefixIsOmittedA = false;
493        boolean trunkPrefixIsOmittedB = false;
494        if (cccA != null && cccB != null) {
495            if (cccA.countryCallingCode != cccB.countryCallingCode) {
496                // Different Country Calling Code. Must be different phone number.
497                return false;
498            }
499            // When both have ccc, do not ignore trunk prefix. Without this,
500            // "+81123123" becomes same as "+810123123" (+81 == Japan)
501            okToIgnorePrefix = false;
502            bothHasCountryCallingCode = true;
503            forwardIndexA = cccA.newIndex;
504            forwardIndexB = cccB.newIndex;
505        } else if (cccA == null && cccB == null) {
506            // When both do not have ccc, do not ignore trunk prefix. Without this,
507            // "123123" becomes same as "0123123"
508            okToIgnorePrefix = false;
509        } else {
510            if (cccA != null) {
511                forwardIndexA = cccA.newIndex;
512            } else {
513                int tmp = tryGetTrunkPrefixOmittedIndex(b, 0);
514                if (tmp >= 0) {
515                    forwardIndexA = tmp;
516                    trunkPrefixIsOmittedA = true;
517                }
518            }
519            if (cccB != null) {
520                forwardIndexB = cccB.newIndex;
521            } else {
522                int tmp = tryGetTrunkPrefixOmittedIndex(b, 0);
523                if (tmp >= 0) {
524                    forwardIndexB = tmp;
525                    trunkPrefixIsOmittedB = true;
526                }
527            }
528        }
529
530        int backwardIndexA = a.length() - 1;
531        int backwardIndexB = b.length() - 1;
532        while (backwardIndexA >= forwardIndexA && backwardIndexB >= forwardIndexB) {
533            boolean skip_compare = false;
534            final char chA = a.charAt(backwardIndexA);
535            final char chB = b.charAt(backwardIndexB);
536            if (isSeparator(chA)) {
537                backwardIndexA--;
538                skip_compare = true;
539            }
540            if (isSeparator(chB)) {
541                backwardIndexB--;
542                skip_compare = true;
543            }
544
545            if (!skip_compare) {
546                if (chA != chB) {
547                    return false;
548                }
549                backwardIndexA--;
550                backwardIndexB--;
551            }
552        }
553
554        if (okToIgnorePrefix) {
555            if ((trunkPrefixIsOmittedA && forwardIndexA <= backwardIndexA) ||
556                !checkPrefixIsIgnorable(a, forwardIndexA, backwardIndexA)) {
557                if (acceptInvalidCCCPrefix) {
558                    // Maybe the code handling the special case for Thailand makes the
559                    // result garbled, so disable the code and try again.
560                    // e.g. "16610001234" must equal to "6610001234", but with
561                    //      Thailand-case handling code, they become equal to each other.
562                    //
563                    // Note: we select simplicity rather than adding some complicated
564                    //       logic here for performance(like "checking whether remaining
565                    //       numbers are just 66 or not"), assuming inputs are small
566                    //       enough.
567                    return compare(a, b, false);
568                } else {
569                    return false;
570                }
571            }
572            if ((trunkPrefixIsOmittedB && forwardIndexB <= backwardIndexB) ||
573                !checkPrefixIsIgnorable(b, forwardIndexA, backwardIndexB)) {
574                if (acceptInvalidCCCPrefix) {
575                    return compare(a, b, false);
576                } else {
577                    return false;
578                }
579            }
580        } else {
581            // In the US, 1-650-555-1234 must be equal to 650-555-1234,
582            // while 090-1234-1234 must not be equalt to 90-1234-1234 in Japan.
583            // This request exists just in US (with 1 trunk (NDD) prefix).
584            // In addition, "011 11 7005554141" must not equal to "+17005554141",
585            // while "011 1 7005554141" must equal to "+17005554141"
586            //
587            // In this comparison, we ignore the prefix '1' just once, when
588            // - at least either does not have CCC, or
589            // - the remaining non-separator number is 1
590            boolean maybeNamp = !bothHasCountryCallingCode;
591            while (backwardIndexA >= forwardIndexA) {
592                final char chA = a.charAt(backwardIndexA);
593                if (isDialable(chA)) {
594                    if (maybeNamp && tryGetISODigit(chA) == 1) {
595                        maybeNamp = false;
596                    } else {
597                        return false;
598                    }
599                }
600                backwardIndexA--;
601            }
602            while (backwardIndexB >= forwardIndexB) {
603                final char chB = b.charAt(backwardIndexB);
604                if (isDialable(chB)) {
605                    if (maybeNamp && tryGetISODigit(chB) == 1) {
606                        maybeNamp = false;
607                    } else {
608                        return false;
609                    }
610                }
611                backwardIndexB--;
612            }
613        }
614
615        return true;
616    }
617
618    /**
619     * Returns the rightmost MIN_MATCH (5) characters in the network portion
620     * in *reversed* order
621     *
622     * This can be used to do a database lookup against the column
623     * that stores getStrippedReversed()
624     *
625     * Returns null if phoneNumber == null
626     */
627    public static String
628    toCallerIDMinMatch(String phoneNumber) {
629        String np = extractNetworkPortionAlt(phoneNumber);
630        return internalGetStrippedReversed(np, MIN_MATCH);
631    }
632
633    /**
634     * Returns the network portion reversed.
635     * This string is intended to go into an index column for a
636     * database lookup.
637     *
638     * Returns null if phoneNumber == null
639     */
640    public static String
641    getStrippedReversed(String phoneNumber) {
642        String np = extractNetworkPortionAlt(phoneNumber);
643
644        if (np == null) return null;
645
646        return internalGetStrippedReversed(np, np.length());
647    }
648
649    /**
650     * Returns the last numDigits of the reversed phone number
651     * Returns null if np == null
652     */
653    private static String
654    internalGetStrippedReversed(String np, int numDigits) {
655        if (np == null) return null;
656
657        StringBuilder ret = new StringBuilder(numDigits);
658        int length = np.length();
659
660        for (int i = length - 1, s = length
661            ; i >= 0 && (s - i) <= numDigits ; i--
662        ) {
663            char c = np.charAt(i);
664
665            ret.append(c);
666        }
667
668        return ret.toString();
669    }
670
671    /**
672     * Basically: makes sure there's a + in front of a
673     * TOA_International number
674     *
675     * Returns null if s == null
676     */
677    public static String
678    stringFromStringAndTOA(String s, int TOA) {
679        if (s == null) return null;
680
681        if (TOA == TOA_International && s.length() > 0 && s.charAt(0) != '+') {
682            return "+" + s;
683        }
684
685        return s;
686    }
687
688    /**
689     * Returns the TOA for the given dial string
690     * Basically, returns TOA_International if there's a + prefix
691     */
692
693    public static int
694    toaFromString(String s) {
695        if (s != null && s.length() > 0 && s.charAt(0) == '+') {
696            return TOA_International;
697        }
698
699        return TOA_Unknown;
700    }
701
702    /**
703     *  3GPP TS 24.008 10.5.4.7
704     *  Called Party BCD Number
705     *
706     *  See Also TS 51.011 10.5.1 "dialing number/ssc string"
707     *  and TS 11.11 "10.3.1 EF adn (Abbreviated dialing numbers)"
708     *
709     * @param bytes the data buffer
710     * @param offset should point to the TOA (aka. TON/NPI) octet after the length byte
711     * @param length is the number of bytes including TOA byte
712     *                and must be at least 2
713     *
714     * @return partial string on invalid decode
715     *
716     * FIXME(mkf) support alphanumeric address type
717     *  currently implemented in SMSMessage.getAddress()
718     */
719    public static String
720    calledPartyBCDToString (byte[] bytes, int offset, int length) {
721        boolean prependPlus = false;
722        StringBuilder ret = new StringBuilder(1 + length * 2);
723
724        if (length < 2) {
725            return "";
726        }
727
728        if ((bytes[offset] & 0xff) == TOA_International) {
729            prependPlus = true;
730        }
731
732        internalCalledPartyBCDFragmentToString(
733                ret, bytes, offset + 1, length - 1);
734
735        if (prependPlus && ret.length() == 0) {
736            // If the only thing there is a prepended plus, return ""
737            return "";
738        }
739
740        if (prependPlus) {
741            // This is an "international number" and should have
742            // a plus prepended to the dialing number. But there
743            // can also be Gsm MMI codes as defined in TS 22.030 6.5.2
744            // so we need to handle those also.
745            //
746            // http://web.telia.com/~u47904776/gsmkode.htm is a
747            // has a nice list of some of these GSM codes.
748            //
749            // Examples are:
750            //   **21*+886988171479#
751            //   **21*8311234567#
752            //   *21#
753            //   #21#
754            //   *#21#
755            //   *31#+11234567890
756            //   #31#+18311234567
757            //   #31#8311234567
758            //   18311234567
759            //   +18311234567#
760            //   +18311234567
761            // Odd ball cases that some phones handled
762            // where there is no dialing number so they
763            // append the "+"
764            //   *21#+
765            //   **21#+
766            String retString = ret.toString();
767            Pattern p = Pattern.compile("(^[#*])(.*)([#*])(.*)(#)$");
768            Matcher m = p.matcher(retString);
769            if (m.matches()) {
770                if ("".equals(m.group(2))) {
771                    // Started with two [#*] ends with #
772                    // So no dialing number and we'll just
773                    // append a +, this handles **21#+
774                    ret = new StringBuilder();
775                    ret.append(m.group(1));
776                    ret.append(m.group(3));
777                    ret.append(m.group(4));
778                    ret.append(m.group(5));
779                    ret.append("+");
780                } else {
781                    // Starts with [#*] and ends with #
782                    // Assume group 4 is a dialing number
783                    // such as *21*+1234554#
784                    ret = new StringBuilder();
785                    ret.append(m.group(1));
786                    ret.append(m.group(2));
787                    ret.append(m.group(3));
788                    ret.append("+");
789                    ret.append(m.group(4));
790                    ret.append(m.group(5));
791                }
792            } else {
793                p = Pattern.compile("(^[#*])(.*)([#*])(.*)");
794                m = p.matcher(retString);
795                if (m.matches()) {
796                    // Starts with [#*] and only one other [#*]
797                    // Assume the data after last [#*] is dialing
798                    // number (i.e. group 4) such as *31#+11234567890.
799                    // This also includes the odd ball *21#+
800                    ret = new StringBuilder();
801                    ret.append(m.group(1));
802                    ret.append(m.group(2));
803                    ret.append(m.group(3));
804                    ret.append("+");
805                    ret.append(m.group(4));
806                } else {
807                    // Does NOT start with [#*] just prepend '+'
808                    ret = new StringBuilder();
809                    ret.append('+');
810                    ret.append(retString);
811                }
812            }
813        }
814
815        return ret.toString();
816    }
817
818    private static void
819    internalCalledPartyBCDFragmentToString(
820        StringBuilder sb, byte [] bytes, int offset, int length) {
821        for (int i = offset ; i < length + offset ; i++) {
822            byte b;
823            char c;
824
825            c = bcdToChar((byte)(bytes[i] & 0xf));
826
827            if (c == 0) {
828                return;
829            }
830            sb.append(c);
831
832            // FIXME(mkf) TS 23.040 9.1.2.3 says
833            // "if a mobile receives 1111 in a position prior to
834            // the last semi-octet then processing shall commense with
835            // the next semi-octet and the intervening
836            // semi-octet shall be ignored"
837            // How does this jive with 24,008 10.5.4.7
838
839            b = (byte)((bytes[i] >> 4) & 0xf);
840
841            if (b == 0xf && i + 1 == length + offset) {
842                //ignore final 0xf
843                break;
844            }
845
846            c = bcdToChar(b);
847            if (c == 0) {
848                return;
849            }
850
851            sb.append(c);
852        }
853
854    }
855
856    /**
857     * Like calledPartyBCDToString, but field does not start with a
858     * TOA byte. For example: SIM ADN extension fields
859     */
860
861    public static String
862    calledPartyBCDFragmentToString(byte [] bytes, int offset, int length) {
863        StringBuilder ret = new StringBuilder(length * 2);
864
865        internalCalledPartyBCDFragmentToString(ret, bytes, offset, length);
866
867        return ret.toString();
868    }
869
870    /** returns 0 on invalid value */
871    private static char
872    bcdToChar(byte b) {
873        if (b < 0xa) {
874            return (char)('0' + b);
875        } else switch (b) {
876            case 0xa: return '*';
877            case 0xb: return '#';
878            case 0xc: return PAUSE;
879            case 0xd: return WILD;
880
881            default: return 0;
882        }
883    }
884
885    private static int
886    charToBCD(char c) {
887        if (c >= '0' && c <= '9') {
888            return c - '0';
889        } else if (c == '*') {
890            return 0xa;
891        } else if (c == '#') {
892            return 0xb;
893        } else if (c == PAUSE) {
894            return 0xc;
895        } else if (c == WILD) {
896            return 0xd;
897        } else {
898            throw new RuntimeException ("invalid char for BCD " + c);
899        }
900    }
901
902    /**
903     * Return true iff the network portion of <code>address</code> is,
904     * as far as we can tell on the device, suitable for use as an SMS
905     * destination address.
906     */
907    public static boolean isWellFormedSmsAddress(String address) {
908        String networkPortion =
909                PhoneNumberUtils.extractNetworkPortion(address);
910
911        return (!(networkPortion.equals("+")
912                  || TextUtils.isEmpty(networkPortion)))
913               && isDialable(networkPortion);
914    }
915
916    public static boolean isGlobalPhoneNumber(String phoneNumber) {
917        if (TextUtils.isEmpty(phoneNumber)) {
918            return false;
919        }
920
921        Matcher match = GLOBAL_PHONE_NUMBER_PATTERN.matcher(phoneNumber);
922        return match.matches();
923    }
924
925    private static boolean isDialable(String address) {
926        for (int i = 0, count = address.length(); i < count; i++) {
927            if (!isDialable(address.charAt(i))) {
928                return false;
929            }
930        }
931        return true;
932    }
933
934    private static boolean isNonSeparator(String address) {
935        for (int i = 0, count = address.length(); i < count; i++) {
936            if (!isNonSeparator(address.charAt(i))) {
937                return false;
938            }
939        }
940        return true;
941    }
942    /**
943     * Note: calls extractNetworkPortion(), so do not use for
944     * SIM EF[ADN] style records
945     *
946     * Returns null if network portion is empty.
947     */
948    public static byte[]
949    networkPortionToCalledPartyBCD(String s) {
950        String networkPortion = extractNetworkPortion(s);
951        return numberToCalledPartyBCDHelper(networkPortion, false);
952    }
953
954    /**
955     * Same as {@link #networkPortionToCalledPartyBCD}, but includes a
956     * one-byte length prefix.
957     */
958    public static byte[]
959    networkPortionToCalledPartyBCDWithLength(String s) {
960        String networkPortion = extractNetworkPortion(s);
961        return numberToCalledPartyBCDHelper(networkPortion, true);
962    }
963
964    /**
965     * Convert a dialing number to BCD byte array
966     *
967     * @param number dialing number string
968     *        if the dialing number starts with '+', set to internationl TOA
969     * @return BCD byte array
970     */
971    public static byte[]
972    numberToCalledPartyBCD(String number) {
973        return numberToCalledPartyBCDHelper(number, false);
974    }
975
976    /**
977     * If includeLength is true, prepend a one-byte length value to
978     * the return array.
979     */
980    private static byte[]
981    numberToCalledPartyBCDHelper(String number, boolean includeLength) {
982        int numberLenReal = number.length();
983        int numberLenEffective = numberLenReal;
984        boolean hasPlus = number.indexOf('+') != -1;
985        if (hasPlus) numberLenEffective--;
986
987        if (numberLenEffective == 0) return null;
988
989        int resultLen = (numberLenEffective + 1) / 2;  // Encoded numbers require only 4 bits each.
990        int extraBytes = 1;                            // Prepended TOA byte.
991        if (includeLength) extraBytes++;               // Optional prepended length byte.
992        resultLen += extraBytes;
993
994        byte[] result = new byte[resultLen];
995
996        int digitCount = 0;
997        for (int i = 0; i < numberLenReal; i++) {
998            char c = number.charAt(i);
999            if (c == '+') continue;
1000            int shift = ((digitCount & 0x01) == 1) ? 4 : 0;
1001            result[extraBytes + (digitCount >> 1)] |= (byte)((charToBCD(c) & 0x0F) << shift);
1002            digitCount++;
1003        }
1004
1005        // 1-fill any trailing odd nibble/quartet.
1006        if ((digitCount & 0x01) == 1) result[extraBytes + (digitCount >> 1)] |= 0xF0;
1007
1008        int offset = 0;
1009        if (includeLength) result[offset++] = (byte)(resultLen - 1);
1010        result[offset] = (byte)(hasPlus ? TOA_International : TOA_Unknown);
1011
1012        return result;
1013    }
1014
1015    //================ Number formatting =========================
1016
1017    /** The current locale is unknown, look for a country code or don't format */
1018    public static final int FORMAT_UNKNOWN = 0;
1019    /** NANP formatting */
1020    public static final int FORMAT_NANP = 1;
1021    /** Japanese formatting */
1022    public static final int FORMAT_JAPAN = 2;
1023
1024    /** List of country codes for countries that use the NANP */
1025    private static final String[] NANP_COUNTRIES = new String[] {
1026        "US", // United States
1027        "CA", // Canada
1028        "AS", // American Samoa
1029        "AI", // Anguilla
1030        "AG", // Antigua and Barbuda
1031        "BS", // Bahamas
1032        "BB", // Barbados
1033        "BM", // Bermuda
1034        "VG", // British Virgin Islands
1035        "KY", // Cayman Islands
1036        "DM", // Dominica
1037        "DO", // Dominican Republic
1038        "GD", // Grenada
1039        "GU", // Guam
1040        "JM", // Jamaica
1041        "PR", // Puerto Rico
1042        "MS", // Montserrat
1043        "MP", // Northern Mariana Islands
1044        "KN", // Saint Kitts and Nevis
1045        "LC", // Saint Lucia
1046        "VC", // Saint Vincent and the Grenadines
1047        "TT", // Trinidad and Tobago
1048        "TC", // Turks and Caicos Islands
1049        "VI", // U.S. Virgin Islands
1050    };
1051
1052    /**
1053     * Breaks the given number down and formats it according to the rules
1054     * for the country the number is from.
1055     *
1056     * @param source the phone number to format
1057     * @return a locally acceptable formatting of the input, or the raw input if
1058     *  formatting rules aren't known for the number
1059     */
1060    public static String formatNumber(String source) {
1061        SpannableStringBuilder text = new SpannableStringBuilder(source);
1062        formatNumber(text, getFormatTypeForLocale(Locale.getDefault()));
1063        return text.toString();
1064    }
1065
1066    /**
1067     * Returns the phone number formatting type for the given locale.
1068     *
1069     * @param locale The locale of interest, usually {@link Locale#getDefault()}
1070     * @return the formatting type for the given locale, or FORMAT_UNKNOWN if the formatting
1071     * rules are not known for the given locale
1072     */
1073    public static int getFormatTypeForLocale(Locale locale) {
1074        String country = locale.getCountry();
1075
1076        return getFormatTypeFromCountryCode(country);
1077    }
1078
1079    /**
1080     * Formats a phone number in-place. Currently only supports NANP formatting.
1081     *
1082     * @param text The number to be formatted, will be modified with the formatting
1083     * @param defaultFormattingType The default formatting rules to apply if the number does
1084     * not begin with +<country_code>
1085     */
1086    public static void formatNumber(Editable text, int defaultFormattingType) {
1087        int formatType = defaultFormattingType;
1088
1089        if (text.length() > 2 && text.charAt(0) == '+') {
1090            if (text.charAt(1) == '1') {
1091                formatType = FORMAT_NANP;
1092            } else if (text.length() >= 3 && text.charAt(1) == '8'
1093                && text.charAt(2) == '1') {
1094                formatType = FORMAT_JAPAN;
1095            } else {
1096                return;
1097            }
1098        }
1099
1100        switch (formatType) {
1101            case FORMAT_NANP:
1102                formatNanpNumber(text);
1103                return;
1104            case FORMAT_JAPAN:
1105                formatJapaneseNumber(text);
1106                return;
1107        }
1108    }
1109
1110    private static final int NANP_STATE_DIGIT = 1;
1111    private static final int NANP_STATE_PLUS = 2;
1112    private static final int NANP_STATE_ONE = 3;
1113    private static final int NANP_STATE_DASH = 4;
1114
1115    /**
1116     * Formats a phone number in-place using the NANP formatting rules. Numbers will be formatted
1117     * as:
1118     *
1119     * <p><code>
1120     * xxxxx
1121     * xxx-xxxx
1122     * xxx-xxx-xxxx
1123     * 1-xxx-xxx-xxxx
1124     * +1-xxx-xxx-xxxx
1125     * </code></p>
1126     *
1127     * @param text the number to be formatted, will be modified with the formatting
1128     */
1129    public static void formatNanpNumber(Editable text) {
1130        int length = text.length();
1131        if (length > "+1-nnn-nnn-nnnn".length()) {
1132            // The string is too long to be formatted
1133            return;
1134        } else if (length <= 5) {
1135            // The string is either a shortcode or too short to be formatted
1136            return;
1137        }
1138
1139        CharSequence saved = text.subSequence(0, length);
1140
1141        // Strip the dashes first, as we're going to add them back
1142        int p = 0;
1143        while (p < text.length()) {
1144            if (text.charAt(p) == '-') {
1145                text.delete(p, p + 1);
1146            } else {
1147                p++;
1148            }
1149        }
1150        length = text.length();
1151
1152        // When scanning the number we record where dashes need to be added,
1153        // if they're non-0 at the end of the scan the dashes will be added in
1154        // the proper places.
1155        int dashPositions[] = new int[3];
1156        int numDashes = 0;
1157
1158        int state = NANP_STATE_DIGIT;
1159        int numDigits = 0;
1160        for (int i = 0; i < length; i++) {
1161            char c = text.charAt(i);
1162            switch (c) {
1163                case '1':
1164                    if (numDigits == 0 || state == NANP_STATE_PLUS) {
1165                        state = NANP_STATE_ONE;
1166                        break;
1167                    }
1168                    // fall through
1169                case '2':
1170                case '3':
1171                case '4':
1172                case '5':
1173                case '6':
1174                case '7':
1175                case '8':
1176                case '9':
1177                case '0':
1178                    if (state == NANP_STATE_PLUS) {
1179                        // Only NANP number supported for now
1180                        text.replace(0, length, saved);
1181                        return;
1182                    } else if (state == NANP_STATE_ONE) {
1183                        // Found either +1 or 1, follow it up with a dash
1184                        dashPositions[numDashes++] = i;
1185                    } else if (state != NANP_STATE_DASH && (numDigits == 3 || numDigits == 6)) {
1186                        // Found a digit that should be after a dash that isn't
1187                        dashPositions[numDashes++] = i;
1188                    }
1189                    state = NANP_STATE_DIGIT;
1190                    numDigits++;
1191                    break;
1192
1193                case '-':
1194                    state = NANP_STATE_DASH;
1195                    break;
1196
1197                case '+':
1198                    if (i == 0) {
1199                        // Plus is only allowed as the first character
1200                        state = NANP_STATE_PLUS;
1201                        break;
1202                    }
1203                    // Fall through
1204                default:
1205                    // Unknown character, bail on formatting
1206                    text.replace(0, length, saved);
1207                    return;
1208            }
1209        }
1210
1211        if (numDigits == 7) {
1212            // With 7 digits we want xxx-xxxx, not xxx-xxx-x
1213            numDashes--;
1214        }
1215
1216        // Actually put the dashes in place
1217        for (int i = 0; i < numDashes; i++) {
1218            int pos = dashPositions[i];
1219            text.replace(pos + i, pos + i, "-");
1220        }
1221
1222        // Remove trailing dashes
1223        int len = text.length();
1224        while (len > 0) {
1225            if (text.charAt(len - 1) == '-') {
1226                text.delete(len - 1, len);
1227                len--;
1228            } else {
1229                break;
1230            }
1231        }
1232    }
1233
1234    /**
1235     * Formats a phone number in-place using the Japanese formatting rules.
1236     * Numbers will be formatted as:
1237     *
1238     * <p><code>
1239     * 03-xxxx-xxxx
1240     * 090-xxxx-xxxx
1241     * 0120-xxx-xxx
1242     * +81-3-xxxx-xxxx
1243     * +81-90-xxxx-xxxx
1244     * </code></p>
1245     *
1246     * @param text the number to be formatted, will be modified with
1247     * the formatting
1248     */
1249    public static void formatJapaneseNumber(Editable text) {
1250        JapanesePhoneNumberFormatter.format(text);
1251    }
1252
1253    // Three and four digit phone numbers for either special services,
1254    // or 3-6 digit addresses from the network (eg carrier-originated SMS messages) should
1255    // not match.
1256    //
1257    // This constant used to be 5, but SMS short codes has increased in length and
1258    // can be easily 6 digits now days. Most countries have SMS short code length between
1259    // 3 to 6 digits. The exceptions are
1260    //
1261    // Australia: Short codes are six or eight digits in length, starting with the prefix "19"
1262    //            followed by an additional four or six digits and two.
1263    // Czech Republic: Codes are seven digits in length for MO and five (not billed) or
1264    //            eight (billed) for MT direction
1265    //
1266    // see http://en.wikipedia.org/wiki/Short_code#Regional_differences for reference
1267    //
1268    // However, in order to loose match 650-555-1212 and 555-1212, we need to set the min match
1269    // to 7.
1270    static final int MIN_MATCH = 7;
1271
1272    /**
1273     * isEmergencyNumber: checks a given number against the list of
1274     *   emergency numbers provided by the RIL and SIM card.
1275     *
1276     * @param number the number to look up.
1277     * @return if the number is in the list of emergency numbers
1278     * listed in the ril / sim, then return true, otherwise false.
1279     */
1280    public static boolean isEmergencyNumber(String number) {
1281        // If the number passed in is null, just return false:
1282        if (number == null) return false;
1283
1284        // Strip the separators from the number before comparing it
1285        // to the list.
1286        number = extractNetworkPortionAlt(number);
1287
1288        // retrieve the list of emergency numbers
1289        String numbers = SystemProperties.get("ro.ril.ecclist");
1290
1291        if (!TextUtils.isEmpty(numbers)) {
1292            // searches through the comma-separated list for a match,
1293            // return true if one is found.
1294            for (String emergencyNum : numbers.split(",")) {
1295                if (emergencyNum.equals(number)) {
1296                    return true;
1297                }
1298            }
1299            // no matches found against the list!
1300            return false;
1301        }
1302
1303        //no ecclist system property, so use our own list.
1304        return (number.equals("112") || number.equals("911"));
1305    }
1306
1307    /**
1308     * isVoiceMailNumber: checks a given number against the voicemail
1309     *   number provided by the RIL and SIM card. The caller must have
1310     *   the READ_PHONE_STATE credential.
1311     *
1312     * @param number the number to look up.
1313     * @return true if the number is in the list of voicemail. False
1314     * otherwise, including if the caller does not have the permission
1315     * to read the VM number.
1316     * @hide TODO: pending API Council approval
1317     */
1318    public static boolean isVoiceMailNumber(String number) {
1319        String vmNumber;
1320
1321        try {
1322            vmNumber = TelephonyManager.getDefault().getVoiceMailNumber();
1323        } catch (SecurityException ex) {
1324            return false;
1325        }
1326
1327        // Strip the separators from the number before comparing it
1328        // to the list.
1329        number = extractNetworkPortionAlt(number);
1330
1331        // compare tolerates null so we need to make sure that we
1332        // don't return true when both are null.
1333        return !TextUtils.isEmpty(number) && compare(number, vmNumber);
1334    }
1335
1336    /**
1337     * Translates any alphabetic letters (i.e. [A-Za-z]) in the
1338     * specified phone number into the equivalent numeric digits,
1339     * according to the phone keypad letter mapping described in
1340     * ITU E.161 and ISO/IEC 9995-8.
1341     *
1342     * @return the input string, with alpha letters converted to numeric
1343     *         digits using the phone keypad letter mapping.  For example,
1344     *         an input of "1-800-GOOG-411" will return "1-800-4664-411".
1345     */
1346    public static String convertKeypadLettersToDigits(String input) {
1347        if (input == null) {
1348            return input;
1349        }
1350        int len = input.length();
1351        if (len == 0) {
1352            return input;
1353        }
1354
1355        char[] out = input.toCharArray();
1356
1357        for (int i = 0; i < len; i++) {
1358            char c = out[i];
1359            // If this char isn't in KEYPAD_MAP at all, just leave it alone.
1360            out[i] = (char) KEYPAD_MAP.get(c, c);
1361        }
1362
1363        return new String(out);
1364    }
1365
1366    /**
1367     * The phone keypad letter mapping (see ITU E.161 or ISO/IEC 9995-8.)
1368     * TODO: This should come from a resource.
1369     */
1370    private static final SparseIntArray KEYPAD_MAP = new SparseIntArray();
1371    static {
1372        KEYPAD_MAP.put('a', '2'); KEYPAD_MAP.put('b', '2'); KEYPAD_MAP.put('c', '2');
1373        KEYPAD_MAP.put('A', '2'); KEYPAD_MAP.put('B', '2'); KEYPAD_MAP.put('C', '2');
1374
1375        KEYPAD_MAP.put('d', '3'); KEYPAD_MAP.put('e', '3'); KEYPAD_MAP.put('f', '3');
1376        KEYPAD_MAP.put('D', '3'); KEYPAD_MAP.put('E', '3'); KEYPAD_MAP.put('F', '3');
1377
1378        KEYPAD_MAP.put('g', '4'); KEYPAD_MAP.put('h', '4'); KEYPAD_MAP.put('i', '4');
1379        KEYPAD_MAP.put('G', '4'); KEYPAD_MAP.put('H', '4'); KEYPAD_MAP.put('I', '4');
1380
1381        KEYPAD_MAP.put('j', '5'); KEYPAD_MAP.put('k', '5'); KEYPAD_MAP.put('l', '5');
1382        KEYPAD_MAP.put('J', '5'); KEYPAD_MAP.put('K', '5'); KEYPAD_MAP.put('L', '5');
1383
1384        KEYPAD_MAP.put('m', '6'); KEYPAD_MAP.put('n', '6'); KEYPAD_MAP.put('o', '6');
1385        KEYPAD_MAP.put('M', '6'); KEYPAD_MAP.put('N', '6'); KEYPAD_MAP.put('O', '6');
1386
1387        KEYPAD_MAP.put('p', '7'); KEYPAD_MAP.put('q', '7'); KEYPAD_MAP.put('r', '7'); KEYPAD_MAP.put('s', '7');
1388        KEYPAD_MAP.put('P', '7'); KEYPAD_MAP.put('Q', '7'); KEYPAD_MAP.put('R', '7'); KEYPAD_MAP.put('S', '7');
1389
1390        KEYPAD_MAP.put('t', '8'); KEYPAD_MAP.put('u', '8'); KEYPAD_MAP.put('v', '8');
1391        KEYPAD_MAP.put('T', '8'); KEYPAD_MAP.put('U', '8'); KEYPAD_MAP.put('V', '8');
1392
1393        KEYPAD_MAP.put('w', '9'); KEYPAD_MAP.put('x', '9'); KEYPAD_MAP.put('y', '9'); KEYPAD_MAP.put('z', '9');
1394        KEYPAD_MAP.put('W', '9'); KEYPAD_MAP.put('X', '9'); KEYPAD_MAP.put('Y', '9'); KEYPAD_MAP.put('Z', '9');
1395    }
1396
1397    //================ Plus Code formatting =========================
1398    private static final char PLUS_SIGN_CHAR = '+';
1399    private static final String PLUS_SIGN_STRING = "+";
1400    private static final String NANP_IDP_STRING = "011";
1401    private static final int NANP_LENGTH = 10;
1402
1403    /**
1404     * This function checks if there is a plus sign (+) in the passed-in dialing number.
1405     * If there is, it processes the plus sign based on the default telephone
1406     * numbering plan of the system when the phone is activated and the current
1407     * telephone numbering plan of the system that the phone is camped on.
1408     * Currently, we only support the case that the default and current telephone
1409     * numbering plans are North American Numbering Plan(NANP).
1410     *
1411     * The passed-in dialStr should only contain the valid format as described below,
1412     * 1) the 1st character in the dialStr should be one of the really dialable
1413     *    characters listed below
1414     *    ISO-LATIN characters 0-9, *, # , +
1415     * 2) the dialStr should already strip out the separator characters,
1416     *    every character in the dialStr should be one of the non separator characters
1417     *    listed below
1418     *    ISO-LATIN characters 0-9, *, # , +, WILD, WAIT, PAUSE
1419     *
1420     * Otherwise, this function returns the dial string passed in
1421     *
1422     * @param dialStr the original dial string
1423     * @return the converted dial string if the current/default countries belong to NANP,
1424     * and if there is the "+" in the original dial string. Otherwise, the original dial
1425     * string returns.
1426     *
1427     * This API is for CDMA only
1428     *
1429     * @hide TODO: pending API Council approval
1430     */
1431    public static String cdmaCheckAndProcessPlusCode(String dialStr) {
1432        if (!TextUtils.isEmpty(dialStr)) {
1433            if (isReallyDialable(dialStr.charAt(0)) &&
1434                isNonSeparator(dialStr)) {
1435                String currIso = SystemProperties.get(PROPERTY_OPERATOR_ISO_COUNTRY, "");
1436                String defaultIso = SystemProperties.get(PROPERTY_ICC_OPERATOR_ISO_COUNTRY, "");
1437                if (!TextUtils.isEmpty(currIso) && !TextUtils.isEmpty(defaultIso)) {
1438                    return cdmaCheckAndProcessPlusCodeByNumberFormat(dialStr,
1439                            getFormatTypeFromCountryCode(currIso),
1440                            getFormatTypeFromCountryCode(defaultIso));
1441                }
1442            }
1443        }
1444        return dialStr;
1445    }
1446
1447    /**
1448     * This function should be called from checkAndProcessPlusCode only
1449     * And it is used for test purpose also.
1450     *
1451     * It checks the dial string by looping through the network portion,
1452     * post dial portion 1, post dial porting 2, etc. If there is any
1453     * plus sign, then process the plus sign.
1454     * Currently, this function supports the plus sign conversion within NANP only.
1455     * Specifically, it handles the plus sign in the following ways:
1456     * 1)+1NANP,remove +, e.g.
1457     *   +18475797000 is converted to 18475797000,
1458     * 2)+NANP or +non-NANP Numbers,replace + with the current NANP IDP, e.g,
1459     *   +8475797000 is converted to 0118475797000,
1460     *   +11875767800 is converted to 01111875767800
1461     * 3)+1NANP in post dial string(s), e.g.
1462     *   8475797000;+18475231753 is converted to 8475797000;18475231753
1463     *
1464     *
1465     * @param dialStr the original dial string
1466     * @param currFormat the numbering system of the current country that the phone is camped on
1467     * @param defaultFormat the numbering system of the country that the phone is activated on
1468     * @return the converted dial string if the current/default countries belong to NANP,
1469     * and if there is the "+" in the original dial string. Otherwise, the original dial
1470     * string returns.
1471     *
1472     * @hide
1473     */
1474    public static String
1475    cdmaCheckAndProcessPlusCodeByNumberFormat(String dialStr,int currFormat,int defaultFormt) {
1476        String retStr = dialStr;
1477
1478        // Checks if the plus sign character is in the passed-in dial string
1479        if (dialStr != null &&
1480            dialStr.lastIndexOf(PLUS_SIGN_STRING) != -1) {
1481            // Format the string based on the rules for the country the number is from,
1482            // and the current country the phone is camped on.
1483            if ((currFormat == defaultFormt) && (currFormat == FORMAT_NANP)) {
1484                // Handle case where default and current telephone numbering plans are NANP.
1485                String postDialStr = null;
1486                String tempDialStr = dialStr;
1487
1488                // Sets the retStr to null since the conversion will be performed below.
1489                retStr = null;
1490                if (DBG) log("checkAndProcessPlusCode,dialStr=" + dialStr);
1491                // This routine is to process the plus sign in the dial string by loop through
1492                // the network portion, post dial portion 1, post dial portion 2... etc. if
1493                // applied
1494                do {
1495                    String networkDialStr;
1496                    networkDialStr = extractNetworkPortion(tempDialStr);
1497                    // Handles the conversion within NANP
1498                    networkDialStr = processPlusCodeWithinNanp(networkDialStr);
1499
1500                    // Concatenates the string that is converted from network portion
1501                    if (!TextUtils.isEmpty(networkDialStr)) {
1502                        if (retStr == null) {
1503                            retStr = networkDialStr;
1504                        } else {
1505                            retStr = retStr.concat(networkDialStr);
1506                        }
1507                    } else {
1508                        // This should never happen since we checked the if dialStr is null
1509                        // and if it contains the plus sign in the beginning of this function.
1510                        // The plus sign is part of the network portion.
1511                        Log.e("checkAndProcessPlusCode: null newDialStr", networkDialStr);
1512                        return dialStr;
1513                    }
1514                    postDialStr = extractPostDialPortion(tempDialStr);
1515                    if (!TextUtils.isEmpty(postDialStr)) {
1516                        int dialableIndex = findDialableIndexFromPostDialStr(postDialStr);
1517
1518                        // dialableIndex should always be greater than 0
1519                        if (dialableIndex >= 1) {
1520                            retStr = appendPwCharBackToOrigDialStr(dialableIndex,
1521                                     retStr,postDialStr);
1522                            // Skips the P/W character, extracts the dialable portion
1523                            tempDialStr = postDialStr.substring(dialableIndex);
1524                        } else {
1525                            // Non-dialable character such as P/W should not be at the end of
1526                            // the dial string after P/W processing in CdmaConnection.java
1527                            // Set the postDialStr to "" to break out of the loop
1528                            if (dialableIndex < 0) {
1529                                postDialStr = "";
1530                            }
1531                            Log.e("wrong postDialStr=", postDialStr);
1532                        }
1533                    }
1534                    if (DBG) log("checkAndProcessPlusCode,postDialStr=" + postDialStr);
1535                } while (!TextUtils.isEmpty(postDialStr) && !TextUtils.isEmpty(tempDialStr));
1536            } else {
1537                // TODO: Support NANP international conversion and other telephone numbering plans.
1538                // Currently the phone is never used in non-NANP system, so return the original
1539                // dial string.
1540                Log.e("checkAndProcessPlusCode:non-NANP not supported", dialStr);
1541            }
1542        }
1543        return retStr;
1544     }
1545
1546    // This function gets the default international dialing prefix
1547    private static String getDefaultIdp( ) {
1548        String ps = null;
1549        SystemProperties.get(PROPERTY_IDP_STRING, ps);
1550        if (TextUtils.isEmpty(ps)) {
1551            ps = NANP_IDP_STRING;
1552        }
1553        return ps;
1554    }
1555
1556    private static boolean isTwoToNine (char c) {
1557        if (c >= '2' && c <= '9') {
1558            return true;
1559        } else {
1560            return false;
1561        }
1562    }
1563
1564    private static int getFormatTypeFromCountryCode (String country) {
1565        // Check for the NANP countries
1566        int length = NANP_COUNTRIES.length;
1567        for (int i = 0; i < length; i++) {
1568            if (NANP_COUNTRIES[i].compareToIgnoreCase(country) == 0) {
1569                return FORMAT_NANP;
1570            }
1571        }
1572        if ("jp".compareToIgnoreCase(country) == 0) {
1573            return FORMAT_JAPAN;
1574        }
1575        return FORMAT_UNKNOWN;
1576    }
1577
1578    /**
1579     * This function checks if the passed in string conforms to the NANP format
1580     * i.e. NXX-NXX-XXXX, N is any digit 2-9 and X is any digit 0-9
1581     */
1582    private static boolean isNanp (String dialStr) {
1583        boolean retVal = false;
1584        if (dialStr != null) {
1585            if (dialStr.length() == NANP_LENGTH) {
1586                if (isTwoToNine(dialStr.charAt(0)) &&
1587                    isTwoToNine(dialStr.charAt(3))) {
1588                    retVal = true;
1589                    for (int i=1; i<NANP_LENGTH; i++ ) {
1590                        char c=dialStr.charAt(i);
1591                        if (!PhoneNumberUtils.isISODigit(c)) {
1592                            retVal = false;
1593                            break;
1594                        }
1595                    }
1596                }
1597            }
1598        } else {
1599            Log.e("isNanp: null dialStr passed in", dialStr);
1600        }
1601        return retVal;
1602    }
1603
1604   /**
1605    * This function checks if the passed in string conforms to 1-NANP format
1606    */
1607    private static boolean isOneNanp(String dialStr) {
1608        boolean retVal = false;
1609        if (dialStr != null) {
1610            String newDialStr = dialStr.substring(1);
1611            if ((dialStr.charAt(0) == '1') && isNanp(newDialStr)) {
1612                retVal = true;
1613            }
1614        } else {
1615            Log.e("isOneNanp: null dialStr passed in", dialStr);
1616        }
1617        return retVal;
1618    }
1619
1620    /**
1621     * This function handles the plus code conversion within NANP CDMA network
1622     * If the number format is
1623     * 1)+1NANP,remove +,
1624     * 2)other than +1NANP, any + numbers,replace + with the current IDP
1625     */
1626    private static String processPlusCodeWithinNanp(String networkDialStr) {
1627        String retStr = networkDialStr;
1628
1629        if (DBG) log("processPlusCodeWithinNanp,networkDialStr=" + networkDialStr);
1630        // If there is a plus sign at the beginning of the dial string,
1631        // Convert the plus sign to the default IDP since it's an international number
1632        if (networkDialStr != null &
1633            networkDialStr.charAt(0) == PLUS_SIGN_CHAR &&
1634            networkDialStr.length() > 1) {
1635            String newStr = networkDialStr.substring(1);
1636            if (isOneNanp(newStr)) {
1637                // Remove the leading plus sign
1638                retStr = newStr;
1639             } else {
1640                 String idpStr = getDefaultIdp();
1641                 // Replaces the plus sign with the default IDP
1642                 retStr = networkDialStr.replaceFirst("[+]", idpStr);
1643            }
1644        }
1645        if (DBG) log("processPlusCodeWithinNanp,retStr=" + retStr);
1646        return retStr;
1647    }
1648
1649    // This function finds the index of the dialable character(s)
1650    // in the post dial string
1651    private static int findDialableIndexFromPostDialStr(String postDialStr) {
1652        for (int index = 0;index < postDialStr.length();index++) {
1653             char c = postDialStr.charAt(index);
1654             if (isReallyDialable(c)) {
1655                return index;
1656             }
1657        }
1658        return -1;
1659    }
1660
1661    // This function appends the non-diablable P/W character to the original
1662    // dial string based on the dialable index passed in
1663    private static String
1664    appendPwCharBackToOrigDialStr(int dialableIndex,String origStr, String dialStr) {
1665        String retStr;
1666
1667        // There is only 1 P/W character before the dialable characters
1668        if (dialableIndex == 1) {
1669            StringBuilder ret = new StringBuilder(origStr);
1670            ret = ret.append(dialStr.charAt(0));
1671            retStr = ret.toString();
1672        } else {
1673            // It means more than 1 P/W characters in the post dial string,
1674            // appends to retStr
1675            String nonDigitStr = dialStr.substring(0,dialableIndex);
1676            retStr = origStr.concat(nonDigitStr);
1677        }
1678        return retStr;
1679    }
1680
1681    //===== Begining of utility methods used in compareLoosely() =====
1682
1683    /**
1684     * Phone numbers are stored in "lookup" form in the database
1685     * as reversed strings to allow for caller ID lookup
1686     *
1687     * This method takes a phone number and makes a valid SQL "LIKE"
1688     * string that will match the lookup form
1689     *
1690     */
1691    /** all of a up to len must be an international prefix or
1692     *  separators/non-dialing digits
1693     */
1694    private static boolean
1695    matchIntlPrefix(String a, int len) {
1696        /* '([^0-9*#+pwn]\+[^0-9*#+pwn] | [^0-9*#+pwn]0(0|11)[^0-9*#+pwn] )$' */
1697        /*        0       1                           2 3 45               */
1698
1699        int state = 0;
1700        for (int i = 0 ; i < len ; i++) {
1701            char c = a.charAt(i);
1702
1703            switch (state) {
1704                case 0:
1705                    if      (c == '+') state = 1;
1706                    else if (c == '0') state = 2;
1707                    else if (isNonSeparator(c)) return false;
1708                break;
1709
1710                case 2:
1711                    if      (c == '0') state = 3;
1712                    else if (c == '1') state = 4;
1713                    else if (isNonSeparator(c)) return false;
1714                break;
1715
1716                case 4:
1717                    if      (c == '1') state = 5;
1718                    else if (isNonSeparator(c)) return false;
1719                break;
1720
1721                default:
1722                    if (isNonSeparator(c)) return false;
1723                break;
1724
1725            }
1726        }
1727
1728        return state == 1 || state == 3 || state == 5;
1729    }
1730
1731    /** all of 'a' up to len must be a (+|00|011)country code)
1732     *  We're fast and loose with the country code. Any \d{1,3} matches */
1733    private static boolean
1734    matchIntlPrefixAndCC(String a, int len) {
1735        /*  [^0-9*#+pwn]*(\+|0(0|11)\d\d?\d? [^0-9*#+pwn] $ */
1736        /*      0          1 2 3 45  6 7  8                 */
1737
1738        int state = 0;
1739        for (int i = 0 ; i < len ; i++ ) {
1740            char c = a.charAt(i);
1741
1742            switch (state) {
1743                case 0:
1744                    if      (c == '+') state = 1;
1745                    else if (c == '0') state = 2;
1746                    else if (isNonSeparator(c)) return false;
1747                break;
1748
1749                case 2:
1750                    if      (c == '0') state = 3;
1751                    else if (c == '1') state = 4;
1752                    else if (isNonSeparator(c)) return false;
1753                break;
1754
1755                case 4:
1756                    if      (c == '1') state = 5;
1757                    else if (isNonSeparator(c)) return false;
1758                break;
1759
1760                case 1:
1761                case 3:
1762                case 5:
1763                    if      (isISODigit(c)) state = 6;
1764                    else if (isNonSeparator(c)) return false;
1765                break;
1766
1767                case 6:
1768                case 7:
1769                    if      (isISODigit(c)) state++;
1770                    else if (isNonSeparator(c)) return false;
1771                break;
1772
1773                default:
1774                    if (isNonSeparator(c)) return false;
1775            }
1776        }
1777
1778        return state == 6 || state == 7 || state == 8;
1779    }
1780
1781    /** all of 'a' up to len must match non-US trunk prefix ('0') */
1782    private static boolean
1783    matchTrunkPrefix(String a, int len) {
1784        boolean found;
1785
1786        found = false;
1787
1788        for (int i = 0 ; i < len ; i++) {
1789            char c = a.charAt(i);
1790
1791            if (c == '0' && !found) {
1792                found = true;
1793            } else if (isNonSeparator(c)) {
1794                return false;
1795            }
1796        }
1797
1798        return found;
1799    }
1800
1801    //===== End of utility methods used only in compareLoosely() =====
1802
1803    //===== Beggining of utility methods used only in compareStrictly() ====
1804
1805    /*
1806     * If true, the number is country calling code.
1807     */
1808    private static final boolean COUNTLY_CALLING_CALL[] = {
1809        true, true, false, false, false, false, false, true, false, false,
1810        false, false, false, false, false, false, false, false, false, false,
1811        true, false, false, false, false, false, false, true, true, false,
1812        true, true, true, true, true, false, true, false, false, true,
1813        true, false, false, true, true, true, true, true, true, true,
1814        false, true, true, true, true, true, true, true, true, false,
1815        true, true, true, true, true, true, true, false, false, false,
1816        false, false, false, false, false, false, false, false, false, false,
1817        false, true, true, true, true, false, true, false, false, true,
1818        true, true, true, true, true, true, false, false, true, false,
1819    };
1820    private static final int CCC_LENGTH = COUNTLY_CALLING_CALL.length;
1821
1822    /**
1823     * @return true when input is valid Country Calling Code.
1824     */
1825    private static boolean isCountryCallingCode(int countryCallingCodeCandidate) {
1826        return countryCallingCodeCandidate > 0 && countryCallingCodeCandidate < CCC_LENGTH &&
1827                COUNTLY_CALLING_CALL[countryCallingCodeCandidate];
1828    }
1829
1830    /**
1831     * Returns interger corresponding to the input if input "ch" is
1832     * ISO-LATIN characters 0-9.
1833     * Returns -1 otherwise
1834     */
1835    private static int tryGetISODigit(char ch) {
1836        if ('0' <= ch && ch <= '9') {
1837            return ch - '0';
1838        } else {
1839            return -1;
1840        }
1841    }
1842
1843    private static class CountryCallingCodeAndNewIndex {
1844        public final int countryCallingCode;
1845        public final int newIndex;
1846        public CountryCallingCodeAndNewIndex(int countryCode, int newIndex) {
1847            this.countryCallingCode = countryCode;
1848            this.newIndex = newIndex;
1849        }
1850    }
1851
1852    /*
1853     * Note that this function does not strictly care the country calling code with
1854     * 3 length (like Morocco: +212), assuming it is enough to use the first two
1855     * digit to compare two phone numbers.
1856     */
1857    private static CountryCallingCodeAndNewIndex tryGetCountryCallingCodeAndNewIndex(
1858        String str, boolean acceptThailandCase) {
1859        // Rough regexp:
1860        //  ^[^0-9*#+]*((\+|0(0|11)\d\d?|166) [^0-9*#+] $
1861        //         0        1 2 3 45  6 7  89
1862        //
1863        // In all the states, this function ignores separator characters.
1864        // "166" is the special case for the call from Thailand to the US. Uguu!
1865        int state = 0;
1866        int ccc = 0;
1867        final int length = str.length();
1868        for (int i = 0 ; i < length ; i++ ) {
1869            char ch = str.charAt(i);
1870            switch (state) {
1871                case 0:
1872                    if      (ch == '+') state = 1;
1873                    else if (ch == '0') state = 2;
1874                    else if (ch == '1') {
1875                        if (acceptThailandCase) {
1876                            state = 8;
1877                        } else {
1878                            return null;
1879                        }
1880                    } else if (isDialable(ch)) {
1881                        return null;
1882                    }
1883                break;
1884
1885                case 2:
1886                    if      (ch == '0') state = 3;
1887                    else if (ch == '1') state = 4;
1888                    else if (isDialable(ch)) {
1889                        return null;
1890                    }
1891                break;
1892
1893                case 4:
1894                    if      (ch == '1') state = 5;
1895                    else if (isDialable(ch)) {
1896                        return null;
1897                    }
1898                break;
1899
1900                case 1:
1901                case 3:
1902                case 5:
1903                case 6:
1904                case 7:
1905                    {
1906                        int ret = tryGetISODigit(ch);
1907                        if (ret > 0) {
1908                            ccc = ccc * 10 + ret;
1909                            if (ccc >= 100 || isCountryCallingCode(ccc)) {
1910                                return new CountryCallingCodeAndNewIndex(ccc, i + 1);
1911                            }
1912                            if (state == 1 || state == 3 || state == 5) {
1913                                state = 6;
1914                            } else {
1915                                state++;
1916                            }
1917                        } else if (isDialable(ch)) {
1918                            return null;
1919                        }
1920                    }
1921                    break;
1922                case 8:
1923                    if (ch == '6') state = 9;
1924                    else if (isDialable(ch)) {
1925                        return null;
1926                    }
1927                    break;
1928                case 9:
1929                    if (ch == '6') {
1930                        return new CountryCallingCodeAndNewIndex(66, i + 1);
1931                    } else {
1932                        return null;
1933                    }
1934                default:
1935                    return null;
1936            }
1937        }
1938
1939        return null;
1940    }
1941
1942    /**
1943     * Currently this function simply ignore the first digit assuming it is
1944     * trunk prefix. Actually trunk prefix is different in each country.
1945     *
1946     * e.g.
1947     * "+79161234567" equals "89161234567" (Russian trunk digit is 8)
1948     * "+33123456789" equals "0123456789" (French trunk digit is 0)
1949     *
1950     */
1951    private static int tryGetTrunkPrefixOmittedIndex(String str, int currentIndex) {
1952        int length = str.length();
1953        for (int i = currentIndex ; i < length ; i++) {
1954            final char ch = str.charAt(i);
1955            if (tryGetISODigit(ch) >= 0) {
1956                return i + 1;
1957            } else if (isDialable(ch)) {
1958                return -1;
1959            }
1960        }
1961        return -1;
1962    }
1963
1964    /**
1965     * Return true if the prefix of "str" is "ignorable". Here, "ignorable" means
1966     * that "str" has only one digit and separater characters. The one digit is
1967     * assumed to be trunk prefix.
1968     */
1969    private static boolean checkPrefixIsIgnorable(final String str,
1970            int forwardIndex, int backwardIndex) {
1971        boolean trunk_prefix_was_read = false;
1972        while (backwardIndex >= forwardIndex) {
1973            if (tryGetISODigit(str.charAt(backwardIndex)) >= 0) {
1974                if (trunk_prefix_was_read) {
1975                    // More than one digit appeared, meaning that "a" and "b"
1976                    // is different.
1977                    return false;
1978                } else {
1979                    // Ignore just one digit, assuming it is trunk prefix.
1980                    trunk_prefix_was_read = true;
1981                }
1982            } else if (isDialable(str.charAt(backwardIndex))) {
1983                // Trunk prefix is a digit, not "*", "#"...
1984                return false;
1985            }
1986            backwardIndex--;
1987        }
1988
1989        return true;
1990    }
1991
1992    //==== End of utility methods used only in compareStrictly() =====
1993}
1994