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