summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--telephony/common/com/android/internal/telephony/HbpcdLookup.java124
-rw-r--r--telephony/common/com/android/internal/telephony/HbpcdUtils.java163
-rw-r--r--telephony/common/com/android/internal/telephony/SmsNumberUtils.java627
3 files changed, 914 insertions, 0 deletions
diff --git a/telephony/common/com/android/internal/telephony/HbpcdLookup.java b/telephony/common/com/android/internal/telephony/HbpcdLookup.java
new file mode 100644
index 000000000000..d9a3e725b6fb
--- /dev/null
+++ b/telephony/common/com/android/internal/telephony/HbpcdLookup.java
@@ -0,0 +1,124 @@
+/*
+**
+** Copyright 2014, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+package com.android.internal.telephony;
+
+import android.net.Uri;
+import android.provider.BaseColumns;
+
+/**
+ * @hide
+ */
+public class HbpcdLookup {
+ public static final String AUTHORITY = "hbpcd_lookup";
+
+ public static final Uri CONTENT_URI =
+ Uri.parse("content://" + AUTHORITY);
+
+ public static final String PATH_MCC_IDD = "idd";
+ public static final String PATH_MCC_LOOKUP_TABLE = "lookup";
+ public static final String PATH_MCC_SID_CONFLICT = "conflict";
+ public static final String PATH_MCC_SID_RANGE = "range";
+ public static final String PATH_NANP_AREA_CODE = "nanp";
+ public static final String PATH_ARBITRARY_MCC_SID_MATCH = "arbitrary";
+ public static final String PATH_USERADD_COUNTRY = "useradd";
+
+ public static final String ID = "_id";
+ public static final int IDINDEX = 0;
+
+ /**
+ * @hide
+ */
+ public static class MccIdd implements BaseColumns {
+ public static final Uri CONTENT_URI =
+ Uri.parse("content://" + AUTHORITY + "/" + PATH_MCC_IDD);
+ public static final String DEFAULT_SORT_ORDER = "MCC ASC";
+
+ public static final String MCC = "MCC";
+ public static final String IDD = "IDD";
+
+ }
+
+ /**
+ * @hide
+ */
+ public static class MccLookup implements BaseColumns {
+ public static final Uri CONTENT_URI =
+ Uri.parse("content://" + AUTHORITY + "/" + PATH_MCC_LOOKUP_TABLE);
+ public static final String DEFAULT_SORT_ORDER = "MCC ASC";
+
+ public static final String MCC = "MCC";
+ public static final String COUNTRY_CODE = "Country_Code";
+ public static final String COUNTRY_NAME = "Country_Name";
+ public static final String NDD = "NDD";
+ public static final String NANPS = "NANPS";
+ public static final String GMT_OFFSET_LOW = "GMT_Offset_Low";
+ public static final String GMT_OFFSET_HIGH = "GMT_Offset_High";
+ public static final String GMT_DST_LOW = "GMT_DST_Low";
+ public static final String GMT_DST_HIGH = "GMT_DST_High";
+
+ }
+
+ /**
+ * @hide
+ */
+ public static class MccSidConflicts implements BaseColumns {
+ public static final Uri CONTENT_URI =
+ Uri.parse("content://" + AUTHORITY + "/" + PATH_MCC_SID_CONFLICT);
+ public static final String DEFAULT_SORT_ORDER = "MCC ASC";
+
+ public static final String MCC = "MCC";
+ public static final String SID_CONFLICT = "SID_Conflict";
+
+ }
+
+ /**
+ * @hide
+ */
+ public static class MccSidRange implements BaseColumns {
+ public static final Uri CONTENT_URI =
+ Uri.parse("content://" + AUTHORITY + "/" + PATH_MCC_SID_RANGE);
+ public static final String DEFAULT_SORT_ORDER = "MCC ASC";
+
+ public static final String MCC = "MCC";
+ public static final String RANGE_LOW = "SID_Range_Low";
+ public static final String RANGE_HIGH = "SID_Range_High";
+ }
+
+ /**
+ * @hide
+ */
+ public static class ArbitraryMccSidMatch implements BaseColumns {
+ public static final Uri CONTENT_URI =
+ Uri.parse("content://" + AUTHORITY + "/" + PATH_ARBITRARY_MCC_SID_MATCH);
+ public static final String DEFAULT_SORT_ORDER = "MCC ASC";
+
+ public static final String MCC = "MCC";
+ public static final String SID = "SID";
+
+ }
+
+ /**
+ * @hide
+ */
+ public static class NanpAreaCode implements BaseColumns {
+ public static final Uri CONTENT_URI =
+ Uri.parse("content://" + AUTHORITY + "/" + PATH_NANP_AREA_CODE);
+ public static final String DEFAULT_SORT_ORDER = "Area_Code ASC";
+
+ public static final String AREA_CODE = "Area_Code";
+ }
+}
diff --git a/telephony/common/com/android/internal/telephony/HbpcdUtils.java b/telephony/common/com/android/internal/telephony/HbpcdUtils.java
new file mode 100644
index 000000000000..2f3194214be6
--- /dev/null
+++ b/telephony/common/com/android/internal/telephony/HbpcdUtils.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.database.Cursor;
+import android.telephony.Rlog;
+
+import com.android.internal.telephony.HbpcdLookup.ArbitraryMccSidMatch;
+import com.android.internal.telephony.HbpcdLookup.MccIdd;
+import com.android.internal.telephony.HbpcdLookup.MccLookup;
+import com.android.internal.telephony.HbpcdLookup.MccSidConflicts;
+import com.android.internal.telephony.HbpcdLookup.MccSidRange;
+
+public final class HbpcdUtils {
+ private static final String LOG_TAG = "HbpcdUtils";
+ private static final boolean DBG = false;
+ private ContentResolver resolver = null;
+
+ public HbpcdUtils(Context context) {
+ resolver = context.getContentResolver();
+ }
+
+ /**
+ * Resolves the unknown MCC with SID and Timezone information.
+ */
+ public int getMcc(int sid, int tz, int DSTflag, boolean isNitzTimeZone) {
+ int tmpMcc = 0;
+
+ // check if SID exists in arbitrary_mcc_sid_match table.
+ // these SIDs are assigned to more than 1 operators, but they are known to
+ // be used by a specific operator, other operators having the same SID are
+ // not using it currently, if that SID is in this table, we don't need to
+ // check other tables.
+ String projection2[] = {ArbitraryMccSidMatch.MCC};
+ Cursor c2 = resolver.query(ArbitraryMccSidMatch.CONTENT_URI, projection2,
+ ArbitraryMccSidMatch.SID + "=" + sid, null, null);
+
+ if (c2 != null) {
+ int c2Counter = c2.getCount();
+ if (DBG) {
+ Rlog.d(LOG_TAG, "Query unresolved arbitrary table, entries are " + c2Counter);
+ }
+ if (c2Counter == 1) {
+ if (DBG) {
+ Rlog.d(LOG_TAG, "Query Unresolved arbitrary returned the cursor " + c2);
+ }
+ c2.moveToFirst();
+ tmpMcc = c2.getInt(0);
+ if (DBG) {
+ Rlog.d(LOG_TAG, "MCC found in arbitrary_mcc_sid_match: " + tmpMcc);
+ }
+ c2.close();
+ return tmpMcc;
+ }
+ c2.close();
+ }
+
+ // Then check if SID exists in mcc_sid_conflict table.
+ // and use the timezone in mcc_lookup table to check which MCC matches.
+ String projection3[] = {MccSidConflicts.MCC};
+ Cursor c3 = resolver.query(MccSidConflicts.CONTENT_URI, projection3,
+ MccSidConflicts.SID_CONFLICT + "=" + sid + " and (((" +
+ MccLookup.GMT_OFFSET_LOW + "<=" + tz + ") and (" + tz + "<=" +
+ MccLookup.GMT_OFFSET_HIGH + ") and (" + "0=" + DSTflag + ")) or ((" +
+ MccLookup.GMT_DST_LOW + "<=" + tz + ") and (" + tz + "<=" +
+ MccLookup.GMT_DST_HIGH + ") and (" + "1=" + DSTflag + ")))",
+ null, null);
+ if (c3 != null) {
+ int c3Counter = c3.getCount();
+ if (c3Counter > 0) {
+ if (c3Counter > 1) {
+ Rlog.w(LOG_TAG, "something wrong, get more results for 1 conflict SID: " + c3);
+ }
+ if (DBG) Rlog.d(LOG_TAG, "Query conflict sid returned the cursor " + c3);
+ c3.moveToFirst();
+ tmpMcc = c3.getInt(0);
+ if (DBG) {
+ Rlog.d(LOG_TAG, "MCC found in mcc_lookup_table. Return tmpMcc = " + tmpMcc);
+ }
+ if (!isNitzTimeZone) {
+ // time zone is not accurate, it may get wrong mcc, ignore it.
+ if (DBG) {
+ Rlog.d(LOG_TAG, "time zone is not accurate, mcc may be " + tmpMcc);
+ }
+ tmpMcc = 0;
+ }
+ c3.close();
+ return tmpMcc;
+ } else {
+ c3.close();
+ }
+ }
+
+ // if there is no conflict, then check if SID is in mcc_sid_range.
+ String projection5[] = {MccSidRange.MCC};
+ Cursor c5 = resolver.query(MccSidRange.CONTENT_URI, projection5,
+ MccSidRange.RANGE_LOW + "<=" + sid + " and " +
+ MccSidRange.RANGE_HIGH + ">=" + sid,
+ null, null);
+ if (c5 != null) {
+ if (c5.getCount() > 0) {
+ if (DBG) Rlog.d(LOG_TAG, "Query Range returned the cursor " + c5);
+ c5.moveToFirst();
+ tmpMcc = c5.getInt(0);
+ if (DBG) Rlog.d(LOG_TAG, "SID found in mcc_sid_range. Return tmpMcc = " + tmpMcc);
+ c5.close();
+ return tmpMcc;
+ }
+ c5.close();
+ }
+ if (DBG) Rlog.d(LOG_TAG, "SID NOT found in mcc_sid_range.");
+
+ if (DBG) Rlog.d(LOG_TAG, "Exit getMccByOtherFactors. Return tmpMcc = " + tmpMcc);
+ // If unknown MCC still could not be resolved,
+ return tmpMcc;
+ }
+
+ /**
+ * Gets country information with given MCC.
+ */
+ public String getIddByMcc(int mcc) {
+ if (DBG) Rlog.d(LOG_TAG, "Enter getHbpcdInfoByMCC.");
+ String idd = "";
+
+ Cursor c = null;
+
+ String projection[] = {MccIdd.IDD};
+ Cursor cur = resolver.query(MccIdd.CONTENT_URI, projection,
+ MccIdd.MCC + "=" + mcc, null, null);
+ if (cur != null) {
+ if (cur.getCount() > 0) {
+ if (DBG) Rlog.d(LOG_TAG, "Query Idd returned the cursor " + cur);
+ // TODO: for those country having more than 1 IDDs, need more information
+ // to decide which IDD would be used. currently just use the first 1.
+ cur.moveToFirst();
+ idd = cur.getString(0);
+ if (DBG) Rlog.d(LOG_TAG, "IDD = " + idd);
+
+ }
+ cur.close();
+ }
+ if (c != null) c.close();
+
+ if (DBG) Rlog.d(LOG_TAG, "Exit getHbpcdInfoByMCC.");
+ return idd;
+ }
+}
diff --git a/telephony/common/com/android/internal/telephony/SmsNumberUtils.java b/telephony/common/com/android/internal/telephony/SmsNumberUtils.java
new file mode 100644
index 000000000000..0d33af639113
--- /dev/null
+++ b/telephony/common/com/android/internal/telephony/SmsNumberUtils.java
@@ -0,0 +1,627 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+import android.content.Context;
+import android.database.Cursor;
+import android.database.SQLException;
+import android.os.Binder;
+import android.os.Build;
+import android.os.PersistableBundle;
+import android.telephony.CarrierConfigManager;
+import android.telephony.PhoneNumberUtils;
+import android.telephony.Rlog;
+import android.telephony.TelephonyManager;
+import android.text.TextUtils;
+
+import com.android.internal.telephony.HbpcdLookup.MccIdd;
+import com.android.internal.telephony.HbpcdLookup.MccLookup;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+
+
+/**
+ * This class implements handle the MO SMS target address before sending.
+ * This is special for VZW requirement. Follow the specifications of assisted dialing
+ * of MO SMS while traveling on VZW CDMA, international CDMA or GSM markets.
+ * {@hide}
+ */
+public class SmsNumberUtils {
+ private static final String TAG = "SmsNumberUtils";
+ private static final boolean DBG = Build.IS_DEBUGGABLE;
+
+ private static final String PLUS_SIGN = "+";
+
+ private static final int NANP_SHORT_LENGTH = 7;
+ private static final int NANP_MEDIUM_LENGTH = 10;
+ private static final int NANP_LONG_LENGTH = 11;
+
+ private static final int NANP_CC = 1;
+ private static final String NANP_NDD = "1";
+ private static final String NANP_IDD = "011";
+
+ private static final int MIN_COUNTRY_AREA_LOCAL_LENGTH = 10;
+
+ private static final int GSM_UMTS_NETWORK = 0;
+ private static final int CDMA_HOME_NETWORK = 1;
+ private static final int CDMA_ROAMING_NETWORK = 2;
+
+ private static final int NP_NONE = 0;
+ private static final int NP_NANP_BEGIN = 1;
+
+ /* <Phone Number>, <NXX>-<XXXX> N[2-9] */
+ private static final int NP_NANP_LOCAL = NP_NANP_BEGIN;
+
+ /* <Area_code>-<Phone Number>, <NXX>-<NXX>-<XXXX> N[2-9] */
+ private static final int NP_NANP_AREA_LOCAL = NP_NANP_BEGIN + 1;
+
+ /* <1>-<Area_code>-<Phone Number>, 1-<NXX>-<NXX>-<XXXX> N[2-9] */
+ private static final int NP_NANP_NDD_AREA_LOCAL = NP_NANP_BEGIN + 2;
+
+ /* <+><U.S.Country_code><Area_code><Phone Number>, +1-<NXX>-<NXX>-<XXXX> N[2-9] */
+ private static final int NP_NANP_NBPCD_CC_AREA_LOCAL = NP_NANP_BEGIN + 3;
+
+ /* <Local_IDD><Country_code><Area_code><Phone Number>, 001-1-<NXX>-<NXX>-<XXXX> N[2-9] */
+ private static final int NP_NANP_LOCALIDD_CC_AREA_LOCAL = NP_NANP_BEGIN + 4;
+
+ /* <+><Home_IDD><Country_code><Area_code><Phone Number>, +011-1-<NXX>-<NXX>-<XXXX> N[2-9] */
+ private static final int NP_NANP_NBPCD_HOMEIDD_CC_AREA_LOCAL = NP_NANP_BEGIN + 5;
+
+ private static final int NP_INTERNATIONAL_BEGIN = 100;
+ /* <+>-<Home_IDD>-<Country_code>-<Area_code>-<Phone Number>, +011-86-25-86281234 */
+ private static final int NP_NBPCD_HOMEIDD_CC_AREA_LOCAL = NP_INTERNATIONAL_BEGIN;
+
+ /* <Home_IDD>-<Country_code>-<Area_code>-<Phone Number>, 011-86-25-86281234 */
+ private static final int NP_HOMEIDD_CC_AREA_LOCAL = NP_INTERNATIONAL_BEGIN + 1;
+
+ /* <NBPCD>-<Country_code>-<Area_code>-<Phone Number>, +1-86-25-86281234 */
+ private static final int NP_NBPCD_CC_AREA_LOCAL = NP_INTERNATIONAL_BEGIN + 2;
+
+ /* <Local_IDD>-<Country_code>-<Area_code>-<Phone Number>, 00-86-25-86281234 */
+ private static final int NP_LOCALIDD_CC_AREA_LOCAL = NP_INTERNATIONAL_BEGIN + 3;
+
+ /* <Country_code>-<Area_code>-<Phone Number>, 86-25-86281234*/
+ private static final int NP_CC_AREA_LOCAL = NP_INTERNATIONAL_BEGIN + 4;
+
+ private static int[] ALL_COUNTRY_CODES = null;
+ private static int MAX_COUNTRY_CODES_LENGTH;
+ private static HashMap<String, ArrayList<String>> IDDS_MAPS =
+ new HashMap<String, ArrayList<String>>();
+
+ private static class NumberEntry {
+ public String number;
+ public String IDD;
+ public int countryCode;
+ public NumberEntry(String number) {
+ this.number = number;
+ }
+ }
+
+ /**
+ * Breaks the given number down and formats it according to the rules
+ * for different number plans and different network.
+ *
+ * @param number destination number which need to be format
+ * @param activeMcc current network's mcc
+ * @param networkType current network type
+ *
+ * @return the number after formatting.
+ */
+ private static String formatNumber(Context context, String number,
+ 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;
+ }
+
+ /**
+ * Query International direct dialing from HbpcdLookup.db
+ * for specified country code
+ *
+ * @param mcc current network's country code
+ *
+ * @return the IDD array list.
+ */
+ private static ArrayList<String> getAllIDDs(Context context, 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;
+ }
+
+
+ /**
+ * Verify if the the destination number is a NANP number
+ *
+ * @param numberEntry including number and IDD array
+ * @param allIDDs the IDD array list of the current network's country code
+ *
+ * @return the number plan type related NANP
+ */
+ private static int checkNANP(NumberEntry numberEntry, ArrayList<String> 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 boolean isNANP(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;
+ }
+
+ /**
+ * Verify if the the destination number is an internal number
+ *
+ * @param numberEntry including number and IDD array
+ * @param allIDDs the IDD array list of the current network's country code
+ *
+ * @return the number plan type related international number
+ */
+ private static int checkInternationalNumberPlan(Context context, NumberEntry numberEntry,
+ ArrayList<String> allIDDs,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;
+ }
+
+ /**
+ * Returns the country code from the given number.
+ */
+ private static int getCountryCode(Context context, String 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.parseInt(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;
+ }
+
+ /**
+ * Gets all country Codes information with given MCC.
+ */
+ private static int[] getAllCountryCodes(Context context) {
+ 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 boolean inExceptionListForNpCcAreaLocal(NumberEntry numberEntry) {
+ int countryCode = numberEntry.countryCode;
+ boolean result = (numberEntry.number.length() == 12
+ && (countryCode == 7 || countryCode == 20
+ || countryCode == 65 || countryCode == 90));
+ return result;
+ }
+
+ private static String getNumberPlanType(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_HOMEIDD_CC_AREA_LOCAL";
+ } else if (state == NP_HOMEIDD_CC_AREA_LOCAL) {
+ numberPlanType = "NP_HOMEIDD_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_LOCALIDD_CC_AREA_LOCAL";
+ } else if (state == NP_CC_AREA_LOCAL) {
+ numberPlanType = "NP_CC_AREA_LOCAL";
+ } else {
+ numberPlanType = "Unknown type";
+ }
+ return numberPlanType;
+ }
+
+ /**
+ * Filter the destination number if using VZW sim card.
+ */
+ public static String filterDestAddr(Context context, int subId, String destAddr) {
+ if (DBG) Rlog.d(TAG, "enter filterDestAddr. destAddr=\"" + Rlog.pii(TAG, destAddr) + "\"" );
+
+ if (destAddr == null || !PhoneNumberUtils.isGlobalPhoneNumber(destAddr)) {
+ Rlog.w(TAG, "destAddr" + Rlog.pii(TAG, destAddr) +
+ " is not a global phone number! Nothing changed.");
+ return destAddr;
+ }
+
+ final TelephonyManager telephonyManager = ((TelephonyManager) context
+ .getSystemService(Context.TELEPHONY_SERVICE)).createForSubscriptionId(subId);
+ final String networkOperator = telephonyManager.getNetworkOperator();
+ String result = null;
+
+ if (needToConvert(context, subId)) {
+ final int networkType = getNetworkType(telephonyManager);
+ if (networkType != -1 && !TextUtils.isEmpty(networkOperator)) {
+ String networkMcc = networkOperator.substring(0, 3);
+ if (networkMcc != null && networkMcc.trim().length() > 0) {
+ result = formatNumber(context, destAddr, networkMcc, networkType);
+ }
+ }
+ }
+
+ if (DBG) {
+ Rlog.d(TAG, "destAddr is " + ((result != null)?"formatted.":"not formatted."));
+ Rlog.d(TAG, "leave filterDestAddr, new destAddr=\"" + (result != null ? Rlog.pii(TAG,
+ result) : Rlog.pii(TAG, destAddr)) + "\"");
+ }
+ return result != null ? result : destAddr;
+ }
+
+ /**
+ * Returns the current network type
+ */
+ private static int getNetworkType(TelephonyManager telephonyManager) {
+ int networkType = -1;
+ int phoneType = telephonyManager.getPhoneType();
+
+ if (phoneType == PhoneConstants.PHONE_TYPE_GSM) {
+ networkType = GSM_UMTS_NETWORK;
+ } else if (phoneType == PhoneConstants.PHONE_TYPE_CDMA) {
+ if (isInternationalRoaming(telephonyManager)) {
+ networkType = CDMA_ROAMING_NETWORK;
+ } else {
+ networkType = CDMA_HOME_NETWORK;
+ }
+ } else {
+ if (DBG) Rlog.w(TAG, "warning! unknown mPhoneType value=" + phoneType);
+ }
+
+ return networkType;
+ }
+
+ private static boolean isInternationalRoaming(TelephonyManager telephonyManager) {
+ String operatorIsoCountry = telephonyManager.getNetworkCountryIso();
+ String simIsoCountry = telephonyManager.getSimCountryIso();
+ 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 boolean needToConvert(Context context, int subId) {
+ // Calling package may not have READ_PHONE_STATE which is required for getConfig().
+ // Clear the calling identity so that it is called as self.
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ CarrierConfigManager configManager = (CarrierConfigManager)
+ context.getSystemService(Context.CARRIER_CONFIG_SERVICE);
+ if (configManager != null) {
+ PersistableBundle bundle = configManager.getConfigForSubId(subId);
+ if (bundle != null) {
+ return bundle.getBoolean(CarrierConfigManager
+ .KEY_SMS_REQUIRES_DESTINATION_NUMBER_CONVERSION_BOOL);
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ // by default this value is false
+ return false;
+ }
+}