Send a protected intent ACTION_MMSSMS_DATABASE_LOST upon mmssms.db corruption or loss
am: f51686d502
Change-Id: Icacfc3414d784d3455939b25309204835f8c720e
diff --git a/OWNERS b/OWNERS
index 0716d33..be3c684 100644
--- a/OWNERS
+++ b/OWNERS
@@ -6,4 +6,6 @@
mpq@google.com
jminjie@google.com
shuoq@google.com
-
+hallliu@google.com
+tgunn@google.com
+breadley@google.com
diff --git a/res/values-as/config.xml b/res/values-as/config.xml
deleted file mode 100644
index 99877a6..0000000
--- a/res/values-as/config.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string-array name="persist_apns_for_plmn">
- <item msgid="6413072509259000954">"20404"</item>
- <item msgid="5639159280778239123">"310004"</item>
- <item msgid="3860605521380788028">"310120"</item>
- <item msgid="537693705785480198">"311480"</item>
- </string-array>
-</resources>
diff --git a/res/values-as/strings.xml b/res/values-as/strings.xml
deleted file mode 100644
index 5a39f18..0000000
--- a/res/values-as/strings.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- Copyright (C) 2008 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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_label" product="tablet" msgid="9194799012395299737">"ম\'বাইল নেটৱৰ্ক কনফিগাৰেশ্বন"</string>
- <string name="app_label" product="default" msgid="8338087656149558019">"ফ\'ন আৰু বাৰ্তাৰ সঞ্চয়াগাৰ"</string>
-</resources>
diff --git a/res/values-or/config.xml b/res/values-or/config.xml
deleted file mode 100644
index 99877a6..0000000
--- a/res/values-or/config.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string-array name="persist_apns_for_plmn">
- <item msgid="6413072509259000954">"20404"</item>
- <item msgid="5639159280778239123">"310004"</item>
- <item msgid="3860605521380788028">"310120"</item>
- <item msgid="537693705785480198">"311480"</item>
- </string-array>
-</resources>
diff --git a/res/values-or/strings.xml b/res/values-or/strings.xml
deleted file mode 100644
index b004c4f..0000000
--- a/res/values-or/strings.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- Copyright (C) 2008 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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_label" product="tablet" msgid="9194799012395299737">"ମୋବାଇଲ୍ ନେଟ୍ୱର୍କ କନଫିଗରେଶନ୍"</string>
- <string name="app_label" product="default" msgid="8338087656149558019">"ଫୋନ୍ ଓ ମେସେଜିଙ୍ଗ ଷ୍ଟୋରେଜ୍"</string>
-</resources>
diff --git a/src/com/android/providers/telephony/ServiceStateProvider.java b/src/com/android/providers/telephony/ServiceStateProvider.java
index e589171..7f870b0 100644
--- a/src/com/android/providers/telephony/ServiceStateProvider.java
+++ b/src/com/android/providers/telephony/ServiceStateProvider.java
@@ -61,7 +61,6 @@
import static android.provider.Telephony.ServiceStateTable.CDMA_ERI_ICON_INDEX;
import static android.provider.Telephony.ServiceStateTable.CDMA_ERI_ICON_MODE;
import static android.provider.Telephony.ServiceStateTable.IS_EMERGENCY_ONLY;
-import static android.provider.Telephony.ServiceStateTable.IS_DATA_ROAMING_FROM_REGISTRATION;
import static android.provider.Telephony.ServiceStateTable.IS_USING_CARRIER_AGGREGATION;
@@ -94,7 +93,6 @@
CDMA_ERI_ICON_INDEX,
CDMA_ERI_ICON_MODE,
IS_EMERGENCY_ONLY,
- IS_DATA_ROAMING_FROM_REGISTRATION,
IS_USING_CARRIER_AGGREGATION,
};
@@ -153,8 +151,6 @@
newSS.setCdmaEriIconIndex(values.getAsInteger(CDMA_ERI_ICON_INDEX));
newSS.setCdmaEriIconMode(values.getAsInteger(CDMA_ERI_ICON_MODE));
newSS.setEmergencyOnly(values.getAsBoolean(IS_EMERGENCY_ONLY));
- newSS.setDataRoamingFromRegistration(
- values.getAsBoolean(IS_DATA_ROAMING_FROM_REGISTRATION));
newSS.setIsUsingCarrierAggregation(values.getAsBoolean(IS_USING_CARRIER_AGGREGATION));
// notify listeners
@@ -235,8 +231,6 @@
final int cdma_eri_icon_index = ss.getCdmaEriIconIndex();
final int cdma_eri_icon_mode = ss.getCdmaEriIconMode();
final int is_emergency_only = (ss.isEmergencyOnly()) ? 1 : 0;
- final int is_data_roaming_from_registration =
- (ss.getDataRoamingFromRegistration()) ? 1 : 0;
final int is_using_carrier_aggregation = (ss.isUsingCarrierAggregation()) ? 1 : 0;
return buildSingleRowResult(projection, sColumns, new Object[] {
@@ -261,7 +255,6 @@
cdma_eri_icon_index,
cdma_eri_icon_mode,
is_emergency_only,
- is_data_roaming_from_registration,
is_using_carrier_aggregation,
});
}
diff --git a/src/com/android/providers/telephony/TelephonyProvider.java b/src/com/android/providers/telephony/TelephonyProvider.java
index df1e58c..fffd78c 100644
--- a/src/com/android/providers/telephony/TelephonyProvider.java
+++ b/src/com/android/providers/telephony/TelephonyProvider.java
@@ -95,10 +95,12 @@
import android.os.RemoteException;
import android.os.SystemProperties;
import android.os.UserHandle;
+import android.provider.Telephony;
import android.telephony.ServiceState;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
+import android.telephony.data.ApnSetting;
import android.text.TextUtils;
import android.util.Log;
import android.util.Pair;
@@ -108,7 +110,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.IApnSourceService;
import com.android.internal.telephony.PhoneConstants;
-import com.android.internal.telephony.dataconnection.ApnSetting;
+import com.android.internal.telephony.dataconnection.ApnSettingUtils;
import com.android.internal.telephony.uicc.IccRecords;
import com.android.internal.telephony.uicc.UiccController;
import com.android.internal.util.XmlUtils;
@@ -116,15 +118,21 @@
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
+import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
+import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
+import java.util.Locale;
import java.util.Map;
+import java.util.zip.CRC32;
+import java.util.Set;
public class TelephonyProvider extends ContentProvider
{
@@ -133,7 +141,7 @@
private static final boolean DBG = true;
private static final boolean VDBG = false; // STOPSHIP if true
- private static final int DATABASE_VERSION = 26 << 16;
+ private static final int DATABASE_VERSION = 28 << 16;
private static final int URL_UNKNOWN = 0;
private static final int URL_TELEPHONY = 1;
private static final int URL_CURRENT = 2;
@@ -220,6 +228,7 @@
private static final int INVALID_APN_ID = -1;
private static final List<String> CARRIERS_UNIQUE_FIELDS = new ArrayList<String>();
+ private static final Set<String> CARRIERS_BOOLEAN_FIELDS = new HashSet<String>();
private static final Map<String, String> CARRIERS_UNIQUE_FIELDS_DEFAULTS = new HashMap();
@VisibleForTesting
@@ -257,6 +266,14 @@
CARRIERS_UNIQUE_FIELDS_DEFAULTS.put(APN_SET_ID, String.valueOf(NO_SET_SET));
CARRIERS_UNIQUE_FIELDS.addAll(CARRIERS_UNIQUE_FIELDS_DEFAULTS.keySet());
+
+ // SQLite databases store bools as ints but the ContentValues objects passed in through
+ // queries use bools. As a result there is some special handling of boolean fields within
+ // the TelephonyProvider.
+ CARRIERS_BOOLEAN_FIELDS.add(CARRIER_ENABLED);
+ CARRIERS_BOOLEAN_FIELDS.add(MODEM_COGNITIVE);
+ CARRIERS_BOOLEAN_FIELDS.add(USER_VISIBLE);
+ CARRIERS_BOOLEAN_FIELDS.add(USER_EDITABLE);
}
@VisibleForTesting
@@ -281,7 +298,7 @@
CURRENT + " INTEGER," +
PROTOCOL + " TEXT DEFAULT " + DEFAULT_PROTOCOL + "," +
ROAMING_PROTOCOL + " TEXT DEFAULT " + DEFAULT_ROAMING_PROTOCOL + "," +
- CARRIER_ENABLED + " BOOLEAN DEFAULT 1," +
+ CARRIER_ENABLED + " BOOLEAN DEFAULT 1," + // SQLite databases store bools as ints
BEARER + " INTEGER DEFAULT 0," +
BEARER_BITMASK + " INTEGER DEFAULT 0," +
NETWORK_TYPE_BITMASK + " INTEGER DEFAULT 0," +
@@ -330,6 +347,8 @@
+ " INTEGER DEFAULT " + SubscriptionManager.DATA_ROAMING_DEFAULT + ","
+ SubscriptionManager.MCC + " INTEGER DEFAULT 0,"
+ SubscriptionManager.MNC + " INTEGER DEFAULT 0,"
+ + SubscriptionManager.MCC_STRING + " TEXT,"
+ + SubscriptionManager.MNC_STRING + " TEXT,"
+ SubscriptionManager.SIM_PROVISIONING_STATUS
+ " INTEGER DEFAULT " + SubscriptionManager.SIM_PROVISIONED + ","
+ SubscriptionManager.IS_EMBEDDED + " INTEGER DEFAULT 0,"
@@ -353,7 +372,9 @@
+ SubscriptionManager.WFC_IMS_ENABLED + " INTEGER DEFAULT -1,"
+ SubscriptionManager.WFC_IMS_MODE + " INTEGER DEFAULT -1,"
+ SubscriptionManager.WFC_IMS_ROAMING_MODE + " INTEGER DEFAULT -1,"
- + SubscriptionManager.WFC_IMS_ROAMING_ENABLED + " INTEGER DEFAULT -1"
+ + SubscriptionManager.WFC_IMS_ROAMING_ENABLED + " INTEGER DEFAULT -1,"
+ + SubscriptionManager.IS_OPPORTUNISTIC + " INTEGER DEFAULT 0,"
+ + SubscriptionManager.PARENT_SUB_ID + " INTEGER DEFAULT -1"
+ ");";
}
@@ -367,6 +388,7 @@
s_urlMatcher.addURI("telephony", "carriers/preferapnset", URL_PREFERAPNSET);
s_urlMatcher.addURI("telephony", "siminfo", URL_SIMINFO);
+ s_urlMatcher.addURI("telephony", "siminfo/#", URL_SIMINFO_USING_SUBID);
s_urlMatcher.addURI("telephony", "carriers/subId/*", URL_TELEPHONY_USING_SUBID);
s_urlMatcher.addURI("telephony", "carriers/current/subId/*", URL_CURRENT_USING_SUBID);
@@ -419,7 +441,8 @@
mInjector = injector;
}
- private static class DatabaseHelper extends SQLiteOpenHelper {
+ @VisibleForTesting
+ public static class DatabaseHelper extends SQLiteOpenHelper {
// Context to access resources with
private Context mContext;
@@ -435,10 +458,15 @@
setIdleConnectionTimeout(IDLE_CONNECTION_TIMEOUT_MS);
}
- private static int getVersion(Context context) {
+ @VisibleForTesting
+ public static int getVersion(Context context) {
if (VDBG) log("getVersion:+");
// Get the database version, combining a static schema version and the XML version
Resources r = context.getResources();
+ if (r == null) {
+ loge("resources=null, return version=" + Integer.toHexString(DATABASE_VERSION));
+ return DATABASE_VERSION;
+ }
XmlResourceParser parser = r.getXml(com.android.internal.R.xml.apns);
try {
XmlUtils.beginDocument(parser, "apns");
@@ -518,9 +546,32 @@
} catch (IOException e) {
loge("IOException for " + file.getAbsolutePath() + ":" + e);
}
+
+ // The RRO may have been updated in a firmware upgrade. Add checksum for the
+ // resources to the total checksum so that apns in an RRO update is not missed.
+ try (InputStream inputStream = mContext.getResources().
+ openRawResource(com.android.internal.R.xml.apns)) {
+ byte[] array = toByteArray(inputStream);
+ CRC32 c = new CRC32();
+ c.update(array);
+ checksum += c.getValue();
+ if (DBG) log("Checksum after adding resource is " + checksum);
+ } catch (IOException | Resources.NotFoundException e) {
+ loge("Exception when calculating checksum for internal apn resources: " + e);
+ }
return checksum;
}
+ private static byte[] toByteArray(InputStream input) throws IOException {
+ byte[] buffer = new byte[128];
+ int bytesRead;
+ ByteArrayOutputStream output = new ByteArrayOutputStream();
+ while ((bytesRead = input.read(buffer)) != -1) {
+ output.write(buffer, 0, bytesRead);
+ }
+ return output.toByteArray();
+ }
+
private long getApnConfChecksum() {
SharedPreferences sp = mContext.getSharedPreferences(PREF_FILE, Context.MODE_PRIVATE);
return sp.getLong(APN_CONF_CHECKSUM, -1);
@@ -538,8 +589,8 @@
File confFile = new File(Environment.getRootDirectory(), PARTNER_APNS_PATH);
File oemConfFile = new File(Environment.getOemDirectory(), OEM_APNS_PATH);
File updatedConfFile = new File(Environment.getDataDirectory(), OTA_UPDATED_APNS_PATH);
- confFile = getNewerFile(confFile, oemConfFile);
- confFile = getNewerFile(confFile, updatedConfFile);
+ confFile = pickSecondIfExists(confFile, oemConfFile);
+ confFile = pickSecondIfExists(confFile, updatedConfFile);
return confFile;
}
@@ -570,16 +621,20 @@
if (VDBG) log("dbh.initDatabase:+ db=" + db);
// Read internal APNS data
Resources r = mContext.getResources();
- XmlResourceParser parser = r.getXml(com.android.internal.R.xml.apns);
int publicversion = -1;
- try {
- XmlUtils.beginDocument(parser, "apns");
- publicversion = Integer.parseInt(parser.getAttributeValue(null, "version"));
- loadApns(db, parser);
- } catch (Exception e) {
- loge("Got exception while loading APN database." + e);
- } finally {
- parser.close();
+ if (r != null) {
+ XmlResourceParser parser = r.getXml(com.android.internal.R.xml.apns);
+ try {
+ XmlUtils.beginDocument(parser, "apns");
+ publicversion = Integer.parseInt(parser.getAttributeValue(null, "version"));
+ loadApns(db, parser);
+ } catch (Exception e) {
+ loge("Got exception while loading APN database." + e);
+ } finally {
+ parser.close();
+ }
+ } else {
+ loge("initDatabase: resources=null");
}
// Read external APNS data (partner-provided)
@@ -645,26 +700,16 @@
}
- private File getNewerFile(File sysApnFile, File altApnFile) {
+ private File pickSecondIfExists(File sysApnFile, File altApnFile) {
if (altApnFile.exists()) {
- // Alternate file exists. Use the newer one.
- long altFileTime = altApnFile.lastModified();
- long currFileTime = sysApnFile.lastModified();
- if (DBG) log("APNs Timestamp: altFileTime = " + altFileTime + " currFileTime = "
- + currFileTime);
-
- // To get the latest version from OEM or System image
- if (altFileTime > currFileTime) {
- if (DBG) log("APNs Timestamp: Alternate image " + altApnFile.getPath() +
- " is greater than System image");
- return altApnFile;
- }
+ if (DBG) log("Load APNs from " + altApnFile.getPath() +
+ " instead of " + sysApnFile.getPath());
+ return altApnFile;
} else {
- // No Apn in alternate image, so load it from system image.
- if (DBG) log("No APNs in OEM image = " + altApnFile.getPath() +
- " Load APNs from system image");
+ if (DBG) log("Load APNs from " + sysApnFile.getPath() +
+ " instead of " + altApnFile.getPath());
+ return sysApnFile;
}
- return sysApnFile;
}
@Override
@@ -1033,9 +1078,53 @@
}
oldVersion = 26 << 16 | 6;
}
+
+ if (oldVersion < (27 << 16 | 6)) {
+ // Add the new MCC_STRING and MNC_STRING columns into the subscription table,
+ // and attempt to populate them.
+ try {
+ // Try to update the siminfo table. It might not be there.
+ db.execSQL("ALTER TABLE " + SIMINFO_TABLE +
+ " ADD COLUMN " + SubscriptionManager.MCC_STRING + " TEXT;");
+ db.execSQL("ALTER TABLE " + SIMINFO_TABLE +
+ " ADD COLUMN " + SubscriptionManager.MNC_STRING + " TEXT;");
+ } catch (SQLiteException e) {
+ if (DBG) {
+ log("onUpgrade skipping " + SIMINFO_TABLE + " upgrade. " +
+ " The table will get created in onOpen.");
+ }
+ }
+ // Migrate the old integer values over to strings
+ String[] proj = {SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID,
+ SubscriptionManager.MCC, SubscriptionManager.MNC};
+ try (Cursor c = db.query(SIMINFO_TABLE, proj, null, null, null, null, null)) {
+ while (c.moveToNext()) {
+ fillInMccMncStringAtCursor(mContext, db, c);
+ }
+ }
+ oldVersion = 27 << 16 | 6;
+ }
+
+ if (oldVersion < (28 << 16 | 6)) {
+ try {
+ // Try to update the siminfo table. It might not be there.
+ db.execSQL("ALTER TABLE " + SIMINFO_TABLE + " ADD COLUMN "
+ + SubscriptionManager.IS_OPPORTUNISTIC + " INTEGER DEFAULT 0;");
+ db.execSQL("ALTER TABLE " + SIMINFO_TABLE + " ADD COLUMN "
+ + SubscriptionManager.PARENT_SUB_ID + " INTEGER DEFAULT -1;");
+ } catch (SQLiteException e) {
+ if (DBG) {
+ log("onUpgrade skipping " + SIMINFO_TABLE + " upgrade. " +
+ "The table will get created in onOpen.");
+ }
+ }
+ oldVersion = 28 << 16 | 6;
+ }
if (DBG) {
log("dbh.onUpgrade:- db=" + db + " oldV=" + oldVersion + " newV=" + newVersion);
}
+ // when adding fields to onUpgrade, also add a unit test to TelephonyDatabaseHelperTest
+ // and update the DATABASE_VERSION field
}
private void recreateSimInfoDB(Cursor c, SQLiteDatabase db, String[] proj) {
@@ -1309,15 +1398,13 @@
whereArgs[i++] = values.containsKey(ROAMING_PROTOCOL) ?
values.getAsString(ROAMING_PROTOCOL) : DEFAULT_ROAMING_PROTOCOL;
- if (values.containsKey(CARRIER_ENABLED) &&
- (values.getAsString(CARRIER_ENABLED).
- equalsIgnoreCase("false") ||
- values.getAsString(CARRIER_ENABLED).equals("0"))) {
- whereArgs[i++] = "false";
- whereArgs[i++] = "0";
+ if (values.containsKey(CARRIER_ENABLED)) {
+ whereArgs[i++] = convertStringToBoolString(values.getAsString(CARRIER_ENABLED));
+ whereArgs[i++] = convertStringToIntString(values.getAsString(CARRIER_ENABLED));
} else {
- whereArgs[i++] = "true";
- whereArgs[i++] = "1";
+ String defaultIntString = CARRIERS_UNIQUE_FIELDS_DEFAULTS.get(CARRIER_ENABLED);
+ whereArgs[i++] = convertStringToBoolString(defaultIntString);
+ whereArgs[i++] = defaultIntString;
}
whereArgs[i++] = values.containsKey(BEARER) ?
@@ -1430,82 +1517,87 @@
private void copyPreservedApnsToNewTable(SQLiteDatabase db, Cursor c) {
// Move entries from CARRIERS_TABLE to CARRIERS_TABLE_TMP
- if (c != null) {
- String[] persistApnsForPlmns = mContext.getResources().getStringArray(
- R.array.persist_apns_for_plmn);
- while (c.moveToNext()) {
- ContentValues cv = new ContentValues();
- String val;
- // Using V17 copy function for V15 upgrade. This should be fine since it handles
- // columns that may not exist properly (getStringValueFromCursor() and
- // getIntValueFromCursor() handle column index -1)
- copyApnValuesV17(cv, c);
- // Change bearer to a bitmask
- String bearerStr = c.getString(c.getColumnIndex(BEARER));
- if (!TextUtils.isEmpty(bearerStr)) {
- int bearer_bitmask = ServiceState.getBitmaskForTech(
- Integer.parseInt(bearerStr));
- cv.put(BEARER_BITMASK, bearer_bitmask);
+ if (c != null && mContext.getResources() != null) {
+ try {
+ String[] persistApnsForPlmns = mContext.getResources().getStringArray(
+ R.array.persist_apns_for_plmn);
+ while (c.moveToNext()) {
+ ContentValues cv = new ContentValues();
+ String val;
+ // Using V17 copy function for V15 upgrade. This should be fine since it handles
+ // columns that may not exist properly (getStringValueFromCursor() and
+ // getIntValueFromCursor() handle column index -1)
+ copyApnValuesV17(cv, c);
+ // Change bearer to a bitmask
+ String bearerStr = c.getString(c.getColumnIndex(BEARER));
+ if (!TextUtils.isEmpty(bearerStr)) {
+ int bearer_bitmask = ServiceState.getBitmaskForTech(
+ Integer.parseInt(bearerStr));
+ cv.put(BEARER_BITMASK, bearer_bitmask);
- int networkTypeBitmask = ServiceState.getBitmaskForTech(
- ServiceState.rilRadioTechnologyToNetworkType(
- Integer.parseInt(bearerStr)));
- cv.put(NETWORK_TYPE_BITMASK, networkTypeBitmask);
- }
-
- int userEditedColumnIdx = c.getColumnIndex("user_edited");
- if (userEditedColumnIdx != -1) {
- String user_edited = c.getString(userEditedColumnIdx);
- if (!TextUtils.isEmpty(user_edited)) {
- cv.put(EDITED, new Integer(user_edited));
+ int networkTypeBitmask = ServiceState.getBitmaskForTech(
+ ServiceState.rilRadioTechnologyToNetworkType(
+ Integer.parseInt(bearerStr)));
+ cv.put(NETWORK_TYPE_BITMASK, networkTypeBitmask);
}
- } else {
- cv.put(EDITED, CARRIER_EDITED);
- }
- // New EDITED column. Default value (UNEDITED) will
- // be used for all rows except for non-mvno entries for plmns indicated
- // by resource: those will be set to CARRIER_EDITED to preserve
- // their current values
- val = c.getString(c.getColumnIndex(NUMERIC));
- for (String s : persistApnsForPlmns) {
- if (!TextUtils.isEmpty(val) && val.equals(s) &&
- (!cv.containsKey(MVNO_TYPE) ||
- TextUtils.isEmpty(cv.getAsString(MVNO_TYPE)))) {
- if (userEditedColumnIdx == -1) {
- cv.put(EDITED, CARRIER_EDITED);
- } else { // if (oldVersion == 14) -- if db had user_edited column
- if (cv.getAsInteger(EDITED) == USER_EDITED) {
- cv.put(EDITED, CARRIER_EDITED);
- }
+ int userEditedColumnIdx = c.getColumnIndex("user_edited");
+ if (userEditedColumnIdx != -1) {
+ String user_edited = c.getString(userEditedColumnIdx);
+ if (!TextUtils.isEmpty(user_edited)) {
+ cv.put(EDITED, new Integer(user_edited));
}
+ } else {
+ cv.put(EDITED, CARRIER_EDITED);
+ }
- break;
+ // New EDITED column. Default value (UNEDITED) will
+ // be used for all rows except for non-mvno entries for plmns indicated
+ // by resource: those will be set to CARRIER_EDITED to preserve
+ // their current values
+ val = c.getString(c.getColumnIndex(NUMERIC));
+ for (String s : persistApnsForPlmns) {
+ if (!TextUtils.isEmpty(val) && val.equals(s) &&
+ (!cv.containsKey(MVNO_TYPE) ||
+ TextUtils.isEmpty(cv.getAsString(MVNO_TYPE)))) {
+ if (userEditedColumnIdx == -1) {
+ cv.put(EDITED, CARRIER_EDITED);
+ } else { // if (oldVersion == 14) -- if db had user_edited column
+ if (cv.getAsInteger(EDITED) == USER_EDITED) {
+ cv.put(EDITED, CARRIER_EDITED);
+ }
+ }
+
+ break;
+ }
+ }
+
+ try {
+ db.insertWithOnConflict(CARRIERS_TABLE_TMP, null, cv,
+ SQLiteDatabase.CONFLICT_ABORT);
+ if (VDBG) {
+ log("dbh.copyPreservedApnsToNewTable: db.insert returned >= 0; " +
+ "insert successful for cv " + cv);
+ }
+ } catch (SQLException e) {
+ if (VDBG)
+ log("dbh.copyPreservedApnsToNewTable insertWithOnConflict exception " +
+ e + " for cv " + cv);
+ // Insertion failed which could be due to a conflict. Check if that is
+ // the case and merge the entries
+ Cursor oldRow = DatabaseHelper.selectConflictingRow(db,
+ CARRIERS_TABLE_TMP, cv);
+ if (oldRow != null) {
+ ContentValues mergedValues = new ContentValues();
+ mergeFieldsAndUpdateDb(db, CARRIERS_TABLE_TMP, oldRow, cv,
+ mergedValues, true, mContext);
+ oldRow.close();
+ }
}
}
-
- try {
- db.insertWithOnConflict(CARRIERS_TABLE_TMP, null, cv,
- SQLiteDatabase.CONFLICT_ABORT);
- if (VDBG) {
- log("dbh.copyPreservedApnsToNewTable: db.insert returned >= 0; " +
- "insert successful for cv " + cv);
- }
- } catch (SQLException e) {
- if (VDBG)
- log("dbh.copyPreservedApnsToNewTable insertWithOnConflict exception " +
- e + " for cv " + cv);
- // Insertion failed which could be due to a conflict. Check if that is
- // the case and merge the entries
- Cursor oldRow = DatabaseHelper.selectConflictingRow(db,
- CARRIERS_TABLE_TMP, cv);
- if (oldRow != null) {
- ContentValues mergedValues = new ContentValues();
- mergeFieldsAndUpdateDb(db, CARRIERS_TABLE_TMP, oldRow, cv,
- mergedValues, true, mContext);
- oldRow.close();
- }
- }
+ } catch (Resources.NotFoundException e) {
+ loge("array.persist_apns_for_plmn is not found");
+ return;
}
}
}
@@ -1893,13 +1985,17 @@
boolean match = false;
// Check if APN falls under persist_apns_for_plmn
- String[] persistApnsForPlmns = context.getResources().getStringArray(
- R.array.persist_apns_for_plmn);
- for (String s : persistApnsForPlmns) {
- if (s.equalsIgnoreCase(newRow.getAsString(NUMERIC))) {
- match = true;
- break;
+ if (context.getResources() != null) {
+ String[] persistApnsForPlmns = context.getResources().getStringArray(
+ R.array.persist_apns_for_plmn);
+ for (String s : persistApnsForPlmns) {
+ if (s.equalsIgnoreCase(newRow.getAsString(NUMERIC))) {
+ match = true;
+ break;
+ }
}
+ } else {
+ loge("separateRowsNeeded: resources=null");
}
if (!match) return false;
@@ -1994,15 +2090,16 @@
int i = 0;
String[] selectionArgs = new String[CARRIERS_UNIQUE_FIELDS.size()];
for (String field : CARRIERS_UNIQUE_FIELDS) {
- if (CARRIER_ENABLED.equals(field)) {
- // for CARRIER_ENABLED we overwrite the value "false" with "0"
- selectionArgs[i++] = row.containsKey(CARRIER_ENABLED) &&
- (row.getAsString(CARRIER_ENABLED).equals("0") ||
- row.getAsString(CARRIER_ENABLED).equals("false")) ?
- "0" : CARRIERS_UNIQUE_FIELDS_DEFAULTS.get(CARRIER_ENABLED);
+ if (!row.containsKey(field)) {
+ selectionArgs[i++] = CARRIERS_UNIQUE_FIELDS_DEFAULTS.get(field);
} else {
- selectionArgs[i++] = row.containsKey(field) ?
- row.getAsString(field) : CARRIERS_UNIQUE_FIELDS_DEFAULTS.get(field);
+ if (CARRIERS_BOOLEAN_FIELDS.contains(field)) {
+ // for boolean fields we overwrite the strings "true" and "false" with "1"
+ // and "0"
+ selectionArgs[i++] = convertStringToIntString(row.getAsString(field));
+ } else {
+ selectionArgs[i++] = row.getAsString(field);
+ }
}
}
@@ -2032,6 +2129,24 @@
}
/**
+ * Convert "true" and "false" to "1" and "0".
+ * If the passed in string is already "1" or "0" returns the passed in string.
+ */
+ private static String convertStringToIntString(String boolString) {
+ if ("0".equals(boolString) || "false".equalsIgnoreCase(boolString)) return "0";
+ return "1";
+ }
+
+ /**
+ * Convert "1" and "0" to "true" and "false".
+ * If the passed in string is already "true" or "false" returns the passed in string.
+ */
+ private static String convertStringToBoolString(String intString) {
+ if ("0".equals(intString) || "false".equalsIgnoreCase(intString)) return "false";
+ return "true";
+ }
+
+ /**
* These methods can be overridden in a subclass for testing TelephonyProvider using an
* in-memory database.
*/
@@ -2095,7 +2210,8 @@
r.getString(R.string.apn_source_service)));
log("binding to service to restore apns, intent=" + intent);
try {
- if (context.bindService(intent, connection, Context.BIND_AUTO_CREATE)) {
+ if (context.bindService(intent, connection, Context.BIND_IMPORTANT |
+ Context.BIND_AUTO_CREATE)) {
synchronized (mLock) {
while (mIApnSourceService == null) {
try {
@@ -2304,7 +2420,7 @@
private long getPreferredApnIdFromApn(int subId) {
log("getPreferredApnIdFromApn: for subId " + subId);
- SQLiteDatabase db = getWritableDatabase();
+ SQLiteDatabase db = getReadableDatabase();
String where = TextUtils.join("=? and ", CARRIERS_UNIQUE_FIELDS) + "=?";
String[] whereArgs = new String[CARRIERS_UNIQUE_FIELDS.size()];
SharedPreferences sp = getContext().getSharedPreferences(PREF_FILE_FULL_APN,
@@ -2517,6 +2633,8 @@
MMSC.equals(column) ||
MMSPROXY.equals(column) ||
MMSPORT.equals(column) ||
+ MVNO_TYPE.equals(column) ||
+ MVNO_MATCH_DATA.equals(column) ||
APN.equals(column)) {
// noop
} else {
@@ -3126,6 +3244,24 @@
break;
}
+ case URL_SIMINFO_USING_SUBID:
+ String subIdString = url.getLastPathSegment();
+ try {
+ subId = Integer.parseInt(subIdString);
+ } catch (NumberFormatException e) {
+ loge("NumberFormatException" + e);
+ throw new IllegalArgumentException("Invalid subId " + url);
+ }
+ if (DBG) log("subIdString = " + subIdString + " subId = " + subId);
+ if (where != null || whereArgs != null) {
+ throw new UnsupportedOperationException(
+ "Cannot update URL " + url + " with a where clause");
+ }
+ count = db.update(SIMINFO_TABLE, values, _ID + "=?",
+ new String[] { subIdString});
+ uriType = URL_SIMINFO_USING_SUBID;
+ break;
+
case URL_SIMINFO: {
count = db.update(SIMINFO_TABLE, values, where, whereArgs);
uriType = URL_SIMINFO;
@@ -3138,10 +3274,27 @@
}
if (count > 0) {
+ Uri wfcNotifyUri = SubscriptionManager.WFC_ENABLED_CONTENT_URI;
+ Uri enhanced4GNotifyUri = SubscriptionManager.ENHANCED_4G_ENABLED_CONTENT_URI;
switch (uriType) {
+ case URL_SIMINFO_USING_SUBID:
+ wfcNotifyUri = Uri.withAppendedPath(
+ SubscriptionManager.WFC_ENABLED_CONTENT_URI, "" + subId);
+ enhanced4GNotifyUri = Uri.withAppendedPath(
+ SubscriptionManager.ENHANCED_4G_ENABLED_CONTENT_URI, "" + subId);
+ // intentional fall through from above case
case URL_SIMINFO:
getContext().getContentResolver().notifyChange(
SubscriptionManager.CONTENT_URI, null, true, UserHandle.USER_ALL);
+ // notify observers on specific user settings changes.
+ if (values.containsKey(SubscriptionManager.WFC_IMS_ENABLED)) {
+ getContext().getContentResolver().notifyChange(
+ wfcNotifyUri, null, true, UserHandle.USER_ALL);
+ }
+ if (values.containsKey(SubscriptionManager.ENHANCED_4G_MODE_ENABLED)) {
+ getContext().getContentResolver().notifyChange(
+ enhanced4GNotifyUri, null, true, UserHandle.USER_ALL);
+ }
break;
default:
getContext().getContentResolver().notifyChange(
@@ -3165,7 +3318,7 @@
TelephonyManager telephonyManager =
(TelephonyManager) getContext().getSystemService(Context.TELEPHONY_SERVICE);
for (String pkg : packages) {
- if (telephonyManager.checkCarrierPrivilegesForPackage(pkg) ==
+ if (telephonyManager.checkCarrierPrivilegesForPackageAnyPhone(pkg) ==
TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) {
return;
}
@@ -3233,7 +3386,8 @@
String mvnoType = cursor.getString(0 /* MVNO_TYPE index */);
String mvnoMatchData = cursor.getString(1 /* MVNO_MATCH_DATA index */);
if (!TextUtils.isEmpty(mvnoType) && !TextUtils.isEmpty(mvnoMatchData)
- && ApnSetting.mvnoMatches(iccRecords, mvnoType, mvnoMatchData)) {
+ && ApnSettingUtils.mvnoMatches(iccRecords,
+ ApnSetting.getMvnoTypeIntFromString(mvnoType), mvnoMatchData)) {
where = NUMERIC + "='" + simOperator + "'"
+ " AND " + MVNO_TYPE + "='" + mvnoType + "'"
+ " AND " + MVNO_MATCH_DATA + "='" + mvnoMatchData + "'"
@@ -3289,12 +3443,60 @@
initDatabaseWithDatabaseHelper(db);
- // Notify listereners of DB change since DB has been updated
+ // Notify listeners of DB change since DB has been updated
getContext().getContentResolver().notifyChange(
CONTENT_URI, null, true, UserHandle.USER_ALL);
}
+ public static void fillInMccMncStringAtCursor(Context context, SQLiteDatabase db, Cursor c) {
+ int mcc, mnc;
+ String subId;
+ try {
+ mcc = c.getInt(c.getColumnIndexOrThrow(SubscriptionManager.MCC));
+ mnc = c.getInt(c.getColumnIndexOrThrow(SubscriptionManager.MNC));
+ subId = c.getString(c.getColumnIndexOrThrow(
+ SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID));
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "Possible database corruption -- some columns not found.");
+ return;
+ }
+
+ String mccString = String.format(Locale.getDefault(), "%03d", mcc);
+ String mncString = getBestStringMnc(context, mccString, mnc);
+ ContentValues cv = new ContentValues(2);
+ cv.put(SubscriptionManager.MCC_STRING, mccString);
+ cv.put(SubscriptionManager.MNC_STRING, mncString);
+ db.update(SIMINFO_TABLE, cv,
+ SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "=?",
+ new String[]{subId});
+ }
+
+ /*
+ * Find the best string-form mnc by looking up possibilities in the carrier id db.
+ * Default to the three-digit version if neither/both are valid.
+ */
+ private static String getBestStringMnc(Context context, String mcc, int mnc) {
+ if (mnc >= 100 && mnc <= 999) {
+ return String.valueOf(mnc);
+ }
+ String twoDigitMnc = String.format(Locale.getDefault(), "%02d", mnc);
+ String threeDigitMnc = "0" + twoDigitMnc;
+
+ try (
+ Cursor twoDigitMncCursor = context.getContentResolver().query(
+ Telephony.CarrierId.All.CONTENT_URI,
+ /* projection */ null,
+ /* selection */ Telephony.CarrierId.All.MCCMNC + "=?",
+ /* selectionArgs */ new String[]{mcc + twoDigitMnc}, null)
+ ) {
+ if (twoDigitMncCursor.getCount() > 0) {
+ return twoDigitMnc;
+ }
+ return threeDigitMnc;
+ }
+ }
+
/**
* Sync the bearer bitmask and network type bitmask when inserting and updating.
* Since bearerBitmask is deprecating, map the networkTypeBitmask to bearerBitmask if
diff --git a/tests/AndroidTest.xml b/tests/AndroidTest.xml
index 129f4d9..ad703de 100644
--- a/tests/AndroidTest.xml
+++ b/tests/AndroidTest.xml
@@ -22,7 +22,7 @@
<option name="test-tag" value="TelephonyProviderTests" />
<test class="com.android.tradefed.testtype.InstrumentationTest" >
<option name="package" value="com.android.providers.telephony.tests" />
- <option name="runner" value="android.test.InstrumentationTestRunner" />
+ <option name="runner" value="android.support.test.runner.AndroidJUnitRunner" />
<option name="hidden-api-checks" value="false"/>
</test>
</configuration>
diff --git a/tests/src/com/android/providers/telephony/CarrierIdProviderTest.java b/tests/src/com/android/providers/telephony/CarrierIdProviderTest.java
index 4adcf43..44d9ec9 100644
--- a/tests/src/com/android/providers/telephony/CarrierIdProviderTest.java
+++ b/tests/src/com/android/providers/telephony/CarrierIdProviderTest.java
@@ -104,14 +104,10 @@
private class MockContextWithProvider extends MockContext {
private final MockContentResolver mResolver;
- public MockContextWithProvider(CarrierIdProvider carrierIdProvider) {
+ public MockContextWithProvider(CarrierIdProviderTestable carrierIdProvider) {
mResolver = new FakeContentResolver();
- ProviderInfo providerInfo = new ProviderInfo();
- providerInfo.authority = CarrierIdProvider.AUTHORITY;
-
- // Add context to given carrierIdProvider
- carrierIdProvider.attachInfoForTesting(this, providerInfo);
+ carrierIdProvider.initializeForTesting(this);
Log.d(TAG, "MockContextWithProvider: carrierIdProvider.getContext(): "
+ carrierIdProvider.getContext());
diff --git a/tests/src/com/android/providers/telephony/CarrierIdProviderTestable.java b/tests/src/com/android/providers/telephony/CarrierIdProviderTestable.java
index 8468801..9ecd93a 100644
--- a/tests/src/com/android/providers/telephony/CarrierIdProviderTestable.java
+++ b/tests/src/com/android/providers/telephony/CarrierIdProviderTestable.java
@@ -15,6 +15,8 @@
*/
package com.android.providers.telephony;
+import android.content.Context;
+import android.content.pm.ProviderInfo;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;
@@ -51,6 +53,14 @@
return mDbHelper.getWritableDatabase();
}
+ void initializeForTesting(Context context) {
+ ProviderInfo providerInfo = new ProviderInfo();
+ providerInfo.authority = CarrierIdProvider.AUTHORITY;
+
+ // Add context to given carrierIdProvider
+ attachInfoForTesting(context, providerInfo);
+ }
+
/**
* An in memory DB for CarrierIdProviderTestable to use
*/
diff --git a/tests/src/com/android/providers/telephony/ServiceStateProviderTest.java b/tests/src/com/android/providers/telephony/ServiceStateProviderTest.java
index fd15d01..c5ad5a0 100644
--- a/tests/src/com/android/providers/telephony/ServiceStateProviderTest.java
+++ b/tests/src/com/android/providers/telephony/ServiceStateProviderTest.java
@@ -87,7 +87,6 @@
ServiceStateTable.CDMA_ERI_ICON_INDEX,
ServiceStateTable.CDMA_ERI_ICON_MODE,
ServiceStateTable.IS_EMERGENCY_ONLY,
- ServiceStateTable.IS_DATA_ROAMING_FROM_REGISTRATION,
ServiceStateTable.IS_USING_CARRIER_AGGREGATION,
};
@@ -188,7 +187,6 @@
final int cdmaEriIconIndex = ss.getCdmaEriIconIndex();
final int cdmaEriIconMode = ss.getCdmaEriIconMode();
final int isEmergencyOnly = (ss.isEmergencyOnly()) ? 1 : 0;
- final int isDataRoamingFromRegistration = (ss.getDataRoamingFromRegistration()) ? 1 : 0;
final int isUsingCarrierAggregation = (ss.isUsingCarrierAggregation()) ? 1 : 0;
assertEquals(voiceRegState, cursor.getInt(0));
@@ -210,8 +208,7 @@
assertEquals(cdmaEriIconIndex, cursor.getInt(16));
assertEquals(cdmaEriIconMode, cursor.getInt(17));
assertEquals(isEmergencyOnly, cursor.getInt(18));
- assertEquals(isDataRoamingFromRegistration, cursor.getInt(19));
- assertEquals(isUsingCarrierAggregation, cursor.getInt(20));
+ assertEquals(isUsingCarrierAggregation, cursor.getInt(19));
}
/**
diff --git a/tests/src/com/android/providers/telephony/TelephonyDatabaseHelperTest.java b/tests/src/com/android/providers/telephony/TelephonyDatabaseHelperTest.java
new file mode 100644
index 0000000..d6b989a
--- /dev/null
+++ b/tests/src/com/android/providers/telephony/TelephonyDatabaseHelperTest.java
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) 2018 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.providers.telephony;
+
+import static android.provider.Telephony.Carriers;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.not;
+import static org.hamcrest.Matchers.equalTo;
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyInt;
+
+import android.content.Context;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.database.sqlite.SQLiteDatabase;
+import android.support.test.InstrumentationRegistry;
+import android.telephony.SubscriptionManager;
+import android.text.TextUtils;
+import android.util.Log;
+
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Arrays;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public final class TelephonyDatabaseHelperTest {
+
+ private final static String TAG = TelephonyDatabaseHelperTest.class.getSimpleName();
+
+ private Context mContext;
+ private TelephonyProvider.DatabaseHelper mHelper; // the actual class being tested
+ private SQLiteOpenHelper mInMemoryDbHelper; // used to give us an in-memory db
+
+ @Before
+ public void setUp() {
+ Log.d(TAG, "setUp() +");
+ mContext = InstrumentationRegistry.getContext();
+ mHelper = new TelephonyProvider.DatabaseHelper(mContext);
+ mInMemoryDbHelper = new InMemoryTelephonyProviderV5DbHelper();
+ Log.d(TAG, "setUp() -");
+ }
+
+ @Test
+ public void databaseHelperOnUpgrade_hasApnSetIdField() {
+ Log.d(TAG, "databaseHelperOnUpgrade_hasApnSetIdField");
+ // (5 << 16 | 6) is the first upgrade trigger in onUpgrade
+ SQLiteDatabase db = mInMemoryDbHelper.getWritableDatabase();
+ mHelper.onUpgrade(db, (4 << 16), TelephonyProvider.DatabaseHelper.getVersion(mContext));
+
+ // the upgraded db must have the APN_SET_ID field
+ Cursor cursor = db.query("carriers", null, null, null, null, null, null);
+ String[] upgradedColumns = cursor.getColumnNames();
+ Log.d(TAG, "carriers columns: " + Arrays.toString(upgradedColumns));
+
+ assertTrue(Arrays.asList(upgradedColumns).contains(Carriers.APN_SET_ID));
+ }
+
+ @Test
+ public void databaseHelperOnUpgrade_columnsMatchNewlyCreatedDb() {
+ Log.d(TAG, "databaseHelperOnUpgrade_columnsMatchNewlyCreatedDb");
+ // (5 << 16 | 6) is the first upgrade trigger in onUpgrade
+ SQLiteDatabase db = mInMemoryDbHelper.getWritableDatabase();
+ mHelper.onUpgrade(db, (4 << 16), TelephonyProvider.DatabaseHelper.getVersion(mContext));
+
+ // compare upgraded carriers table to a carriers table created from scratch
+ db.execSQL(TelephonyProvider.getStringForCarrierTableCreation("carriers_full"));
+
+ Cursor cursor = db.query("carriers", null, null, null, null, null, null);
+ String[] upgradedColumns = cursor.getColumnNames();
+ Log.d(TAG, "carriers columns: " + Arrays.toString(upgradedColumns));
+
+ cursor = db.query("carriers_full", null, null, null, null, null, null);
+ String[] fullColumns = cursor.getColumnNames();
+ Log.d(TAG, "carriers_full colunmns: " + Arrays.toString(fullColumns));
+
+ assertArrayEquals("Carriers table from onUpgrade doesn't match full table",
+ fullColumns, upgradedColumns);
+
+ // compare upgraded siminfo table to siminfo table created from scratch
+ db.execSQL(TelephonyProvider.getStringForSimInfoTableCreation("siminfo_full"));
+
+ cursor = db.query("siminfo", null, null, null, null, null, null);
+ upgradedColumns = cursor.getColumnNames();
+ Log.d(TAG, "siminfo columns: " + Arrays.toString(upgradedColumns));
+
+ cursor = db.query("siminfo_full", null, null, null, null, null, null);
+ fullColumns = cursor.getColumnNames();
+ Log.d(TAG, "siminfo_full colunmns: " + Arrays.toString(fullColumns));
+
+ assertArrayEquals("Siminfo table from onUpgrade doesn't match full table",
+ fullColumns, upgradedColumns);
+ }
+
+ /**
+ * Helper for an in memory DB used to test the TelephonyProvider#DatabaseHelper.
+ *
+ * We pass this in-memory db to DatabaseHelper#onUpgrade so we can use the actual function
+ * without using the actual telephony db.
+ */
+ private static class InMemoryTelephonyProviderV5DbHelper extends SQLiteOpenHelper {
+
+ public InMemoryTelephonyProviderV5DbHelper() {
+ super(InstrumentationRegistry.getContext(),
+ null, // db file name is null for in-memory db
+ null, // CursorFactory is null by default
+ 1); // in-memory db version doesn't seem to matter
+ Log.d(TAG, "InMemoryTelephonyProviderV5DbHelper creating in-memory database");
+ }
+
+ @Override
+ public void onCreate(SQLiteDatabase db) {
+ // Set up the carriers table without any fields added in onUpgrade
+ // since these are the initial fields, there is no need to update this test fixture in
+ // the future
+ List<String> originalUniqueFields = new ArrayList<String>();
+ originalUniqueFields.add(Carriers.NUMERIC);
+ originalUniqueFields.add(Carriers.MCC);
+ originalUniqueFields.add(Carriers.MNC);
+ originalUniqueFields.add(Carriers.APN);
+ originalUniqueFields.add(Carriers.PROXY);
+ originalUniqueFields.add(Carriers.PORT);
+ originalUniqueFields.add(Carriers.MMSPROXY);
+ originalUniqueFields.add(Carriers.MMSPORT);
+ originalUniqueFields.add(Carriers.MMSC);
+ Log.d(TAG, "InMemoryTelephonyProviderV5DbHelper onCreate creating the carriers table");
+ db.execSQL(
+ "CREATE TABLE carriers" +
+ "(_id INTEGER PRIMARY KEY," +
+ Carriers.NAME + " TEXT DEFAULT ''," +
+ Carriers.NUMERIC + " TEXT DEFAULT ''," +
+ Carriers.MCC + " TEXT DEFAULT ''," +
+ Carriers.MNC + " TEXT DEFAULT ''," +
+ Carriers.APN + " TEXT DEFAULT ''," +
+ Carriers.USER + " TEXT DEFAULT ''," +
+ Carriers.SERVER + " TEXT DEFAULT ''," +
+ Carriers.PASSWORD + " TEXT DEFAULT ''," +
+ Carriers.PROXY + " TEXT DEFAULT ''," +
+ Carriers.PORT + " TEXT DEFAULT ''," +
+ Carriers.MMSPROXY + " TEXT DEFAULT ''," +
+ Carriers.MMSPORT + " TEXT DEFAULT ''," +
+ Carriers.MMSC + " TEXT DEFAULT ''," +
+ Carriers.TYPE + " TEXT DEFAULT ''," +
+ Carriers.CURRENT + " INTEGER," +
+ "UNIQUE (" + TextUtils.join(", ", originalUniqueFields) + "));");
+
+ // set up the siminfo table without any fields added in onUpgrade
+ // since these are the initial fields, there is no need to update this test fixture in
+ // the future
+ Log.d(TAG, "InMemoryTelephonyProviderV5DbHelper onCreate creating the siminfo table");
+ db.execSQL(
+ "CREATE TABLE siminfo ("
+ + SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID
+ + " INTEGER PRIMARY KEY AUTOINCREMENT,"
+ + SubscriptionManager.ICC_ID + " TEXT NOT NULL,"
+ + SubscriptionManager.SIM_SLOT_INDEX
+ + " INTEGER DEFAULT " + SubscriptionManager.SIM_NOT_INSERTED + ","
+ + SubscriptionManager.DISPLAY_NAME + " TEXT,"
+ + SubscriptionManager.NAME_SOURCE
+ + " INTEGER DEFAULT " + SubscriptionManager.NAME_SOURCE_DEFAULT_SOURCE + ","
+ + SubscriptionManager.COLOR
+ + " INTEGER DEFAULT " + SubscriptionManager.COLOR_DEFAULT + ","
+ + SubscriptionManager.NUMBER + " TEXT,"
+ + SubscriptionManager.DISPLAY_NUMBER_FORMAT + " INTEGER NOT NULL"
+ + " DEFAULT " + SubscriptionManager.DISPLAY_NUMBER_DEFAULT + ","
+ + SubscriptionManager.DATA_ROAMING
+ + " INTEGER DEFAULT " + SubscriptionManager.DATA_ROAMING_DEFAULT + ","
+ + SubscriptionManager.CARD_ID + " TEXT NOT NULL"
+ + ");");
+ }
+
+ @Override
+ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+ Log.d(TAG, "InMemoryTelephonyProviderV5DbHelper onUpgrade doing nothing");
+ return;
+ }
+ }
+}
diff --git a/tests/src/com/android/providers/telephony/TelephonyProviderTest.java b/tests/src/com/android/providers/telephony/TelephonyProviderTest.java
index dde9e5c..715f3dd 100644
--- a/tests/src/com/android/providers/telephony/TelephonyProviderTest.java
+++ b/tests/src/com/android/providers/telephony/TelephonyProviderTest.java
@@ -16,59 +16,40 @@
package com.android.providers.telephony;
-import android.annotation.TargetApi;
-import android.content.ContentProvider;
-import android.content.ContentResolver;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+
+import android.Manifest;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
+import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.content.pm.ProviderInfo;
import android.content.res.Resources;
-import android.content.SharedPreferences;
-import android.database.Cursor;
import android.database.ContentObserver;
-import android.database.DatabaseErrorHandler;
-import android.database.sqlite.SQLiteDatabase;
-import android.database.sqlite.SQLiteOpenHelper;
-import android.database.sqlite.SQLiteDatabase.CursorFactory;
+import android.database.Cursor;
import android.net.Uri;
-import android.os.Build;
-import android.os.FileUtils;
import android.os.Process;
+import android.provider.Telephony;
import android.provider.Telephony.Carriers;
import android.support.test.InstrumentationRegistry;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
-import android.test.AndroidTestCase;
-import android.test.mock.MockContentProvider;
import android.test.mock.MockContentResolver;
import android.test.mock.MockContext;
import android.test.suitebuilder.annotation.SmallTest;
import android.text.TextUtils;
import android.util.Log;
-import com.android.providers.telephony.TelephonyProvider;
-
import junit.framework.TestCase;
-import org.junit.After;
-import org.junit.Before;
import org.junit.Test;
-import org.mockito.ArgumentCaptor;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyLong;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import java.util.Map;
-import java.util.Set;
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.IntStream;
/**
@@ -92,6 +73,8 @@
private int notifyChangeCount;
private int notifyChangeRestoreCount;
+ private int notifyWfcCount;
+ private int notifyWfcCountWithTestSubId;
private static final String TEST_SUBID = "1";
private static final String TEST_OPERATOR = "123456";
@@ -107,6 +90,8 @@
// Used to test the preferred apn
private static final Uri URL_PREFERAPN_USING_SUBID = Uri.parse(
"content://telephony/carriers/preferapn/subId/" + TEST_SUBID);
+ private static final Uri URL_WFC_ENABLED_USING_SUBID = Uri.parse(
+ "content://telephony/siminfo/" + TEST_SUBID);
private static final String COLUMN_APN_ID = "apn_id";
@@ -126,6 +111,10 @@
private final MockContentResolver mResolver;
private TelephonyManager mTelephonyManager = mock(TelephonyManager.class);
+ private final List<String> GRANTED_PERMISSIONS = Arrays.asList(
+ Manifest.permission.MODIFY_PHONE_STATE, Manifest.permission.WRITE_APN_SETTINGS,
+ Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
+
public MockContextWithProvider(TelephonyProvider telephonyProvider) {
mResolver = new MockContentResolver() {
@Override
@@ -134,6 +123,10 @@
notifyChangeCount++;
if (URL_RESTOREAPN_USING_SUBID.equals(uri)) {
notifyChangeRestoreCount++;
+ } else if (SubscriptionManager.WFC_ENABLED_CONTENT_URI.equals(uri)) {
+ notifyWfcCount++;
+ } else if (URL_WFC_ENABLED_USING_SUBID.equals(uri)) {
+ notifyWfcCountWithTestSubId++;
}
}
};
@@ -186,7 +179,7 @@
// Gives permission to write to the APN table within the MockContext
@Override
public int checkCallingOrSelfPermission(String permission) {
- if (TextUtils.equals(permission, "android.permission.WRITE_APN_SETTINGS")) {
+ if (GRANTED_PERMISSIONS.contains(permission)) {
Log.d(TAG, "checkCallingOrSelfPermission: permission=" + permission
+ ", returning PackageManager.PERMISSION_GRANTED");
return PackageManager.PERMISSION_GRANTED;
@@ -287,6 +280,64 @@
}
/**
+ * Test migrating int-based MCC/MNCs over to Strings in the sim info table
+ */
+ @Test
+ @SmallTest
+ public void testMccMncMigration() {
+ CarrierIdProviderTestable carrierIdProvider = new CarrierIdProviderTestable();
+ carrierIdProvider.initializeForTesting(mContext);
+ mContentResolver.addProvider(Telephony.CarrierId.All.CONTENT_URI.getAuthority(),
+ carrierIdProvider);
+ // Insert a few values into the carrier ID db
+ List<String> mccMncs = Arrays.asList("99910", "999110", "999060", "99905");
+ ContentValues[] carrierIdMccMncs = mccMncs.stream()
+ .map((mccMnc) -> {
+ ContentValues cv = new ContentValues(1);
+ cv.put(Telephony.CarrierId.All.MCCMNC, mccMnc);
+ return cv;
+ }).toArray(ContentValues[]::new);
+ mContentResolver.bulkInsert(Telephony.CarrierId.All.CONTENT_URI, carrierIdMccMncs);
+
+ // Populate the sim info db with int-format entries
+ ContentValues[] existingSimInfoEntries = IntStream.range(0, mccMncs.size())
+ .mapToObj((idx) -> {
+ int mcc = Integer.valueOf(mccMncs.get(idx).substring(0, 3));
+ int mnc = Integer.valueOf(mccMncs.get(idx).substring(3));
+ ContentValues cv = new ContentValues(4);
+ cv.put(SubscriptionManager.MCC, mcc);
+ cv.put(SubscriptionManager.MNC, mnc);
+ cv.put(SubscriptionManager.ICC_ID, String.valueOf(idx));
+ cv.put(SubscriptionManager.CARD_ID, String.valueOf(idx));
+ return cv;
+ }).toArray(ContentValues[]::new);
+
+ mContentResolver.bulkInsert(SubscriptionManager.CONTENT_URI, existingSimInfoEntries);
+
+ // Run the upgrade helper on all the sim info entries.
+ String[] proj = {SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID,
+ SubscriptionManager.MCC, SubscriptionManager.MNC,
+ SubscriptionManager.MCC_STRING, SubscriptionManager.MNC_STRING};
+ try (Cursor c = mContentResolver.query(SubscriptionManager.CONTENT_URI, proj,
+ null, null, null)) {
+ while (c.moveToNext()) {
+ TelephonyProvider.fillInMccMncStringAtCursor(mContext,
+ mTelephonyProviderTestable.getWritableDatabase(), c);
+ }
+ }
+
+ // Loop through and make sure that everything got filled in correctly.
+ try (Cursor c = mContentResolver.query(SubscriptionManager.CONTENT_URI, proj,
+ null, null, null)) {
+ while (c.moveToNext()) {
+ String mcc = c.getString(c.getColumnIndexOrThrow(SubscriptionManager.MCC_STRING));
+ String mnc = c.getString(c.getColumnIndexOrThrow(SubscriptionManager.MNC_STRING));
+ assertTrue(mccMncs.contains(mcc + mnc));
+ }
+ }
+ }
+
+ /**
* Test updating values in carriers table. Verify that when update hits a conflict using URL_ID
* we merge the rows.
*/
@@ -1339,4 +1390,53 @@
assertEquals(0, cursor.getCount());
assertEquals(3, notifyChangeRestoreCount);
}
+
+ /**
+ * Test changes to siminfo/WFC_IMS_ENABLED and simInfo/ENHANCED_4G
+ */
+ @Test
+ @SmallTest
+ public void testUpdateWfcEnabled() {
+ // insert test contentValues
+ ContentValues contentValues = new ContentValues();
+ final int insertSubId = 1;
+ final String insertDisplayName = "exampleDisplayName";
+ final String insertCarrierName = "exampleCarrierName";
+ final String insertIccId = "exampleIccId";
+ final String insertCardId = "exampleCardId";
+ contentValues.put(SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID, insertSubId);
+ contentValues.put(SubscriptionManager.DISPLAY_NAME, insertDisplayName);
+ contentValues.put(SubscriptionManager.CARRIER_NAME, insertCarrierName);
+ contentValues.put(SubscriptionManager.ICC_ID, insertIccId);
+ contentValues.put(SubscriptionManager.CARD_ID, insertCardId);
+
+ Log.d(TAG, "testSimTable Inserting wfc contentValues: " + contentValues);
+ mContentResolver.insert(SubscriptionManager.CONTENT_URI, contentValues);
+ assertEquals(0, notifyWfcCount);
+
+ // update wfc_enabled
+ ContentValues values = new ContentValues();
+ values.put(SubscriptionManager.WFC_IMS_ENABLED, true);
+ final String selection = SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "=?";
+ final String[] selectionArgs = { "" + insertSubId };
+ mContentResolver.update(SubscriptionManager.CONTENT_URI, values, selection, selectionArgs);
+ assertEquals(1, notifyWfcCount);
+ assertEquals(0, notifyWfcCountWithTestSubId);
+
+ // update other fields
+ values = new ContentValues();
+ values.put(SubscriptionManager.DISPLAY_NAME, "exampleDisplayNameNew");
+ mContentResolver.update(SubscriptionManager.CONTENT_URI, values, selection, selectionArgs);
+ // expect no change on wfc count
+ assertEquals(1, notifyWfcCount);
+ assertEquals(0, notifyWfcCountWithTestSubId);
+
+ // update WFC using subId
+ values = new ContentValues();
+ values.put(SubscriptionManager.WFC_IMS_ENABLED, false);
+ mContentResolver.update(SubscriptionManager.getUriForSubscriptionId(insertSubId),
+ values, null, null);
+ assertEquals(1, notifyWfcCount);
+ assertEquals(0, notifyWfcCountWithTestSubId);
+ }
}