• 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.ShortNumberUtil;
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     /**
1144      * Breaks the given number down and formats it according to the rules
1145      * for the country the number is from.
1146      *
1147      * @param source The phone number to format
1148      * @return A locally acceptable formatting of the input, or the raw input if
1149      *  formatting rules aren't known for the number
1150      *
1151      * @deprecated Use link #formatNumber(String phoneNumber, String defaultCountryIso) instead
1152      */
1153     @Deprecated
formatNumber(String source)1154     public static String formatNumber(String source) {
1155         SpannableStringBuilder text = new SpannableStringBuilder(source);
1156         formatNumber(text, getFormatTypeForLocale(Locale.getDefault()));
1157         return text.toString();
1158     }
1159 
1160     /**
1161      * Formats the given number with the given formatting type. Currently
1162      * {@link #FORMAT_NANP} and {@link #FORMAT_JAPAN} are supported as a formating type.
1163      *
1164      * @param source the phone number to format
1165      * @param defaultFormattingType The default formatting rules to apply if the number does
1166      * not begin with +[country_code]
1167      * @return The phone number formatted with the given formatting type.
1168      *
1169      * @hide
1170      * @deprecated Use link #formatNumber(String phoneNumber, String defaultCountryIso) instead
1171      */
1172     @Deprecated
formatNumber(String source, int defaultFormattingType)1173     public static String formatNumber(String source, int defaultFormattingType) {
1174         SpannableStringBuilder text = new SpannableStringBuilder(source);
1175         formatNumber(text, defaultFormattingType);
1176         return text.toString();
1177     }
1178 
1179     /**
1180      * Returns the phone number formatting type for the given locale.
1181      *
1182      * @param locale The locale of interest, usually {@link Locale#getDefault()}
1183      * @return The formatting type for the given locale, or FORMAT_UNKNOWN if the formatting
1184      * rules are not known for the given locale
1185      *
1186      * @deprecated Use link #formatNumber(String phoneNumber, String defaultCountryIso) instead
1187      */
1188     @Deprecated
getFormatTypeForLocale(Locale locale)1189     public static int getFormatTypeForLocale(Locale locale) {
1190         String country = locale.getCountry();
1191 
1192         return getFormatTypeFromCountryCode(country);
1193     }
1194 
1195     /**
1196      * Formats a phone number in-place. Currently {@link #FORMAT_JAPAN} and {@link #FORMAT_NANP}
1197      * is supported as a second argument.
1198      *
1199      * @param text The number to be formatted, will be modified with the formatting
1200      * @param defaultFormattingType The default formatting rules to apply if the number does
1201      * not begin with +[country_code]
1202      *
1203      * @deprecated Use link #formatNumber(String phoneNumber, String defaultCountryIso) instead
1204      */
1205     @Deprecated
formatNumber(Editable text, int defaultFormattingType)1206     public static void formatNumber(Editable text, int defaultFormattingType) {
1207         int formatType = defaultFormattingType;
1208 
1209         if (text.length() > 2 && text.charAt(0) == '+') {
1210             if (text.charAt(1) == '1') {
1211                 formatType = FORMAT_NANP;
1212             } else if (text.length() >= 3 && text.charAt(1) == '8'
1213                 && text.charAt(2) == '1') {
1214                 formatType = FORMAT_JAPAN;
1215             } else {
1216                 formatType = FORMAT_UNKNOWN;
1217             }
1218         }
1219 
1220         switch (formatType) {
1221             case FORMAT_NANP:
1222                 formatNanpNumber(text);
1223                 return;
1224             case FORMAT_JAPAN:
1225                 formatJapaneseNumber(text);
1226                 return;
1227             case FORMAT_UNKNOWN:
1228                 removeDashes(text);
1229                 return;
1230         }
1231     }
1232 
1233     private static final int NANP_STATE_DIGIT = 1;
1234     private static final int NANP_STATE_PLUS = 2;
1235     private static final int NANP_STATE_ONE = 3;
1236     private static final int NANP_STATE_DASH = 4;
1237 
1238     /**
1239      * Formats a phone number in-place using the NANP formatting rules. Numbers will be formatted
1240      * as:
1241      *
1242      * <p><code>
1243      * xxxxx
1244      * xxx-xxxx
1245      * xxx-xxx-xxxx
1246      * 1-xxx-xxx-xxxx
1247      * +1-xxx-xxx-xxxx
1248      * </code></p>
1249      *
1250      * @param text the number to be formatted, will be modified with the formatting
1251      *
1252      * @deprecated Use link #formatNumber(String phoneNumber, String defaultCountryIso) instead
1253      */
1254     @Deprecated
formatNanpNumber(Editable text)1255     public static void formatNanpNumber(Editable text) {
1256         int length = text.length();
1257         if (length > "+1-nnn-nnn-nnnn".length()) {
1258             // The string is too long to be formatted
1259             return;
1260         } else if (length <= 5) {
1261             // The string is either a shortcode or too short to be formatted
1262             return;
1263         }
1264 
1265         CharSequence saved = text.subSequence(0, length);
1266 
1267         // Strip the dashes first, as we're going to add them back
1268         removeDashes(text);
1269         length = text.length();
1270 
1271         // When scanning the number we record where dashes need to be added,
1272         // if they're non-0 at the end of the scan the dashes will be added in
1273         // the proper places.
1274         int dashPositions[] = new int[3];
1275         int numDashes = 0;
1276 
1277         int state = NANP_STATE_DIGIT;
1278         int numDigits = 0;
1279         for (int i = 0; i < length; i++) {
1280             char c = text.charAt(i);
1281             switch (c) {
1282                 case '1':
1283                     if (numDigits == 0 || state == NANP_STATE_PLUS) {
1284                         state = NANP_STATE_ONE;
1285                         break;
1286                     }
1287                     // fall through
1288                 case '2':
1289                 case '3':
1290                 case '4':
1291                 case '5':
1292                 case '6':
1293                 case '7':
1294                 case '8':
1295                 case '9':
1296                 case '0':
1297                     if (state == NANP_STATE_PLUS) {
1298                         // Only NANP number supported for now
1299                         text.replace(0, length, saved);
1300                         return;
1301                     } else if (state == NANP_STATE_ONE) {
1302                         // Found either +1 or 1, follow it up with a dash
1303                         dashPositions[numDashes++] = i;
1304                     } else if (state != NANP_STATE_DASH && (numDigits == 3 || numDigits == 6)) {
1305                         // Found a digit that should be after a dash that isn't
1306                         dashPositions[numDashes++] = i;
1307                     }
1308                     state = NANP_STATE_DIGIT;
1309                     numDigits++;
1310                     break;
1311 
1312                 case '-':
1313                     state = NANP_STATE_DASH;
1314                     break;
1315 
1316                 case '+':
1317                     if (i == 0) {
1318                         // Plus is only allowed as the first character
1319                         state = NANP_STATE_PLUS;
1320                         break;
1321                     }
1322                     // Fall through
1323                 default:
1324                     // Unknown character, bail on formatting
1325                     text.replace(0, length, saved);
1326                     return;
1327             }
1328         }
1329 
1330         if (numDigits == 7) {
1331             // With 7 digits we want xxx-xxxx, not xxx-xxx-x
1332             numDashes--;
1333         }
1334 
1335         // Actually put the dashes in place
1336         for (int i = 0; i < numDashes; i++) {
1337             int pos = dashPositions[i];
1338             text.replace(pos + i, pos + i, "-");
1339         }
1340 
1341         // Remove trailing dashes
1342         int len = text.length();
1343         while (len > 0) {
1344             if (text.charAt(len - 1) == '-') {
1345                 text.delete(len - 1, len);
1346                 len--;
1347             } else {
1348                 break;
1349             }
1350         }
1351     }
1352 
1353     /**
1354      * Formats a phone number in-place using the Japanese formatting rules.
1355      * Numbers will be formatted as:
1356      *
1357      * <p><code>
1358      * 03-xxxx-xxxx
1359      * 090-xxxx-xxxx
1360      * 0120-xxx-xxx
1361      * +81-3-xxxx-xxxx
1362      * +81-90-xxxx-xxxx
1363      * </code></p>
1364      *
1365      * @param text the number to be formatted, will be modified with
1366      * the formatting
1367      *
1368      * @deprecated Use link #formatNumber(String phoneNumber, String defaultCountryIso) instead
1369      */
1370     @Deprecated
formatJapaneseNumber(Editable text)1371     public static void formatJapaneseNumber(Editable text) {
1372         JapanesePhoneNumberFormatter.format(text);
1373     }
1374 
1375     /**
1376      * Removes all dashes from the number.
1377      *
1378      * @param text the number to clear from dashes
1379      */
removeDashes(Editable text)1380     private static void removeDashes(Editable text) {
1381         int p = 0;
1382         while (p < text.length()) {
1383             if (text.charAt(p) == '-') {
1384                 text.delete(p, p + 1);
1385            } else {
1386                 p++;
1387            }
1388         }
1389     }
1390 
1391     /**
1392      * Formats the specified {@code phoneNumber} to the E.164 representation.
1393      *
1394      * @param phoneNumber the phone number to format.
1395      * @param defaultCountryIso the ISO 3166-1 two letters country code.
1396      * @return the E.164 representation, or null if the given phone number is not valid.
1397      */
formatNumberToE164(String phoneNumber, String defaultCountryIso)1398     public static String formatNumberToE164(String phoneNumber, String defaultCountryIso) {
1399         return formatNumberInternal(phoneNumber, defaultCountryIso, PhoneNumberFormat.E164);
1400     }
1401 
1402     /**
1403      * Formats the specified {@code phoneNumber} to the RFC3966 representation.
1404      *
1405      * @param phoneNumber the phone number to format.
1406      * @param defaultCountryIso the ISO 3166-1 two letters country code.
1407      * @return the RFC3966 representation, or null if the given phone number is not valid.
1408      */
formatNumberToRFC3966(String phoneNumber, String defaultCountryIso)1409     public static String formatNumberToRFC3966(String phoneNumber, String defaultCountryIso) {
1410         return formatNumberInternal(phoneNumber, defaultCountryIso, PhoneNumberFormat.RFC3966);
1411     }
1412 
1413     /**
1414      * Formats the raw phone number (string) using the specified {@code formatIdentifier}.
1415      * <p>
1416      * The given phone number must have an area code and could have a country code.
1417      * <p>
1418      * The defaultCountryIso is used to validate the given number and generate the formatted number
1419      * if the specified number doesn't have a country code.
1420      *
1421      * @param rawPhoneNumber The phone number to format.
1422      * @param defaultCountryIso The ISO 3166-1 two letters country code.
1423      * @param formatIdentifier The (enum) identifier of the desired format.
1424      * @return the formatted representation, or null if the specified number is not valid.
1425      */
formatNumberInternal( String rawPhoneNumber, String defaultCountryIso, PhoneNumberFormat formatIdentifier)1426     private static String formatNumberInternal(
1427             String rawPhoneNumber, String defaultCountryIso, PhoneNumberFormat formatIdentifier) {
1428 
1429         PhoneNumberUtil util = PhoneNumberUtil.getInstance();
1430         try {
1431             PhoneNumber phoneNumber = util.parse(rawPhoneNumber, defaultCountryIso);
1432             if (util.isValidNumber(phoneNumber)) {
1433                 return util.format(phoneNumber, formatIdentifier);
1434             }
1435         } catch (NumberParseException ignored) { }
1436 
1437         return null;
1438     }
1439 
1440     /**
1441      * Format a phone number.
1442      * <p>
1443      * If the given number doesn't have the country code, the phone will be
1444      * formatted to the default country's convention.
1445      *
1446      * @param phoneNumber
1447      *            the number to be formatted.
1448      * @param defaultCountryIso
1449      *            the ISO 3166-1 two letters country code whose convention will
1450      *            be used if the given number doesn't have the country code.
1451      * @return the formatted number, or null if the given number is not valid.
1452      */
formatNumber(String phoneNumber, String defaultCountryIso)1453     public static String formatNumber(String phoneNumber, String defaultCountryIso) {
1454         // Do not attempt to format numbers that start with a hash or star symbol.
1455         if (phoneNumber.startsWith("#") || phoneNumber.startsWith("*")) {
1456             return phoneNumber;
1457         }
1458 
1459         PhoneNumberUtil util = PhoneNumberUtil.getInstance();
1460         String result = null;
1461         try {
1462             PhoneNumber pn = util.parseAndKeepRawInput(phoneNumber, defaultCountryIso);
1463             /**
1464              * Need to reformat any local Korean phone numbers (when the user is in Korea) with
1465              * country code to corresponding national format which would replace the leading
1466              * +82 with 0.
1467              */
1468             if (KOREA_ISO_COUNTRY_CODE.equals(defaultCountryIso) &&
1469                     (pn.getCountryCode() == util.getCountryCodeForRegion(KOREA_ISO_COUNTRY_CODE)) &&
1470                     (pn.getCountryCodeSource() ==
1471                             PhoneNumber.CountryCodeSource.FROM_NUMBER_WITH_PLUS_SIGN)) {
1472                 result = util.format(pn, PhoneNumberUtil.PhoneNumberFormat.NATIONAL);
1473             } else {
1474                 result = util.formatInOriginalFormat(pn, defaultCountryIso);
1475             }
1476         } catch (NumberParseException e) {
1477         }
1478         return result;
1479     }
1480 
1481     /**
1482      * Format the phone number only if the given number hasn't been formatted.
1483      * <p>
1484      * The number which has only dailable character is treated as not being
1485      * formatted.
1486      *
1487      * @param phoneNumber
1488      *            the number to be formatted.
1489      * @param phoneNumberE164
1490      *            the E164 format number whose country code is used if the given
1491      *            phoneNumber doesn't have the country code.
1492      * @param defaultCountryIso
1493      *            the ISO 3166-1 two letters country code whose convention will
1494      *            be used if the phoneNumberE164 is null or invalid, or if phoneNumber
1495      *            contains IDD.
1496      * @return the formatted number if the given number has been formatted,
1497      *            otherwise, return the given number.
1498      */
formatNumber( String phoneNumber, String phoneNumberE164, String defaultCountryIso)1499     public static String formatNumber(
1500             String phoneNumber, String phoneNumberE164, String defaultCountryIso) {
1501         int len = phoneNumber.length();
1502         for (int i = 0; i < len; i++) {
1503             if (!isDialable(phoneNumber.charAt(i))) {
1504                 return phoneNumber;
1505             }
1506         }
1507         PhoneNumberUtil util = PhoneNumberUtil.getInstance();
1508         // Get the country code from phoneNumberE164
1509         if (phoneNumberE164 != null && phoneNumberE164.length() >= 2
1510                 && phoneNumberE164.charAt(0) == '+') {
1511             try {
1512                 // The number to be parsed is in E164 format, so the default region used doesn't
1513                 // matter.
1514                 PhoneNumber pn = util.parse(phoneNumberE164, "ZZ");
1515                 String regionCode = util.getRegionCodeForNumber(pn);
1516                 if (!TextUtils.isEmpty(regionCode) &&
1517                     // This makes sure phoneNumber doesn't contain an IDD
1518                     normalizeNumber(phoneNumber).indexOf(phoneNumberE164.substring(1)) <= 0) {
1519                     defaultCountryIso = regionCode;
1520                 }
1521             } catch (NumberParseException e) {
1522             }
1523         }
1524         String result = formatNumber(phoneNumber, defaultCountryIso);
1525         return result != null ? result : phoneNumber;
1526     }
1527 
1528     /**
1529      * Normalize a phone number by removing the characters other than digits. If
1530      * the given number has keypad letters, the letters will be converted to
1531      * digits first.
1532      *
1533      * @param phoneNumber the number to be normalized.
1534      * @return the normalized number.
1535      */
normalizeNumber(String phoneNumber)1536     public static String normalizeNumber(String phoneNumber) {
1537         if (TextUtils.isEmpty(phoneNumber)) {
1538             return "";
1539         }
1540 
1541         StringBuilder sb = new StringBuilder();
1542         int len = phoneNumber.length();
1543         for (int i = 0; i < len; i++) {
1544             char c = phoneNumber.charAt(i);
1545             // Character.digit() supports ASCII and Unicode digits (fullwidth, Arabic-Indic, etc.)
1546             int digit = Character.digit(c, 10);
1547             if (digit != -1) {
1548                 sb.append(digit);
1549             } else if (sb.length() == 0 && c == '+') {
1550                 sb.append(c);
1551             } else if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) {
1552                 return normalizeNumber(PhoneNumberUtils.convertKeypadLettersToDigits(phoneNumber));
1553             }
1554         }
1555         return sb.toString();
1556     }
1557 
1558     /**
1559      * Replaces all unicode(e.g. Arabic, Persian) digits with their decimal digit equivalents.
1560      *
1561      * @param number the number to perform the replacement on.
1562      * @return the replaced number.
1563      */
replaceUnicodeDigits(String number)1564     public static String replaceUnicodeDigits(String number) {
1565         StringBuilder normalizedDigits = new StringBuilder(number.length());
1566         for (char c : number.toCharArray()) {
1567             int digit = Character.digit(c, 10);
1568             if (digit != -1) {
1569                 normalizedDigits.append(digit);
1570             } else {
1571                 normalizedDigits.append(c);
1572             }
1573         }
1574         return normalizedDigits.toString();
1575     }
1576 
1577     // Three and four digit phone numbers for either special services,
1578     // or 3-6 digit addresses from the network (eg carrier-originated SMS messages) should
1579     // not match.
1580     //
1581     // This constant used to be 5, but SMS short codes has increased in length and
1582     // can be easily 6 digits now days. Most countries have SMS short code length between
1583     // 3 to 6 digits. The exceptions are
1584     //
1585     // Australia: Short codes are six or eight digits in length, starting with the prefix "19"
1586     //            followed by an additional four or six digits and two.
1587     // Czech Republic: Codes are seven digits in length for MO and five (not billed) or
1588     //            eight (billed) for MT direction
1589     //
1590     // see http://en.wikipedia.org/wiki/Short_code#Regional_differences for reference
1591     //
1592     // However, in order to loose match 650-555-1212 and 555-1212, we need to set the min match
1593     // to 7.
1594     static final int MIN_MATCH = 7;
1595 
1596     /**
1597      * Checks a given number against the list of
1598      * emergency numbers provided by the RIL and SIM card.
1599      *
1600      * @param number the number to look up.
1601      * @return true if the number is in the list of emergency numbers
1602      *         listed in the RIL / SIM, otherwise return false.
1603      */
isEmergencyNumber(String number)1604     public static boolean isEmergencyNumber(String number) {
1605         return isEmergencyNumber(getDefaultVoiceSubId(), number);
1606     }
1607 
1608     /**
1609      * Checks a given number against the list of
1610      * emergency numbers provided by the RIL and SIM card.
1611      *
1612      * @param subId the subscription id of the SIM.
1613      * @param number the number to look up.
1614      * @return true if the number is in the list of emergency numbers
1615      *         listed in the RIL / SIM, otherwise return false.
1616      * @hide
1617      */
isEmergencyNumber(int subId, String number)1618     public static boolean isEmergencyNumber(int subId, String number) {
1619         // Return true only if the specified number *exactly* matches
1620         // one of the emergency numbers listed by the RIL / SIM.
1621         return isEmergencyNumberInternal(subId, number, true /* useExactMatch */);
1622     }
1623 
1624     /**
1625      * Checks if given number might *potentially* result in
1626      * a call to an emergency service on the current network.
1627      *
1628      * Specifically, this method will return true if the specified number
1629      * is an emergency number according to the list managed by the RIL or
1630      * SIM, *or* if the specified number simply starts with the same
1631      * digits as any of the emergency numbers listed in the RIL / SIM.
1632      *
1633      * This method is intended for internal use by the phone app when
1634      * deciding whether to allow ACTION_CALL intents from 3rd party apps
1635      * (where we're required to *not* allow emergency calls to be placed.)
1636      *
1637      * @param number the number to look up.
1638      * @return true if the number is in the list of emergency numbers
1639      *         listed in the RIL / SIM, *or* if the number starts with the
1640      *         same digits as any of those emergency numbers.
1641      *
1642      * @hide
1643      */
isPotentialEmergencyNumber(String number)1644     public static boolean isPotentialEmergencyNumber(String number) {
1645         return isPotentialEmergencyNumber(getDefaultVoiceSubId(), number);
1646     }
1647 
1648     /**
1649      * Checks if given number might *potentially* result in
1650      * a call to an emergency service on the current network.
1651      *
1652      * Specifically, this method will return true if the specified number
1653      * is an emergency number according to the list managed by the RIL or
1654      * SIM, *or* if the specified number simply starts with the same
1655      * digits as any of the emergency numbers listed in the RIL / SIM.
1656      *
1657      * This method is intended for internal use by the phone app when
1658      * deciding whether to allow ACTION_CALL intents from 3rd party apps
1659      * (where we're required to *not* allow emergency calls to be placed.)
1660      *
1661      * @param subId the subscription id of the SIM.
1662      * @param number the number to look up.
1663      * @return true if the number is in the list of emergency numbers
1664      *         listed in the RIL / SIM, *or* if the number starts with the
1665      *         same digits as any of those emergency numbers.
1666      * @hide
1667      */
isPotentialEmergencyNumber(int subId, String number)1668     public static boolean isPotentialEmergencyNumber(int subId, String number) {
1669         // Check against the emergency numbers listed by the RIL / SIM,
1670         // and *don't* require an exact match.
1671         return isEmergencyNumberInternal(subId, number, false /* useExactMatch */);
1672     }
1673 
1674     /**
1675      * Helper function for isEmergencyNumber(String) and
1676      * isPotentialEmergencyNumber(String).
1677      *
1678      * @param number the number to look up.
1679      *
1680      * @param useExactMatch if true, consider a number to be an emergency
1681      *           number only if it *exactly* matches a number listed in
1682      *           the RIL / SIM.  If false, a number is considered to be an
1683      *           emergency number if it simply starts with the same digits
1684      *           as any of the emergency numbers listed in the RIL / SIM.
1685      *           (Setting useExactMatch to false allows you to identify
1686      *           number that could *potentially* result in emergency calls
1687      *           since many networks will actually ignore trailing digits
1688      *           after a valid emergency number.)
1689      *
1690      * @return true if the number is in the list of emergency numbers
1691      *         listed in the RIL / sim, otherwise return false.
1692      */
isEmergencyNumberInternal(String number, boolean useExactMatch)1693     private static boolean isEmergencyNumberInternal(String number, boolean useExactMatch) {
1694         return isEmergencyNumberInternal(getDefaultVoiceSubId(), number, useExactMatch);
1695     }
1696 
1697     /**
1698      * Helper function for isEmergencyNumber(String) and
1699      * isPotentialEmergencyNumber(String).
1700      *
1701      * @param subId the subscription id of the SIM.
1702      * @param number the number to look up.
1703      *
1704      * @param useExactMatch if true, consider a number to be an emergency
1705      *           number only if it *exactly* matches a number listed in
1706      *           the RIL / SIM.  If false, a number is considered to be an
1707      *           emergency number if it simply starts with the same digits
1708      *           as any of the emergency numbers listed in the RIL / SIM.
1709      *           (Setting useExactMatch to false allows you to identify
1710      *           number that could *potentially* result in emergency calls
1711      *           since many networks will actually ignore trailing digits
1712      *           after a valid emergency number.)
1713      *
1714      * @return true if the number is in the list of emergency numbers
1715      *         listed in the RIL / sim, otherwise return false.
1716      */
isEmergencyNumberInternal(int subId, String number, boolean useExactMatch)1717     private static boolean isEmergencyNumberInternal(int subId, String number,
1718             boolean useExactMatch) {
1719         return isEmergencyNumberInternal(subId, number, null, useExactMatch);
1720     }
1721 
1722     /**
1723      * Checks if a given number is an emergency number for a specific country.
1724      *
1725      * @param number the number to look up.
1726      * @param defaultCountryIso the specific country which the number should be checked against
1727      * @return if the number is an emergency number for the specific country, then return true,
1728      * otherwise false
1729      *
1730      * @hide
1731      */
isEmergencyNumber(String number, String defaultCountryIso)1732     public static boolean isEmergencyNumber(String number, String defaultCountryIso) {
1733             return isEmergencyNumber(getDefaultVoiceSubId(), number, defaultCountryIso);
1734     }
1735 
1736     /**
1737      * Checks if a given number is an emergency number for a specific country.
1738      *
1739      * @param subId the subscription id of the SIM.
1740      * @param number the number to look up.
1741      * @param defaultCountryIso the specific country which the number should be checked against
1742      * @return if the number is an emergency number for the specific country, then return true,
1743      * otherwise false
1744      * @hide
1745      */
isEmergencyNumber(int subId, String number, String defaultCountryIso)1746     public static boolean isEmergencyNumber(int subId, String number, String defaultCountryIso) {
1747         return isEmergencyNumberInternal(subId, number,
1748                                          defaultCountryIso,
1749                                          true /* useExactMatch */);
1750     }
1751 
1752     /**
1753      * Checks if a given number might *potentially* result in a call to an
1754      * emergency service, for a specific country.
1755      *
1756      * Specifically, this method will return true if the specified number
1757      * is an emergency number in the specified country, *or* if the number
1758      * simply starts with the same digits as any emergency number for that
1759      * country.
1760      *
1761      * This method is intended for internal use by the phone app when
1762      * deciding whether to allow ACTION_CALL intents from 3rd party apps
1763      * (where we're required to *not* allow emergency calls to be placed.)
1764      *
1765      * @param number the number to look up.
1766      * @param defaultCountryIso the specific country which the number should be checked against
1767      * @return true if the number is an emergency number for the specific
1768      *         country, *or* if the number starts with the same digits as
1769      *         any of those emergency numbers.
1770      *
1771      * @hide
1772      */
isPotentialEmergencyNumber(String number, String defaultCountryIso)1773     public static boolean isPotentialEmergencyNumber(String number, String defaultCountryIso) {
1774         return isPotentialEmergencyNumber(getDefaultVoiceSubId(), number, defaultCountryIso);
1775     }
1776 
1777     /**
1778      * Checks if a given number might *potentially* result in a call to an
1779      * emergency service, for a specific country.
1780      *
1781      * Specifically, this method will return true if the specified number
1782      * is an emergency number in the specified country, *or* if the number
1783      * simply starts with the same digits as any emergency number for that
1784      * country.
1785      *
1786      * This method is intended for internal use by the phone app when
1787      * deciding whether to allow ACTION_CALL intents from 3rd party apps
1788      * (where we're required to *not* allow emergency calls to be placed.)
1789      *
1790      * @param subId the subscription id of the SIM.
1791      * @param number the number to look up.
1792      * @param defaultCountryIso the specific country which the number should be checked against
1793      * @return true if the number is an emergency number for the specific
1794      *         country, *or* if the number starts with the same digits as
1795      *         any of those emergency numbers.
1796      * @hide
1797      */
isPotentialEmergencyNumber(int subId, String number, String defaultCountryIso)1798     public static boolean isPotentialEmergencyNumber(int subId, String number,
1799             String defaultCountryIso) {
1800         return isEmergencyNumberInternal(subId, number,
1801                                          defaultCountryIso,
1802                                          false /* useExactMatch */);
1803     }
1804 
1805     /**
1806      * Helper function for isEmergencyNumber(String, String) and
1807      * isPotentialEmergencyNumber(String, String).
1808      *
1809      * @param number the number to look up.
1810      * @param defaultCountryIso the specific country which the number should be checked against
1811      * @param useExactMatch if true, consider a number to be an emergency
1812      *           number only if it *exactly* matches a number listed in
1813      *           the RIL / SIM.  If false, a number is considered to be an
1814      *           emergency number if it simply starts with the same digits
1815      *           as any of the emergency numbers listed in the RIL / SIM.
1816      *
1817      * @return true if the number is an emergency number for the specified country.
1818      */
isEmergencyNumberInternal(String number, String defaultCountryIso, boolean useExactMatch)1819     private static boolean isEmergencyNumberInternal(String number,
1820                                                      String defaultCountryIso,
1821                                                      boolean useExactMatch) {
1822         return isEmergencyNumberInternal(getDefaultVoiceSubId(), number, defaultCountryIso,
1823                 useExactMatch);
1824     }
1825 
1826     /**
1827      * Helper function for isEmergencyNumber(String, String) and
1828      * isPotentialEmergencyNumber(String, String).
1829      *
1830      * @param subId the subscription id of the SIM.
1831      * @param number the number to look up.
1832      * @param defaultCountryIso the specific country which the number should be checked against
1833      * @param useExactMatch if true, consider a number to be an emergency
1834      *           number only if it *exactly* matches a number listed in
1835      *           the RIL / SIM.  If false, a number is considered to be an
1836      *           emergency number if it simply starts with the same digits
1837      *           as any of the emergency numbers listed in the RIL / SIM.
1838      *
1839      * @return true if the number is an emergency number for the specified country.
1840      * @hide
1841      */
isEmergencyNumberInternal(int subId, String number, String defaultCountryIso, boolean useExactMatch)1842     private static boolean isEmergencyNumberInternal(int subId, String number,
1843                                                      String defaultCountryIso,
1844                                                      boolean useExactMatch) {
1845         // If the number passed in is null, just return false:
1846         if (number == null) return false;
1847 
1848         // If the number passed in is a SIP address, return false, since the
1849         // concept of "emergency numbers" is only meaningful for calls placed
1850         // over the cell network.
1851         // (Be sure to do this check *before* calling extractNetworkPortionAlt(),
1852         // since the whole point of extractNetworkPortionAlt() is to filter out
1853         // any non-dialable characters (which would turn 'abc911def@example.com'
1854         // into '911', for example.))
1855         if (isUriNumber(number)) {
1856             return false;
1857         }
1858 
1859         // Strip the separators from the number before comparing it
1860         // to the list.
1861         number = extractNetworkPortionAlt(number);
1862 
1863         String emergencyNumbers = "";
1864         int slotId = SubscriptionManager.getSlotId(subId);
1865 
1866         // retrieve the list of emergency numbers
1867         // check read-write ecclist property first
1868         String ecclist = (slotId <= 0) ? "ril.ecclist" : ("ril.ecclist" + slotId);
1869 
1870         emergencyNumbers = SystemProperties.get(ecclist, "");
1871 
1872         Rlog.d(LOG_TAG, "slotId:" + slotId + " subId:" + subId + " country:"
1873                 + defaultCountryIso + " emergencyNumbers: " +  emergencyNumbers);
1874 
1875         if (TextUtils.isEmpty(emergencyNumbers)) {
1876             // then read-only ecclist property since old RIL only uses this
1877             emergencyNumbers = SystemProperties.get("ro.ril.ecclist");
1878         }
1879 
1880         if (!TextUtils.isEmpty(emergencyNumbers)) {
1881             // searches through the comma-separated list for a match,
1882             // return true if one is found.
1883             for (String emergencyNum : emergencyNumbers.split(",")) {
1884                 // It is not possible to append additional digits to an emergency number to dial
1885                 // the number in Brazil - it won't connect.
1886                 if (useExactMatch || "BR".equalsIgnoreCase(defaultCountryIso)) {
1887                     if (number.equals(emergencyNum)) {
1888                         return true;
1889                     }
1890                 } else {
1891                     if (number.startsWith(emergencyNum)) {
1892                         return true;
1893                     }
1894                 }
1895             }
1896             // no matches found against the list!
1897             return false;
1898         }
1899 
1900         Rlog.d(LOG_TAG, "System property doesn't provide any emergency numbers."
1901                 + " Use embedded logic for determining ones.");
1902 
1903         // If slot id is invalid, means that there is no sim card.
1904         // According spec 3GPP TS22.101, the following numbers should be
1905         // ECC numbers when SIM/USIM is not present.
1906         emergencyNumbers = ((slotId < 0) ? "112,911,000,08,110,118,119,999" : "112,911");
1907 
1908         for (String emergencyNum : emergencyNumbers.split(",")) {
1909             if (useExactMatch) {
1910                 if (number.equals(emergencyNum)) {
1911                     return true;
1912                 }
1913             } else {
1914                 if (number.startsWith(emergencyNum)) {
1915                     return true;
1916                 }
1917             }
1918         }
1919 
1920         // No ecclist system property, so use our own list.
1921         if (defaultCountryIso != null) {
1922             ShortNumberUtil util = new ShortNumberUtil();
1923             if (useExactMatch) {
1924                 return util.isEmergencyNumber(number, defaultCountryIso);
1925             } else {
1926                 return util.connectsToEmergencyNumber(number, defaultCountryIso);
1927             }
1928         }
1929 
1930         return false;
1931     }
1932 
1933     /**
1934      * Checks if a given number is an emergency number for the country that the user is in.
1935      *
1936      * @param number the number to look up.
1937      * @param context the specific context which the number should be checked against
1938      * @return true if the specified number is an emergency number for the country the user
1939      * is currently in.
1940      */
isLocalEmergencyNumber(Context context, String number)1941     public static boolean isLocalEmergencyNumber(Context context, String number) {
1942         return isLocalEmergencyNumber(context, getDefaultVoiceSubId(), number);
1943     }
1944 
1945     /**
1946      * Checks if a given number is an emergency number for the country that the user is in.
1947      *
1948      * @param subId the subscription id of the SIM.
1949      * @param number the number to look up.
1950      * @param context the specific context which the number should be checked against
1951      * @return true if the specified number is an emergency number for the country the user
1952      * is currently in.
1953      * @hide
1954      */
isLocalEmergencyNumber(Context context, int subId, String number)1955     public static boolean isLocalEmergencyNumber(Context context, int subId, String number) {
1956         return isLocalEmergencyNumberInternal(subId, number,
1957                                               context,
1958                                               true /* useExactMatch */);
1959     }
1960 
1961     /**
1962      * Checks if a given number might *potentially* result in a call to an
1963      * emergency service, for the country that the user is in. The current
1964      * country is determined using the CountryDetector.
1965      *
1966      * Specifically, this method will return true if the specified number
1967      * is an emergency number in the current country, *or* if the number
1968      * simply starts with the same digits as any emergency number for the
1969      * current country.
1970      *
1971      * This method is intended for internal use by the phone app when
1972      * deciding whether to allow ACTION_CALL intents from 3rd party apps
1973      * (where we're required to *not* allow emergency calls to be placed.)
1974      *
1975      * @param number the number to look up.
1976      * @param context the specific context which the number should be checked against
1977      * @return true if the specified number is an emergency number for a local country, based on the
1978      *              CountryDetector.
1979      *
1980      * @see android.location.CountryDetector
1981      * @hide
1982      */
isPotentialLocalEmergencyNumber(Context context, String number)1983     public static boolean isPotentialLocalEmergencyNumber(Context context, String number) {
1984         return isPotentialLocalEmergencyNumber(context, getDefaultVoiceSubId(), number);
1985     }
1986 
1987     /**
1988      * Checks if a given number might *potentially* result in a call to an
1989      * emergency service, for the country that the user is in. The current
1990      * country is determined using the CountryDetector.
1991      *
1992      * Specifically, this method will return true if the specified number
1993      * is an emergency number in the current country, *or* if the number
1994      * simply starts with the same digits as any emergency number for the
1995      * current country.
1996      *
1997      * This method is intended for internal use by the phone app when
1998      * deciding whether to allow ACTION_CALL intents from 3rd party apps
1999      * (where we're required to *not* allow emergency calls to be placed.)
2000      *
2001      * @param subId the subscription id of the SIM.
2002      * @param number the number to look up.
2003      * @param context the specific context which the number should be checked against
2004      * @return true if the specified number is an emergency number for a local country, based on the
2005      *              CountryDetector.
2006      *
2007      * @hide
2008      */
isPotentialLocalEmergencyNumber(Context context, int subId, String number)2009     public static boolean isPotentialLocalEmergencyNumber(Context context, int subId,
2010             String number) {
2011         return isLocalEmergencyNumberInternal(subId, number,
2012                                               context,
2013                                               false /* useExactMatch */);
2014     }
2015 
2016     /**
2017      * Helper function for isLocalEmergencyNumber() and
2018      * isPotentialLocalEmergencyNumber().
2019      *
2020      * @param number the number to look up.
2021      * @param context the specific context which the number should be checked against
2022      * @param useExactMatch if true, consider a number to be an emergency
2023      *           number only if it *exactly* matches a number listed in
2024      *           the RIL / SIM.  If false, a number is considered to be an
2025      *           emergency number if it simply starts with the same digits
2026      *           as any of the emergency numbers listed in the RIL / SIM.
2027      *
2028      * @return true if the specified number is an emergency number for a
2029      *              local country, based on the CountryDetector.
2030      *
2031      * @see android.location.CountryDetector
2032      * @hide
2033      */
isLocalEmergencyNumberInternal(String number, Context context, boolean useExactMatch)2034     private static boolean isLocalEmergencyNumberInternal(String number,
2035                                                           Context context,
2036                                                           boolean useExactMatch) {
2037         return isLocalEmergencyNumberInternal(getDefaultVoiceSubId(), number, context,
2038                 useExactMatch);
2039     }
2040 
2041     /**
2042      * Helper function for isLocalEmergencyNumber() and
2043      * isPotentialLocalEmergencyNumber().
2044      *
2045      * @param subId the subscription id of the SIM.
2046      * @param number the number to look up.
2047      * @param context the specific context which the number should be checked against
2048      * @param useExactMatch if true, consider a number to be an emergency
2049      *           number only if it *exactly* matches a number listed in
2050      *           the RIL / SIM.  If false, a number is considered to be an
2051      *           emergency number if it simply starts with the same digits
2052      *           as any of the emergency numbers listed in the RIL / SIM.
2053      *
2054      * @return true if the specified number is an emergency number for a
2055      *              local country, based on the CountryDetector.
2056      * @hide
2057      */
isLocalEmergencyNumberInternal(int subId, String number, Context context, boolean useExactMatch)2058     private static boolean isLocalEmergencyNumberInternal(int subId, String number,
2059                                                           Context context,
2060                                                           boolean useExactMatch) {
2061         String countryIso;
2062         CountryDetector detector = (CountryDetector) context.getSystemService(
2063                 Context.COUNTRY_DETECTOR);
2064         if (detector != null && detector.detectCountry() != null) {
2065             countryIso = detector.detectCountry().getCountryIso();
2066         } else {
2067             Locale locale = context.getResources().getConfiguration().locale;
2068             countryIso = locale.getCountry();
2069             Rlog.w(LOG_TAG, "No CountryDetector; falling back to countryIso based on locale: "
2070                     + countryIso);
2071         }
2072         return isEmergencyNumberInternal(subId, number, countryIso, useExactMatch);
2073     }
2074 
2075     /**
2076      * isVoiceMailNumber: checks a given number against the voicemail
2077      *   number provided by the RIL and SIM card. The caller must have
2078      *   the READ_PHONE_STATE credential.
2079      *
2080      * @param number the number to look up.
2081      * @return true if the number is in the list of voicemail. False
2082      * otherwise, including if the caller does not have the permission
2083      * to read the VM number.
2084      */
isVoiceMailNumber(String number)2085     public static boolean isVoiceMailNumber(String number) {
2086         return isVoiceMailNumber(SubscriptionManager.getDefaultSubscriptionId(), number);
2087     }
2088 
2089     /**
2090      * isVoiceMailNumber: checks a given number against the voicemail
2091      *   number provided by the RIL and SIM card. The caller must have
2092      *   the READ_PHONE_STATE credential.
2093      *
2094      * @param subId the subscription id of the SIM.
2095      * @param number the number to look up.
2096      * @return true if the number is in the list of voicemail. False
2097      * otherwise, including if the caller does not have the permission
2098      * to read the VM number.
2099      * @hide
2100      */
isVoiceMailNumber(int subId, String number)2101     public static boolean isVoiceMailNumber(int subId, String number) {
2102         return isVoiceMailNumber(null, subId, number);
2103     }
2104 
2105     /**
2106      * isVoiceMailNumber: checks a given number against the voicemail
2107      *   number provided by the RIL and SIM card. The caller must have
2108      *   the READ_PHONE_STATE credential.
2109      *
2110      * @param context {@link Context}.
2111      * @param subId the subscription id of the SIM.
2112      * @param number the number to look up.
2113      * @return true if the number is in the list of voicemail. False
2114      * otherwise, including if the caller does not have the permission
2115      * to read the VM number.
2116      * @hide
2117      */
isVoiceMailNumber(Context context, int subId, String number)2118     public static boolean isVoiceMailNumber(Context context, int subId, String number) {
2119         String vmNumber, mdn;
2120         try {
2121             final TelephonyManager tm;
2122             if (context == null) {
2123                 tm = TelephonyManager.getDefault();
2124                 if (DBG) log("isVoiceMailNumber: default tm");
2125             } else {
2126                 tm = TelephonyManager.from(context);
2127                 if (DBG) log("isVoiceMailNumber: tm from context");
2128             }
2129             vmNumber = tm.getVoiceMailNumber(subId);
2130             mdn = tm.getLine1Number(subId);
2131             if (DBG) log("isVoiceMailNumber: mdn=" + mdn + ", vmNumber=" + vmNumber
2132                     + ", number=" + number);
2133         } catch (SecurityException ex) {
2134             if (DBG) log("isVoiceMailNumber: SecurityExcpetion caught");
2135             return false;
2136         }
2137         // Strip the separators from the number before comparing it
2138         // to the list.
2139         number = extractNetworkPortionAlt(number);
2140         if (TextUtils.isEmpty(number)) {
2141             if (DBG) log("isVoiceMailNumber: number is empty after stripping");
2142             return false;
2143         }
2144 
2145         // check if the carrier considers MDN to be an additional voicemail number
2146         boolean compareWithMdn = false;
2147         if (context != null) {
2148             CarrierConfigManager configManager = (CarrierConfigManager)
2149                     context.getSystemService(Context.CARRIER_CONFIG_SERVICE);
2150             if (configManager != null) {
2151                 PersistableBundle b = configManager.getConfigForSubId(subId);
2152                 if (b != null) {
2153                     compareWithMdn = b.getBoolean(CarrierConfigManager.
2154                             KEY_MDN_IS_ADDITIONAL_VOICEMAIL_NUMBER_BOOL);
2155                     if (DBG) log("isVoiceMailNumber: compareWithMdn=" + compareWithMdn);
2156                 }
2157             }
2158         }
2159 
2160         if (compareWithMdn) {
2161             if (DBG) log("isVoiceMailNumber: treating mdn as additional vm number");
2162             return compare(number, vmNumber) || compare(number, mdn);
2163         } else {
2164             if (DBG) log("isVoiceMailNumber: returning regular compare");
2165             return compare(number, vmNumber);
2166         }
2167     }
2168 
2169     /**
2170      * Translates any alphabetic letters (i.e. [A-Za-z]) in the
2171      * specified phone number into the equivalent numeric digits,
2172      * according to the phone keypad letter mapping described in
2173      * ITU E.161 and ISO/IEC 9995-8.
2174      *
2175      * @return the input string, with alpha letters converted to numeric
2176      *         digits using the phone keypad letter mapping.  For example,
2177      *         an input of "1-800-GOOG-411" will return "1-800-4664-411".
2178      */
convertKeypadLettersToDigits(String input)2179     public static String convertKeypadLettersToDigits(String input) {
2180         if (input == null) {
2181             return input;
2182         }
2183         int len = input.length();
2184         if (len == 0) {
2185             return input;
2186         }
2187 
2188         char[] out = input.toCharArray();
2189 
2190         for (int i = 0; i < len; i++) {
2191             char c = out[i];
2192             // If this char isn't in KEYPAD_MAP at all, just leave it alone.
2193             out[i] = (char) KEYPAD_MAP.get(c, c);
2194         }
2195 
2196         return new String(out);
2197     }
2198 
2199     /**
2200      * The phone keypad letter mapping (see ITU E.161 or ISO/IEC 9995-8.)
2201      * TODO: This should come from a resource.
2202      */
2203     private static final SparseIntArray KEYPAD_MAP = new SparseIntArray();
2204     static {
2205         KEYPAD_MAP.put('a', '2'); KEYPAD_MAP.put('b', '2'); KEYPAD_MAP.put('c', '2');
2206         KEYPAD_MAP.put('A', '2'); KEYPAD_MAP.put('B', '2'); KEYPAD_MAP.put('C', '2');
2207 
2208         KEYPAD_MAP.put('d', '3'); KEYPAD_MAP.put('e', '3'); KEYPAD_MAP.put('f', '3');
2209         KEYPAD_MAP.put('D', '3'); KEYPAD_MAP.put('E', '3'); KEYPAD_MAP.put('F', '3');
2210 
2211         KEYPAD_MAP.put('g', '4'); KEYPAD_MAP.put('h', '4'); KEYPAD_MAP.put('i', '4');
2212         KEYPAD_MAP.put('G', '4'); KEYPAD_MAP.put('H', '4'); KEYPAD_MAP.put('I', '4');
2213 
2214         KEYPAD_MAP.put('j', '5'); KEYPAD_MAP.put('k', '5'); KEYPAD_MAP.put('l', '5');
2215         KEYPAD_MAP.put('J', '5'); KEYPAD_MAP.put('K', '5'); KEYPAD_MAP.put('L', '5');
2216 
2217         KEYPAD_MAP.put('m', '6'); KEYPAD_MAP.put('n', '6'); KEYPAD_MAP.put('o', '6');
2218         KEYPAD_MAP.put('M', '6'); KEYPAD_MAP.put('N', '6'); KEYPAD_MAP.put('O', '6');
2219 
2220         KEYPAD_MAP.put('p', '7'); KEYPAD_MAP.put('q', '7'); KEYPAD_MAP.put('r', '7'); KEYPAD_MAP.put('s', '7');
2221         KEYPAD_MAP.put('P', '7'); KEYPAD_MAP.put('Q', '7'); KEYPAD_MAP.put('R', '7'); KEYPAD_MAP.put('S', '7');
2222 
2223         KEYPAD_MAP.put('t', '8'); KEYPAD_MAP.put('u', '8'); KEYPAD_MAP.put('v', '8');
2224         KEYPAD_MAP.put('T', '8'); KEYPAD_MAP.put('U', '8'); KEYPAD_MAP.put('V', '8');
2225 
2226         KEYPAD_MAP.put('w', '9'); KEYPAD_MAP.put('x', '9'); KEYPAD_MAP.put('y', '9'); KEYPAD_MAP.put('z', '9');
2227         KEYPAD_MAP.put('W', '9'); KEYPAD_MAP.put('X', '9'); KEYPAD_MAP.put('Y', '9'); KEYPAD_MAP.put('Z', '9');
2228     }
2229 
2230     //================ Plus Code formatting =========================
2231     private static final char PLUS_SIGN_CHAR = '+';
2232     private static final String PLUS_SIGN_STRING = "+";
2233     private static final String NANP_IDP_STRING = "011";
2234     private static final int NANP_LENGTH = 10;
2235 
2236     /**
2237      * This function checks if there is a plus sign (+) in the passed-in dialing number.
2238      * If there is, it processes the plus sign based on the default telephone
2239      * numbering plan of the system when the phone is activated and the current
2240      * telephone numbering plan of the system that the phone is camped on.
2241      * Currently, we only support the case that the default and current telephone
2242      * numbering plans are North American Numbering Plan(NANP).
2243      *
2244      * The passed-in dialStr should only contain the valid format as described below,
2245      * 1) the 1st character in the dialStr should be one of the really dialable
2246      *    characters listed below
2247      *    ISO-LATIN characters 0-9, *, # , +
2248      * 2) the dialStr should already strip out the separator characters,
2249      *    every character in the dialStr should be one of the non separator characters
2250      *    listed below
2251      *    ISO-LATIN characters 0-9, *, # , +, WILD, WAIT, PAUSE
2252      *
2253      * Otherwise, this function returns the dial string passed in
2254      *
2255      * @param dialStr the original dial string
2256      * @return the converted dial string if the current/default countries belong to NANP,
2257      * and if there is the "+" in the original dial string. Otherwise, the original dial
2258      * string returns.
2259      *
2260      * This API is for CDMA only
2261      *
2262      * @hide TODO: pending API Council approval
2263      */
cdmaCheckAndProcessPlusCode(String dialStr)2264     public static String cdmaCheckAndProcessPlusCode(String dialStr) {
2265         if (!TextUtils.isEmpty(dialStr)) {
2266             if (isReallyDialable(dialStr.charAt(0)) &&
2267                 isNonSeparator(dialStr)) {
2268                 String currIso = TelephonyManager.getDefault().getNetworkCountryIso();
2269                 String defaultIso = TelephonyManager.getDefault().getSimCountryIso();
2270                 if (!TextUtils.isEmpty(currIso) && !TextUtils.isEmpty(defaultIso)) {
2271                     return cdmaCheckAndProcessPlusCodeByNumberFormat(dialStr,
2272                             getFormatTypeFromCountryCode(currIso),
2273                             getFormatTypeFromCountryCode(defaultIso));
2274                 }
2275             }
2276         }
2277         return dialStr;
2278     }
2279 
2280     /**
2281      * Process phone number for CDMA, converting plus code using the home network number format.
2282      * This is used for outgoing SMS messages.
2283      *
2284      * @param dialStr the original dial string
2285      * @return the converted dial string
2286      * @hide for internal use
2287      */
cdmaCheckAndProcessPlusCodeForSms(String dialStr)2288     public static String cdmaCheckAndProcessPlusCodeForSms(String dialStr) {
2289         if (!TextUtils.isEmpty(dialStr)) {
2290             if (isReallyDialable(dialStr.charAt(0)) && isNonSeparator(dialStr)) {
2291                 String defaultIso = TelephonyManager.getDefault().getSimCountryIso();
2292                 if (!TextUtils.isEmpty(defaultIso)) {
2293                     int format = getFormatTypeFromCountryCode(defaultIso);
2294                     return cdmaCheckAndProcessPlusCodeByNumberFormat(dialStr, format, format);
2295                 }
2296             }
2297         }
2298         return dialStr;
2299     }
2300 
2301     /**
2302      * This function should be called from checkAndProcessPlusCode only
2303      * And it is used for test purpose also.
2304      *
2305      * It checks the dial string by looping through the network portion,
2306      * post dial portion 1, post dial porting 2, etc. If there is any
2307      * plus sign, then process the plus sign.
2308      * Currently, this function supports the plus sign conversion within NANP only.
2309      * Specifically, it handles the plus sign in the following ways:
2310      * 1)+1NANP,remove +, e.g.
2311      *   +18475797000 is converted to 18475797000,
2312      * 2)+NANP or +non-NANP Numbers,replace + with the current NANP IDP, e.g,
2313      *   +8475797000 is converted to 0118475797000,
2314      *   +11875767800 is converted to 01111875767800
2315      * 3)+1NANP in post dial string(s), e.g.
2316      *   8475797000;+18475231753 is converted to 8475797000;18475231753
2317      *
2318      *
2319      * @param dialStr the original dial string
2320      * @param currFormat the numbering system of the current country that the phone is camped on
2321      * @param defaultFormat the numbering system of the country that the phone is activated on
2322      * @return the converted dial string if the current/default countries belong to NANP,
2323      * and if there is the "+" in the original dial string. Otherwise, the original dial
2324      * string returns.
2325      *
2326      * @hide
2327      */
2328     public static String
cdmaCheckAndProcessPlusCodeByNumberFormat(String dialStr,int currFormat,int defaultFormat)2329     cdmaCheckAndProcessPlusCodeByNumberFormat(String dialStr,int currFormat,int defaultFormat) {
2330         String retStr = dialStr;
2331 
2332         boolean useNanp = (currFormat == defaultFormat) && (currFormat == FORMAT_NANP);
2333 
2334         // Checks if the plus sign character is in the passed-in dial string
2335         if (dialStr != null &&
2336             dialStr.lastIndexOf(PLUS_SIGN_STRING) != -1) {
2337 
2338             // Handle case where default and current telephone numbering plans are NANP.
2339             String postDialStr = null;
2340             String tempDialStr = dialStr;
2341 
2342             // Sets the retStr to null since the conversion will be performed below.
2343             retStr = null;
2344             if (DBG) log("checkAndProcessPlusCode,dialStr=" + dialStr);
2345             // This routine is to process the plus sign in the dial string by loop through
2346             // the network portion, post dial portion 1, post dial portion 2... etc. if
2347             // applied
2348             do {
2349                 String networkDialStr;
2350                 // Format the string based on the rules for the country the number is from,
2351                 // and the current country the phone is camped
2352                 if (useNanp) {
2353                     networkDialStr = extractNetworkPortion(tempDialStr);
2354                 } else  {
2355                     networkDialStr = extractNetworkPortionAlt(tempDialStr);
2356 
2357                 }
2358 
2359                 networkDialStr = processPlusCode(networkDialStr, useNanp);
2360 
2361                 // Concatenates the string that is converted from network portion
2362                 if (!TextUtils.isEmpty(networkDialStr)) {
2363                     if (retStr == null) {
2364                         retStr = networkDialStr;
2365                     } else {
2366                         retStr = retStr.concat(networkDialStr);
2367                     }
2368                 } else {
2369                     // This should never happen since we checked the if dialStr is null
2370                     // and if it contains the plus sign in the beginning of this function.
2371                     // The plus sign is part of the network portion.
2372                     Rlog.e("checkAndProcessPlusCode: null newDialStr", networkDialStr);
2373                     return dialStr;
2374                 }
2375                 postDialStr = extractPostDialPortion(tempDialStr);
2376                 if (!TextUtils.isEmpty(postDialStr)) {
2377                     int dialableIndex = findDialableIndexFromPostDialStr(postDialStr);
2378 
2379                     // dialableIndex should always be greater than 0
2380                     if (dialableIndex >= 1) {
2381                         retStr = appendPwCharBackToOrigDialStr(dialableIndex,
2382                                  retStr,postDialStr);
2383                         // Skips the P/W character, extracts the dialable portion
2384                         tempDialStr = postDialStr.substring(dialableIndex);
2385                     } else {
2386                         // Non-dialable character such as P/W should not be at the end of
2387                         // the dial string after P/W processing in GsmCdmaConnection.java
2388                         // Set the postDialStr to "" to break out of the loop
2389                         if (dialableIndex < 0) {
2390                             postDialStr = "";
2391                         }
2392                         Rlog.e("wrong postDialStr=", postDialStr);
2393                     }
2394                 }
2395                 if (DBG) log("checkAndProcessPlusCode,postDialStr=" + postDialStr);
2396             } while (!TextUtils.isEmpty(postDialStr) && !TextUtils.isEmpty(tempDialStr));
2397         }
2398         return retStr;
2399     }
2400 
2401     /**
2402      * Wrap the supplied {@code CharSequence} with a {@code TtsSpan}, annotating it as
2403      * containing a phone number in its entirety.
2404      *
2405      * @param phoneNumber A {@code CharSequence} the entirety of which represents a phone number.
2406      * @return A {@code CharSequence} with appropriate annotations.
2407      */
createTtsSpannable(CharSequence phoneNumber)2408     public static CharSequence createTtsSpannable(CharSequence phoneNumber) {
2409         if (phoneNumber == null) {
2410             return null;
2411         }
2412         Spannable spannable = Spannable.Factory.getInstance().newSpannable(phoneNumber);
2413         PhoneNumberUtils.addTtsSpan(spannable, 0, spannable.length());
2414         return spannable;
2415     }
2416 
2417     /**
2418      * Attach a {@link TtsSpan} to the supplied {@code Spannable} at the indicated location,
2419      * annotating that location as containing a phone number.
2420      *
2421      * @param s A {@code Spannable} to annotate.
2422      * @param start The starting character position of the phone number in {@code s}.
2423      * @param endExclusive The position after the ending character in the phone number {@code s}.
2424      */
addTtsSpan(Spannable s, int start, int endExclusive)2425     public static void addTtsSpan(Spannable s, int start, int endExclusive) {
2426         s.setSpan(createTtsSpan(s.subSequence(start, endExclusive).toString()),
2427                 start,
2428                 endExclusive,
2429                 Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
2430     }
2431 
2432     /**
2433      * Wrap the supplied {@code CharSequence} with a {@code TtsSpan}, annotating it as
2434      * containing a phone number in its entirety.
2435      *
2436      * @param phoneNumber A {@code CharSequence} the entirety of which represents a phone number.
2437      * @return A {@code CharSequence} with appropriate annotations.
2438      * @deprecated Renamed {@link #createTtsSpannable}.
2439      *
2440      * @hide
2441      */
2442     @Deprecated
ttsSpanAsPhoneNumber(CharSequence phoneNumber)2443     public static CharSequence ttsSpanAsPhoneNumber(CharSequence phoneNumber) {
2444         return createTtsSpannable(phoneNumber);
2445     }
2446 
2447     /**
2448      * Attach a {@link TtsSpan} to the supplied {@code Spannable} at the indicated location,
2449      * annotating that location as containing a phone number.
2450      *
2451      * @param s A {@code Spannable} to annotate.
2452      * @param start The starting character position of the phone number in {@code s}.
2453      * @param end The ending character position of the phone number in {@code s}.
2454      *
2455      * @deprecated Renamed {@link #addTtsSpan}.
2456      *
2457      * @hide
2458      */
2459     @Deprecated
ttsSpanAsPhoneNumber(Spannable s, int start, int end)2460     public static void ttsSpanAsPhoneNumber(Spannable s, int start, int end) {
2461         addTtsSpan(s, start, end);
2462     }
2463 
2464     /**
2465      * Create a {@code TtsSpan} for the supplied {@code String}.
2466      *
2467      * @param phoneNumberString A {@code String} the entirety of which represents a phone number.
2468      * @return A {@code TtsSpan} for {@param phoneNumberString}.
2469      */
createTtsSpan(String phoneNumberString)2470     public static TtsSpan createTtsSpan(String phoneNumberString) {
2471         if (phoneNumberString == null) {
2472             return null;
2473         }
2474 
2475         // Parse the phone number
2476         final PhoneNumberUtil phoneNumberUtil = PhoneNumberUtil.getInstance();
2477         PhoneNumber phoneNumber = null;
2478         try {
2479             // Don't supply a defaultRegion so this fails for non-international numbers because
2480             // we don't want to TalkBalk to read a country code (e.g. +1) if it is not already
2481             // present
2482             phoneNumber = phoneNumberUtil.parse(phoneNumberString, /* defaultRegion */ null);
2483         } catch (NumberParseException ignored) {
2484         }
2485 
2486         // Build a telephone tts span
2487         final TtsSpan.TelephoneBuilder builder = new TtsSpan.TelephoneBuilder();
2488         if (phoneNumber == null) {
2489             // Strip separators otherwise TalkBack will be silent
2490             // (this behavior was observed with TalkBalk 4.0.2 from their alpha channel)
2491             builder.setNumberParts(splitAtNonNumerics(phoneNumberString));
2492         } else {
2493             if (phoneNumber.hasCountryCode()) {
2494                 builder.setCountryCode(Integer.toString(phoneNumber.getCountryCode()));
2495             }
2496             builder.setNumberParts(Long.toString(phoneNumber.getNationalNumber()));
2497         }
2498         return builder.build();
2499     }
2500 
2501     // Split a phone number like "+20(123)-456#" using spaces, ignoring anything that is not
2502     // a digit, to produce a result like "20 123 456".
splitAtNonNumerics(CharSequence number)2503     private static String splitAtNonNumerics(CharSequence number) {
2504         StringBuilder sb = new StringBuilder(number.length());
2505         for (int i = 0; i < number.length(); i++) {
2506             sb.append(PhoneNumberUtils.isISODigit(number.charAt(i))
2507                     ? number.charAt(i)
2508                     : " ");
2509         }
2510         // It is very important to remove extra spaces. At time of writing, any leading or trailing
2511         // spaces, or any sequence of more than one space, will confuse TalkBack and cause the TTS
2512         // span to be non-functional!
2513         return sb.toString().replaceAll(" +", " ").trim();
2514     }
2515 
getCurrentIdp(boolean useNanp)2516     private static String getCurrentIdp(boolean useNanp) {
2517         String ps = null;
2518         if (useNanp) {
2519             ps = NANP_IDP_STRING;
2520         } else {
2521             // in case, there is no IDD is found, we shouldn't convert it.
2522             ps = SystemProperties.get(PROPERTY_OPERATOR_IDP_STRING, PLUS_SIGN_STRING);
2523         }
2524         return ps;
2525     }
2526 
isTwoToNine(char c)2527     private static boolean isTwoToNine (char c) {
2528         if (c >= '2' && c <= '9') {
2529             return true;
2530         } else {
2531             return false;
2532         }
2533     }
2534 
getFormatTypeFromCountryCode(String country)2535     private static int getFormatTypeFromCountryCode (String country) {
2536         // Check for the NANP countries
2537         int length = NANP_COUNTRIES.length;
2538         for (int i = 0; i < length; i++) {
2539             if (NANP_COUNTRIES[i].compareToIgnoreCase(country) == 0) {
2540                 return FORMAT_NANP;
2541             }
2542         }
2543         if ("jp".compareToIgnoreCase(country) == 0) {
2544             return FORMAT_JAPAN;
2545         }
2546         return FORMAT_UNKNOWN;
2547     }
2548 
2549     /**
2550      * This function checks if the passed in string conforms to the NANP format
2551      * i.e. NXX-NXX-XXXX, N is any digit 2-9 and X is any digit 0-9
2552      * @hide
2553      */
isNanp(String dialStr)2554     public static boolean isNanp (String dialStr) {
2555         boolean retVal = false;
2556         if (dialStr != null) {
2557             if (dialStr.length() == NANP_LENGTH) {
2558                 if (isTwoToNine(dialStr.charAt(0)) &&
2559                     isTwoToNine(dialStr.charAt(3))) {
2560                     retVal = true;
2561                     for (int i=1; i<NANP_LENGTH; i++ ) {
2562                         char c=dialStr.charAt(i);
2563                         if (!PhoneNumberUtils.isISODigit(c)) {
2564                             retVal = false;
2565                             break;
2566                         }
2567                     }
2568                 }
2569             }
2570         } else {
2571             Rlog.e("isNanp: null dialStr passed in", dialStr);
2572         }
2573         return retVal;
2574     }
2575 
2576    /**
2577     * This function checks if the passed in string conforms to 1-NANP format
2578     */
isOneNanp(String dialStr)2579     private static boolean isOneNanp(String dialStr) {
2580         boolean retVal = false;
2581         if (dialStr != null) {
2582             String newDialStr = dialStr.substring(1);
2583             if ((dialStr.charAt(0) == '1') && isNanp(newDialStr)) {
2584                 retVal = true;
2585             }
2586         } else {
2587             Rlog.e("isOneNanp: null dialStr passed in", dialStr);
2588         }
2589         return retVal;
2590     }
2591 
2592     /**
2593      * Determines if the specified number is actually a URI
2594      * (i.e. a SIP address) rather than a regular PSTN phone number,
2595      * based on whether or not the number contains an "@" character.
2596      *
2597      * @hide
2598      * @param number
2599      * @return true if number contains @
2600      */
isUriNumber(String number)2601     public static boolean isUriNumber(String number) {
2602         // Note we allow either "@" or "%40" to indicate a URI, in case
2603         // the passed-in string is URI-escaped.  (Neither "@" nor "%40"
2604         // will ever be found in a legal PSTN number.)
2605         return number != null && (number.contains("@") || number.contains("%40"));
2606     }
2607 
2608     /**
2609      * @return the "username" part of the specified SIP address,
2610      *         i.e. the part before the "@" character (or "%40").
2611      *
2612      * @param number SIP address of the form "username@domainname"
2613      *               (or the URI-escaped equivalent "username%40domainname")
2614      * @see #isUriNumber
2615      *
2616      * @hide
2617      */
getUsernameFromUriNumber(String number)2618     public static String getUsernameFromUriNumber(String number) {
2619         // The delimiter between username and domain name can be
2620         // either "@" or "%40" (the URI-escaped equivalent.)
2621         int delimiterIndex = number.indexOf('@');
2622         if (delimiterIndex < 0) {
2623             delimiterIndex = number.indexOf("%40");
2624         }
2625         if (delimiterIndex < 0) {
2626             Rlog.w(LOG_TAG,
2627                   "getUsernameFromUriNumber: no delimiter found in SIP addr '" + number + "'");
2628             delimiterIndex = number.length();
2629         }
2630         return number.substring(0, delimiterIndex);
2631     }
2632 
2633     /**
2634      * Given a {@link Uri} with a {@code sip} scheme, attempts to build an equivalent {@code tel}
2635      * scheme {@link Uri}.  If the source {@link Uri} does not contain a valid number, or is not
2636      * using the {@code sip} scheme, the original {@link Uri} is returned.
2637      *
2638      * @param source The {@link Uri} to convert.
2639      * @return The equivalent {@code tel} scheme {@link Uri}.
2640      *
2641      * @hide
2642      */
convertSipUriToTelUri(Uri source)2643     public static Uri convertSipUriToTelUri(Uri source) {
2644         // A valid SIP uri has the format: sip:user:password@host:port;uri-parameters?headers
2645         // Per RFC3261, the "user" can be a telephone number.
2646         // For example: sip:1650555121;phone-context=blah.com@host.com
2647         // In this case, the phone number is in the user field of the URI, and the parameters can be
2648         // ignored.
2649         //
2650         // A SIP URI can also specify a phone number in a format similar to:
2651         // sip:+1-212-555-1212@something.com;user=phone
2652         // In this case, the phone number is again in user field and the parameters can be ignored.
2653         // We can get the user field in these instances by splitting the string on the @, ;, or :
2654         // and looking at the first found item.
2655 
2656         String scheme = source.getScheme();
2657 
2658         if (!PhoneAccount.SCHEME_SIP.equals(scheme)) {
2659             // Not a sip URI, bail.
2660             return source;
2661         }
2662 
2663         String number = source.getSchemeSpecificPart();
2664         String numberParts[] = number.split("[@;:]");
2665 
2666         if (numberParts.length == 0) {
2667             // Number not found, bail.
2668             return source;
2669         }
2670         number = numberParts[0];
2671 
2672         return Uri.fromParts(PhoneAccount.SCHEME_TEL, number, null);
2673     }
2674 
2675     /**
2676      * This function handles the plus code conversion
2677      * If the number format is
2678      * 1)+1NANP,remove +,
2679      * 2)other than +1NANP, any + numbers,replace + with the current IDP
2680      */
processPlusCode(String networkDialStr, boolean useNanp)2681     private static String processPlusCode(String networkDialStr, boolean useNanp) {
2682         String retStr = networkDialStr;
2683 
2684         if (DBG) log("processPlusCode, networkDialStr = " + networkDialStr
2685                 + "for NANP = " + useNanp);
2686         // If there is a plus sign at the beginning of the dial string,
2687         // Convert the plus sign to the default IDP since it's an international number
2688         if (networkDialStr != null &&
2689             networkDialStr.charAt(0) == PLUS_SIGN_CHAR &&
2690             networkDialStr.length() > 1) {
2691             String newStr = networkDialStr.substring(1);
2692             // TODO: for nonNanp, should the '+' be removed if following number is country code
2693             if (useNanp && isOneNanp(newStr)) {
2694                 // Remove the leading plus sign
2695                 retStr = newStr;
2696             } else {
2697                 // Replaces the plus sign with the default IDP
2698                 retStr = networkDialStr.replaceFirst("[+]", getCurrentIdp(useNanp));
2699             }
2700         }
2701         if (DBG) log("processPlusCode, retStr=" + retStr);
2702         return retStr;
2703     }
2704 
2705     // This function finds the index of the dialable character(s)
2706     // in the post dial string
findDialableIndexFromPostDialStr(String postDialStr)2707     private static int findDialableIndexFromPostDialStr(String postDialStr) {
2708         for (int index = 0;index < postDialStr.length();index++) {
2709              char c = postDialStr.charAt(index);
2710              if (isReallyDialable(c)) {
2711                 return index;
2712              }
2713         }
2714         return -1;
2715     }
2716 
2717     // This function appends the non-dialable P/W character to the original
2718     // dial string based on the dialable index passed in
2719     private static String
appendPwCharBackToOrigDialStr(int dialableIndex,String origStr, String dialStr)2720     appendPwCharBackToOrigDialStr(int dialableIndex,String origStr, String dialStr) {
2721         String retStr;
2722 
2723         // There is only 1 P/W character before the dialable characters
2724         if (dialableIndex == 1) {
2725             StringBuilder ret = new StringBuilder(origStr);
2726             ret = ret.append(dialStr.charAt(0));
2727             retStr = ret.toString();
2728         } else {
2729             // It means more than 1 P/W characters in the post dial string,
2730             // appends to retStr
2731             String nonDigitStr = dialStr.substring(0,dialableIndex);
2732             retStr = origStr.concat(nonDigitStr);
2733         }
2734         return retStr;
2735     }
2736 
2737     //===== Beginning of utility methods used in compareLoosely() =====
2738 
2739     /**
2740      * Phone numbers are stored in "lookup" form in the database
2741      * as reversed strings to allow for caller ID lookup
2742      *
2743      * This method takes a phone number and makes a valid SQL "LIKE"
2744      * string that will match the lookup form
2745      *
2746      */
2747     /** all of a up to len must be an international prefix or
2748      *  separators/non-dialing digits
2749      */
2750     private static boolean
matchIntlPrefix(String a, int len)2751     matchIntlPrefix(String a, int len) {
2752         /* '([^0-9*#+pwn]\+[^0-9*#+pwn] | [^0-9*#+pwn]0(0|11)[^0-9*#+pwn] )$' */
2753         /*        0       1                           2 3 45               */
2754 
2755         int state = 0;
2756         for (int i = 0 ; i < len ; i++) {
2757             char c = a.charAt(i);
2758 
2759             switch (state) {
2760                 case 0:
2761                     if      (c == '+') state = 1;
2762                     else if (c == '0') state = 2;
2763                     else if (isNonSeparator(c)) return false;
2764                 break;
2765 
2766                 case 2:
2767                     if      (c == '0') state = 3;
2768                     else if (c == '1') state = 4;
2769                     else if (isNonSeparator(c)) return false;
2770                 break;
2771 
2772                 case 4:
2773                     if      (c == '1') state = 5;
2774                     else if (isNonSeparator(c)) return false;
2775                 break;
2776 
2777                 default:
2778                     if (isNonSeparator(c)) return false;
2779                 break;
2780 
2781             }
2782         }
2783 
2784         return state == 1 || state == 3 || state == 5;
2785     }
2786 
2787     /** all of 'a' up to len must be a (+|00|011)country code)
2788      *  We're fast and loose with the country code. Any \d{1,3} matches */
2789     private static boolean
matchIntlPrefixAndCC(String a, int len)2790     matchIntlPrefixAndCC(String a, int len) {
2791         /*  [^0-9*#+pwn]*(\+|0(0|11)\d\d?\d? [^0-9*#+pwn] $ */
2792         /*      0          1 2 3 45  6 7  8                 */
2793 
2794         int state = 0;
2795         for (int i = 0 ; i < len ; i++ ) {
2796             char c = a.charAt(i);
2797 
2798             switch (state) {
2799                 case 0:
2800                     if      (c == '+') state = 1;
2801                     else if (c == '0') state = 2;
2802                     else if (isNonSeparator(c)) return false;
2803                 break;
2804 
2805                 case 2:
2806                     if      (c == '0') state = 3;
2807                     else if (c == '1') state = 4;
2808                     else if (isNonSeparator(c)) return false;
2809                 break;
2810 
2811                 case 4:
2812                     if      (c == '1') state = 5;
2813                     else if (isNonSeparator(c)) return false;
2814                 break;
2815 
2816                 case 1:
2817                 case 3:
2818                 case 5:
2819                     if      (isISODigit(c)) state = 6;
2820                     else if (isNonSeparator(c)) return false;
2821                 break;
2822 
2823                 case 6:
2824                 case 7:
2825                     if      (isISODigit(c)) state++;
2826                     else if (isNonSeparator(c)) return false;
2827                 break;
2828 
2829                 default:
2830                     if (isNonSeparator(c)) return false;
2831             }
2832         }
2833 
2834         return state == 6 || state == 7 || state == 8;
2835     }
2836 
2837     /** all of 'a' up to len must match non-US trunk prefix ('0') */
2838     private static boolean
matchTrunkPrefix(String a, int len)2839     matchTrunkPrefix(String a, int len) {
2840         boolean found;
2841 
2842         found = false;
2843 
2844         for (int i = 0 ; i < len ; i++) {
2845             char c = a.charAt(i);
2846 
2847             if (c == '0' && !found) {
2848                 found = true;
2849             } else if (isNonSeparator(c)) {
2850                 return false;
2851             }
2852         }
2853 
2854         return found;
2855     }
2856 
2857     //===== End of utility methods used only in compareLoosely() =====
2858 
2859     //===== Beginning of utility methods used only in compareStrictly() ====
2860 
2861     /*
2862      * If true, the number is country calling code.
2863      */
2864     private static final boolean COUNTRY_CALLING_CALL[] = {
2865         true, true, false, false, false, false, false, true, false, false,
2866         false, false, false, false, false, false, false, false, false, false,
2867         true, false, false, false, false, false, false, true, true, false,
2868         true, true, true, true, true, false, true, false, false, true,
2869         true, false, false, true, true, true, true, true, true, true,
2870         false, true, true, true, true, true, true, true, true, false,
2871         true, true, true, true, true, true, true, false, false, false,
2872         false, false, false, false, false, false, false, false, false, false,
2873         false, true, true, true, true, false, true, false, false, true,
2874         true, true, true, true, true, true, false, false, true, false,
2875     };
2876     private static final int CCC_LENGTH = COUNTRY_CALLING_CALL.length;
2877 
2878     /**
2879      * @return true when input is valid Country Calling Code.
2880      */
isCountryCallingCode(int countryCallingCodeCandidate)2881     private static boolean isCountryCallingCode(int countryCallingCodeCandidate) {
2882         return countryCallingCodeCandidate > 0 && countryCallingCodeCandidate < CCC_LENGTH &&
2883                 COUNTRY_CALLING_CALL[countryCallingCodeCandidate];
2884     }
2885 
2886     /**
2887      * Returns integer corresponding to the input if input "ch" is
2888      * ISO-LATIN characters 0-9.
2889      * Returns -1 otherwise
2890      */
tryGetISODigit(char ch)2891     private static int tryGetISODigit(char ch) {
2892         if ('0' <= ch && ch <= '9') {
2893             return ch - '0';
2894         } else {
2895             return -1;
2896         }
2897     }
2898 
2899     private static class CountryCallingCodeAndNewIndex {
2900         public final int countryCallingCode;
2901         public final int newIndex;
CountryCallingCodeAndNewIndex(int countryCode, int newIndex)2902         public CountryCallingCodeAndNewIndex(int countryCode, int newIndex) {
2903             this.countryCallingCode = countryCode;
2904             this.newIndex = newIndex;
2905         }
2906     }
2907 
2908     /*
2909      * Note that this function does not strictly care the country calling code with
2910      * 3 length (like Morocco: +212), assuming it is enough to use the first two
2911      * digit to compare two phone numbers.
2912      */
tryGetCountryCallingCodeAndNewIndex( String str, boolean acceptThailandCase)2913     private static CountryCallingCodeAndNewIndex tryGetCountryCallingCodeAndNewIndex(
2914         String str, boolean acceptThailandCase) {
2915         // Rough regexp:
2916         //  ^[^0-9*#+]*((\+|0(0|11)\d\d?|166) [^0-9*#+] $
2917         //         0        1 2 3 45  6 7  89
2918         //
2919         // In all the states, this function ignores separator characters.
2920         // "166" is the special case for the call from Thailand to the US. Uguu!
2921         int state = 0;
2922         int ccc = 0;
2923         final int length = str.length();
2924         for (int i = 0 ; i < length ; i++ ) {
2925             char ch = str.charAt(i);
2926             switch (state) {
2927                 case 0:
2928                     if      (ch == '+') state = 1;
2929                     else if (ch == '0') state = 2;
2930                     else if (ch == '1') {
2931                         if (acceptThailandCase) {
2932                             state = 8;
2933                         } else {
2934                             return null;
2935                         }
2936                     } else if (isDialable(ch)) {
2937                         return null;
2938                     }
2939                 break;
2940 
2941                 case 2:
2942                     if      (ch == '0') state = 3;
2943                     else if (ch == '1') state = 4;
2944                     else if (isDialable(ch)) {
2945                         return null;
2946                     }
2947                 break;
2948 
2949                 case 4:
2950                     if      (ch == '1') state = 5;
2951                     else if (isDialable(ch)) {
2952                         return null;
2953                     }
2954                 break;
2955 
2956                 case 1:
2957                 case 3:
2958                 case 5:
2959                 case 6:
2960                 case 7:
2961                     {
2962                         int ret = tryGetISODigit(ch);
2963                         if (ret > 0) {
2964                             ccc = ccc * 10 + ret;
2965                             if (ccc >= 100 || isCountryCallingCode(ccc)) {
2966                                 return new CountryCallingCodeAndNewIndex(ccc, i + 1);
2967                             }
2968                             if (state == 1 || state == 3 || state == 5) {
2969                                 state = 6;
2970                             } else {
2971                                 state++;
2972                             }
2973                         } else if (isDialable(ch)) {
2974                             return null;
2975                         }
2976                     }
2977                     break;
2978                 case 8:
2979                     if (ch == '6') state = 9;
2980                     else if (isDialable(ch)) {
2981                         return null;
2982                     }
2983                     break;
2984                 case 9:
2985                     if (ch == '6') {
2986                         return new CountryCallingCodeAndNewIndex(66, i + 1);
2987                     } else {
2988                         return null;
2989                     }
2990                 default:
2991                     return null;
2992             }
2993         }
2994 
2995         return null;
2996     }
2997 
2998     /**
2999      * Currently this function simply ignore the first digit assuming it is
3000      * trunk prefix. Actually trunk prefix is different in each country.
3001      *
3002      * e.g.
3003      * "+79161234567" equals "89161234567" (Russian trunk digit is 8)
3004      * "+33123456789" equals "0123456789" (French trunk digit is 0)
3005      *
3006      */
tryGetTrunkPrefixOmittedIndex(String str, int currentIndex)3007     private static int tryGetTrunkPrefixOmittedIndex(String str, int currentIndex) {
3008         int length = str.length();
3009         for (int i = currentIndex ; i < length ; i++) {
3010             final char ch = str.charAt(i);
3011             if (tryGetISODigit(ch) >= 0) {
3012                 return i + 1;
3013             } else if (isDialable(ch)) {
3014                 return -1;
3015             }
3016         }
3017         return -1;
3018     }
3019 
3020     /**
3021      * Return true if the prefix of "str" is "ignorable". Here, "ignorable" means
3022      * that "str" has only one digit and separator characters. The one digit is
3023      * assumed to be trunk prefix.
3024      */
checkPrefixIsIgnorable(final String str, int forwardIndex, int backwardIndex)3025     private static boolean checkPrefixIsIgnorable(final String str,
3026             int forwardIndex, int backwardIndex) {
3027         boolean trunk_prefix_was_read = false;
3028         while (backwardIndex >= forwardIndex) {
3029             if (tryGetISODigit(str.charAt(backwardIndex)) >= 0) {
3030                 if (trunk_prefix_was_read) {
3031                     // More than one digit appeared, meaning that "a" and "b"
3032                     // is different.
3033                     return false;
3034                 } else {
3035                     // Ignore just one digit, assuming it is trunk prefix.
3036                     trunk_prefix_was_read = true;
3037                 }
3038             } else if (isDialable(str.charAt(backwardIndex))) {
3039                 // Trunk prefix is a digit, not "*", "#"...
3040                 return false;
3041             }
3042             backwardIndex--;
3043         }
3044 
3045         return true;
3046     }
3047 
3048     /**
3049      * Returns Default voice subscription Id.
3050      */
getDefaultVoiceSubId()3051     private static int getDefaultVoiceSubId() {
3052         return SubscriptionManager.getDefaultVoiceSubscriptionId();
3053     }
3054     //==== End of utility methods used only in compareStrictly() =====
3055 
3056 
3057     /*
3058      * The config held calling number conversion map, expected to convert to emergency number.
3059      */
3060     private static final String[] CONVERT_TO_EMERGENCY_MAP = Resources.getSystem().getStringArray(
3061             com.android.internal.R.array.config_convert_to_emergency_number_map);
3062     /**
3063      * Check whether conversion to emergency number is enabled
3064      *
3065      * @return {@code true} when conversion to emergency numbers is enabled,
3066      *         {@code false} otherwise
3067      *
3068      * @hide
3069      */
isConvertToEmergencyNumberEnabled()3070     public static boolean isConvertToEmergencyNumberEnabled() {
3071         return CONVERT_TO_EMERGENCY_MAP != null && CONVERT_TO_EMERGENCY_MAP.length > 0;
3072     }
3073 
3074     /**
3075      * Converts to emergency number based on the conversion map.
3076      * The conversion map is declared as config_convert_to_emergency_number_map.
3077      *
3078      * Make sure {@link #isConvertToEmergencyNumberEnabled} is true before calling
3079      * this function.
3080      *
3081      * @return The converted emergency number if the number matches conversion map,
3082      * otherwise original number.
3083      *
3084      * @hide
3085      */
convertToEmergencyNumber(String number)3086     public static String convertToEmergencyNumber(String number) {
3087         if (TextUtils.isEmpty(number)) {
3088             return number;
3089         }
3090 
3091         String normalizedNumber = normalizeNumber(number);
3092 
3093         // The number is already emergency number. Skip conversion.
3094         if (isEmergencyNumber(normalizedNumber)) {
3095             return number;
3096         }
3097 
3098         for (String convertMap : CONVERT_TO_EMERGENCY_MAP) {
3099             if (DBG) log("convertToEmergencyNumber: " + convertMap);
3100             String[] entry = null;
3101             String[] filterNumbers = null;
3102             String convertedNumber = null;
3103             if (!TextUtils.isEmpty(convertMap)) {
3104                 entry = convertMap.split(":");
3105             }
3106             if (entry != null && entry.length == 2) {
3107                 convertedNumber = entry[1];
3108                 if (!TextUtils.isEmpty(entry[0])) {
3109                     filterNumbers = entry[0].split(",");
3110                 }
3111             }
3112             // Skip if the format of entry is invalid
3113             if (TextUtils.isEmpty(convertedNumber) || filterNumbers == null
3114                     || filterNumbers.length == 0) {
3115                 continue;
3116             }
3117 
3118             for (String filterNumber : filterNumbers) {
3119                 if (DBG) log("convertToEmergencyNumber: filterNumber = " + filterNumber
3120                         + ", convertedNumber = " + convertedNumber);
3121                 if (!TextUtils.isEmpty(filterNumber) && filterNumber.equals(normalizedNumber)) {
3122                     if (DBG) log("convertToEmergencyNumber: Matched. Successfully converted to: "
3123                             + convertedNumber);
3124                     return convertedNumber;
3125                 }
3126             }
3127         }
3128         return number;
3129     }
3130 }
3131