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