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