• 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 android.content.Context;
20 import android.content.Intent;
21 import android.database.Cursor;
22 import android.net.Uri;
23 import android.os.SystemProperties;
24 import android.provider.Contacts;
25 import android.provider.ContactsContract;
26 import android.text.Editable;
27 import android.text.SpannableStringBuilder;
28 import android.text.TextUtils;
29 import android.util.Log;
30 import android.util.SparseIntArray;
31 
32 import static com.android.internal.telephony.TelephonyProperties.PROPERTY_ICC_OPERATOR_ISO_COUNTRY;
33 import static com.android.internal.telephony.TelephonyProperties.PROPERTY_IDP_STRING;
34 import static com.android.internal.telephony.TelephonyProperties.PROPERTY_OPERATOR_ISO_COUNTRY;
35 
36 import java.util.Locale;
37 import java.util.regex.Matcher;
38 import java.util.regex.Pattern;
39 
40 /**
41  * Various utilities for dealing with phone number strings.
42  */
43 public class PhoneNumberUtils
44 {
45     /*
46      * Special characters
47      *
48      * (See "What is a phone number?" doc)
49      * 'p' --- GSM pause character, same as comma
50      * 'n' --- GSM wild character
51      * 'w' --- GSM wait character
52      */
53     public static final char PAUSE = ',';
54     public static final char WAIT = ';';
55     public static final char WILD = 'N';
56 
57     /*
58      * TOA = TON + NPI
59      * See TS 24.008 section 10.5.4.7 for details.
60      * These are the only really useful TOA values
61      */
62     public static final int TOA_International = 0x91;
63     public static final int TOA_Unknown = 0x81;
64 
65     static final String LOG_TAG = "PhoneNumberUtils";
66     private static final boolean DBG = false;
67 
68     /*
69      * global-phone-number = ["+"] 1*( DIGIT / written-sep )
70      * written-sep         = ("-"/".")
71      */
72     private static final Pattern GLOBAL_PHONE_NUMBER_PATTERN =
73             Pattern.compile("[\\+]?[0-9.-]+");
74 
75     /** True if c is ISO-LATIN characters 0-9 */
76     public static boolean
isISODigit(char c)77     isISODigit (char c) {
78         return c >= '0' && c <= '9';
79     }
80 
81     /** True if c is ISO-LATIN characters 0-9, *, # */
82     public final static boolean
is12Key(char c)83     is12Key(char c) {
84         return (c >= '0' && c <= '9') || c == '*' || c == '#';
85     }
86 
87     /** True if c is ISO-LATIN characters 0-9, *, # , +, WILD  */
88     public final static boolean
isDialable(char c)89     isDialable(char c) {
90         return (c >= '0' && c <= '9') || c == '*' || c == '#' || c == '+' || c == WILD;
91     }
92 
93     /** True if c is ISO-LATIN characters 0-9, *, # , + (no WILD)  */
94     public final static boolean
isReallyDialable(char c)95     isReallyDialable(char c) {
96         return (c >= '0' && c <= '9') || c == '*' || c == '#' || c == '+';
97     }
98 
99     /** True if c is ISO-LATIN characters 0-9, *, # , +, WILD, WAIT, PAUSE   */
100     public final static boolean
isNonSeparator(char c)101     isNonSeparator(char c) {
102         return (c >= '0' && c <= '9') || c == '*' || c == '#' || c == '+'
103                 || c == WILD || c == WAIT || c == PAUSE;
104     }
105 
106     /** This any anything to the right of this char is part of the
107      *  post-dial string (eg this is PAUSE or WAIT)
108      */
109     public final static boolean
isStartsPostDial(char c)110     isStartsPostDial (char c) {
111         return c == PAUSE || c == WAIT;
112     }
113 
114     /** Returns true if ch is not dialable or alpha char */
isSeparator(char ch)115     private static boolean isSeparator(char ch) {
116         return !isDialable(ch) && !(('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z'));
117     }
118 
119     /** Extracts the phone number from an Intent.
120      *
121      * @param intent the intent to get the number of
122      * @param context a context to use for database access
123      *
124      * @return the phone number that would be called by the intent, or
125      *         <code>null</code> if the number cannot be found.
126      */
getNumberFromIntent(Intent intent, Context context)127     public static String getNumberFromIntent(Intent intent, Context context) {
128         String number = null;
129 
130         Uri uri = intent.getData();
131         String scheme = uri.getScheme();
132 
133         if (scheme.equals("tel")) {
134             return uri.getSchemeSpecificPart();
135         }
136 
137         // TODO: We don't check for SecurityException here (requires
138         // READ_PHONE_STATE permission).
139         if (scheme.equals("voicemail")) {
140             return TelephonyManager.getDefault().getVoiceMailNumber();
141         }
142 
143         if (context == null) {
144             return null;
145         }
146 
147         String type = intent.resolveType(context);
148         String phoneColumn = null;
149 
150         // Correctly read out the phone entry based on requested provider
151         final String authority = uri.getAuthority();
152         if (Contacts.AUTHORITY.equals(authority)) {
153             phoneColumn = Contacts.People.Phones.NUMBER;
154         } else if (ContactsContract.AUTHORITY.equals(authority)) {
155             phoneColumn = ContactsContract.CommonDataKinds.Phone.NUMBER;
156         }
157 
158         final Cursor c = context.getContentResolver().query(uri, new String[] {
159             phoneColumn
160         }, null, null, null);
161         if (c != null) {
162             try {
163                 if (c.moveToFirst()) {
164                     number = c.getString(c.getColumnIndex(phoneColumn));
165                 }
166             } finally {
167                 c.close();
168             }
169         }
170 
171         return number;
172     }
173 
174     /** Extracts the network address portion and canonicalizes
175      *  (filters out separators.)
176      *  Network address portion is everything up to DTMF control digit
177      *  separators (pause or wait), but without non-dialable characters.
178      *
179      *  Please note that the GSM wild character is allowed in the result.
180      *  This must be resolved before dialing.
181      *
182      *  Allows + only in the first  position in the result string.
183      *
184      *  Returns null if phoneNumber == null
185      */
186     public static String
extractNetworkPortion(String phoneNumber)187     extractNetworkPortion(String phoneNumber) {
188         if (phoneNumber == null) {
189             return null;
190         }
191 
192         int len = phoneNumber.length();
193         StringBuilder ret = new StringBuilder(len);
194         boolean firstCharAdded = false;
195 
196         for (int i = 0; i < len; i++) {
197             char c = phoneNumber.charAt(i);
198             if (isDialable(c) && (c != '+' || !firstCharAdded)) {
199                 firstCharAdded = true;
200                 ret.append(c);
201             } else if (isStartsPostDial (c)) {
202                 break;
203             }
204         }
205 
206         return ret.toString();
207     }
208 
209     /**
210      * Strips separators from a phone number string.
211      * @param phoneNumber phone number to strip.
212      * @return phone string stripped of separators.
213      */
stripSeparators(String phoneNumber)214     public static String stripSeparators(String phoneNumber) {
215         if (phoneNumber == null) {
216             return null;
217         }
218         int len = phoneNumber.length();
219         StringBuilder ret = new StringBuilder(len);
220 
221         for (int i = 0; i < len; i++) {
222             char c = phoneNumber.charAt(i);
223             if (isNonSeparator(c)) {
224                 ret.append(c);
225             }
226         }
227 
228         return ret.toString();
229     }
230 
231     /** or -1 if both are negative */
232     static private int
minPositive(int a, int b)233     minPositive (int a, int b) {
234         if (a >= 0 && b >= 0) {
235             return (a < b) ? a : b;
236         } else if (a >= 0) { /* && b < 0 */
237             return a;
238         } else if (b >= 0) { /* && a < 0 */
239             return b;
240         } else { /* a < 0 && b < 0 */
241             return -1;
242         }
243     }
244 
log(String msg)245     private static void log(String msg) {
246         Log.d(LOG_TAG, msg);
247     }
248     /** index of the last character of the network portion
249      *  (eg anything after is a post-dial string)
250      */
251     static private int
indexOfLastNetworkChar(String a)252     indexOfLastNetworkChar(String a) {
253         int pIndex, wIndex;
254         int origLength;
255         int trimIndex;
256 
257         origLength = a.length();
258 
259         pIndex = a.indexOf(PAUSE);
260         wIndex = a.indexOf(WAIT);
261 
262         trimIndex = minPositive(pIndex, wIndex);
263 
264         if (trimIndex < 0) {
265             return origLength - 1;
266         } else {
267             return trimIndex - 1;
268         }
269     }
270 
271     /**
272      * Extracts the post-dial sequence of DTMF control digits, pauses, and
273      * waits. Strips separators. This string may be empty, but will not be null
274      * unless phoneNumber == null.
275      *
276      * Returns null if phoneNumber == null
277      */
278 
279     public static String
extractPostDialPortion(String phoneNumber)280     extractPostDialPortion(String phoneNumber) {
281         if (phoneNumber == null) return null;
282 
283         int trimIndex;
284         StringBuilder ret = new StringBuilder();
285 
286         trimIndex = indexOfLastNetworkChar (phoneNumber);
287 
288         for (int i = trimIndex + 1, s = phoneNumber.length()
289                 ; i < s; i++
290         ) {
291             char c = phoneNumber.charAt(i);
292             if (isNonSeparator(c)) {
293                 ret.append(c);
294             }
295         }
296 
297         return ret.toString();
298     }
299 
300     /**
301      * Compare phone numbers a and b, return true if they're identical enough for caller ID purposes.
302      */
compare(String a, String b)303     public static boolean compare(String a, String b) {
304         // We've used loose comparation at least Eclair, which may change in the future.
305 
306         return compare(a, b, false);
307     }
308 
309     /**
310      * Compare phone numbers a and b, and return true if they're identical
311      * enough for caller ID purposes. Checks a resource to determine whether
312      * to use a strict or loose comparison algorithm.
313      */
compare(Context context, String a, String b)314     public static boolean compare(Context context, String a, String b) {
315         boolean useStrict = context.getResources().getBoolean(
316                com.android.internal.R.bool.config_use_strict_phone_number_comparation);
317         return compare(a, b, useStrict);
318     }
319 
320     /**
321      * @hide only for testing.
322      */
compare(String a, String b, boolean useStrictComparation)323     public static boolean compare(String a, String b, boolean useStrictComparation) {
324         return (useStrictComparation ? compareStrictly(a, b) : compareLoosely(a, b));
325     }
326 
327     /**
328      * Compare phone numbers a and b, return true if they're identical
329      * enough for caller ID purposes.
330      *
331      * - Compares from right to left
332      * - requires MIN_MATCH (7) characters to match
333      * - handles common trunk prefixes and international prefixes
334      *   (basically, everything except the Russian trunk prefix)
335      *
336      * Note that this method does not return false even when the two phone numbers
337      * are not exactly same; rather; we can call this method "similar()", not "equals()".
338      *
339      * @hide
340      */
341     public static boolean
compareLoosely(String a, String b)342     compareLoosely(String a, String b) {
343         int ia, ib;
344         int matched;
345 
346         if (a == null || b == null) return a == b;
347 
348         if (a.length() == 0 || b.length() == 0) {
349             return false;
350         }
351 
352         ia = indexOfLastNetworkChar (a);
353         ib = indexOfLastNetworkChar (b);
354         matched = 0;
355 
356         while (ia >= 0 && ib >=0) {
357             char ca, cb;
358             boolean skipCmp = false;
359 
360             ca = a.charAt(ia);
361 
362             if (!isDialable(ca)) {
363                 ia--;
364                 skipCmp = true;
365             }
366 
367             cb = b.charAt(ib);
368 
369             if (!isDialable(cb)) {
370                 ib--;
371                 skipCmp = true;
372             }
373 
374             if (!skipCmp) {
375                 if (cb != ca && ca != WILD && cb != WILD) {
376                     break;
377                 }
378                 ia--; ib--; matched++;
379             }
380         }
381 
382         if (matched < MIN_MATCH) {
383             int aLen = a.length();
384 
385             // if the input strings match, but their lengths < MIN_MATCH,
386             // treat them as equal.
387             if (aLen == b.length() && aLen == matched) {
388                 return true;
389             }
390             return false;
391         }
392 
393         // At least one string has matched completely;
394         if (matched >= MIN_MATCH && (ia < 0 || ib < 0)) {
395             return true;
396         }
397 
398         /*
399          * Now, what remains must be one of the following for a
400          * match:
401          *
402          *  - a '+' on one and a '00' or a '011' on the other
403          *  - a '0' on one and a (+,00)<country code> on the other
404          *     (for this, a '0' and a '00' prefix would have succeeded above)
405          */
406 
407         if (matchIntlPrefix(a, ia + 1)
408             && matchIntlPrefix (b, ib +1)
409         ) {
410             return true;
411         }
412 
413         if (matchTrunkPrefix(a, ia + 1)
414             && matchIntlPrefixAndCC(b, ib +1)
415         ) {
416             return true;
417         }
418 
419         if (matchTrunkPrefix(b, ib + 1)
420             && matchIntlPrefixAndCC(a, ia +1)
421         ) {
422             return true;
423         }
424 
425         return false;
426     }
427 
428     /**
429      * @hide
430      */
431     public static boolean
compareStrictly(String a, String b)432     compareStrictly(String a, String b) {
433         return compareStrictly(a, b, true);
434     }
435 
436     /**
437      * @hide
438      */
439     public static boolean
compareStrictly(String a, String b, boolean acceptInvalidCCCPrefix)440     compareStrictly(String a, String b, boolean acceptInvalidCCCPrefix) {
441         if (a == null || b == null) {
442             return a == b;
443         } else if (a.length() == 0 && b.length() == 0) {
444             return false;
445         }
446 
447         int forwardIndexA = 0;
448         int forwardIndexB = 0;
449 
450         CountryCallingCodeAndNewIndex cccA =
451             tryGetCountryCallingCodeAndNewIndex(a, acceptInvalidCCCPrefix);
452         CountryCallingCodeAndNewIndex cccB =
453             tryGetCountryCallingCodeAndNewIndex(b, acceptInvalidCCCPrefix);
454         boolean bothHasCountryCallingCode = false;
455         boolean okToIgnorePrefix = true;
456         boolean trunkPrefixIsOmittedA = false;
457         boolean trunkPrefixIsOmittedB = false;
458         if (cccA != null && cccB != null) {
459             if (cccA.countryCallingCode != cccB.countryCallingCode) {
460                 // Different Country Calling Code. Must be different phone number.
461                 return false;
462             }
463             // When both have ccc, do not ignore trunk prefix. Without this,
464             // "+81123123" becomes same as "+810123123" (+81 == Japan)
465             okToIgnorePrefix = false;
466             bothHasCountryCallingCode = true;
467             forwardIndexA = cccA.newIndex;
468             forwardIndexB = cccB.newIndex;
469         } else if (cccA == null && cccB == null) {
470             // When both do not have ccc, do not ignore trunk prefix. Without this,
471             // "123123" becomes same as "0123123"
472             okToIgnorePrefix = false;
473         } else {
474             if (cccA != null) {
475                 forwardIndexA = cccA.newIndex;
476             } else {
477                 int tmp = tryGetTrunkPrefixOmittedIndex(b, 0);
478                 if (tmp >= 0) {
479                     forwardIndexA = tmp;
480                     trunkPrefixIsOmittedA = true;
481                 }
482             }
483             if (cccB != null) {
484                 forwardIndexB = cccB.newIndex;
485             } else {
486                 int tmp = tryGetTrunkPrefixOmittedIndex(b, 0);
487                 if (tmp >= 0) {
488                     forwardIndexB = tmp;
489                     trunkPrefixIsOmittedB = true;
490                 }
491             }
492         }
493 
494         int backwardIndexA = a.length() - 1;
495         int backwardIndexB = b.length() - 1;
496         while (backwardIndexA >= forwardIndexA && backwardIndexB >= forwardIndexB) {
497             boolean skip_compare = false;
498             final char chA = a.charAt(backwardIndexA);
499             final char chB = b.charAt(backwardIndexB);
500             if (isSeparator(chA)) {
501                 backwardIndexA--;
502                 skip_compare = true;
503             }
504             if (isSeparator(chB)) {
505                 backwardIndexB--;
506                 skip_compare = true;
507             }
508 
509             if (!skip_compare) {
510                 if (chA != chB) {
511                     return false;
512                 }
513                 backwardIndexA--;
514                 backwardIndexB--;
515             }
516         }
517 
518         if (okToIgnorePrefix) {
519             if ((trunkPrefixIsOmittedA && forwardIndexA <= backwardIndexA) ||
520                 !checkPrefixIsIgnorable(a, forwardIndexA, backwardIndexA)) {
521                 if (acceptInvalidCCCPrefix) {
522                     // Maybe the code handling the special case for Thailand makes the
523                     // result garbled, so disable the code and try again.
524                     // e.g. "16610001234" must equal to "6610001234", but with
525                     //      Thailand-case handling code, they become equal to each other.
526                     //
527                     // Note: we select simplicity rather than adding some complicated
528                     //       logic here for performance(like "checking whether remaining
529                     //       numbers are just 66 or not"), assuming inputs are small
530                     //       enough.
531                     return compare(a, b, false);
532                 } else {
533                     return false;
534                 }
535             }
536             if ((trunkPrefixIsOmittedB && forwardIndexB <= backwardIndexB) ||
537                 !checkPrefixIsIgnorable(b, forwardIndexA, backwardIndexB)) {
538                 if (acceptInvalidCCCPrefix) {
539                     return compare(a, b, false);
540                 } else {
541                     return false;
542                 }
543             }
544         } else {
545             // In the US, 1-650-555-1234 must be equal to 650-555-1234,
546             // while 090-1234-1234 must not be equalt to 90-1234-1234 in Japan.
547             // This request exists just in US (with 1 trunk (NDD) prefix).
548             // In addition, "011 11 7005554141" must not equal to "+17005554141",
549             // while "011 1 7005554141" must equal to "+17005554141"
550             //
551             // In this comparison, we ignore the prefix '1' just once, when
552             // - at least either does not have CCC, or
553             // - the remaining non-separator number is 1
554             boolean maybeNamp = !bothHasCountryCallingCode;
555             while (backwardIndexA >= forwardIndexA) {
556                 final char chA = a.charAt(backwardIndexA);
557                 if (isDialable(chA)) {
558                     if (maybeNamp && tryGetISODigit(chA) == 1) {
559                         maybeNamp = false;
560                     } else {
561                         return false;
562                     }
563                 }
564                 backwardIndexA--;
565             }
566             while (backwardIndexB >= forwardIndexB) {
567                 final char chB = b.charAt(backwardIndexB);
568                 if (isDialable(chB)) {
569                     if (maybeNamp && tryGetISODigit(chB) == 1) {
570                         maybeNamp = false;
571                     } else {
572                         return false;
573                     }
574                 }
575                 backwardIndexB--;
576             }
577         }
578 
579         return true;
580     }
581 
582     /**
583      * Returns the rightmost MIN_MATCH (5) characters in the network portion
584      * in *reversed* order
585      *
586      * This can be used to do a database lookup against the column
587      * that stores getStrippedReversed()
588      *
589      * Returns null if phoneNumber == null
590      */
591     public static String
toCallerIDMinMatch(String phoneNumber)592     toCallerIDMinMatch(String phoneNumber) {
593         String np = extractNetworkPortion(phoneNumber);
594         return internalGetStrippedReversed(np, MIN_MATCH);
595     }
596 
597     /**
598      * Returns the network portion reversed.
599      * This string is intended to go into an index column for a
600      * database lookup.
601      *
602      * Returns null if phoneNumber == null
603      */
604     public static String
getStrippedReversed(String phoneNumber)605     getStrippedReversed(String phoneNumber) {
606         String np = extractNetworkPortion(phoneNumber);
607 
608         if (np == null) return null;
609 
610         return internalGetStrippedReversed(np, np.length());
611     }
612 
613     /**
614      * Returns the last numDigits of the reversed phone number
615      * Returns null if np == null
616      */
617     private static String
internalGetStrippedReversed(String np, int numDigits)618     internalGetStrippedReversed(String np, int numDigits) {
619         if (np == null) return null;
620 
621         StringBuilder ret = new StringBuilder(numDigits);
622         int length = np.length();
623 
624         for (int i = length - 1, s = length
625             ; i >= 0 && (s - i) <= numDigits ; i--
626         ) {
627             char c = np.charAt(i);
628 
629             ret.append(c);
630         }
631 
632         return ret.toString();
633     }
634 
635     /**
636      * Basically: makes sure there's a + in front of a
637      * TOA_International number
638      *
639      * Returns null if s == null
640      */
641     public static String
stringFromStringAndTOA(String s, int TOA)642     stringFromStringAndTOA(String s, int TOA) {
643         if (s == null) return null;
644 
645         if (TOA == TOA_International && s.length() > 0 && s.charAt(0) != '+') {
646             return "+" + s;
647         }
648 
649         return s;
650     }
651 
652     /**
653      * Returns the TOA for the given dial string
654      * Basically, returns TOA_International if there's a + prefix
655      */
656 
657     public static int
toaFromString(String s)658     toaFromString(String s) {
659         if (s != null && s.length() > 0 && s.charAt(0) == '+') {
660             return TOA_International;
661         }
662 
663         return TOA_Unknown;
664     }
665 
666     /**
667      *  3GPP TS 24.008 10.5.4.7
668      *  Called Party BCD Number
669      *
670      *  See Also TS 51.011 10.5.1 "dialing number/ssc string"
671      *  and TS 11.11 "10.3.1 EF adn (Abbreviated dialing numbers)"
672      *
673      * @param bytes the data buffer
674      * @param offset should point to the TOA (aka. TON/NPI) octet after the length byte
675      * @param length is the number of bytes including TOA byte
676      *                and must be at least 2
677      *
678      * @return partial string on invalid decode
679      *
680      * FIXME(mkf) support alphanumeric address type
681      *  currently implemented in SMSMessage.getAddress()
682      */
683     public static String
calledPartyBCDToString(byte[] bytes, int offset, int length)684     calledPartyBCDToString (byte[] bytes, int offset, int length) {
685         boolean prependPlus = false;
686         StringBuilder ret = new StringBuilder(1 + length * 2);
687 
688         if (length < 2) {
689             return "";
690         }
691 
692         if ((bytes[offset] & 0xff) == TOA_International) {
693             prependPlus = true;
694         }
695 
696         internalCalledPartyBCDFragmentToString(
697                 ret, bytes, offset + 1, length - 1);
698 
699         if (prependPlus && ret.length() == 0) {
700             // If the only thing there is a prepended plus, return ""
701             return "";
702         }
703 
704         if (prependPlus) {
705             // This is an "international number" and should have
706             // a plus prepended to the dialing number. But there
707             // can also be Gsm MMI codes as defined in TS 22.030 6.5.2
708             // so we need to handle those also.
709             //
710             // http://web.telia.com/~u47904776/gsmkode.htm is a
711             // has a nice list of some of these GSM codes.
712             //
713             // Examples are:
714             //   **21*+886988171479#
715             //   **21*8311234567#
716             //   *21#
717             //   #21#
718             //   *#21#
719             //   *31#+11234567890
720             //   #31#+18311234567
721             //   #31#8311234567
722             //   18311234567
723             //   +18311234567#
724             //   +18311234567
725             // Odd ball cases that some phones handled
726             // where there is no dialing number so they
727             // append the "+"
728             //   *21#+
729             //   **21#+
730             String retString = ret.toString();
731             Pattern p = Pattern.compile("(^[#*])(.*)([#*])(.*)(#)$");
732             Matcher m = p.matcher(retString);
733             if (m.matches()) {
734                 if ("".equals(m.group(2))) {
735                     // Started with two [#*] ends with #
736                     // So no dialing number and we'll just
737                     // append a +, this handles **21#+
738                     ret = new StringBuilder();
739                     ret.append(m.group(1));
740                     ret.append(m.group(3));
741                     ret.append(m.group(4));
742                     ret.append(m.group(5));
743                     ret.append("+");
744                 } else {
745                     // Starts with [#*] and ends with #
746                     // Assume group 4 is a dialing number
747                     // such as *21*+1234554#
748                     ret = new StringBuilder();
749                     ret.append(m.group(1));
750                     ret.append(m.group(2));
751                     ret.append(m.group(3));
752                     ret.append("+");
753                     ret.append(m.group(4));
754                     ret.append(m.group(5));
755                 }
756             } else {
757                 p = Pattern.compile("(^[#*])(.*)([#*])(.*)");
758                 m = p.matcher(retString);
759                 if (m.matches()) {
760                     // Starts with [#*] and only one other [#*]
761                     // Assume the data after last [#*] is dialing
762                     // number (i.e. group 4) such as *31#+11234567890.
763                     // This also includes the odd ball *21#+
764                     ret = new StringBuilder();
765                     ret.append(m.group(1));
766                     ret.append(m.group(2));
767                     ret.append(m.group(3));
768                     ret.append("+");
769                     ret.append(m.group(4));
770                 } else {
771                     // Does NOT start with [#*] just prepend '+'
772                     ret = new StringBuilder();
773                     ret.append('+');
774                     ret.append(retString);
775                 }
776             }
777         }
778 
779         return ret.toString();
780     }
781 
782     private static void
internalCalledPartyBCDFragmentToString( StringBuilder sb, byte [] bytes, int offset, int length)783     internalCalledPartyBCDFragmentToString(
784         StringBuilder sb, byte [] bytes, int offset, int length) {
785         for (int i = offset ; i < length + offset ; i++) {
786             byte b;
787             char c;
788 
789             c = bcdToChar((byte)(bytes[i] & 0xf));
790 
791             if (c == 0) {
792                 return;
793             }
794             sb.append(c);
795 
796             // FIXME(mkf) TS 23.040 9.1.2.3 says
797             // "if a mobile receives 1111 in a position prior to
798             // the last semi-octet then processing shall commense with
799             // the next semi-octet and the intervening
800             // semi-octet shall be ignored"
801             // How does this jive with 24,008 10.5.4.7
802 
803             b = (byte)((bytes[i] >> 4) & 0xf);
804 
805             if (b == 0xf && i + 1 == length + offset) {
806                 //ignore final 0xf
807                 break;
808             }
809 
810             c = bcdToChar(b);
811             if (c == 0) {
812                 return;
813             }
814 
815             sb.append(c);
816         }
817 
818     }
819 
820     /**
821      * Like calledPartyBCDToString, but field does not start with a
822      * TOA byte. For example: SIM ADN extension fields
823      */
824 
825     public static String
calledPartyBCDFragmentToString(byte [] bytes, int offset, int length)826     calledPartyBCDFragmentToString(byte [] bytes, int offset, int length) {
827         StringBuilder ret = new StringBuilder(length * 2);
828 
829         internalCalledPartyBCDFragmentToString(ret, bytes, offset, length);
830 
831         return ret.toString();
832     }
833 
834     /** returns 0 on invalid value */
835     private static char
bcdToChar(byte b)836     bcdToChar(byte b) {
837         if (b < 0xa) {
838             return (char)('0' + b);
839         } else switch (b) {
840             case 0xa: return '*';
841             case 0xb: return '#';
842             case 0xc: return PAUSE;
843             case 0xd: return WILD;
844 
845             default: return 0;
846         }
847     }
848 
849     private static int
charToBCD(char c)850     charToBCD(char c) {
851         if (c >= '0' && c <= '9') {
852             return c - '0';
853         } else if (c == '*') {
854             return 0xa;
855         } else if (c == '#') {
856             return 0xb;
857         } else if (c == PAUSE) {
858             return 0xc;
859         } else if (c == WILD) {
860             return 0xd;
861         } else {
862             throw new RuntimeException ("invalid char for BCD " + c);
863         }
864     }
865 
866     /**
867      * Return true iff the network portion of <code>address</code> is,
868      * as far as we can tell on the device, suitable for use as an SMS
869      * destination address.
870      */
isWellFormedSmsAddress(String address)871     public static boolean isWellFormedSmsAddress(String address) {
872         String networkPortion =
873                 PhoneNumberUtils.extractNetworkPortion(address);
874 
875         return (!(networkPortion.equals("+")
876                   || TextUtils.isEmpty(networkPortion)))
877                && isDialable(networkPortion);
878     }
879 
isGlobalPhoneNumber(String phoneNumber)880     public static boolean isGlobalPhoneNumber(String phoneNumber) {
881         if (TextUtils.isEmpty(phoneNumber)) {
882             return false;
883         }
884 
885         Matcher match = GLOBAL_PHONE_NUMBER_PATTERN.matcher(phoneNumber);
886         return match.matches();
887     }
888 
isDialable(String address)889     private static boolean isDialable(String address) {
890         for (int i = 0, count = address.length(); i < count; i++) {
891             if (!isDialable(address.charAt(i))) {
892                 return false;
893             }
894         }
895         return true;
896     }
897 
isNonSeparator(String address)898     private static boolean isNonSeparator(String address) {
899         for (int i = 0, count = address.length(); i < count; i++) {
900             if (!isNonSeparator(address.charAt(i))) {
901                 return false;
902             }
903         }
904         return true;
905     }
906     /**
907      * Note: calls extractNetworkPortion(), so do not use for
908      * SIM EF[ADN] style records
909      *
910      * Returns null if network portion is empty.
911      */
912     public static byte[]
networkPortionToCalledPartyBCD(String s)913     networkPortionToCalledPartyBCD(String s) {
914         String networkPortion = extractNetworkPortion(s);
915         return numberToCalledPartyBCDHelper(networkPortion, false);
916     }
917 
918     /**
919      * Same as {@link #networkPortionToCalledPartyBCD}, but includes a
920      * one-byte length prefix.
921      */
922     public static byte[]
networkPortionToCalledPartyBCDWithLength(String s)923     networkPortionToCalledPartyBCDWithLength(String s) {
924         String networkPortion = extractNetworkPortion(s);
925         return numberToCalledPartyBCDHelper(networkPortion, true);
926     }
927 
928     /**
929      * Convert a dialing number to BCD byte array
930      *
931      * @param number dialing number string
932      *        if the dialing number starts with '+', set to internationl TOA
933      * @return BCD byte array
934      */
935     public static byte[]
numberToCalledPartyBCD(String number)936     numberToCalledPartyBCD(String number) {
937         return numberToCalledPartyBCDHelper(number, false);
938     }
939 
940     /**
941      * If includeLength is true, prepend a one-byte length value to
942      * the return array.
943      */
944     private static byte[]
numberToCalledPartyBCDHelper(String number, boolean includeLength)945     numberToCalledPartyBCDHelper(String number, boolean includeLength) {
946         int numberLenReal = number.length();
947         int numberLenEffective = numberLenReal;
948         boolean hasPlus = number.indexOf('+') != -1;
949         if (hasPlus) numberLenEffective--;
950 
951         if (numberLenEffective == 0) return null;
952 
953         int resultLen = (numberLenEffective + 1) / 2;  // Encoded numbers require only 4 bits each.
954         int extraBytes = 1;                            // Prepended TOA byte.
955         if (includeLength) extraBytes++;               // Optional prepended length byte.
956         resultLen += extraBytes;
957 
958         byte[] result = new byte[resultLen];
959 
960         int digitCount = 0;
961         for (int i = 0; i < numberLenReal; i++) {
962             char c = number.charAt(i);
963             if (c == '+') continue;
964             int shift = ((digitCount & 0x01) == 1) ? 4 : 0;
965             result[extraBytes + (digitCount >> 1)] |= (byte)((charToBCD(c) & 0x0F) << shift);
966             digitCount++;
967         }
968 
969         // 1-fill any trailing odd nibble/quartet.
970         if ((digitCount & 0x01) == 1) result[extraBytes + (digitCount >> 1)] |= 0xF0;
971 
972         int offset = 0;
973         if (includeLength) result[offset++] = (byte)(resultLen - 1);
974         result[offset] = (byte)(hasPlus ? TOA_International : TOA_Unknown);
975 
976         return result;
977     }
978 
979     //================ Number formatting =========================
980 
981     /** The current locale is unknown, look for a country code or don't format */
982     public static final int FORMAT_UNKNOWN = 0;
983     /** NANP formatting */
984     public static final int FORMAT_NANP = 1;
985     /** Japanese formatting */
986     public static final int FORMAT_JAPAN = 2;
987 
988     /** List of country codes for countries that use the NANP */
989     private static final String[] NANP_COUNTRIES = new String[] {
990         "US", // United States
991         "CA", // Canada
992         "AS", // American Samoa
993         "AI", // Anguilla
994         "AG", // Antigua and Barbuda
995         "BS", // Bahamas
996         "BB", // Barbados
997         "BM", // Bermuda
998         "VG", // British Virgin Islands
999         "KY", // Cayman Islands
1000         "DM", // Dominica
1001         "DO", // Dominican Republic
1002         "GD", // Grenada
1003         "GU", // Guam
1004         "JM", // Jamaica
1005         "PR", // Puerto Rico
1006         "MS", // Montserrat
1007         "MP", // Northern Mariana Islands
1008         "KN", // Saint Kitts and Nevis
1009         "LC", // Saint Lucia
1010         "VC", // Saint Vincent and the Grenadines
1011         "TT", // Trinidad and Tobago
1012         "TC", // Turks and Caicos Islands
1013         "VI", // U.S. Virgin Islands
1014     };
1015 
1016     /**
1017      * Breaks the given number down and formats it according to the rules
1018      * for the country the number is from.
1019      *
1020      * @param source the phone number to format
1021      * @return a locally acceptable formatting of the input, or the raw input if
1022      *  formatting rules aren't known for the number
1023      */
formatNumber(String source)1024     public static String formatNumber(String source) {
1025         SpannableStringBuilder text = new SpannableStringBuilder(source);
1026         formatNumber(text, getFormatTypeForLocale(Locale.getDefault()));
1027         return text.toString();
1028     }
1029 
1030     /**
1031      * Returns the phone number formatting type for the given locale.
1032      *
1033      * @param locale The locale of interest, usually {@link Locale#getDefault()}
1034      * @return the formatting type for the given locale, or FORMAT_UNKNOWN if the formatting
1035      * rules are not known for the given locale
1036      */
getFormatTypeForLocale(Locale locale)1037     public static int getFormatTypeForLocale(Locale locale) {
1038         String country = locale.getCountry();
1039 
1040         return getFormatTypeFromCountryCode(country);
1041     }
1042 
1043     /**
1044      * Formats a phone number in-place. Currently only supports NANP formatting.
1045      *
1046      * @param text The number to be formatted, will be modified with the formatting
1047      * @param defaultFormattingType The default formatting rules to apply if the number does
1048      * not begin with +<country_code>
1049      */
formatNumber(Editable text, int defaultFormattingType)1050     public static void formatNumber(Editable text, int defaultFormattingType) {
1051         int formatType = defaultFormattingType;
1052 
1053         if (text.length() > 2 && text.charAt(0) == '+') {
1054             if (text.charAt(1) == '1') {
1055                 formatType = FORMAT_NANP;
1056             } else if (text.length() >= 3 && text.charAt(1) == '8'
1057                 && text.charAt(2) == '1') {
1058                 formatType = FORMAT_JAPAN;
1059             } else {
1060                 return;
1061             }
1062         }
1063 
1064         switch (formatType) {
1065             case FORMAT_NANP:
1066                 formatNanpNumber(text);
1067                 return;
1068             case FORMAT_JAPAN:
1069                 formatJapaneseNumber(text);
1070                 return;
1071         }
1072     }
1073 
1074     private static final int NANP_STATE_DIGIT = 1;
1075     private static final int NANP_STATE_PLUS = 2;
1076     private static final int NANP_STATE_ONE = 3;
1077     private static final int NANP_STATE_DASH = 4;
1078 
1079     /**
1080      * Formats a phone number in-place using the NANP formatting rules. Numbers will be formatted
1081      * as:
1082      *
1083      * <p><code>
1084      * xxxxx
1085      * xxx-xxxx
1086      * xxx-xxx-xxxx
1087      * 1-xxx-xxx-xxxx
1088      * +1-xxx-xxx-xxxx
1089      * </code></p>
1090      *
1091      * @param text the number to be formatted, will be modified with the formatting
1092      */
formatNanpNumber(Editable text)1093     public static void formatNanpNumber(Editable text) {
1094         int length = text.length();
1095         if (length > "+1-nnn-nnn-nnnn".length()) {
1096             // The string is too long to be formatted
1097             return;
1098         } else if (length <= 5) {
1099             // The string is either a shortcode or too short to be formatted
1100             return;
1101         }
1102 
1103         CharSequence saved = text.subSequence(0, length);
1104 
1105         // Strip the dashes first, as we're going to add them back
1106         int p = 0;
1107         while (p < text.length()) {
1108             if (text.charAt(p) == '-') {
1109                 text.delete(p, p + 1);
1110             } else {
1111                 p++;
1112             }
1113         }
1114         length = text.length();
1115 
1116         // When scanning the number we record where dashes need to be added,
1117         // if they're non-0 at the end of the scan the dashes will be added in
1118         // the proper places.
1119         int dashPositions[] = new int[3];
1120         int numDashes = 0;
1121 
1122         int state = NANP_STATE_DIGIT;
1123         int numDigits = 0;
1124         for (int i = 0; i < length; i++) {
1125             char c = text.charAt(i);
1126             switch (c) {
1127                 case '1':
1128                     if (numDigits == 0 || state == NANP_STATE_PLUS) {
1129                         state = NANP_STATE_ONE;
1130                         break;
1131                     }
1132                     // fall through
1133                 case '2':
1134                 case '3':
1135                 case '4':
1136                 case '5':
1137                 case '6':
1138                 case '7':
1139                 case '8':
1140                 case '9':
1141                 case '0':
1142                     if (state == NANP_STATE_PLUS) {
1143                         // Only NANP number supported for now
1144                         text.replace(0, length, saved);
1145                         return;
1146                     } else if (state == NANP_STATE_ONE) {
1147                         // Found either +1 or 1, follow it up with a dash
1148                         dashPositions[numDashes++] = i;
1149                     } else if (state != NANP_STATE_DASH && (numDigits == 3 || numDigits == 6)) {
1150                         // Found a digit that should be after a dash that isn't
1151                         dashPositions[numDashes++] = i;
1152                     }
1153                     state = NANP_STATE_DIGIT;
1154                     numDigits++;
1155                     break;
1156 
1157                 case '-':
1158                     state = NANP_STATE_DASH;
1159                     break;
1160 
1161                 case '+':
1162                     if (i == 0) {
1163                         // Plus is only allowed as the first character
1164                         state = NANP_STATE_PLUS;
1165                         break;
1166                     }
1167                     // Fall through
1168                 default:
1169                     // Unknown character, bail on formatting
1170                     text.replace(0, length, saved);
1171                     return;
1172             }
1173         }
1174 
1175         if (numDigits == 7) {
1176             // With 7 digits we want xxx-xxxx, not xxx-xxx-x
1177             numDashes--;
1178         }
1179 
1180         // Actually put the dashes in place
1181         for (int i = 0; i < numDashes; i++) {
1182             int pos = dashPositions[i];
1183             text.replace(pos + i, pos + i, "-");
1184         }
1185 
1186         // Remove trailing dashes
1187         int len = text.length();
1188         while (len > 0) {
1189             if (text.charAt(len - 1) == '-') {
1190                 text.delete(len - 1, len);
1191                 len--;
1192             } else {
1193                 break;
1194             }
1195         }
1196     }
1197 
1198     /**
1199      * Formats a phone number in-place using the Japanese formatting rules.
1200      * Numbers will be formatted as:
1201      *
1202      * <p><code>
1203      * 03-xxxx-xxxx
1204      * 090-xxxx-xxxx
1205      * 0120-xxx-xxx
1206      * +81-3-xxxx-xxxx
1207      * +81-90-xxxx-xxxx
1208      * </code></p>
1209      *
1210      * @param text the number to be formatted, will be modified with
1211      * the formatting
1212      */
formatJapaneseNumber(Editable text)1213     public static void formatJapaneseNumber(Editable text) {
1214         JapanesePhoneNumberFormatter.format(text);
1215     }
1216 
1217     // Three and four digit phone numbers for either special services,
1218     // or 3-6 digit addresses from the network (eg carrier-originated SMS messages) should
1219     // not match.
1220     //
1221     // This constant used to be 5, but SMS short codes has increased in length and
1222     // can be easily 6 digits now days. Most countries have SMS short code length between
1223     // 3 to 6 digits. The exceptions are
1224     //
1225     // Australia: Short codes are six or eight digits in length, starting with the prefix "19"
1226     //            followed by an additional four or six digits and two.
1227     // Czech Republic: Codes are seven digits in length for MO and five (not billed) or
1228     //            eight (billed) for MT direction
1229     //
1230     // see http://en.wikipedia.org/wiki/Short_code#Regional_differences for reference
1231     //
1232     // However, in order to loose match 650-555-1212 and 555-1212, we need to set the min match
1233     // to 7.
1234     static final int MIN_MATCH = 7;
1235 
1236     /**
1237      * isEmergencyNumber: checks a given number against the list of
1238      *   emergency numbers provided by the RIL and SIM card.
1239      *
1240      * @param number the number to look up.
1241      * @return if the number is in the list of emergency numbers
1242      * listed in the ril / sim, then return true, otherwise false.
1243      */
isEmergencyNumber(String number)1244     public static boolean isEmergencyNumber(String number) {
1245         // If the number passed in is null, just return false:
1246         if (number == null) return false;
1247 
1248         // Strip the separators from the number before comparing it
1249         // to the list.
1250         number = extractNetworkPortion(number);
1251 
1252         // retrieve the list of emergency numbers
1253         String numbers = SystemProperties.get("ro.ril.ecclist");
1254 
1255         if (!TextUtils.isEmpty(numbers)) {
1256             // searches through the comma-separated list for a match,
1257             // return true if one is found.
1258             for (String emergencyNum : numbers.split(",")) {
1259                 if (emergencyNum.equals(number)) {
1260                     return true;
1261                 }
1262             }
1263             // no matches found against the list!
1264             return false;
1265         }
1266 
1267         //no ecclist system property, so use our own list.
1268         return (number.equals("112") || number.equals("911"));
1269     }
1270 
1271     /**
1272      * isVoiceMailNumber: checks a given number against the voicemail
1273      *   number provided by the RIL and SIM card. The caller must have
1274      *   the READ_PHONE_STATE credential.
1275      *
1276      * @param number the number to look up.
1277      * @return true if the number is in the list of voicemail. False
1278      * otherwise, including if the caller does not have the permission
1279      * to read the VM number.
1280      * @hide TODO: pending API Council approval
1281      */
isVoiceMailNumber(String number)1282     public static boolean isVoiceMailNumber(String number) {
1283         String vmNumber;
1284 
1285         try {
1286             vmNumber = TelephonyManager.getDefault().getVoiceMailNumber();
1287         } catch (SecurityException ex) {
1288             return false;
1289         }
1290 
1291         // Strip the separators from the number before comparing it
1292         // to the list.
1293         number = extractNetworkPortion(number);
1294 
1295         // compare tolerates null so we need to make sure that we
1296         // don't return true when both are null.
1297         return !TextUtils.isEmpty(number) && compare(number, vmNumber);
1298     }
1299 
1300     /**
1301      * Translates any alphabetic letters (i.e. [A-Za-z]) in the
1302      * specified phone number into the equivalent numeric digits,
1303      * according to the phone keypad letter mapping described in
1304      * ITU E.161 and ISO/IEC 9995-8.
1305      *
1306      * @return the input string, with alpha letters converted to numeric
1307      *         digits using the phone keypad letter mapping.  For example,
1308      *         an input of "1-800-GOOG-411" will return "1-800-4664-411".
1309      */
convertKeypadLettersToDigits(String input)1310     public static String convertKeypadLettersToDigits(String input) {
1311         if (input == null) {
1312             return input;
1313         }
1314         int len = input.length();
1315         if (len == 0) {
1316             return input;
1317         }
1318 
1319         char[] out = input.toCharArray();
1320 
1321         for (int i = 0; i < len; i++) {
1322             char c = out[i];
1323             // If this char isn't in KEYPAD_MAP at all, just leave it alone.
1324             out[i] = (char) KEYPAD_MAP.get(c, c);
1325         }
1326 
1327         return new String(out);
1328     }
1329 
1330     /**
1331      * The phone keypad letter mapping (see ITU E.161 or ISO/IEC 9995-8.)
1332      * TODO: This should come from a resource.
1333      */
1334     private static final SparseIntArray KEYPAD_MAP = new SparseIntArray();
1335     static {
1336         KEYPAD_MAP.put('a', '2'); KEYPAD_MAP.put('b', '2'); KEYPAD_MAP.put('c', '2');
1337         KEYPAD_MAP.put('A', '2'); KEYPAD_MAP.put('B', '2'); KEYPAD_MAP.put('C', '2');
1338 
1339         KEYPAD_MAP.put('d', '3'); KEYPAD_MAP.put('e', '3'); KEYPAD_MAP.put('f', '3');
1340         KEYPAD_MAP.put('D', '3'); KEYPAD_MAP.put('E', '3'); KEYPAD_MAP.put('F', '3');
1341 
1342         KEYPAD_MAP.put('g', '4'); KEYPAD_MAP.put('h', '4'); KEYPAD_MAP.put('i', '4');
1343         KEYPAD_MAP.put('G', '4'); KEYPAD_MAP.put('H', '4'); KEYPAD_MAP.put('I', '4');
1344 
1345         KEYPAD_MAP.put('j', '5'); KEYPAD_MAP.put('k', '5'); KEYPAD_MAP.put('l', '5');
1346         KEYPAD_MAP.put('J', '5'); KEYPAD_MAP.put('K', '5'); KEYPAD_MAP.put('L', '5');
1347 
1348         KEYPAD_MAP.put('m', '6'); KEYPAD_MAP.put('n', '6'); KEYPAD_MAP.put('o', '6');
1349         KEYPAD_MAP.put('M', '6'); KEYPAD_MAP.put('N', '6'); KEYPAD_MAP.put('O', '6');
1350 
1351         KEYPAD_MAP.put('p', '7'); KEYPAD_MAP.put('q', '7'); KEYPAD_MAP.put('r', '7'); KEYPAD_MAP.put('s', '7');
1352         KEYPAD_MAP.put('P', '7'); KEYPAD_MAP.put('Q', '7'); KEYPAD_MAP.put('R', '7'); KEYPAD_MAP.put('S', '7');
1353 
1354         KEYPAD_MAP.put('t', '8'); KEYPAD_MAP.put('u', '8'); KEYPAD_MAP.put('v', '8');
1355         KEYPAD_MAP.put('T', '8'); KEYPAD_MAP.put('U', '8'); KEYPAD_MAP.put('V', '8');
1356 
1357         KEYPAD_MAP.put('w', '9'); KEYPAD_MAP.put('x', '9'); KEYPAD_MAP.put('y', '9'); KEYPAD_MAP.put('z', '9');
1358         KEYPAD_MAP.put('W', '9'); KEYPAD_MAP.put('X', '9'); KEYPAD_MAP.put('Y', '9'); KEYPAD_MAP.put('Z', '9');
1359     }
1360 
1361     //================ Plus Code formatting =========================
1362     private static final char PLUS_SIGN_CHAR = '+';
1363     private static final String PLUS_SIGN_STRING = "+";
1364     private static final String NANP_IDP_STRING = "011";
1365     private static final int NANP_LENGTH = 10;
1366 
1367     /**
1368      * This function checks if there is a plus sign (+) in the passed-in dialing number.
1369      * If there is, it processes the plus sign based on the default telephone
1370      * numbering plan of the system when the phone is activated and the current
1371      * telephone numbering plan of the system that the phone is camped on.
1372      * Currently, we only support the case that the default and current telephone
1373      * numbering plans are North American Numbering Plan(NANP).
1374      *
1375      * The passed-in dialStr should only contain the valid format as described below,
1376      * 1) the 1st character in the dialStr should be one of the really dialable
1377      *    characters listed below
1378      *    ISO-LATIN characters 0-9, *, # , +
1379      * 2) the dialStr should already strip out the separator characters,
1380      *    every character in the dialStr should be one of the non separator characters
1381      *    listed below
1382      *    ISO-LATIN characters 0-9, *, # , +, WILD, WAIT, PAUSE
1383      *
1384      * Otherwise, this function returns the dial string passed in
1385      *
1386      * @param dialStr the original dial string
1387      * @return the converted dial string if the current/default countries belong to NANP,
1388      * and if there is the "+" in the original dial string. Otherwise, the original dial
1389      * string returns.
1390      *
1391      * This API is for CDMA only
1392      *
1393      * @hide TODO: pending API Council approval
1394      */
cdmaCheckAndProcessPlusCode(String dialStr)1395     public static String cdmaCheckAndProcessPlusCode(String dialStr) {
1396         if (!TextUtils.isEmpty(dialStr)) {
1397             if (isReallyDialable(dialStr.charAt(0)) &&
1398                 isNonSeparator(dialStr)) {
1399                 String currIso = SystemProperties.get(PROPERTY_OPERATOR_ISO_COUNTRY, "");
1400                 String defaultIso = SystemProperties.get(PROPERTY_ICC_OPERATOR_ISO_COUNTRY, "");
1401                 if (!TextUtils.isEmpty(currIso) && !TextUtils.isEmpty(defaultIso)) {
1402                     return cdmaCheckAndProcessPlusCodeByNumberFormat(dialStr,
1403                             getFormatTypeFromCountryCode(currIso),
1404                             getFormatTypeFromCountryCode(defaultIso));
1405                 }
1406             }
1407         }
1408         return dialStr;
1409     }
1410 
1411     /**
1412      * This function should be called from checkAndProcessPlusCode only
1413      * And it is used for test purpose also.
1414      *
1415      * It checks the dial string by looping through the network portion,
1416      * post dial portion 1, post dial porting 2, etc. If there is any
1417      * plus sign, then process the plus sign.
1418      * Currently, this function supports the plus sign conversion within NANP only.
1419      * Specifically, it handles the plus sign in the following ways:
1420      * 1)+1NANP,remove +, e.g.
1421      *   +18475797000 is converted to 18475797000,
1422      * 2)+NANP or +non-NANP Numbers,replace + with the current NANP IDP, e.g,
1423      *   +8475797000 is converted to 0118475797000,
1424      *   +11875767800 is converted to 01111875767800
1425      * 3)+1NANP in post dial string(s), e.g.
1426      *   8475797000;+18475231753 is converted to 8475797000;18475231753
1427      *
1428      *
1429      * @param dialStr the original dial string
1430      * @param currFormat the numbering system of the current country that the phone is camped on
1431      * @param defaultFormat the numbering system of the country that the phone is activated on
1432      * @return the converted dial string if the current/default countries belong to NANP,
1433      * and if there is the "+" in the original dial string. Otherwise, the original dial
1434      * string returns.
1435      *
1436      * @hide
1437      */
1438     public static String
cdmaCheckAndProcessPlusCodeByNumberFormat(String dialStr,int currFormat,int defaultFormt)1439     cdmaCheckAndProcessPlusCodeByNumberFormat(String dialStr,int currFormat,int defaultFormt) {
1440         String retStr = dialStr;
1441 
1442         // Checks if the plus sign character is in the passed-in dial string
1443         if (dialStr != null &&
1444             dialStr.lastIndexOf(PLUS_SIGN_STRING) != -1) {
1445             // Format the string based on the rules for the country the number is from,
1446             // and the current country the phone is camped on.
1447             if ((currFormat == defaultFormt) && (currFormat == FORMAT_NANP)) {
1448                 // Handle case where default and current telephone numbering plans are NANP.
1449                 String postDialStr = null;
1450                 String tempDialStr = dialStr;
1451 
1452                 // Sets the retStr to null since the conversion will be performed below.
1453                 retStr = null;
1454                 if (DBG) log("checkAndProcessPlusCode,dialStr=" + dialStr);
1455                 // This routine is to process the plus sign in the dial string by loop through
1456                 // the network portion, post dial portion 1, post dial portion 2... etc. if
1457                 // applied
1458                 do {
1459                     String networkDialStr;
1460                     networkDialStr = extractNetworkPortion(tempDialStr);
1461                     // Handles the conversion within NANP
1462                     networkDialStr = processPlusCodeWithinNanp(networkDialStr);
1463 
1464                     // Concatenates the string that is converted from network portion
1465                     if (!TextUtils.isEmpty(networkDialStr)) {
1466                         if (retStr == null) {
1467                             retStr = networkDialStr;
1468                         } else {
1469                             retStr = retStr.concat(networkDialStr);
1470                         }
1471                     } else {
1472                         // This should never happen since we checked the if dialStr is null
1473                         // and if it contains the plus sign in the beginning of this function.
1474                         // The plus sign is part of the network portion.
1475                         Log.e("checkAndProcessPlusCode: null newDialStr", networkDialStr);
1476                         return dialStr;
1477                     }
1478                     postDialStr = extractPostDialPortion(tempDialStr);
1479                     if (!TextUtils.isEmpty(postDialStr)) {
1480                         int dialableIndex = findDialableIndexFromPostDialStr(postDialStr);
1481 
1482                         // dialableIndex should always be greater than 0
1483                         if (dialableIndex >= 1) {
1484                             retStr = appendPwCharBackToOrigDialStr(dialableIndex,
1485                                      retStr,postDialStr);
1486                             // Skips the P/W character, extracts the dialable portion
1487                             tempDialStr = postDialStr.substring(dialableIndex);
1488                         } else {
1489                             // Non-dialable character such as P/W should not be at the end of
1490                             // the dial string after P/W processing in CdmaConnection.java
1491                             // Set the postDialStr to "" to break out of the loop
1492                             if (dialableIndex < 0) {
1493                                 postDialStr = "";
1494                             }
1495                             Log.e("wrong postDialStr=", postDialStr);
1496                         }
1497                     }
1498                     if (DBG) log("checkAndProcessPlusCode,postDialStr=" + postDialStr);
1499                 } while (!TextUtils.isEmpty(postDialStr) && !TextUtils.isEmpty(tempDialStr));
1500             } else {
1501                 // TODO: Support NANP international conversion and other telephone numbering plans.
1502                 // Currently the phone is never used in non-NANP system, so return the original
1503                 // dial string.
1504                 Log.e("checkAndProcessPlusCode:non-NANP not supported", dialStr);
1505             }
1506         }
1507         return retStr;
1508      }
1509 
1510     // This function gets the default international dialing prefix
getDefaultIdp( )1511     private static String getDefaultIdp( ) {
1512         String ps = null;
1513         SystemProperties.get(PROPERTY_IDP_STRING, ps);
1514         if (TextUtils.isEmpty(ps)) {
1515             ps = NANP_IDP_STRING;
1516         }
1517         return ps;
1518     }
1519 
isTwoToNine(char c)1520     private static boolean isTwoToNine (char c) {
1521         if (c >= '2' && c <= '9') {
1522             return true;
1523         } else {
1524             return false;
1525         }
1526     }
1527 
getFormatTypeFromCountryCode(String country)1528     private static int getFormatTypeFromCountryCode (String country) {
1529         // Check for the NANP countries
1530         int length = NANP_COUNTRIES.length;
1531         for (int i = 0; i < length; i++) {
1532             if (NANP_COUNTRIES[i].compareToIgnoreCase(country) == 0) {
1533                 return FORMAT_NANP;
1534             }
1535         }
1536         if ("jp".compareToIgnoreCase(country) == 0) {
1537             return FORMAT_JAPAN;
1538         }
1539         return FORMAT_UNKNOWN;
1540     }
1541 
1542     /**
1543      * This function checks if the passed in string conforms to the NANP format
1544      * i.e. NXX-NXX-XXXX, N is any digit 2-9 and X is any digit 0-9
1545      */
isNanp(String dialStr)1546     private static boolean isNanp (String dialStr) {
1547         boolean retVal = false;
1548         if (dialStr != null) {
1549             if (dialStr.length() == NANP_LENGTH) {
1550                 if (isTwoToNine(dialStr.charAt(0)) &&
1551                     isTwoToNine(dialStr.charAt(3))) {
1552                     retVal = true;
1553                     for (int i=1; i<NANP_LENGTH; i++ ) {
1554                         char c=dialStr.charAt(i);
1555                         if (!PhoneNumberUtils.isISODigit(c)) {
1556                             retVal = false;
1557                             break;
1558                         }
1559                     }
1560                 }
1561             }
1562         } else {
1563             Log.e("isNanp: null dialStr passed in", dialStr);
1564         }
1565         return retVal;
1566     }
1567 
1568    /**
1569     * This function checks if the passed in string conforms to 1-NANP format
1570     */
isOneNanp(String dialStr)1571     private static boolean isOneNanp(String dialStr) {
1572         boolean retVal = false;
1573         if (dialStr != null) {
1574             String newDialStr = dialStr.substring(1);
1575             if ((dialStr.charAt(0) == '1') && isNanp(newDialStr)) {
1576                 retVal = true;
1577             }
1578         } else {
1579             Log.e("isOneNanp: null dialStr passed in", dialStr);
1580         }
1581         return retVal;
1582     }
1583 
1584     /**
1585      * This function handles the plus code conversion within NANP CDMA network
1586      * If the number format is
1587      * 1)+1NANP,remove +,
1588      * 2)other than +1NANP, any + numbers,replace + with the current IDP
1589      */
processPlusCodeWithinNanp(String networkDialStr)1590     private static String processPlusCodeWithinNanp(String networkDialStr) {
1591         String retStr = networkDialStr;
1592 
1593         if (DBG) log("processPlusCodeWithinNanp,networkDialStr=" + networkDialStr);
1594         // If there is a plus sign at the beginning of the dial string,
1595         // Convert the plus sign to the default IDP since it's an international number
1596         if (networkDialStr != null &
1597             networkDialStr.charAt(0) == PLUS_SIGN_CHAR &&
1598             networkDialStr.length() > 1) {
1599             String newStr = networkDialStr.substring(1);
1600             if (isOneNanp(newStr)) {
1601                 // Remove the leading plus sign
1602                 retStr = newStr;
1603              } else {
1604                  String idpStr = getDefaultIdp();
1605                  // Replaces the plus sign with the default IDP
1606                  retStr = networkDialStr.replaceFirst("[+]", idpStr);
1607             }
1608         }
1609         if (DBG) log("processPlusCodeWithinNanp,retStr=" + retStr);
1610         return retStr;
1611     }
1612 
1613     // This function finds the index of the dialable character(s)
1614     // in the post dial string
findDialableIndexFromPostDialStr(String postDialStr)1615     private static int findDialableIndexFromPostDialStr(String postDialStr) {
1616         for (int index = 0;index < postDialStr.length();index++) {
1617              char c = postDialStr.charAt(index);
1618              if (isReallyDialable(c)) {
1619                 return index;
1620              }
1621         }
1622         return -1;
1623     }
1624 
1625     // This function appends the non-diablable P/W character to the original
1626     // dial string based on the dialable index passed in
1627     private static String
appendPwCharBackToOrigDialStr(int dialableIndex,String origStr, String dialStr)1628     appendPwCharBackToOrigDialStr(int dialableIndex,String origStr, String dialStr) {
1629         String retStr;
1630 
1631         // There is only 1 P/W character before the dialable characters
1632         if (dialableIndex == 1) {
1633             StringBuilder ret = new StringBuilder(origStr);
1634             ret = ret.append(dialStr.charAt(0));
1635             retStr = ret.toString();
1636         } else {
1637             // It means more than 1 P/W characters in the post dial string,
1638             // appends to retStr
1639             String nonDigitStr = dialStr.substring(0,dialableIndex);
1640             retStr = origStr.concat(nonDigitStr);
1641         }
1642         return retStr;
1643     }
1644 
1645     //===== Begining of utility methods used in compareLoosely() =====
1646 
1647     /**
1648      * Phone numbers are stored in "lookup" form in the database
1649      * as reversed strings to allow for caller ID lookup
1650      *
1651      * This method takes a phone number and makes a valid SQL "LIKE"
1652      * string that will match the lookup form
1653      *
1654      */
1655     /** all of a up to len must be an international prefix or
1656      *  separators/non-dialing digits
1657      */
1658     private static boolean
matchIntlPrefix(String a, int len)1659     matchIntlPrefix(String a, int len) {
1660         /* '([^0-9*#+pwn]\+[^0-9*#+pwn] | [^0-9*#+pwn]0(0|11)[^0-9*#+pwn] )$' */
1661         /*        0       1                           2 3 45               */
1662 
1663         int state = 0;
1664         for (int i = 0 ; i < len ; i++) {
1665             char c = a.charAt(i);
1666 
1667             switch (state) {
1668                 case 0:
1669                     if      (c == '+') state = 1;
1670                     else if (c == '0') state = 2;
1671                     else if (isNonSeparator(c)) return false;
1672                 break;
1673 
1674                 case 2:
1675                     if      (c == '0') state = 3;
1676                     else if (c == '1') state = 4;
1677                     else if (isNonSeparator(c)) return false;
1678                 break;
1679 
1680                 case 4:
1681                     if      (c == '1') state = 5;
1682                     else if (isNonSeparator(c)) return false;
1683                 break;
1684 
1685                 default:
1686                     if (isNonSeparator(c)) return false;
1687                 break;
1688 
1689             }
1690         }
1691 
1692         return state == 1 || state == 3 || state == 5;
1693     }
1694 
1695     /** all of 'a' up to len must be a (+|00|011)country code)
1696      *  We're fast and loose with the country code. Any \d{1,3} matches */
1697     private static boolean
matchIntlPrefixAndCC(String a, int len)1698     matchIntlPrefixAndCC(String a, int len) {
1699         /*  [^0-9*#+pwn]*(\+|0(0|11)\d\d?\d? [^0-9*#+pwn] $ */
1700         /*      0          1 2 3 45  6 7  8                 */
1701 
1702         int state = 0;
1703         for (int i = 0 ; i < len ; i++ ) {
1704             char c = a.charAt(i);
1705 
1706             switch (state) {
1707                 case 0:
1708                     if      (c == '+') state = 1;
1709                     else if (c == '0') state = 2;
1710                     else if (isNonSeparator(c)) return false;
1711                 break;
1712 
1713                 case 2:
1714                     if      (c == '0') state = 3;
1715                     else if (c == '1') state = 4;
1716                     else if (isNonSeparator(c)) return false;
1717                 break;
1718 
1719                 case 4:
1720                     if      (c == '1') state = 5;
1721                     else if (isNonSeparator(c)) return false;
1722                 break;
1723 
1724                 case 1:
1725                 case 3:
1726                 case 5:
1727                     if      (isISODigit(c)) state = 6;
1728                     else if (isNonSeparator(c)) return false;
1729                 break;
1730 
1731                 case 6:
1732                 case 7:
1733                     if      (isISODigit(c)) state++;
1734                     else if (isNonSeparator(c)) return false;
1735                 break;
1736 
1737                 default:
1738                     if (isNonSeparator(c)) return false;
1739             }
1740         }
1741 
1742         return state == 6 || state == 7 || state == 8;
1743     }
1744 
1745     /** all of 'a' up to len must match non-US trunk prefix ('0') */
1746     private static boolean
matchTrunkPrefix(String a, int len)1747     matchTrunkPrefix(String a, int len) {
1748         boolean found;
1749 
1750         found = false;
1751 
1752         for (int i = 0 ; i < len ; i++) {
1753             char c = a.charAt(i);
1754 
1755             if (c == '0' && !found) {
1756                 found = true;
1757             } else if (isNonSeparator(c)) {
1758                 return false;
1759             }
1760         }
1761 
1762         return found;
1763     }
1764 
1765     //===== End of utility methods used only in compareLoosely() =====
1766 
1767     //===== Beggining of utility methods used only in compareStrictly() ====
1768 
1769     /*
1770      * If true, the number is country calling code.
1771      */
1772     private static final boolean COUNTLY_CALLING_CALL[] = {
1773         true, true, false, false, false, false, false, true, false, false,
1774         false, false, false, false, false, false, false, false, false, false,
1775         true, false, false, false, false, false, false, true, true, false,
1776         true, true, true, true, true, false, true, false, false, true,
1777         true, false, false, true, true, true, true, true, true, true,
1778         false, true, true, true, true, true, true, true, true, false,
1779         true, true, true, true, true, true, true, false, false, false,
1780         false, false, false, false, false, false, false, false, false, false,
1781         false, true, true, true, true, false, true, false, false, true,
1782         true, true, true, true, true, true, false, false, true, false,
1783     };
1784     private static final int CCC_LENGTH = COUNTLY_CALLING_CALL.length;
1785 
1786     /**
1787      * @return true when input is valid Country Calling Code.
1788      */
isCountryCallingCode(int countryCallingCodeCandidate)1789     private static boolean isCountryCallingCode(int countryCallingCodeCandidate) {
1790         return countryCallingCodeCandidate > 0 && countryCallingCodeCandidate < CCC_LENGTH &&
1791                 COUNTLY_CALLING_CALL[countryCallingCodeCandidate];
1792     }
1793 
1794     /**
1795      * Returns interger corresponding to the input if input "ch" is
1796      * ISO-LATIN characters 0-9.
1797      * Returns -1 otherwise
1798      */
tryGetISODigit(char ch)1799     private static int tryGetISODigit(char ch) {
1800         if ('0' <= ch && ch <= '9') {
1801             return ch - '0';
1802         } else {
1803             return -1;
1804         }
1805     }
1806 
1807     private static class CountryCallingCodeAndNewIndex {
1808         public final int countryCallingCode;
1809         public final int newIndex;
CountryCallingCodeAndNewIndex(int countryCode, int newIndex)1810         public CountryCallingCodeAndNewIndex(int countryCode, int newIndex) {
1811             this.countryCallingCode = countryCode;
1812             this.newIndex = newIndex;
1813         }
1814     }
1815 
1816     /*
1817      * Note that this function does not strictly care the country calling code with
1818      * 3 length (like Morocco: +212), assuming it is enough to use the first two
1819      * digit to compare two phone numbers.
1820      */
tryGetCountryCallingCodeAndNewIndex( String str, boolean acceptThailandCase)1821     private static CountryCallingCodeAndNewIndex tryGetCountryCallingCodeAndNewIndex(
1822         String str, boolean acceptThailandCase) {
1823         // Rough regexp:
1824         //  ^[^0-9*#+]*((\+|0(0|11)\d\d?|166) [^0-9*#+] $
1825         //         0        1 2 3 45  6 7  89
1826         //
1827         // In all the states, this function ignores separator characters.
1828         // "166" is the special case for the call from Thailand to the US. Uguu!
1829         int state = 0;
1830         int ccc = 0;
1831         final int length = str.length();
1832         for (int i = 0 ; i < length ; i++ ) {
1833             char ch = str.charAt(i);
1834             switch (state) {
1835                 case 0:
1836                     if      (ch == '+') state = 1;
1837                     else if (ch == '0') state = 2;
1838                     else if (ch == '1') {
1839                         if (acceptThailandCase) {
1840                             state = 8;
1841                         } else {
1842                             return null;
1843                         }
1844                     } else if (isDialable(ch)) {
1845                         return null;
1846                     }
1847                 break;
1848 
1849                 case 2:
1850                     if      (ch == '0') state = 3;
1851                     else if (ch == '1') state = 4;
1852                     else if (isDialable(ch)) {
1853                         return null;
1854                     }
1855                 break;
1856 
1857                 case 4:
1858                     if      (ch == '1') state = 5;
1859                     else if (isDialable(ch)) {
1860                         return null;
1861                     }
1862                 break;
1863 
1864                 case 1:
1865                 case 3:
1866                 case 5:
1867                 case 6:
1868                 case 7:
1869                     {
1870                         int ret = tryGetISODigit(ch);
1871                         if (ret > 0) {
1872                             ccc = ccc * 10 + ret;
1873                             if (ccc >= 100 || isCountryCallingCode(ccc)) {
1874                                 return new CountryCallingCodeAndNewIndex(ccc, i + 1);
1875                             }
1876                             if (state == 1 || state == 3 || state == 5) {
1877                                 state = 6;
1878                             } else {
1879                                 state++;
1880                             }
1881                         } else if (isDialable(ch)) {
1882                             return null;
1883                         }
1884                     }
1885                     break;
1886                 case 8:
1887                     if (ch == '6') state = 9;
1888                     else if (isDialable(ch)) {
1889                         return null;
1890                     }
1891                     break;
1892                 case 9:
1893                     if (ch == '6') {
1894                         return new CountryCallingCodeAndNewIndex(66, i + 1);
1895                     } else {
1896                         return null;
1897                     }
1898                 default:
1899                     return null;
1900             }
1901         }
1902 
1903         return null;
1904     }
1905 
1906     /**
1907      * Currently this function simply ignore the first digit assuming it is
1908      * trunk prefix. Actually trunk prefix is different in each country.
1909      *
1910      * e.g.
1911      * "+79161234567" equals "89161234567" (Russian trunk digit is 8)
1912      * "+33123456789" equals "0123456789" (French trunk digit is 0)
1913      *
1914      */
tryGetTrunkPrefixOmittedIndex(String str, int currentIndex)1915     private static int tryGetTrunkPrefixOmittedIndex(String str, int currentIndex) {
1916         int length = str.length();
1917         for (int i = currentIndex ; i < length ; i++) {
1918             final char ch = str.charAt(i);
1919             if (tryGetISODigit(ch) >= 0) {
1920                 return i + 1;
1921             } else if (isDialable(ch)) {
1922                 return -1;
1923             }
1924         }
1925         return -1;
1926     }
1927 
1928     /**
1929      * Return true if the prefix of "str" is "ignorable". Here, "ignorable" means
1930      * that "str" has only one digit and separater characters. The one digit is
1931      * assumed to be trunk prefix.
1932      */
checkPrefixIsIgnorable(final String str, int forwardIndex, int backwardIndex)1933     private static boolean checkPrefixIsIgnorable(final String str,
1934             int forwardIndex, int backwardIndex) {
1935         boolean trunk_prefix_was_read = false;
1936         while (backwardIndex >= forwardIndex) {
1937             if (tryGetISODigit(str.charAt(backwardIndex)) >= 0) {
1938                 if (trunk_prefix_was_read) {
1939                     // More than one digit appeared, meaning that "a" and "b"
1940                     // is different.
1941                     return false;
1942                 } else {
1943                     // Ignore just one digit, assuming it is trunk prefix.
1944                     trunk_prefix_was_read = true;
1945                 }
1946             } else if (isDialable(str.charAt(backwardIndex))) {
1947                 // Trunk prefix is a digit, not "*", "#"...
1948                 return false;
1949             }
1950             backwardIndex--;
1951         }
1952 
1953         return true;
1954     }
1955 
1956     //==== End of utility methods used only in compareStrictly() =====
1957 }
1958