FileDocCategorySizeDatePackage
SmsNumberUtils.javaAPI DocAndroid 5.1 API26204Thu Mar 12 22:22:54 GMT 2015com.android.internal.telephony

SmsNumberUtils

public class SmsNumberUtils extends Object
This class implements handle the MO SMS target address before sending. This is special for VZW requirement. Follow the specificaitons of assisted dialing of MO SMS while traveling on VZW CDMA, international CDMA or GSM markets. {@hide}

Fields Summary
private static final String
TAG
private static final boolean
DBG
private static final String
PLUS_SIGN
private static final int
NANP_SHORT_LENGTH
private static final int
NANP_MEDIUM_LENGTH
private static final int
NANP_LONG_LENGTH
private static final int
NANP_CC
private static final String
NANP_NDD
private static final String
NANP_IDD
private static final int
MIN_COUNTRY_AREA_LOCAL_LENGTH
private static final int
GSM_UMTS_NETWORK
private static final int
CDMA_HOME_NETWORK
private static final int
CDMA_ROAMING_NETWORK
private static final int
NP_NONE
private static final int
NP_NANP_BEGIN
private static final int
NP_NANP_LOCAL
private static final int
NP_NANP_AREA_LOCAL
private static final int
NP_NANP_NDD_AREA_LOCAL
private static final int
NP_NANP_NBPCD_CC_AREA_LOCAL
private static final int
NP_NANP_LOCALIDD_CC_AREA_LOCAL
private static final int
NP_NANP_NBPCD_HOMEIDD_CC_AREA_LOCAL
private static final int
NP_INTERNATIONAL_BEGIN
private static final int
NP_NBPCD_HOMEIDD_CC_AREA_LOCAL
private static final int
NP_HOMEIDD_CC_AREA_LOCAL
private static final int
NP_NBPCD_CC_AREA_LOCAL
private static final int
NP_LOCALIDD_CC_AREA_LOCAL
private static final int
NP_CC_AREA_LOCAL
private static int[]
ALL_COUNTRY_CODES
private static int
MAX_COUNTRY_CODES_LENGTH
private static HashMap
IDDS_MAPS
Constructors Summary
Methods Summary
private static intcheckInternationalNumberPlan(android.content.Context context, com.android.internal.telephony.SmsNumberUtils$NumberEntry numberEntry, java.util.ArrayList allIDDs, java.lang.String homeIDD)

        String number = numberEntry.number;
        int countryCode = -1;

        if (number.startsWith(PLUS_SIGN)) {
            // +xxxxxxxxxx
            String numberNoNBPCD = number.substring(1);
            if (numberNoNBPCD.startsWith(homeIDD)) {
                // +011xxxxxxxx
                String numberCountryAreaLocal = numberNoNBPCD.substring(homeIDD.length());
                if ((countryCode = getCountryCode(context, numberCountryAreaLocal)) > 0) {
                    numberEntry.countryCode = countryCode;
                    return NP_NBPCD_HOMEIDD_CC_AREA_LOCAL;
                }
            } else if ((countryCode = getCountryCode(context, numberNoNBPCD)) > 0) {
                numberEntry.countryCode = countryCode;
                return NP_NBPCD_CC_AREA_LOCAL;
            }

        } else if (number.startsWith(homeIDD)) {
            // 011xxxxxxxxx
            String numberCountryAreaLocal = number.substring(homeIDD.length());
            if ((countryCode = getCountryCode(context, numberCountryAreaLocal)) > 0) {
                numberEntry.countryCode = countryCode;
                return NP_HOMEIDD_CC_AREA_LOCAL;
            }
        } else {
            for (String exitCode : allIDDs) {
                if (number.startsWith(exitCode)) {
                    String numberNoIDD = number.substring(exitCode.length());
                    if ((countryCode = getCountryCode(context, numberNoIDD)) > 0) {
                        numberEntry.countryCode = countryCode;
                        numberEntry.IDD = exitCode;
                        return NP_LOCALIDD_CC_AREA_LOCAL;
                    }
                }
            }

            if (!number.startsWith("0") && (countryCode = getCountryCode(context, number)) > 0) {
                numberEntry.countryCode = countryCode;
                return NP_CC_AREA_LOCAL;
            }
        }
        return NP_NONE;
    
private static intcheckNANP(com.android.internal.telephony.SmsNumberUtils$NumberEntry numberEntry, java.util.ArrayList allIDDs)

        boolean isNANP = false;
        String number = numberEntry.number;

        if (number.length() == NANP_SHORT_LENGTH) {
            // 7 digits - Seven digit phone numbers
            char firstChar = number.charAt(0);
            if (firstChar >= '2" && firstChar <= '9") {
                isNANP = true;
                for (int i=1; i< NANP_SHORT_LENGTH; i++ ) {
                    char c= number.charAt(i);
                    if (!PhoneNumberUtils.isISODigit(c)) {
                        isNANP = false;
                        break;
                    }
                }
            }
            if (isNANP) {
                return NP_NANP_LOCAL;
            }
        } else if (number.length() == NANP_MEDIUM_LENGTH) {
            // 10 digits - Three digit area code followed by seven digit phone numbers/
            if (isNANP(number)) {
                return NP_NANP_AREA_LOCAL;
            }
        } else if (number.length() == NANP_LONG_LENGTH) {
            // 11 digits - One digit U.S. NDD(National Direct Dial) prefix '1',
            // followed by three digit area code and seven digit phone numbers
            if (isNANP(number)) {
                return NP_NANP_NDD_AREA_LOCAL;
            }
        } else if (number.startsWith(PLUS_SIGN)) {
            number = number.substring(1);
            if (number.length() == NANP_LONG_LENGTH) {
                // '+' and 11 digits -'+', followed by NANP CC prefix '1' followed by
                // three digit area code and seven digit phone numbers
                if (isNANP(number)) {
                    return NP_NANP_NBPCD_CC_AREA_LOCAL;
                }
            } else if (number.startsWith(NANP_IDD) && number.length() == NANP_LONG_LENGTH + 3) {
                // '+' and 14 digits -'+', followed by NANP IDD "011" followed by NANP CC
                // prefix '1' followed by three digit area code and seven digit phone numbers
                number = number.substring(3);
                if (isNANP(number)) {
                    return NP_NANP_NBPCD_HOMEIDD_CC_AREA_LOCAL;
                }
            }
        } else {
            // Check whether it's NP_NANP_LOCALIDD_CC_AREA_LOCAL
            for (String idd : allIDDs) {
                if (number.startsWith(idd)) {
                    String number2 = number.substring(idd.length());
                    if(number2 !=null && number2.startsWith(String.valueOf(NANP_CC))){
                        if (isNANP(number2)) {
                            numberEntry.IDD = idd;
                            return NP_NANP_LOCALIDD_CC_AREA_LOCAL;
                        }
                    }
                }
            }
        }

        return NP_NONE;
    
private static booleancompareGid1(PhoneBase phoneBase, java.lang.String serviceGid1)

        String gid1 = phoneBase.getGroupIdLevel1();
        boolean ret = true;

        if (TextUtils.isEmpty(serviceGid1)) {
            if (DBG) Rlog.d(TAG, "compareGid1 serviceGid is empty, return " + ret);
            return ret;
        }

        int gid_length = serviceGid1.length();
        // Check if gid1 match service GID1
        if (!((gid1 != null) && (gid1.length() >= gid_length) &&
                gid1.substring(0, gid_length).equalsIgnoreCase(serviceGid1))) {
            if (DBG) Rlog.d(TAG, " gid1 " + gid1 + " serviceGid1 " + serviceGid1);
            ret = false;
        }
        if (DBG) Rlog.d(TAG, "compareGid1 is " + (ret?"Same":"Different"));
        return ret;
    
public static java.lang.StringfilterDestAddr(PhoneBase phoneBase, java.lang.String destAddr)
Filter the destination number if using VZW sim card.

        if (DBG) Rlog.d(TAG, "enter filterDestAddr. destAddr=\"" + destAddr + "\"" );

        if (destAddr == null || !PhoneNumberUtils.isGlobalPhoneNumber(destAddr)) {
            Rlog.w(TAG, "destAddr" + destAddr + " is not a global phone number!");
            return destAddr;
        }

        final String networkOperator = TelephonyManager.getDefault().getNetworkOperator();
        String result = null;

        if (needToConvert(phoneBase)) {
            final int networkType = getNetworkType(phoneBase);
            if (networkType != -1 && !TextUtils.isEmpty(networkOperator)) {
                String networkMcc = networkOperator.substring(0, 3);
                if (networkMcc != null && networkMcc.trim().length() > 0) {
                    result = formatNumber(phoneBase.getContext(), destAddr, networkMcc, networkType);
                }
            }
        }

        if (DBG) Rlog.d(TAG, "leave filterDestAddr, new destAddr=\"" + result + "\"" );
        return result != null ? result : destAddr;
    
private static java.lang.StringformatNumber(android.content.Context context, java.lang.String number, java.lang.String activeMcc, int networkType)

        if (number == null ) {
            throw new IllegalArgumentException("number is null");
        }

        if (activeMcc == null || activeMcc.trim().length() == 0) {
            throw new IllegalArgumentException("activeMcc is null or empty!");
        }

        String networkPortionNumber = PhoneNumberUtils.extractNetworkPortion(number);
        if (networkPortionNumber == null || networkPortionNumber.length() == 0) {
            throw new IllegalArgumentException("Number is invalid!");
        }

        NumberEntry numberEntry = new NumberEntry(networkPortionNumber);
        ArrayList<String> allIDDs = getAllIDDs(context, activeMcc);

        // First check whether the number is a NANP number.
        int nanpState = checkNANP(numberEntry, allIDDs);
        if (DBG) Rlog.d(TAG, "NANP type: " + getNumberPlanType(nanpState));

        if ((nanpState == NP_NANP_LOCAL)
            || (nanpState == NP_NANP_AREA_LOCAL)
            || (nanpState == NP_NANP_NDD_AREA_LOCAL)) {
            return networkPortionNumber;
        } else if (nanpState == NP_NANP_NBPCD_CC_AREA_LOCAL) {
            if (networkType == CDMA_HOME_NETWORK
                    || networkType == CDMA_ROAMING_NETWORK) {
                // Remove "+"
                return networkPortionNumber.substring(1);
            } else {
                return networkPortionNumber;
            }
        } else if (nanpState == NP_NANP_LOCALIDD_CC_AREA_LOCAL) {
            if (networkType == CDMA_HOME_NETWORK) {
                return networkPortionNumber;
            } else if (networkType == GSM_UMTS_NETWORK) {
                // Remove the local IDD and replace with "+"
                int iddLength  =  numberEntry.IDD != null ? numberEntry.IDD.length() : 0;
                return PLUS_SIGN + networkPortionNumber.substring(iddLength);
            } else if (networkType == CDMA_ROAMING_NETWORK) {
                // Remove the local IDD
                int iddLength  =  numberEntry.IDD != null ? numberEntry.IDD.length() : 0;
                return  networkPortionNumber.substring(iddLength);
            }
        }

        int internationalState = checkInternationalNumberPlan(context, numberEntry, allIDDs,
                NANP_IDD);
        if (DBG) Rlog.d(TAG, "International type: " + getNumberPlanType(internationalState));
        String returnNumber = null;

        switch (internationalState) {
            case NP_NBPCD_HOMEIDD_CC_AREA_LOCAL:
                if (networkType == GSM_UMTS_NETWORK) {
                    // Remove "+"
                    returnNumber = networkPortionNumber.substring(1);
                }
                break;

            case NP_NBPCD_CC_AREA_LOCAL:
                // Replace "+" with "011"
                returnNumber = NANP_IDD + networkPortionNumber.substring(1);
                break;

            case NP_LOCALIDD_CC_AREA_LOCAL:
                if (networkType == GSM_UMTS_NETWORK || networkType == CDMA_ROAMING_NETWORK) {
                    int iddLength  =  numberEntry.IDD != null ? numberEntry.IDD.length() : 0;
                    // Replace <Local IDD> to <Home IDD>("011")
                    returnNumber = NANP_IDD + networkPortionNumber.substring(iddLength);
                }
                break;

            case NP_CC_AREA_LOCAL:
                int countryCode = numberEntry.countryCode;

                if (!inExceptionListForNpCcAreaLocal(numberEntry)
                    && networkPortionNumber.length() >= 11 && countryCode != NANP_CC) {
                    // Add "011"
                    returnNumber = NANP_IDD + networkPortionNumber;
                }
                break;

            case NP_HOMEIDD_CC_AREA_LOCAL:
                returnNumber = networkPortionNumber;
                break;

            default:
                // Replace "+" with 011 in CDMA network if the number's country
                // code is not in the HbpcdLookup database.
                if (networkPortionNumber.startsWith(PLUS_SIGN)
                    && (networkType == CDMA_HOME_NETWORK || networkType == CDMA_ROAMING_NETWORK)) {
                    if (networkPortionNumber.startsWith(PLUS_SIGN + NANP_IDD)) {
                        // Only remove "+"
                        returnNumber = networkPortionNumber.substring(1);
                    } else {
                        // Replace "+" with "011"
                        returnNumber = NANP_IDD + networkPortionNumber.substring(1);
                    }
                }
        }

        if (returnNumber == null) {
            returnNumber = networkPortionNumber;
        }
        return returnNumber;
    
private static int[]getAllCountryCodes(android.content.Context context)
Gets all country Codes information with given MCC.

        if (ALL_COUNTRY_CODES != null) {
            return ALL_COUNTRY_CODES;
        }

        Cursor cursor = null;
        try {
            String projection[] = {MccLookup.COUNTRY_CODE};
            cursor = context.getContentResolver().query(MccLookup.CONTENT_URI,
                    projection, null, null, null);

            if (cursor.getCount() > 0) {
                ALL_COUNTRY_CODES = new int[cursor.getCount()];
                int i = 0;
                while (cursor.moveToNext()) {
                    int countryCode = cursor.getInt(0);
                    ALL_COUNTRY_CODES[i++] = countryCode;
                    int length = String.valueOf(countryCode).trim().length();
                    if (length > MAX_COUNTRY_CODES_LENGTH) {
                        MAX_COUNTRY_CODES_LENGTH = length;
                    }
                }
            }
        } catch (SQLException e) {
            Rlog.e(TAG, "Can't access HbpcdLookup database", e);
        } finally {
            if (cursor != null) {
                cursor.close();
            }
        }
        return ALL_COUNTRY_CODES;
    
private static java.util.ArrayListgetAllIDDs(android.content.Context context, java.lang.String mcc)

        ArrayList<String> allIDDs = IDDS_MAPS.get(mcc);
        if (allIDDs != null) {
            return allIDDs;
        } else {
            allIDDs = new ArrayList<String>();
        }

        String projection[] = {MccIdd.IDD, MccIdd.MCC};
        String where = null;

        // if mcc is null         : return all rows
        // if mcc is empty-string : return those rows whose mcc is emptry-string
        String[] selectionArgs = null;
        if (mcc != null) {
            where = MccIdd.MCC + "=?";
            selectionArgs = new String[] {mcc};
        }

        Cursor cursor = null;
        try {
            cursor = context.getContentResolver().query(MccIdd.CONTENT_URI, projection,
                    where, selectionArgs, null);
            if (cursor.getCount() > 0) {
                while (cursor.moveToNext()) {
                    String idd = cursor.getString(0);
                    if (!allIDDs.contains(idd)) {
                        allIDDs.add(idd);
                    }
                }
            }
        } catch (SQLException e) {
            Rlog.e(TAG, "Can't access HbpcdLookup database", e);
        } finally {
            if (cursor != null) {
                cursor.close();
            }
        }

        IDDS_MAPS.put(mcc, allIDDs);

        if (DBG) Rlog.d(TAG, "MCC = " + mcc + ", all IDDs = " + allIDDs);
        return allIDDs;
    
private static intgetCountryCode(android.content.Context context, java.lang.String number)
Returns the country code from the given number.

        int countryCode = -1;
        if (number.length() >= MIN_COUNTRY_AREA_LOCAL_LENGTH) {
            // Check Country code
            int[] allCCs = getAllCountryCodes(context);
            if (allCCs == null) {
                return countryCode;
            }

            int[] ccArray = new int[MAX_COUNTRY_CODES_LENGTH];
            for (int i = 0; i < MAX_COUNTRY_CODES_LENGTH; i ++) {
                ccArray[i] = Integer.valueOf(number.substring(0, i+1));
            }

            for (int i = 0; i < allCCs.length; i ++) {
                int tempCC = allCCs[i];
                for (int j = 0; j < MAX_COUNTRY_CODES_LENGTH; j ++) {
                    if (tempCC == ccArray[j]) {
                        if (DBG) Rlog.d(TAG, "Country code = " + tempCC);
                        return tempCC;
                    }
                }
            }
        }

        return countryCode;
    
private static intgetNetworkType(PhoneBase phoneBase)
Returns the current network type

        int networkType = -1;
        int phoneType = TelephonyManager.getDefault().getPhoneType();

        if (phoneType == TelephonyManager.PHONE_TYPE_GSM) {
            networkType = GSM_UMTS_NETWORK;
        } else if (phoneType == TelephonyManager.PHONE_TYPE_CDMA) {
            if (isInternationalRoaming(phoneBase)) {
                networkType = CDMA_ROAMING_NETWORK;
            } else {
                networkType = CDMA_HOME_NETWORK;
            }
        } else {
            if (DBG) Rlog.w(TAG, "warning! unknown mPhoneType value=" + phoneType);
        }

        return networkType;
    
private static java.lang.StringgetNumberPlanType(int state)

        String numberPlanType = "Number Plan type (" + state + "): ";

        if (state == NP_NANP_LOCAL) {
            numberPlanType = "NP_NANP_LOCAL";
        } else if (state == NP_NANP_AREA_LOCAL) {
            numberPlanType = "NP_NANP_AREA_LOCAL";
        } else if (state  == NP_NANP_NDD_AREA_LOCAL) {
            numberPlanType = "NP_NANP_NDD_AREA_LOCAL";
        } else if (state == NP_NANP_NBPCD_CC_AREA_LOCAL) {
            numberPlanType = "NP_NANP_NBPCD_CC_AREA_LOCAL";
        } else if (state == NP_NANP_LOCALIDD_CC_AREA_LOCAL) {
            numberPlanType = "NP_NANP_LOCALIDD_CC_AREA_LOCAL";
        } else if (state == NP_NANP_NBPCD_HOMEIDD_CC_AREA_LOCAL) {
            numberPlanType = "NP_NANP_NBPCD_HOMEIDD_CC_AREA_LOCAL";
        } else if (state == NP_NBPCD_HOMEIDD_CC_AREA_LOCAL) {
            numberPlanType = "NP_NBPCD_IDD_CC_AREA_LOCAL";
        } else if (state == NP_HOMEIDD_CC_AREA_LOCAL) {
            numberPlanType = "NP_IDD_CC_AREA_LOCAL";
        } else if (state == NP_NBPCD_CC_AREA_LOCAL) {
            numberPlanType = "NP_NBPCD_CC_AREA_LOCAL";
        } else if (state == NP_LOCALIDD_CC_AREA_LOCAL) {
            numberPlanType = "NP_IDD_CC_AREA_LOCAL";
        } else if (state == NP_CC_AREA_LOCAL) {
            numberPlanType = "NP_CC_AREA_LOCAL";
        } else {
            numberPlanType = "Unknown type";
        }
        return numberPlanType;
    
private static booleaninExceptionListForNpCcAreaLocal(com.android.internal.telephony.SmsNumberUtils$NumberEntry numberEntry)

        int countryCode = numberEntry.countryCode;
        boolean result = (numberEntry.number.length() == 12
                          && (countryCode == 7 || countryCode == 20
                              || countryCode == 65 || countryCode == 90));
        return result;
    
private static booleanisInternationalRoaming(PhoneBase phoneBase)

        String operatorIsoCountry = TelephonyManager.getDefault().getNetworkCountryIsoForPhone(
                phoneBase.getPhoneId());
        String simIsoCountry = TelephonyManager.getDefault().getSimCountryIsoForPhone(
                phoneBase.getPhoneId());
        boolean internationalRoaming = !TextUtils.isEmpty(operatorIsoCountry)
                && !TextUtils.isEmpty(simIsoCountry)
                && !simIsoCountry.equals(operatorIsoCountry);
        if (internationalRoaming) {
            if ("us".equals(simIsoCountry)) {
                internationalRoaming = !"vi".equals(operatorIsoCountry);
            } else if ("vi".equals(simIsoCountry)) {
                internationalRoaming = !"us".equals(operatorIsoCountry);
            }
        }
        return internationalRoaming;
    
private static booleanisNANP(java.lang.String number)

        if (number.length() == NANP_MEDIUM_LENGTH
            || (number.length() == NANP_LONG_LENGTH  && number.startsWith(NANP_NDD))) {
            if (number.length() == NANP_LONG_LENGTH) {
                number = number.substring(1);
            }
            return (PhoneNumberUtils.isNanp(number));
        }
        return false;
    
private static booleanneedToConvert(PhoneBase phoneBase)

        boolean bNeedToConvert  = false;
        String[] listArray = phoneBase.getContext().getResources()
                .getStringArray(com.android.internal.R.array
                .config_sms_convert_destination_number_support);
        if (listArray != null && listArray.length > 0) {
            for (int i=0; i<listArray.length; i++) {
                if (!TextUtils.isEmpty(listArray[i])) {
                    String[] needToConvertArray = listArray[i].split(";");
                    if (needToConvertArray != null && needToConvertArray.length > 0) {
                        if (needToConvertArray.length == 1) {
                            bNeedToConvert = "true".equalsIgnoreCase(needToConvertArray[0]);
                        } else if (needToConvertArray.length == 2 &&
                                !TextUtils.isEmpty(needToConvertArray[1]) &&
                                compareGid1(phoneBase, needToConvertArray[1])) {
                            bNeedToConvert = "true".equalsIgnoreCase(needToConvertArray[0]);
                            break;
                        }
                    }
                }
            }
        }
        return bNeedToConvert;