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