OSDN Git Service

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