PhoneNumberUtils.java revision ab971d3c26346cfd94a37e40591318bf3b3ae4fd
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.
1442     * @return the formatted number if the given number has been formatted,
1443     *            otherwise, return the given number.
1444     *
1445     * @hide
1446     */
1447    public static String formatNumber(
1448            String phoneNumber, String phoneNumberE164, String defaultCountryIso) {
1449        int len = phoneNumber.length();
1450        for (int i = 0; i < len; i++) {
1451            if (!isDialable(phoneNumber.charAt(i))) {
1452                return phoneNumber;
1453            }
1454        }
1455        PhoneNumberUtil util = PhoneNumberUtil.getInstance();
1456        // Get the country code from phoneNumberE164
1457        if (phoneNumberE164 != null && phoneNumberE164.length() >= 2
1458                && phoneNumberE164.charAt(0) == '+') {
1459            try {
1460                PhoneNumber pn = util.parse(phoneNumberE164, defaultCountryIso);
1461                String regionCode = util.getRegionCodeForNumber(pn);
1462                if (!TextUtils.isEmpty(regionCode)) {
1463                    defaultCountryIso = regionCode;
1464                }
1465            } catch (NumberParseException e) {
1466            }
1467        }
1468        String result = formatNumber(phoneNumber, defaultCountryIso);
1469        return result != null ? result : phoneNumber;
1470    }
1471
1472    /**
1473     * Normalize a phone number by removing the characters other than digits. If
1474     * the given number has keypad letters, the letters will be converted to
1475     * digits first.
1476     *
1477     * @param phoneNumber
1478     *            the number to be normalized.
1479     * @return the normalized number.
1480     *
1481     * @hide
1482     */
1483    public static String normalizeNumber(String phoneNumber) {
1484        StringBuilder sb = new StringBuilder();
1485        int len = phoneNumber.length();
1486        for (int i = 0; i < len; i++) {
1487            char c = phoneNumber.charAt(i);
1488            if ((i == 0 && c == '+') || PhoneNumberUtils.isISODigit(c)) {
1489                sb.append(c);
1490            } else if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) {
1491                return normalizeNumber(PhoneNumberUtils.convertKeypadLettersToDigits(phoneNumber));
1492            }
1493        }
1494        return sb.toString();
1495    }
1496
1497    // Three and four digit phone numbers for either special services,
1498    // or 3-6 digit addresses from the network (eg carrier-originated SMS messages) should
1499    // not match.
1500    //
1501    // This constant used to be 5, but SMS short codes has increased in length and
1502    // can be easily 6 digits now days. Most countries have SMS short code length between
1503    // 3 to 6 digits. The exceptions are
1504    //
1505    // Australia: Short codes are six or eight digits in length, starting with the prefix "19"
1506    //            followed by an additional four or six digits and two.
1507    // Czech Republic: Codes are seven digits in length for MO and five (not billed) or
1508    //            eight (billed) for MT direction
1509    //
1510    // see http://en.wikipedia.org/wiki/Short_code#Regional_differences for reference
1511    //
1512    // However, in order to loose match 650-555-1212 and 555-1212, we need to set the min match
1513    // to 7.
1514    static final int MIN_MATCH = 7;
1515
1516    /**
1517     * Checks a given number against the list of
1518     * emergency numbers provided by the RIL and SIM card.
1519     *
1520     * @param number the number to look up.
1521     * @return true if the number is in the list of emergency numbers
1522     *         listed in the RIL / SIM, otherwise return false.
1523     */
1524    public static boolean isEmergencyNumber(String number) {
1525        // Return true only if the specified number *exactly* matches
1526        // one of the emergency numbers listed by the RIL / SIM.
1527        return isEmergencyNumberInternal(number, true /* useExactMatch */);
1528    }
1529
1530    /**
1531     * Checks if given number might *potentially* result in
1532     * a call to an emergency service on the current network.
1533     *
1534     * Specifically, this method will return true if the specified number
1535     * is an emergency number according to the list managed by the RIL or
1536     * SIM, *or* if the specified number simply starts with the same
1537     * digits as any of the emergency numbers listed in the RIL / SIM.
1538     *
1539     * This method is intended for internal use by the phone app when
1540     * deciding whether to allow ACTION_CALL intents from 3rd party apps
1541     * (where we're required to *not* allow emergency calls to be placed.)
1542     *
1543     * @param number the number to look up.
1544     * @return true if the number is in the list of emergency numbers
1545     *         listed in the RIL / SIM, *or* if the number starts with the
1546     *         same digits as any of those emergency numbers.
1547     *
1548     * @hide
1549     */
1550    public static boolean isPotentialEmergencyNumber(String number) {
1551        // Check against the emergency numbers listed by the RIL / SIM,
1552        // and *don't* require an exact match.
1553        return isEmergencyNumberInternal(number, false /* useExactMatch */);
1554    }
1555
1556    /**
1557     * Helper function for isEmergencyNumber(String) and
1558     * isPotentialEmergencyNumber(String).
1559     *
1560     * @param number the number to look up.
1561     *
1562     * @param useExactMatch if true, consider a number to be an emergency
1563     *           number only if it *exactly* matches a number listed in
1564     *           the RIL / SIM.  If false, a number is considered to be an
1565     *           emergency number if it simply starts with the same digits
1566     *           as any of the emergency numbers listed in the RIL / SIM.
1567     *           (Setting useExactMatch to false allows you to identify
1568     *           number that could *potentially* result in emergency calls
1569     *           since many networks will actually ignore trailing digits
1570     *           after a valid emergency number.)
1571     *
1572     * @return true if the number is in the list of emergency numbers
1573     *         listed in the RIL / sim, otherwise return false.
1574     */
1575    private static boolean isEmergencyNumberInternal(String number, boolean useExactMatch) {
1576        return isEmergencyNumberInternal(number, null, useExactMatch);
1577    }
1578
1579    /**
1580     * Checks if a given number is an emergency number for a specific country.
1581     *
1582     * @param number the number to look up.
1583     * @param defaultCountryIso the specific country which the number should be checked against
1584     * @return if the number is an emergency number for the specific country, then return true,
1585     * otherwise false
1586     *
1587     * @hide
1588     */
1589    public static boolean isEmergencyNumber(String number, String defaultCountryIso) {
1590        return isEmergencyNumberInternal(number,
1591                                         defaultCountryIso,
1592                                         true /* useExactMatch */);
1593    }
1594
1595    /**
1596     * Checks if a given number might *potentially* result in a call to an
1597     * emergency service, for a specific country.
1598     *
1599     * Specifically, this method will return true if the specified number
1600     * is an emergency number in the specified country, *or* if the number
1601     * simply starts with the same digits as any emergency number for that
1602     * country.
1603     *
1604     * This method is intended for internal use by the phone app when
1605     * deciding whether to allow ACTION_CALL intents from 3rd party apps
1606     * (where we're required to *not* allow emergency calls to be placed.)
1607     *
1608     * @param number the number to look up.
1609     * @param defaultCountryIso the specific country which the number should be checked against
1610     * @return true if the number is an emergency number for the specific
1611     *         country, *or* if the number starts with the same digits as
1612     *         any of those emergency numbers.
1613     *
1614     * @hide
1615     */
1616    public static boolean isPotentialEmergencyNumber(String number, String defaultCountryIso) {
1617        return isEmergencyNumberInternal(number,
1618                                         defaultCountryIso,
1619                                         false /* useExactMatch */);
1620    }
1621
1622    /**
1623     * Helper function for isEmergencyNumber(String, String) and
1624     * isPotentialEmergencyNumber(String, String).
1625     *
1626     * @param number the number to look up.
1627     * @param defaultCountryIso the specific country which the number should be checked against
1628     * @param useExactMatch if true, consider a number to be an emergency
1629     *           number only if it *exactly* matches a number listed in
1630     *           the RIL / SIM.  If false, a number is considered to be an
1631     *           emergency number if it simply starts with the same digits
1632     *           as any of the emergency numbers listed in the RIL / SIM.
1633     *
1634     * @return true if the number is an emergency number for the specified country.
1635     */
1636    private static boolean isEmergencyNumberInternal(String number,
1637                                                     String defaultCountryIso,
1638                                                     boolean useExactMatch) {
1639        // If the number passed in is null, just return false:
1640        if (number == null) return false;
1641
1642        // If the number passed in is a SIP address, return false, since the
1643        // concept of "emergency numbers" is only meaningful for calls placed
1644        // over the cell network.
1645        // (Be sure to do this check *before* calling extractNetworkPortionAlt(),
1646        // since the whole point of extractNetworkPortionAlt() is to filter out
1647        // any non-dialable characters (which would turn 'abc911def@example.com'
1648        // into '911', for example.))
1649        if (isUriNumber(number)) {
1650            return false;
1651        }
1652
1653        // Strip the separators from the number before comparing it
1654        // to the list.
1655        number = extractNetworkPortionAlt(number);
1656
1657        // retrieve the list of emergency numbers
1658        // check read-write ecclist property first
1659        String numbers = SystemProperties.get("ril.ecclist");
1660        if (TextUtils.isEmpty(numbers)) {
1661            // then read-only ecclist property since old RIL only uses this
1662            numbers = SystemProperties.get("ro.ril.ecclist");
1663        }
1664
1665        if (!TextUtils.isEmpty(numbers)) {
1666            // searches through the comma-separated list for a match,
1667            // return true if one is found.
1668            for (String emergencyNum : numbers.split(",")) {
1669                // It is not possible to append additional digits to an emergency number to dial
1670                // the number in Brazil - it won't connect.
1671                if (useExactMatch || "BR".equalsIgnoreCase(defaultCountryIso)) {
1672                    if (number.equals(emergencyNum)) {
1673                        return true;
1674                    }
1675                } else {
1676                    if (number.startsWith(emergencyNum)) {
1677                        return true;
1678                    }
1679                }
1680            }
1681            // no matches found against the list!
1682            return false;
1683        }
1684
1685        // No ecclist system property, so use our own list.
1686        if (defaultCountryIso != null) {
1687            ShortNumberUtil util = new ShortNumberUtil();
1688            if (useExactMatch) {
1689                return util.isEmergencyNumber(number, defaultCountryIso);
1690            } else {
1691                return util.connectsToEmergencyNumber(number, defaultCountryIso);
1692            }
1693        } else {
1694            if (useExactMatch) {
1695                return (number.equals("112") || number.equals("911"));
1696            } else {
1697                return (number.startsWith("112") || number.startsWith("911"));
1698            }
1699        }
1700    }
1701
1702    /**
1703     * Checks if a given number is an emergency number for the country that the user is in. The
1704     * current country is determined using the CountryDetector.
1705     *
1706     * @param number the number to look up.
1707     * @param context the specific context which the number should be checked against
1708     * @return true if the specified number is an emergency number for a local country, based on the
1709     *              CountryDetector.
1710     *
1711     * @see android.location.CountryDetector
1712     * @hide
1713     */
1714    public static boolean isLocalEmergencyNumber(String number, Context context) {
1715        return isLocalEmergencyNumberInternal(number,
1716                                              context,
1717                                              true /* useExactMatch */);
1718    }
1719
1720    /**
1721     * Checks if a given number might *potentially* result in a call to an
1722     * emergency service, for the country that the user is in. The current
1723     * country is determined using the CountryDetector.
1724     *
1725     * Specifically, this method will return true if the specified number
1726     * is an emergency number in the current country, *or* if the number
1727     * simply starts with the same digits as any emergency number for the
1728     * current country.
1729     *
1730     * This method is intended for internal use by the phone app when
1731     * deciding whether to allow ACTION_CALL intents from 3rd party apps
1732     * (where we're required to *not* allow emergency calls to be placed.)
1733     *
1734     * @param number the number to look up.
1735     * @param context the specific context which the number should be checked against
1736     * @return true if the specified number is an emergency number for a local country, based on the
1737     *              CountryDetector.
1738     *
1739     * @see android.location.CountryDetector
1740     * @hide
1741     */
1742    public static boolean isPotentialLocalEmergencyNumber(String number, Context context) {
1743        return isLocalEmergencyNumberInternal(number,
1744                                              context,
1745                                              false /* useExactMatch */);
1746    }
1747
1748    /**
1749     * Helper function for isLocalEmergencyNumber() and
1750     * isPotentialLocalEmergencyNumber().
1751     *
1752     * @param number the number to look up.
1753     * @param context the specific context which the number should be checked against
1754     * @param useExactMatch if true, consider a number to be an emergency
1755     *           number only if it *exactly* matches a number listed in
1756     *           the RIL / SIM.  If false, a number is considered to be an
1757     *           emergency number if it simply starts with the same digits
1758     *           as any of the emergency numbers listed in the RIL / SIM.
1759     *
1760     * @return true if the specified number is an emergency number for a
1761     *              local country, based on the CountryDetector.
1762     *
1763     * @see android.location.CountryDetector
1764     */
1765    private static boolean isLocalEmergencyNumberInternal(String number,
1766                                                          Context context,
1767                                                          boolean useExactMatch) {
1768        String countryIso;
1769        CountryDetector detector = (CountryDetector) context.getSystemService(
1770                Context.COUNTRY_DETECTOR);
1771        if (detector != null) {
1772            countryIso = detector.detectCountry().getCountryIso();
1773        } else {
1774            Locale locale = context.getResources().getConfiguration().locale;
1775            countryIso = locale.getCountry();
1776            Log.w(LOG_TAG, "No CountryDetector; falling back to countryIso based on locale: "
1777                    + countryIso);
1778        }
1779        return isEmergencyNumberInternal(number, countryIso, useExactMatch);
1780    }
1781
1782    /**
1783     * isVoiceMailNumber: checks a given number against the voicemail
1784     *   number provided by the RIL and SIM card. The caller must have
1785     *   the READ_PHONE_STATE credential.
1786     *
1787     * @param number the number to look up.
1788     * @return true if the number is in the list of voicemail. False
1789     * otherwise, including if the caller does not have the permission
1790     * to read the VM number.
1791     * @hide TODO: pending API Council approval
1792     */
1793    public static boolean isVoiceMailNumber(String number) {
1794        String vmNumber;
1795
1796        try {
1797            vmNumber = TelephonyManager.getDefault().getVoiceMailNumber();
1798        } catch (SecurityException ex) {
1799            return false;
1800        }
1801
1802        // Strip the separators from the number before comparing it
1803        // to the list.
1804        number = extractNetworkPortionAlt(number);
1805
1806        // compare tolerates null so we need to make sure that we
1807        // don't return true when both are null.
1808        return !TextUtils.isEmpty(number) && compare(number, vmNumber);
1809    }
1810
1811    /**
1812     * Translates any alphabetic letters (i.e. [A-Za-z]) in the
1813     * specified phone number into the equivalent numeric digits,
1814     * according to the phone keypad letter mapping described in
1815     * ITU E.161 and ISO/IEC 9995-8.
1816     *
1817     * @return the input string, with alpha letters converted to numeric
1818     *         digits using the phone keypad letter mapping.  For example,
1819     *         an input of "1-800-GOOG-411" will return "1-800-4664-411".
1820     */
1821    public static String convertKeypadLettersToDigits(String input) {
1822        if (input == null) {
1823            return input;
1824        }
1825        int len = input.length();
1826        if (len == 0) {
1827            return input;
1828        }
1829
1830        char[] out = input.toCharArray();
1831
1832        for (int i = 0; i < len; i++) {
1833            char c = out[i];
1834            // If this char isn't in KEYPAD_MAP at all, just leave it alone.
1835            out[i] = (char) KEYPAD_MAP.get(c, c);
1836        }
1837
1838        return new String(out);
1839    }
1840
1841    /**
1842     * The phone keypad letter mapping (see ITU E.161 or ISO/IEC 9995-8.)
1843     * TODO: This should come from a resource.
1844     */
1845    private static final SparseIntArray KEYPAD_MAP = new SparseIntArray();
1846    static {
1847        KEYPAD_MAP.put('a', '2'); KEYPAD_MAP.put('b', '2'); KEYPAD_MAP.put('c', '2');
1848        KEYPAD_MAP.put('A', '2'); KEYPAD_MAP.put('B', '2'); KEYPAD_MAP.put('C', '2');
1849
1850        KEYPAD_MAP.put('d', '3'); KEYPAD_MAP.put('e', '3'); KEYPAD_MAP.put('f', '3');
1851        KEYPAD_MAP.put('D', '3'); KEYPAD_MAP.put('E', '3'); KEYPAD_MAP.put('F', '3');
1852
1853        KEYPAD_MAP.put('g', '4'); KEYPAD_MAP.put('h', '4'); KEYPAD_MAP.put('i', '4');
1854        KEYPAD_MAP.put('G', '4'); KEYPAD_MAP.put('H', '4'); KEYPAD_MAP.put('I', '4');
1855
1856        KEYPAD_MAP.put('j', '5'); KEYPAD_MAP.put('k', '5'); KEYPAD_MAP.put('l', '5');
1857        KEYPAD_MAP.put('J', '5'); KEYPAD_MAP.put('K', '5'); KEYPAD_MAP.put('L', '5');
1858
1859        KEYPAD_MAP.put('m', '6'); KEYPAD_MAP.put('n', '6'); KEYPAD_MAP.put('o', '6');
1860        KEYPAD_MAP.put('M', '6'); KEYPAD_MAP.put('N', '6'); KEYPAD_MAP.put('O', '6');
1861
1862        KEYPAD_MAP.put('p', '7'); KEYPAD_MAP.put('q', '7'); KEYPAD_MAP.put('r', '7'); KEYPAD_MAP.put('s', '7');
1863        KEYPAD_MAP.put('P', '7'); KEYPAD_MAP.put('Q', '7'); KEYPAD_MAP.put('R', '7'); KEYPAD_MAP.put('S', '7');
1864
1865        KEYPAD_MAP.put('t', '8'); KEYPAD_MAP.put('u', '8'); KEYPAD_MAP.put('v', '8');
1866        KEYPAD_MAP.put('T', '8'); KEYPAD_MAP.put('U', '8'); KEYPAD_MAP.put('V', '8');
1867
1868        KEYPAD_MAP.put('w', '9'); KEYPAD_MAP.put('x', '9'); KEYPAD_MAP.put('y', '9'); KEYPAD_MAP.put('z', '9');
1869        KEYPAD_MAP.put('W', '9'); KEYPAD_MAP.put('X', '9'); KEYPAD_MAP.put('Y', '9'); KEYPAD_MAP.put('Z', '9');
1870    }
1871
1872    //================ Plus Code formatting =========================
1873    private static final char PLUS_SIGN_CHAR = '+';
1874    private static final String PLUS_SIGN_STRING = "+";
1875    private static final String NANP_IDP_STRING = "011";
1876    private static final int NANP_LENGTH = 10;
1877
1878    /**
1879     * This function checks if there is a plus sign (+) in the passed-in dialing number.
1880     * If there is, it processes the plus sign based on the default telephone
1881     * numbering plan of the system when the phone is activated and the current
1882     * telephone numbering plan of the system that the phone is camped on.
1883     * Currently, we only support the case that the default and current telephone
1884     * numbering plans are North American Numbering Plan(NANP).
1885     *
1886     * The passed-in dialStr should only contain the valid format as described below,
1887     * 1) the 1st character in the dialStr should be one of the really dialable
1888     *    characters listed below
1889     *    ISO-LATIN characters 0-9, *, # , +
1890     * 2) the dialStr should already strip out the separator characters,
1891     *    every character in the dialStr should be one of the non separator characters
1892     *    listed below
1893     *    ISO-LATIN characters 0-9, *, # , +, WILD, WAIT, PAUSE
1894     *
1895     * Otherwise, this function returns the dial string passed in
1896     *
1897     * @param dialStr the original dial string
1898     * @return the converted dial string if the current/default countries belong to NANP,
1899     * and if there is the "+" in the original dial string. Otherwise, the original dial
1900     * string returns.
1901     *
1902     * This API is for CDMA only
1903     *
1904     * @hide TODO: pending API Council approval
1905     */
1906    public static String cdmaCheckAndProcessPlusCode(String dialStr) {
1907        if (!TextUtils.isEmpty(dialStr)) {
1908            if (isReallyDialable(dialStr.charAt(0)) &&
1909                isNonSeparator(dialStr)) {
1910                String currIso = SystemProperties.get(PROPERTY_OPERATOR_ISO_COUNTRY, "");
1911                String defaultIso = SystemProperties.get(PROPERTY_ICC_OPERATOR_ISO_COUNTRY, "");
1912                if (!TextUtils.isEmpty(currIso) && !TextUtils.isEmpty(defaultIso)) {
1913                    return cdmaCheckAndProcessPlusCodeByNumberFormat(dialStr,
1914                            getFormatTypeFromCountryCode(currIso),
1915                            getFormatTypeFromCountryCode(defaultIso));
1916                }
1917            }
1918        }
1919        return dialStr;
1920    }
1921
1922    /**
1923     * This function should be called from checkAndProcessPlusCode only
1924     * And it is used for test purpose also.
1925     *
1926     * It checks the dial string by looping through the network portion,
1927     * post dial portion 1, post dial porting 2, etc. If there is any
1928     * plus sign, then process the plus sign.
1929     * Currently, this function supports the plus sign conversion within NANP only.
1930     * Specifically, it handles the plus sign in the following ways:
1931     * 1)+1NANP,remove +, e.g.
1932     *   +18475797000 is converted to 18475797000,
1933     * 2)+NANP or +non-NANP Numbers,replace + with the current NANP IDP, e.g,
1934     *   +8475797000 is converted to 0118475797000,
1935     *   +11875767800 is converted to 01111875767800
1936     * 3)+1NANP in post dial string(s), e.g.
1937     *   8475797000;+18475231753 is converted to 8475797000;18475231753
1938     *
1939     *
1940     * @param dialStr the original dial string
1941     * @param currFormat the numbering system of the current country that the phone is camped on
1942     * @param defaultFormat the numbering system of the country that the phone is activated on
1943     * @return the converted dial string if the current/default countries belong to NANP,
1944     * and if there is the "+" in the original dial string. Otherwise, the original dial
1945     * string returns.
1946     *
1947     * @hide
1948     */
1949    public static String
1950    cdmaCheckAndProcessPlusCodeByNumberFormat(String dialStr,int currFormat,int defaultFormat) {
1951        String retStr = dialStr;
1952
1953        // Checks if the plus sign character is in the passed-in dial string
1954        if (dialStr != null &&
1955            dialStr.lastIndexOf(PLUS_SIGN_STRING) != -1) {
1956            // Format the string based on the rules for the country the number is from,
1957            // and the current country the phone is camped on.
1958            if ((currFormat == defaultFormat) && (currFormat == FORMAT_NANP)) {
1959                // Handle case where default and current telephone numbering plans are NANP.
1960                String postDialStr = null;
1961                String tempDialStr = dialStr;
1962
1963                // Sets the retStr to null since the conversion will be performed below.
1964                retStr = null;
1965                if (DBG) log("checkAndProcessPlusCode,dialStr=" + dialStr);
1966                // This routine is to process the plus sign in the dial string by loop through
1967                // the network portion, post dial portion 1, post dial portion 2... etc. if
1968                // applied
1969                do {
1970                    String networkDialStr;
1971                    networkDialStr = extractNetworkPortion(tempDialStr);
1972                    // Handles the conversion within NANP
1973                    networkDialStr = processPlusCodeWithinNanp(networkDialStr);
1974
1975                    // Concatenates the string that is converted from network portion
1976                    if (!TextUtils.isEmpty(networkDialStr)) {
1977                        if (retStr == null) {
1978                            retStr = networkDialStr;
1979                        } else {
1980                            retStr = retStr.concat(networkDialStr);
1981                        }
1982                    } else {
1983                        // This should never happen since we checked the if dialStr is null
1984                        // and if it contains the plus sign in the beginning of this function.
1985                        // The plus sign is part of the network portion.
1986                        Log.e("checkAndProcessPlusCode: null newDialStr", networkDialStr);
1987                        return dialStr;
1988                    }
1989                    postDialStr = extractPostDialPortion(tempDialStr);
1990                    if (!TextUtils.isEmpty(postDialStr)) {
1991                        int dialableIndex = findDialableIndexFromPostDialStr(postDialStr);
1992
1993                        // dialableIndex should always be greater than 0
1994                        if (dialableIndex >= 1) {
1995                            retStr = appendPwCharBackToOrigDialStr(dialableIndex,
1996                                     retStr,postDialStr);
1997                            // Skips the P/W character, extracts the dialable portion
1998                            tempDialStr = postDialStr.substring(dialableIndex);
1999                        } else {
2000                            // Non-dialable character such as P/W should not be at the end of
2001                            // the dial string after P/W processing in CdmaConnection.java
2002                            // Set the postDialStr to "" to break out of the loop
2003                            if (dialableIndex < 0) {
2004                                postDialStr = "";
2005                            }
2006                            Log.e("wrong postDialStr=", postDialStr);
2007                        }
2008                    }
2009                    if (DBG) log("checkAndProcessPlusCode,postDialStr=" + postDialStr);
2010                } while (!TextUtils.isEmpty(postDialStr) && !TextUtils.isEmpty(tempDialStr));
2011            } else {
2012                // TODO: Support NANP international conversion and other telephone numbering plans.
2013                // Currently the phone is never used in non-NANP system, so return the original
2014                // dial string.
2015                Log.e("checkAndProcessPlusCode:non-NANP not supported", dialStr);
2016            }
2017        }
2018        return retStr;
2019     }
2020
2021    // This function gets the default international dialing prefix
2022    private static String getDefaultIdp( ) {
2023        String ps = null;
2024        SystemProperties.get(PROPERTY_IDP_STRING, ps);
2025        if (TextUtils.isEmpty(ps)) {
2026            ps = NANP_IDP_STRING;
2027        }
2028        return ps;
2029    }
2030
2031    private static boolean isTwoToNine (char c) {
2032        if (c >= '2' && c <= '9') {
2033            return true;
2034        } else {
2035            return false;
2036        }
2037    }
2038
2039    private static int getFormatTypeFromCountryCode (String country) {
2040        // Check for the NANP countries
2041        int length = NANP_COUNTRIES.length;
2042        for (int i = 0; i < length; i++) {
2043            if (NANP_COUNTRIES[i].compareToIgnoreCase(country) == 0) {
2044                return FORMAT_NANP;
2045            }
2046        }
2047        if ("jp".compareToIgnoreCase(country) == 0) {
2048            return FORMAT_JAPAN;
2049        }
2050        return FORMAT_UNKNOWN;
2051    }
2052
2053    /**
2054     * This function checks if the passed in string conforms to the NANP format
2055     * i.e. NXX-NXX-XXXX, N is any digit 2-9 and X is any digit 0-9
2056     */
2057    private static boolean isNanp (String dialStr) {
2058        boolean retVal = false;
2059        if (dialStr != null) {
2060            if (dialStr.length() == NANP_LENGTH) {
2061                if (isTwoToNine(dialStr.charAt(0)) &&
2062                    isTwoToNine(dialStr.charAt(3))) {
2063                    retVal = true;
2064                    for (int i=1; i<NANP_LENGTH; i++ ) {
2065                        char c=dialStr.charAt(i);
2066                        if (!PhoneNumberUtils.isISODigit(c)) {
2067                            retVal = false;
2068                            break;
2069                        }
2070                    }
2071                }
2072            }
2073        } else {
2074            Log.e("isNanp: null dialStr passed in", dialStr);
2075        }
2076        return retVal;
2077    }
2078
2079   /**
2080    * This function checks if the passed in string conforms to 1-NANP format
2081    */
2082    private static boolean isOneNanp(String dialStr) {
2083        boolean retVal = false;
2084        if (dialStr != null) {
2085            String newDialStr = dialStr.substring(1);
2086            if ((dialStr.charAt(0) == '1') && isNanp(newDialStr)) {
2087                retVal = true;
2088            }
2089        } else {
2090            Log.e("isOneNanp: null dialStr passed in", dialStr);
2091        }
2092        return retVal;
2093    }
2094
2095    /**
2096     * Determines if the specified number is actually a URI
2097     * (i.e. a SIP address) rather than a regular PSTN phone number,
2098     * based on whether or not the number contains an "@" character.
2099     *
2100     * @hide
2101     * @param number
2102     * @return true if number contains @
2103     */
2104    public static boolean isUriNumber(String number) {
2105        // Note we allow either "@" or "%40" to indicate a URI, in case
2106        // the passed-in string is URI-escaped.  (Neither "@" nor "%40"
2107        // will ever be found in a legal PSTN number.)
2108        return number != null && (number.contains("@") || number.contains("%40"));
2109    }
2110
2111    /**
2112     * @return the "username" part of the specified SIP address,
2113     *         i.e. the part before the "@" character (or "%40").
2114     *
2115     * @param number SIP address of the form "username@domainname"
2116     *               (or the URI-escaped equivalent "username%40domainname")
2117     * @see isUriNumber
2118     *
2119     * @hide
2120     */
2121    public static String getUsernameFromUriNumber(String number) {
2122        // The delimiter between username and domain name can be
2123        // either "@" or "%40" (the URI-escaped equivalent.)
2124        int delimiterIndex = number.indexOf('@');
2125        if (delimiterIndex < 0) {
2126            delimiterIndex = number.indexOf("%40");
2127        }
2128        if (delimiterIndex < 0) {
2129            Log.w(LOG_TAG,
2130                  "getUsernameFromUriNumber: no delimiter found in SIP addr '" + number + "'");
2131            delimiterIndex = number.length();
2132        }
2133        return number.substring(0, delimiterIndex);
2134    }
2135
2136    /**
2137     * This function handles the plus code conversion within NANP CDMA network
2138     * If the number format is
2139     * 1)+1NANP,remove +,
2140     * 2)other than +1NANP, any + numbers,replace + with the current IDP
2141     */
2142    private static String processPlusCodeWithinNanp(String networkDialStr) {
2143        String retStr = networkDialStr;
2144
2145        if (DBG) log("processPlusCodeWithinNanp,networkDialStr=" + networkDialStr);
2146        // If there is a plus sign at the beginning of the dial string,
2147        // Convert the plus sign to the default IDP since it's an international number
2148        if (networkDialStr != null &&
2149            networkDialStr.charAt(0) == PLUS_SIGN_CHAR &&
2150            networkDialStr.length() > 1) {
2151            String newStr = networkDialStr.substring(1);
2152            if (isOneNanp(newStr)) {
2153                // Remove the leading plus sign
2154                retStr = newStr;
2155             } else {
2156                 String idpStr = getDefaultIdp();
2157                 // Replaces the plus sign with the default IDP
2158                 retStr = networkDialStr.replaceFirst("[+]", idpStr);
2159            }
2160        }
2161        if (DBG) log("processPlusCodeWithinNanp,retStr=" + retStr);
2162        return retStr;
2163    }
2164
2165    // This function finds the index of the dialable character(s)
2166    // in the post dial string
2167    private static int findDialableIndexFromPostDialStr(String postDialStr) {
2168        for (int index = 0;index < postDialStr.length();index++) {
2169             char c = postDialStr.charAt(index);
2170             if (isReallyDialable(c)) {
2171                return index;
2172             }
2173        }
2174        return -1;
2175    }
2176
2177    // This function appends the non-dialable P/W character to the original
2178    // dial string based on the dialable index passed in
2179    private static String
2180    appendPwCharBackToOrigDialStr(int dialableIndex,String origStr, String dialStr) {
2181        String retStr;
2182
2183        // There is only 1 P/W character before the dialable characters
2184        if (dialableIndex == 1) {
2185            StringBuilder ret = new StringBuilder(origStr);
2186            ret = ret.append(dialStr.charAt(0));
2187            retStr = ret.toString();
2188        } else {
2189            // It means more than 1 P/W characters in the post dial string,
2190            // appends to retStr
2191            String nonDigitStr = dialStr.substring(0,dialableIndex);
2192            retStr = origStr.concat(nonDigitStr);
2193        }
2194        return retStr;
2195    }
2196
2197    //===== Beginning of utility methods used in compareLoosely() =====
2198
2199    /**
2200     * Phone numbers are stored in "lookup" form in the database
2201     * as reversed strings to allow for caller ID lookup
2202     *
2203     * This method takes a phone number and makes a valid SQL "LIKE"
2204     * string that will match the lookup form
2205     *
2206     */
2207    /** all of a up to len must be an international prefix or
2208     *  separators/non-dialing digits
2209     */
2210    private static boolean
2211    matchIntlPrefix(String a, int len) {
2212        /* '([^0-9*#+pwn]\+[^0-9*#+pwn] | [^0-9*#+pwn]0(0|11)[^0-9*#+pwn] )$' */
2213        /*        0       1                           2 3 45               */
2214
2215        int state = 0;
2216        for (int i = 0 ; i < len ; i++) {
2217            char c = a.charAt(i);
2218
2219            switch (state) {
2220                case 0:
2221                    if      (c == '+') state = 1;
2222                    else if (c == '0') state = 2;
2223                    else if (isNonSeparator(c)) return false;
2224                break;
2225
2226                case 2:
2227                    if      (c == '0') state = 3;
2228                    else if (c == '1') state = 4;
2229                    else if (isNonSeparator(c)) return false;
2230                break;
2231
2232                case 4:
2233                    if      (c == '1') state = 5;
2234                    else if (isNonSeparator(c)) return false;
2235                break;
2236
2237                default:
2238                    if (isNonSeparator(c)) return false;
2239                break;
2240
2241            }
2242        }
2243
2244        return state == 1 || state == 3 || state == 5;
2245    }
2246
2247    /** all of 'a' up to len must be a (+|00|011)country code)
2248     *  We're fast and loose with the country code. Any \d{1,3} matches */
2249    private static boolean
2250    matchIntlPrefixAndCC(String a, int len) {
2251        /*  [^0-9*#+pwn]*(\+|0(0|11)\d\d?\d? [^0-9*#+pwn] $ */
2252        /*      0          1 2 3 45  6 7  8                 */
2253
2254        int state = 0;
2255        for (int i = 0 ; i < len ; i++ ) {
2256            char c = a.charAt(i);
2257
2258            switch (state) {
2259                case 0:
2260                    if      (c == '+') state = 1;
2261                    else if (c == '0') state = 2;
2262                    else if (isNonSeparator(c)) return false;
2263                break;
2264
2265                case 2:
2266                    if      (c == '0') state = 3;
2267                    else if (c == '1') state = 4;
2268                    else if (isNonSeparator(c)) return false;
2269                break;
2270
2271                case 4:
2272                    if      (c == '1') state = 5;
2273                    else if (isNonSeparator(c)) return false;
2274                break;
2275
2276                case 1:
2277                case 3:
2278                case 5:
2279                    if      (isISODigit(c)) state = 6;
2280                    else if (isNonSeparator(c)) return false;
2281                break;
2282
2283                case 6:
2284                case 7:
2285                    if      (isISODigit(c)) state++;
2286                    else if (isNonSeparator(c)) return false;
2287                break;
2288
2289                default:
2290                    if (isNonSeparator(c)) return false;
2291            }
2292        }
2293
2294        return state == 6 || state == 7 || state == 8;
2295    }
2296
2297    /** all of 'a' up to len must match non-US trunk prefix ('0') */
2298    private static boolean
2299    matchTrunkPrefix(String a, int len) {
2300        boolean found;
2301
2302        found = false;
2303
2304        for (int i = 0 ; i < len ; i++) {
2305            char c = a.charAt(i);
2306
2307            if (c == '0' && !found) {
2308                found = true;
2309            } else if (isNonSeparator(c)) {
2310                return false;
2311            }
2312        }
2313
2314        return found;
2315    }
2316
2317    //===== End of utility methods used only in compareLoosely() =====
2318
2319    //===== Beginning of utility methods used only in compareStrictly() ====
2320
2321    /*
2322     * If true, the number is country calling code.
2323     */
2324    private static final boolean COUNTRY_CALLING_CALL[] = {
2325        true, true, false, false, false, false, false, true, false, false,
2326        false, false, false, false, false, false, false, false, false, false,
2327        true, false, false, false, false, false, false, true, true, false,
2328        true, true, true, true, true, false, true, false, false, true,
2329        true, false, false, true, true, true, true, true, true, true,
2330        false, true, true, true, true, true, true, true, true, false,
2331        true, true, true, true, true, true, true, false, false, false,
2332        false, false, false, false, false, false, false, false, false, false,
2333        false, true, true, true, true, false, true, false, false, true,
2334        true, true, true, true, true, true, false, false, true, false,
2335    };
2336    private static final int CCC_LENGTH = COUNTRY_CALLING_CALL.length;
2337
2338    /**
2339     * @return true when input is valid Country Calling Code.
2340     */
2341    private static boolean isCountryCallingCode(int countryCallingCodeCandidate) {
2342        return countryCallingCodeCandidate > 0 && countryCallingCodeCandidate < CCC_LENGTH &&
2343                COUNTRY_CALLING_CALL[countryCallingCodeCandidate];
2344    }
2345
2346    /**
2347     * Returns integer corresponding to the input if input "ch" is
2348     * ISO-LATIN characters 0-9.
2349     * Returns -1 otherwise
2350     */
2351    private static int tryGetISODigit(char ch) {
2352        if ('0' <= ch && ch <= '9') {
2353            return ch - '0';
2354        } else {
2355            return -1;
2356        }
2357    }
2358
2359    private static class CountryCallingCodeAndNewIndex {
2360        public final int countryCallingCode;
2361        public final int newIndex;
2362        public CountryCallingCodeAndNewIndex(int countryCode, int newIndex) {
2363            this.countryCallingCode = countryCode;
2364            this.newIndex = newIndex;
2365        }
2366    }
2367
2368    /*
2369     * Note that this function does not strictly care the country calling code with
2370     * 3 length (like Morocco: +212), assuming it is enough to use the first two
2371     * digit to compare two phone numbers.
2372     */
2373    private static CountryCallingCodeAndNewIndex tryGetCountryCallingCodeAndNewIndex(
2374        String str, boolean acceptThailandCase) {
2375        // Rough regexp:
2376        //  ^[^0-9*#+]*((\+|0(0|11)\d\d?|166) [^0-9*#+] $
2377        //         0        1 2 3 45  6 7  89
2378        //
2379        // In all the states, this function ignores separator characters.
2380        // "166" is the special case for the call from Thailand to the US. Uguu!
2381        int state = 0;
2382        int ccc = 0;
2383        final int length = str.length();
2384        for (int i = 0 ; i < length ; i++ ) {
2385            char ch = str.charAt(i);
2386            switch (state) {
2387                case 0:
2388                    if      (ch == '+') state = 1;
2389                    else if (ch == '0') state = 2;
2390                    else if (ch == '1') {
2391                        if (acceptThailandCase) {
2392                            state = 8;
2393                        } else {
2394                            return null;
2395                        }
2396                    } else if (isDialable(ch)) {
2397                        return null;
2398                    }
2399                break;
2400
2401                case 2:
2402                    if      (ch == '0') state = 3;
2403                    else if (ch == '1') state = 4;
2404                    else if (isDialable(ch)) {
2405                        return null;
2406                    }
2407                break;
2408
2409                case 4:
2410                    if      (ch == '1') state = 5;
2411                    else if (isDialable(ch)) {
2412                        return null;
2413                    }
2414                break;
2415
2416                case 1:
2417                case 3:
2418                case 5:
2419                case 6:
2420                case 7:
2421                    {
2422                        int ret = tryGetISODigit(ch);
2423                        if (ret > 0) {
2424                            ccc = ccc * 10 + ret;
2425                            if (ccc >= 100 || isCountryCallingCode(ccc)) {
2426                                return new CountryCallingCodeAndNewIndex(ccc, i + 1);
2427                            }
2428                            if (state == 1 || state == 3 || state == 5) {
2429                                state = 6;
2430                            } else {
2431                                state++;
2432                            }
2433                        } else if (isDialable(ch)) {
2434                            return null;
2435                        }
2436                    }
2437                    break;
2438                case 8:
2439                    if (ch == '6') state = 9;
2440                    else if (isDialable(ch)) {
2441                        return null;
2442                    }
2443                    break;
2444                case 9:
2445                    if (ch == '6') {
2446                        return new CountryCallingCodeAndNewIndex(66, i + 1);
2447                    } else {
2448                        return null;
2449                    }
2450                default:
2451                    return null;
2452            }
2453        }
2454
2455        return null;
2456    }
2457
2458    /**
2459     * Currently this function simply ignore the first digit assuming it is
2460     * trunk prefix. Actually trunk prefix is different in each country.
2461     *
2462     * e.g.
2463     * "+79161234567" equals "89161234567" (Russian trunk digit is 8)
2464     * "+33123456789" equals "0123456789" (French trunk digit is 0)
2465     *
2466     */
2467    private static int tryGetTrunkPrefixOmittedIndex(String str, int currentIndex) {
2468        int length = str.length();
2469        for (int i = currentIndex ; i < length ; i++) {
2470            final char ch = str.charAt(i);
2471            if (tryGetISODigit(ch) >= 0) {
2472                return i + 1;
2473            } else if (isDialable(ch)) {
2474                return -1;
2475            }
2476        }
2477        return -1;
2478    }
2479
2480    /**
2481     * Return true if the prefix of "str" is "ignorable". Here, "ignorable" means
2482     * that "str" has only one digit and separator characters. The one digit is
2483     * assumed to be trunk prefix.
2484     */
2485    private static boolean checkPrefixIsIgnorable(final String str,
2486            int forwardIndex, int backwardIndex) {
2487        boolean trunk_prefix_was_read = false;
2488        while (backwardIndex >= forwardIndex) {
2489            if (tryGetISODigit(str.charAt(backwardIndex)) >= 0) {
2490                if (trunk_prefix_was_read) {
2491                    // More than one digit appeared, meaning that "a" and "b"
2492                    // is different.
2493                    return false;
2494                } else {
2495                    // Ignore just one digit, assuming it is trunk prefix.
2496                    trunk_prefix_was_read = true;
2497                }
2498            } else if (isDialable(str.charAt(backwardIndex))) {
2499                // Trunk prefix is a digit, not "*", "#"...
2500                return false;
2501            }
2502            backwardIndex--;
2503        }
2504
2505        return true;
2506    }
2507
2508    //==== End of utility methods used only in compareStrictly() =====
2509}
2510