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