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