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