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