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