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