Change to add new column and unique constraint to carriers db.
Includes changes to ensure APN DB updates over the air does not affect
user/app edited entries and unique contraint avoids duplicate entries.
Change-Id: I7cae3d229445607c66b6472124264588f7571097
diff --git a/res/values/config.xml b/res/values/config.xml
new file mode 100644
index 0000000..85e15e3
--- /dev/null
+++ b/res/values/config.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <!-- persist APNs for these PLMNs on db upgrade to version 14 -->
+ <string-array name="persist_apns_for_plmn">
+ <item>20404</item>
+ <item>310120</item>
+ <item>311480</item>
+ </string-array>
+</resources>
diff --git a/src/com/android/providers/telephony/TelephonyProvider.java b/src/com/android/providers/telephony/TelephonyProvider.java
index 64ab430..e604196 100644
--- a/src/com/android/providers/telephony/TelephonyProvider.java
+++ b/src/com/android/providers/telephony/TelephonyProvider.java
@@ -35,16 +35,15 @@
import android.net.Uri;
import android.os.Binder;
import android.os.Environment;
+import android.os.SystemProperties;
import android.os.UserHandle;
import android.provider.Telephony;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
+import android.text.TextUtils;
import android.util.Log;
import android.util.Xml;
-import com.android.internal.telephony.BaseCommands;
-import com.android.internal.telephony.Phone;
-import com.android.internal.telephony.PhoneConstants;
import com.android.internal.util.XmlUtils;
import org.xmlpull.v1.XmlPullParser;
@@ -55,14 +54,16 @@
import java.io.FileReader;
import java.io.IOException;
import java.lang.NumberFormatException;
+import java.util.ArrayList;
+import java.util.Arrays;
public class TelephonyProvider extends ContentProvider
{
private static final String DATABASE_NAME = "telephony.db";
private static final boolean DBG = true;
- private static final boolean VDBG = false;
+ private static final boolean VDBG = false; // STOPSHIP if true
- private static final int DATABASE_VERSION = 13 << 16;
+ private static final int DATABASE_VERSION = 14 << 16;
private static final int URL_UNKNOWN = 0;
private static final int URL_TELEPHONY = 1;
private static final int URL_CURRENT = 2;
@@ -77,14 +78,19 @@
private static final int URL_PREFERAPN_USING_SUBID = 11;
private static final int URL_PREFERAPN_NO_UPDATE_USING_SUBID = 12;
private static final int URL_SIMINFO_USING_SUBID = 13;
+ private static final int URL_UPDATE_DB = 14;
private static final String TAG = "TelephonyProvider";
private static final String CARRIERS_TABLE = "carriers";
+ private static final String CARRIERS_TABLE_TMP = "carriers_tmp";
private static final String SIMINFO_TABLE = "siminfo";
private static final String PREF_FILE = "preferred-apn";
private static final String COLUMN_APN_ID = "apn_id";
+ private static final String BUILD_ID_FILE = "build-id";
+ private static final String RO_BUILD_ID = "ro_build_id";
+
private static final String PARTNER_APNS_PATH = "etc/apns-conf.xml";
private static final String OEM_APNS_PATH = "telephony/apns-conf.xml";
@@ -93,6 +99,14 @@
private static final ContentValues s_currentNullMap;
private static final ContentValues s_currentSetMap;
+ private static final int USER_EDITED_UNTOUCHED = 0;
+ private static final int USER_EDITED_EDITED = 1;
+ private static final int USER_EDITED_DELETED = 2;
+ // DELETED_BUT_PRESENT is an intermediate value used to indicate that an entry deleted
+ // by the user is still present in the new APN database and therefore must remain tagged
+ // as user deleted rather than completely removed from the database
+ private static final int USER_EDITED_DELETED_BUT_PRESENT = 3;
+
static {
s_urlMatcher.addURI("telephony", "carriers", URL_TELEPHONY);
s_urlMatcher.addURI("telephony", "carriers/current", URL_CURRENT);
@@ -110,12 +124,13 @@
s_urlMatcher.addURI("telephony", "carriers/preferapn_no_update/subId/*",
URL_PREFERAPN_NO_UPDATE_USING_SUBID);
+ s_urlMatcher.addURI("telephony", "carriers/update_db", URL_UPDATE_DB);
s_currentNullMap = new ContentValues(1);
- s_currentNullMap.put("current", (Long) null);
+ s_currentNullMap.put(Telephony.Carriers.CURRENT, "0");
s_currentSetMap = new ContentValues(1);
- s_currentSetMap.put("current", "1");
+ s_currentSetMap.put(Telephony.Carriers.CURRENT, "1");
}
private static class DatabaseHelper extends SQLiteOpenHelper {
@@ -156,7 +171,7 @@
public void onCreate(SQLiteDatabase db) {
if (DBG) log("dbh.onCreate:+ db=" + db);
createSimInfoTable(db);
- createCarriersTable(db);
+ createCarriersTable(db, CARRIERS_TABLE);
initDatabase(db);
if (DBG) log("dbh.onCreate:- db=" + db);
}
@@ -180,7 +195,7 @@
} catch (SQLiteException e) {
loge("Exception " + CARRIERS_TABLE + " e=" + e);
if (e.getMessage().startsWith("no such table")) {
- createCarriersTable(db);
+ createCarriersTable(db, CARRIERS_TABLE);
}
}
if (VDBG) log("dbh.onOpen:- db=" + db);
@@ -205,40 +220,49 @@
if (DBG) log("dbh.createSimInfoTable:-");
}
- private void createCarriersTable(SQLiteDatabase db) {
+ private void createCarriersTable(SQLiteDatabase db, String tableName) {
// Set up the database schema
- if (DBG) log("dbh.createCarriersTable:+");
- db.execSQL("CREATE TABLE " + CARRIERS_TABLE +
+ if (DBG) log("dbh.createCarriersTable: " + tableName);
+ db.execSQL("CREATE TABLE " + tableName +
"(_id INTEGER PRIMARY KEY," +
- "name TEXT," +
- "numeric TEXT," +
- "mcc TEXT," +
- "mnc TEXT," +
- "apn TEXT," +
- "user TEXT," +
- "server TEXT," +
- "password TEXT," +
- "proxy TEXT," +
- "port TEXT," +
- "mmsproxy TEXT," +
- "mmsport TEXT," +
- "mmsc TEXT," +
- "authtype INTEGER," +
- "type TEXT," +
+ "name TEXT DEFAULT ''," +
+ "numeric TEXT DEFAULT ''," +
+ "mcc TEXT DEFAULT ''," +
+ "mnc TEXT DEFAULT ''," +
+ "apn TEXT DEFAULT ''," +
+ "user TEXT DEFAULT ''," +
+ "server TEXT DEFAULT ''," +
+ "password TEXT DEFAULT ''," +
+ "proxy TEXT DEFAULT ''," +
+ "port TEXT DEFAULT ''," +
+ "mmsproxy TEXT DEFAULT ''," +
+ "mmsport TEXT DEFAULT ''," +
+ "mmsc TEXT DEFAULT ''," +
+ "authtype INTEGER DEFAULT -1," +
+ "type TEXT DEFAULT ''," +
"current INTEGER," +
- "protocol TEXT," +
- "roaming_protocol TEXT," +
- "carrier_enabled BOOLEAN," +
- "bearer INTEGER," +
- "mvno_type TEXT," +
- "mvno_match_data TEXT," +
+ "protocol TEXT DEFAULT 'IP'," +
+ "roaming_protocol TEXT DEFAULT 'IP'," +
+ "carrier_enabled BOOLEAN DEFAULT 1," +
+ "bearer INTEGER DEFAULT 0," +
+ "mvno_type TEXT DEFAULT ''," +
+ "mvno_match_data TEXT DEFAULT ''," +
"sub_id INTEGER DEFAULT " + SubscriptionManager.INVALID_SUBSCRIPTION_ID + "," +
- "profile_id INTEGER default 0," +
- "modem_cognitive BOOLEAN default 0," +
- "max_conns INTEGER default 0," +
- "wait_time INTEGER default 0," +
- "max_conns_time INTEGER default 0," +
- "mtu INTEGER);");
+ "profile_id INTEGER DEFAULT 0," +
+ "modem_cognitive BOOLEAN DEFAULT 0," +
+ "max_conns INTEGER DEFAULT 0," +
+ "wait_time INTEGER DEFAULT 0," +
+ "max_conns_time INTEGER DEFAULT 0," +
+ "mtu INTEGER DEFAULT 0," +
+ "user_edited INTEGER DEFAULT " + USER_EDITED_UNTOUCHED + "," +
+ // Uniqueness collisions are used to trigger merge code so
+ // if a field is listed
+ // here it means we will accept both (user edited + new apn_conf definition)
+ // Columns not included in UNIQUE constraint: name, current, user_edited,
+ // user, server, password, authtype, type, protocol, roaming_protocol, sub_id,
+ // modem_cognitive, max_conns, wait_time, max_conns_time, mtu
+ "UNIQUE (numeric, mcc, mnc, apn, proxy, port, mmsproxy, mmsport, mmsc," +
+ "carrier_enabled, bearer, mvno_type, mvno_match_data, profile_id));");
if (DBG) log("dbh.createCarriersTable:-");
}
@@ -292,6 +316,7 @@
// Sanity check. Force internal version and confidential versions to agree
int confversion = Integer.parseInt(confparser.getAttributeValue(null, "version"));
if (publicversion != confversion) {
+ log("initDatabase: throwing exception due to version mismatch");
throw new IllegalStateException("Internal APNS file version doesn't match "
+ confFile.getAbsolutePath());
}
@@ -301,9 +326,31 @@
// It's ok if the file isn't found. It means there isn't a confidential file
// Log.e(TAG, "File not found: '" + confFile.getAbsolutePath() + "'");
} catch (Exception e) {
- loge("Exception while parsing '" + confFile.getAbsolutePath() + "'" + e);
+ loge("initDatabase: Exception while parsing '" + confFile.getAbsolutePath() + "'" +
+ e);
} finally {
- try { if (confreader != null) confreader.close(); } catch (IOException e) { }
+ // Get rid of user deleted entries that are not present in apn xml file. Those
+ // entries have user_edited value USER_EDITED_DELETED.
+ // Update user_edited value from USER_EDITED_DELETED_BUT_PRESENT
+ // to USER_EDITED_DELETED.
+ // USER_EDITED_DELETED_BUT_PRESENT indicates rows deleted by user but still
+ // present in the xml file. Mark them as user deleted (2).
+ if (VDBG) {
+ log("initDatabase: deleting USER_EDITED_DELETED and replacing "
+ + "USER_EDITED_DELETED_BUT_PRESENT with USER_EDITED_DELETED");
+ }
+ db.delete(CARRIERS_TABLE, "user_edited=" + USER_EDITED_DELETED, null);
+ ContentValues cv = new ContentValues();
+ cv.put(Telephony.Carriers.USER_EDITED, USER_EDITED_DELETED);
+ db.update(CARRIERS_TABLE, cv, "user_edited=" + USER_EDITED_DELETED_BUT_PRESENT,
+ null);
+ if (confreader != null) {
+ try {
+ confreader.close();
+ } catch (IOException e) {
+ // do nothing
+ }
+ }
}
if (VDBG) log("dbh.initDatabase:- db=" + db);
@@ -400,8 +447,8 @@
if (oldVersion < (13 << 16 | 6)) {
try {
// Try to update the siminfo table. It might not be there.
- db.execSQL("ALTER TABLE " + SIMINFO_TABLE +
- " ADD COLUMN " + SubscriptionManager.CARRIER_NAME + " TEXT DEFAULT '';");
+ db.execSQL("ALTER TABLE " + SIMINFO_TABLE + " ADD COLUMN " +
+ SubscriptionManager.CARRIER_NAME + " TEXT DEFAULT '';");
} catch (SQLiteException e) {
if (DBG) {
log("onUpgrade skipping " + SIMINFO_TABLE + " upgrade. " +
@@ -410,11 +457,218 @@
}
oldVersion = 13 << 16 | 6;
}
+ if (oldVersion < (14 << 16 | 6)) {
+ Cursor c = db.query(CARRIERS_TABLE, null, null, null, null, null, null);
+ if (VDBG) {
+ log("dbh.onUpgrade:- before upgrading total number of rows: " + c.getCount());
+ }
+
+ createCarriersTable(db, CARRIERS_TABLE_TMP);
+
+ // 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;
+
+ // Include only non-null values in cv so that null values can be replaced
+ // with default if there's a default value for the field
+
+ // String vals
+ val = getValueFromCursor(c, Telephony.Carriers.NAME);
+ if (val != null) {
+ cv.put(Telephony.Carriers.NAME, val);
+ }
+ val = getValueFromCursor(c, Telephony.Carriers.NUMERIC);
+ if (val != null) {
+ cv.put(Telephony.Carriers.NUMERIC, val);
+ }
+ val = getValueFromCursor(c, Telephony.Carriers.MCC);
+ if (val != null) {
+ cv.put(Telephony.Carriers.MCC, val);
+ }
+ val = getValueFromCursor(c, Telephony.Carriers.MNC);
+ if (val != null) {
+ cv.put(Telephony.Carriers.MNC, val);
+ }
+ val = getValueFromCursor(c, Telephony.Carriers.APN);
+ if (val != null) {
+ cv.put(Telephony.Carriers.APN, val);
+ }
+ val = getValueFromCursor(c, Telephony.Carriers.USER);
+ if (val != null) {
+ cv.put(Telephony.Carriers.USER, val);
+ }
+ val = getValueFromCursor(c, Telephony.Carriers.SERVER);
+ if (val != null) {
+ cv.put(Telephony.Carriers.SERVER, val);
+ }
+ val = getValueFromCursor(c, Telephony.Carriers.PASSWORD);
+ if (val != null) {
+ cv.put(Telephony.Carriers.PASSWORD, val);
+ }
+ val = getValueFromCursor(c, Telephony.Carriers.PROXY);
+ if (val != null) {
+ cv.put(Telephony.Carriers.PROXY, val);
+ }
+ val = getValueFromCursor(c, Telephony.Carriers.PORT);
+ if (val != null) {
+ cv.put(Telephony.Carriers.PORT, val);
+ }
+ val = getValueFromCursor(c, Telephony.Carriers.MMSPROXY);
+ if (val != null) {
+ cv.put(Telephony.Carriers.MMSPROXY, val);
+ }
+ val = getValueFromCursor(c, Telephony.Carriers.MMSPORT);
+ if (val != null) {
+ cv.put(Telephony.Carriers.MMSPORT, val);
+ }
+ val = getValueFromCursor(c, Telephony.Carriers.MMSC);
+ if (val != null) {
+ cv.put(Telephony.Carriers.MMSC, val);
+ }
+ val = getValueFromCursor(c, Telephony.Carriers.TYPE);
+ if (val != null) {
+ cv.put(Telephony.Carriers.TYPE, val);
+ }
+ val = getValueFromCursor(c, Telephony.Carriers.PROTOCOL);
+ if (val != null) {
+ cv.put(Telephony.Carriers.PROTOCOL, val);
+ }
+ val = getValueFromCursor(c, Telephony.Carriers.ROAMING_PROTOCOL);
+ if (val != null) {
+ cv.put(Telephony.Carriers.ROAMING_PROTOCOL, val);
+ }
+ val = getValueFromCursor(c, Telephony.Carriers.MVNO_TYPE);
+ if (val != null) {
+ cv.put(Telephony.Carriers.MVNO_TYPE, val);
+ }
+ val = getValueFromCursor(c, Telephony.Carriers.MVNO_MATCH_DATA);
+ if (val != null) {
+ cv.put(Telephony.Carriers.MVNO_MATCH_DATA, val);
+ }
+
+ // bool/int vals
+ val = getValueFromCursor(c, Telephony.Carriers.AUTH_TYPE);
+ if (val != null) {
+ cv.put(Telephony.Carriers.AUTH_TYPE, new Integer(val));
+ }
+ val = getValueFromCursor(c, Telephony.Carriers.CURRENT);
+ if (val != null) {
+ cv.put(Telephony.Carriers.CURRENT, new Integer(val));
+ }
+ val = getValueFromCursor(c, Telephony.Carriers.CARRIER_ENABLED);
+ if (val != null) {
+ cv.put(Telephony.Carriers.CARRIER_ENABLED, new Integer(val));
+ }
+ val = getValueFromCursor(c, Telephony.Carriers.BEARER);
+ if (val != null) {
+ cv.put(Telephony.Carriers.BEARER, new Integer(val));
+ }
+ val = getValueFromCursor(c, Telephony.Carriers.SUBSCRIPTION_ID);
+ if (val != null) {
+ cv.put(Telephony.Carriers.SUBSCRIPTION_ID, new Integer(val));
+ }
+ val = getValueFromCursor(c, Telephony.Carriers.PROFILE_ID);
+ if (val != null) {
+ cv.put(Telephony.Carriers.PROFILE_ID, new Integer(val));
+ }
+ val = getValueFromCursor(c, Telephony.Carriers.MODEM_COGNITIVE);
+ if (val != null) {
+ cv.put(Telephony.Carriers.MODEM_COGNITIVE, new Integer(val));
+ }
+ val = getValueFromCursor(c, Telephony.Carriers.MAX_CONNS);
+ if (val != null) {
+ cv.put(Telephony.Carriers.MAX_CONNS, new Integer(val));
+ }
+ val = getValueFromCursor(c, Telephony.Carriers.WAIT_TIME);
+ if (val != null) {
+ cv.put(Telephony.Carriers.WAIT_TIME, new Integer(val));
+ }
+ val = getValueFromCursor(c, Telephony.Carriers.MAX_CONNS_TIME);
+ if (val != null) {
+ cv.put(Telephony.Carriers.MAX_CONNS_TIME, new Integer(val));
+ }
+ val = getValueFromCursor(c, Telephony.Carriers.MTU);
+ if (val != null) {
+ cv.put(Telephony.Carriers.MTU, new Integer(val));
+ }
+
+ // New USER_EDITED column. Default value (USER_EDITED_UNTOUCHED) will
+ // be used for all rows except for non-mvno entries for plmns indicated
+ // by resource: those will be set to USER_EDITED_EDITED to preserve
+ // their current values
+ val = c.getString(c.getColumnIndex(Telephony.Carriers.NUMERIC));
+ for (String s : persistApnsForPlmns) {
+ if (!TextUtils.isEmpty(val) && val.equals(s) &&
+ (!cv.containsKey(Telephony.Carriers.MVNO_TYPE) ||
+ TextUtils.isEmpty(cv.getAsString(Telephony.Carriers.
+ MVNO_TYPE)))) {
+ cv.put(Telephony.Carriers.USER_EDITED, USER_EDITED_EDITED);
+ break;
+ }
+ }
+
+ try {
+ db.insertWithOnConflict(CARRIERS_TABLE_TMP, null, cv,
+ SQLiteDatabase.CONFLICT_ABORT);
+ if (VDBG) {
+ log("dbh.onUpgrade: db.insert returned >= 0; insert "
+ + "successful for cv " + cv);
+ }
+ } catch (SQLException e) {
+ if (VDBG) log("dbh.onUpgrade insertWithOnConflict exception " + e);
+ // 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);
+ oldRow.close();
+ }
+ }
+ }
+ c.close();
+ }
+
+ db.execSQL("DROP TABLE IF EXISTS " + CARRIERS_TABLE);
+
+ db.execSQL("ALTER TABLE " + CARRIERS_TABLE_TMP + " rename to " + CARRIERS_TABLE +
+ ";");
+
+ oldVersion = 14 << 16 | 6;
+
+ if (VDBG) {
+ String[] proj = {"_id"};
+ c = db.query(CARRIERS_TABLE, proj, null, null, null, null, null);
+ log("dbh.onUpgrade:- after upgrading total number of rows: " + c.getCount());
+ c.close();
+ c = db.query(CARRIERS_TABLE, proj, "user_edited=" + USER_EDITED_UNTOUCHED, null,
+ null, null, null);
+ log("dbh.onUpgrade:- after upgrading total number of rows with user_edited="
+ + USER_EDITED_UNTOUCHED + ": " + c.getCount());
+ c.close();
+ c = db.query(CARRIERS_TABLE, proj, "user_edited!=" + USER_EDITED_UNTOUCHED,
+ null, null, null, null);
+ log("dbh.onUpgrade:- after upgrading total number of rows with user_edited!="
+ + USER_EDITED_UNTOUCHED + ": " + c.getCount());
+ c.close();
+ }
+ }
if (DBG) {
log("dbh.onUpgrade:- db=" + db + " oldV=" + oldVersion + " newV=" + newVersion);
}
}
+ private String getValueFromCursor(Cursor c, String key) {
+ String fromCursor = c.getString(c.getColumnIndex(key));
+ return !TextUtils.isEmpty(fromCursor) ? fromCursor : null;
+ }
+
/**
* Gets the next row of apn values.
*
@@ -432,33 +686,57 @@
String mnc = parser.getAttributeValue(null, "mnc");
String numeric = mcc + mnc;
- map.put(Telephony.Carriers.NUMERIC,numeric);
+ map.put(Telephony.Carriers.NUMERIC, numeric);
map.put(Telephony.Carriers.MCC, mcc);
map.put(Telephony.Carriers.MNC, mnc);
map.put(Telephony.Carriers.NAME, parser.getAttributeValue(null, "carrier"));
- map.put(Telephony.Carriers.APN, parser.getAttributeValue(null, "apn"));
- map.put(Telephony.Carriers.USER, parser.getAttributeValue(null, "user"));
- map.put(Telephony.Carriers.SERVER, parser.getAttributeValue(null, "server"));
- map.put(Telephony.Carriers.PASSWORD, parser.getAttributeValue(null, "password"));
- // do not add NULL to the map so that insert() will set the default value
+ // do not add NULL to the map so that default values can be inserted in db
+ String apn = parser.getAttributeValue(null, "apn");
+ if (apn != null) {
+ map.put(Telephony.Carriers.APN, apn);
+ }
+
+ String user = parser.getAttributeValue(null, "user");
+ if (user != null) {
+ map.put(Telephony.Carriers.USER, user);
+ }
+
+ String server = parser.getAttributeValue(null, "server");
+ if (server != null) {
+ map.put(Telephony.Carriers.SERVER, server);
+ }
+
+ String password = parser.getAttributeValue(null, "password");
+ if (password != null) {
+ map.put(Telephony.Carriers.PASSWORD, password);
+ }
+
String proxy = parser.getAttributeValue(null, "proxy");
if (proxy != null) {
map.put(Telephony.Carriers.PROXY, proxy);
}
+
String port = parser.getAttributeValue(null, "port");
if (port != null) {
map.put(Telephony.Carriers.PORT, port);
}
+
String mmsproxy = parser.getAttributeValue(null, "mmsproxy");
if (mmsproxy != null) {
map.put(Telephony.Carriers.MMSPROXY, mmsproxy);
}
+
String mmsport = parser.getAttributeValue(null, "mmsport");
if (mmsport != null) {
map.put(Telephony.Carriers.MMSPORT, mmsport);
}
- map.put(Telephony.Carriers.MMSC, parser.getAttributeValue(null, "mmsc"));
+
+ String mmsc = parser.getAttributeValue(null, "mmsc");
+ if (mmsc != null) {
+ map.put(Telephony.Carriers.MMSC, mmsc);
+ }
+
String type = parser.getAttributeValue(null, "type");
if (type != null) {
map.put(Telephony.Carriers.TYPE, type);
@@ -548,7 +826,7 @@
if (row == null) {
throw new XmlPullParserException("Expected 'apn' tag", parser, null);
}
- insertAddingDefaults(db, CARRIERS_TABLE, row);
+ insertAddingDefaults(db, row);
XmlUtils.nextElement(parser);
}
db.setTransactionSuccessful();
@@ -565,117 +843,232 @@
}
static public ContentValues setDefaultValue(ContentValues values) {
- if (!values.containsKey(Telephony.Carriers.NAME)) {
- values.put(Telephony.Carriers.NAME, "");
- }
- if (!values.containsKey(Telephony.Carriers.APN)) {
- values.put(Telephony.Carriers.APN, "");
- }
- if (!values.containsKey(Telephony.Carriers.PORT)) {
- values.put(Telephony.Carriers.PORT, "");
- }
- if (!values.containsKey(Telephony.Carriers.PROXY)) {
- values.put(Telephony.Carriers.PROXY, "");
- }
- if (!values.containsKey(Telephony.Carriers.USER)) {
- values.put(Telephony.Carriers.USER, "");
- }
- if (!values.containsKey(Telephony.Carriers.SERVER)) {
- values.put(Telephony.Carriers.SERVER, "");
- }
- if (!values.containsKey(Telephony.Carriers.PASSWORD)) {
- values.put(Telephony.Carriers.PASSWORD, "");
- }
- if (!values.containsKey(Telephony.Carriers.MMSPORT)) {
- values.put(Telephony.Carriers.MMSPORT, "");
- }
- if (!values.containsKey(Telephony.Carriers.MMSPROXY)) {
- values.put(Telephony.Carriers.MMSPROXY, "");
- }
- if (!values.containsKey(Telephony.Carriers.AUTH_TYPE)) {
- values.put(Telephony.Carriers.AUTH_TYPE, -1);
- }
- if (!values.containsKey(Telephony.Carriers.PROTOCOL)) {
- values.put(Telephony.Carriers.PROTOCOL, "IP");
- }
- if (!values.containsKey(Telephony.Carriers.ROAMING_PROTOCOL)) {
- values.put(Telephony.Carriers.ROAMING_PROTOCOL, "IP");
- }
- if (!values.containsKey(Telephony.Carriers.CARRIER_ENABLED)) {
- values.put(Telephony.Carriers.CARRIER_ENABLED, true);
- }
- if (!values.containsKey(Telephony.Carriers.BEARER)) {
- values.put(Telephony.Carriers.BEARER, 0);
- }
- if (!values.containsKey(Telephony.Carriers.MVNO_TYPE)) {
- values.put(Telephony.Carriers.MVNO_TYPE, "");
- }
- if (!values.containsKey(Telephony.Carriers.MVNO_MATCH_DATA)) {
- values.put(Telephony.Carriers.MVNO_MATCH_DATA, "");
- }
-
- int subId = SubscriptionManager.getDefaultSubId();
if (!values.containsKey(Telephony.Carriers.SUBSCRIPTION_ID)) {
+ int subId = SubscriptionManager.getDefaultSubId();
values.put(Telephony.Carriers.SUBSCRIPTION_ID, subId);
}
- if (!values.containsKey(Telephony.Carriers.PROFILE_ID)) {
- values.put(Telephony.Carriers.PROFILE_ID, 0);
- }
- if (!values.containsKey(Telephony.Carriers.MODEM_COGNITIVE)) {
- values.put(Telephony.Carriers.MODEM_COGNITIVE, false);
- }
- if (!values.containsKey(Telephony.Carriers.MAX_CONNS)) {
- values.put(Telephony.Carriers.MAX_CONNS, 0);
- }
- if (!values.containsKey(Telephony.Carriers.WAIT_TIME)) {
- values.put(Telephony.Carriers.WAIT_TIME, 0);
- }
- if (!values.containsKey(Telephony.Carriers.MAX_CONNS_TIME)) {
- values.put(Telephony.Carriers.MAX_CONNS_TIME, 0);
- }
-
return values;
}
- private void insertAddingDefaults(SQLiteDatabase db, String table, ContentValues row) {
+ private void insertAddingDefaults(SQLiteDatabase db, ContentValues row) {
row = setDefaultValue(row);
- db.insert(CARRIERS_TABLE, null, row);
+ try {
+ db.insertWithOnConflict(CARRIERS_TABLE, null, row,
+ SQLiteDatabase.CONFLICT_ABORT);
+ if (VDBG) log("dbh.insertAddingDefaults: db.insert returned >= 0; insert " +
+ "successful for cv " + row);
+ } catch (SQLException e) {
+ if (VDBG) log("dbh.insertAddingDefaults: exception " + e);
+ // Insertion failed which could be due to a conflict. Check if that is the case and
+ // update user_edited field accordingly.
+ // Search for the exact same entry and update user_edited field.
+ // If it is USER_EDITED_EDITED change it to USER_EDITED_UNTOUCHED,
+ // and if USER_EDITED_DELETED change it to USER_EDITED_DELETED_BUT_PRESENT.
+ Cursor oldRow = selectConflictingRow(db, CARRIERS_TABLE, row);
+ if (oldRow != null) {
+ // Update the row
+ ContentValues mergedValues = new ContentValues();
+ int user_edited = oldRow.getInt(oldRow.getColumnIndex(
+ Telephony.Carriers.USER_EDITED));
+ int old_user_edited = user_edited;
+ if (user_edited != USER_EDITED_UNTOUCHED) {
+ if (user_edited == USER_EDITED_EDITED) {
+ user_edited = USER_EDITED_UNTOUCHED;
+ } else if (user_edited == USER_EDITED_DELETED) {
+ // user_edited 3 indicates entry has been deleted by user but present in
+ // apn xml file.
+ user_edited = USER_EDITED_DELETED_BUT_PRESENT;
+ }
+ mergedValues.put(Telephony.Carriers.USER_EDITED, user_edited);
+ }
+
+ mergeFieldsAndUpdateDb(db, CARRIERS_TABLE, oldRow, row, mergedValues, false);
+
+ if (VDBG) log("dbh.insertAddingDefaults: old user_edited = " + old_user_edited
+ + " new user_edited = " + user_edited);
+
+ oldRow.close();
+ }
+ }
+ }
+
+ static public void mergeFieldsAndUpdateDb(SQLiteDatabase db, String table, Cursor oldRow,
+ ContentValues newRow, ContentValues mergedValues,
+ boolean useOld) {
+ if (newRow.containsKey(Telephony.Carriers.TYPE)) {
+ // Merge the types
+ String oldType = oldRow.getString(oldRow.getColumnIndex(Telephony.Carriers.TYPE));
+ String newType = newRow.getAsString(Telephony.Carriers.TYPE);
+ if (!oldType.equalsIgnoreCase(newType)) {
+ if (oldType.equals("") || newType.equals("")) {
+ newRow.put(Telephony.Carriers.TYPE, "");
+ } else {
+ // Merge the 2 types
+ String[] oldTypes = oldType.toLowerCase().split(",");
+ String[] newTypes = newType.toLowerCase().split(",");
+ ArrayList<String> mergedTypes = new ArrayList<String>();
+ mergedTypes.addAll(Arrays.asList(oldTypes));
+ for (String s : newTypes) {
+ if (!mergedTypes.contains(s.trim())) {
+ mergedTypes.add(s);
+ }
+ }
+ StringBuilder mergedType = new StringBuilder();
+ for (int i = 0; i < mergedTypes.size(); i++) {
+ mergedType.append((i == 0 ? "" : ",") + mergedTypes.get(i));
+ }
+ newRow.put(Telephony.Carriers.TYPE, mergedType.toString());
+ }
+ }
+ mergedValues.put(Telephony.Carriers.TYPE, newRow.getAsString(
+ Telephony.Carriers.TYPE));
+ }
+
+ if (!useOld) {
+ mergedValues.putAll(newRow);
+ }
+
+ db.update(table, mergedValues, "_id=" + oldRow.getInt(oldRow.getColumnIndex("_id")),
+ null);
+ }
+
+ static public Cursor selectConflictingRow(SQLiteDatabase db, String table,
+ ContentValues row) {
+ // Conflict is possible only when numeric, mnnc, mnc (fields without any default value)
+ // are set in the new row
+ if (row.containsKey(Telephony.Carriers.NUMERIC) ||
+ row.containsKey(Telephony.Carriers.MCC) ||
+ row.containsKey(Telephony.Carriers.MNC)) {
+ loge("dbh.selectConflictingRow: called for non-conflicting row: " + row);
+ return null;
+ }
+
+ String[] columns = { "_id", Telephony.Carriers.TYPE, Telephony.Carriers.USER_EDITED };
+ String selection = "numeric=? AND mcc=? AND mnc=? AND apn=? AND proxy=? AND port=? "
+ + "AND mmsproxy=? AND mmsport=? AND mmsc=? AND carrier_enabled=? AND bearer=? "
+ + "AND mvno_type=? AND mvno_match_data=? AND profile_id=?";
+ int i = 0;
+ String[] selectionArgs = new String[14];
+ selectionArgs[i++] = row.getAsString(Telephony.Carriers.NUMERIC);
+ selectionArgs[i++] = row.getAsString(Telephony.Carriers.MCC);
+ selectionArgs[i++] = row.getAsString(Telephony.Carriers.MNC);
+ selectionArgs[i++] = row.containsKey(Telephony.Carriers.APN) ?
+ row.getAsString(Telephony.Carriers.APN) : "";
+ selectionArgs[i++] = row.containsKey(Telephony.Carriers.PROXY) ?
+ row.getAsString(Telephony.Carriers.PROXY) : "";
+ selectionArgs[i++] = row.containsKey(Telephony.Carriers.PORT) ?
+ row.getAsString(Telephony.Carriers.PORT) : "";
+ selectionArgs[i++] = row.containsKey(Telephony.Carriers.MMSPROXY) ?
+ row.getAsString(Telephony.Carriers.MMSPROXY) : "";
+ selectionArgs[i++] = row.containsKey(Telephony.Carriers.MMSPORT) ?
+ row.getAsString(Telephony.Carriers.MMSPORT) : "";
+ selectionArgs[i++] = row.containsKey(Telephony.Carriers.MMSC) ?
+ row.getAsString(Telephony.Carriers.MMSC) : "";
+ selectionArgs[i++] = row.containsKey(Telephony.Carriers.CARRIER_ENABLED) &&
+ (row.getAsString(Telephony.Carriers.CARRIER_ENABLED).equals("0") ||
+ row.getAsString(Telephony.Carriers.CARRIER_ENABLED).equals("false")) ?
+ "0" : "1";
+ selectionArgs[i++] = row.containsKey(Telephony.Carriers.BEARER) ?
+ row.getAsString(Telephony.Carriers.BEARER) : "0";
+ selectionArgs[i++] = row.containsKey(Telephony.Carriers.MVNO_TYPE) ?
+ row.getAsString(Telephony.Carriers.MVNO_TYPE) : "";
+ selectionArgs[i++] = row.containsKey(Telephony.Carriers.MVNO_MATCH_DATA) ?
+ row.getAsString(Telephony.Carriers.MVNO_MATCH_DATA) : "";
+ selectionArgs[i++] = row.containsKey(Telephony.Carriers.PROFILE_ID) ?
+ row.getAsString(Telephony.Carriers.PROFILE_ID) : "0";
+
+ Cursor c = db.query(table, columns, selection, selectionArgs, null, null, null);
+
+ if (c != null) {
+ if (c.getCount() > 0) {
+ if (VDBG) log("dbh.selectConflictingRow: " + c.getCount() + " conflicting " +
+ "row(s) found");
+ if (c.moveToFirst()) {
+ return c;
+ } else {
+ loge("dbh.selectConflictingRow: moveToFirst() failed");
+ }
+ } else {
+ loge("dbh.selectConflictingRow: " + c.getCount() + " matching row found for " +
+ "cv " + row);
+ }
+ c.close();
+ } else {
+ loge("dbh.selectConflictingRow: Error - c is null; no matching row found for " +
+ "cv " + row);
+ }
+
+ return null;
}
}
@Override
public boolean onCreate() {
- if (VDBG) log("onCreate:+");
mOpenHelper = new DatabaseHelper(getContext());
+
+ // Call getReadableDatabase() to make sure onUpgrade is called
+ if (VDBG) log("onCreate: calling getReadableDatabase to trigger onUpgrade");
+ SQLiteDatabase db = mOpenHelper.getReadableDatabase();
+
+ // Update APN db on build update
+ String newBuildId = SystemProperties.get("ro.build.id", null);
+ if (!TextUtils.isEmpty(newBuildId)) {
+ // Check if build id has changed
+ SharedPreferences sp = getContext().getSharedPreferences(BUILD_ID_FILE,
+ Context.MODE_PRIVATE);
+ String oldBuildId = sp.getString(RO_BUILD_ID, "");
+ if (!newBuildId.equals(oldBuildId)) {
+ if (DBG) log("onCreate: build id changed from " + oldBuildId + " to " +
+ newBuildId);
+ updateApnDb();
+ } else {
+ if (VDBG) log("onCreate: build id did not change: " + oldBuildId);
+ }
+ sp.edit().putString(RO_BUILD_ID, newBuildId).apply();
+ } else {
+ if (VDBG) log("onCreate: newBuildId is empty");
+ }
+
if (VDBG) log("onCreate:- ret true");
return true;
}
private void setPreferredApnId(Long id, int subId) {
+ //todo: remove old PREF_FILE+subId SharedPreferences
SharedPreferences sp = getContext().getSharedPreferences(
- PREF_FILE + subId, Context.MODE_PRIVATE);
+ PREF_FILE, Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sp.edit();
- editor.putLong(COLUMN_APN_ID, id != null ? id.longValue() : -1);
+ editor.putLong(COLUMN_APN_ID + subId, id != null ? id.longValue() : -1);
editor.apply();
}
private long getPreferredApnId(int subId) {
SharedPreferences sp = getContext().getSharedPreferences(
- PREF_FILE + subId, Context.MODE_PRIVATE);
- return sp.getLong(COLUMN_APN_ID, -1);
+ PREF_FILE, Context.MODE_PRIVATE);
+ return sp.getLong(COLUMN_APN_ID + subId, -1);
+ }
+
+ private void deletePreferredApnId() {
+ SharedPreferences sp = getContext().getSharedPreferences(
+ PREF_FILE, Context.MODE_PRIVATE);
+ SharedPreferences.Editor editor = sp.edit();
+ editor.clear();
+ editor.apply();
}
@Override
public Cursor query(Uri url, String[] projectionIn, String selection,
String[] selectionArgs, String sort) {
+ if (VDBG) log("query: url=" + url + ", projectionIn=" + projectionIn + ", selection="
+ + selection + "selectionArgs=" + selectionArgs + ", sort=" + sort);
TelephonyManager mTelephonyManager =
(TelephonyManager)getContext().getSystemService(Context.TELEPHONY_SERVICE);
int subId = SubscriptionManager.getDefaultSubId();
String subIdString;
SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
qb.setStrict(true); // a little protection from injection attacks
- qb.setTables("carriers");
+ qb.setTables(CARRIERS_TABLE);
int match = s_urlMatcher.match(url);
switch (match) {
@@ -692,7 +1085,7 @@
// FIXME alter the selection to pass subId
// selection = selection + "and subId = "
}
- //intentional fall through from above case
+ // intentional fall through from above case
// do nothing
case URL_TELEPHONY: {
break;
@@ -774,6 +1167,17 @@
SQLiteDatabase db = mOpenHelper.getReadableDatabase();
Cursor ret = null;
try {
+ // Exclude entries marked deleted
+ if (CARRIERS_TABLE.equals(qb.getTables())) {
+ if (TextUtils.isEmpty(selection)) {
+ selection = "";
+ } else {
+ selection += " and ";
+ }
+ selection += "user_edited!=" + USER_EDITED_DELETED + " and user_edited!="
+ + USER_EDITED_DELETED_BUT_PRESENT;
+ if (VDBG) log("query: selection modified to " + selection);
+ }
ret = qb.query(db, projectionIn, selection, selectionArgs, null, null, sort);
} catch (SQLException e) {
loge("got exception when querying: " + e);
@@ -841,15 +1245,32 @@
}
values = DatabaseHelper.setDefaultValue(values);
+ values.put(Telephony.Carriers.USER_EDITED, USER_EDITED_EDITED);
- long rowID = db.insert(CARRIERS_TABLE, null, values);
- if (rowID > 0)
- {
- result = ContentUris.withAppendedId(Telephony.Carriers.CONTENT_URI, rowID);
- notify = true;
+ try {
+ // Replace on conflict so that if same APN is present in db with user_edited
+ // as USER_EDITED_UNTOUCHED or USER_EDITED_DELETED, it is replaced with
+ // user_edited USER_EDITED_EDITED
+ long rowID = db.insertWithOnConflict(CARRIERS_TABLE, null, values,
+ SQLiteDatabase.CONFLICT_REPLACE);
+ if (rowID >= 0) {
+ result = ContentUris.withAppendedId(Telephony.Carriers.CONTENT_URI, rowID);
+ notify = true;
+ }
+ if (VDBG) log("insert: inserted " + values.toString() + " rowID = " + rowID);
+ } catch (SQLException e) {
+ log("insert: exception " + e);
+ // 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, values);
+ if (oldRow != null) {
+ ContentValues mergedValues = new ContentValues();
+ DatabaseHelper.mergeFieldsAndUpdateDb(db, CARRIERS_TABLE, oldRow, values,
+ mergedValues, false);
+ oldRow.close();
+ }
}
- if (VDBG) log("inserted " + values.toString() + " rowID = " + rowID);
break;
}
@@ -869,11 +1290,11 @@
case URL_CURRENT:
{
- // null out the previous operator
- db.update("carriers", s_currentNullMap, "current IS NOT NULL", null);
+ // zero out the previous operator
+ db.update(CARRIERS_TABLE, s_currentNullMap, "current!=0", null);
- String numeric = initialValues.getAsString("numeric");
- int updated = db.update("carriers", s_currentSetMap,
+ String numeric = initialValues.getAsString(Telephony.Carriers.NUMERIC);
+ int updated = db.update(CARRIERS_TABLE, s_currentSetMap,
"numeric = '" + numeric + "'", null);
if (updated > 0)
@@ -932,6 +1353,8 @@
{
int count = 0;
int subId = SubscriptionManager.getDefaultSubId();
+ ContentValues cv = new ContentValues();
+ cv.put(Telephony.Carriers.USER_EDITED, USER_EDITED_DELETED);
checkPermission();
@@ -955,7 +1378,8 @@
case URL_TELEPHONY:
{
- count = db.delete(CARRIERS_TABLE, where, whereArgs);
+ // Mark as user deleted instead of deleting
+ count = db.update(CARRIERS_TABLE, cv, where, whereArgs);
break;
}
@@ -974,13 +1398,15 @@
case URL_CURRENT:
{
- count = db.delete(CARRIERS_TABLE, where, whereArgs);
+ // Mark as user deleted instead of deleting
+ count = db.update(CARRIERS_TABLE, cv, where, whereArgs);
break;
}
case URL_ID:
{
- count = db.delete(CARRIERS_TABLE, Telephony.Carriers._ID + "=?",
+ // Mark as user deleted instead of deleting
+ count = db.update(CARRIERS_TABLE, cv, Telephony.Carriers._ID + "=?",
new String[] { url.getLastPathSegment() });
break;
}
@@ -1028,6 +1454,12 @@
break;
}
+ case URL_UPDATE_DB: {
+ updateApnDb();
+ count = 1;
+ break;
+ }
+
default: {
throw new UnsupportedOperationException("Cannot delete that URL: " + url);
}
@@ -1070,7 +1502,13 @@
case URL_TELEPHONY:
{
- count = db.update(CARRIERS_TABLE, values, where, whereArgs);
+ values.put(Telephony.Carriers.USER_EDITED, USER_EDITED_EDITED);
+
+ // Replace on conflict so that if same APN is present in db with user_edited
+ // as USER_EDITED_UNTOUCHED or USER_EDITED_DELETED, it is replaced with
+ // user_edited USER_EDITED_EDITED
+ count = db.updateWithOnConflict(CARRIERS_TABLE, values, where, whereArgs,
+ SQLiteDatabase.CONFLICT_REPLACE);
break;
}
@@ -1090,7 +1528,12 @@
case URL_CURRENT:
{
- count = db.update(CARRIERS_TABLE, values, where, whereArgs);
+ values.put(Telephony.Carriers.USER_EDITED, USER_EDITED_EDITED);
+ // Replace on conflict so that if same APN is present in db with user_edited
+ // as USER_EDITED_UNTOUCHED or USER_EDITED_DELETED, it is replaced with
+ // user_edited USER_EDITED_EDITED
+ count = db.updateWithOnConflict(CARRIERS_TABLE, values, where, whereArgs,
+ SQLiteDatabase.CONFLICT_REPLACE);
break;
}
@@ -1100,8 +1543,13 @@
throw new UnsupportedOperationException(
"Cannot update URL " + url + " with a where clause");
}
- count = db.update(CARRIERS_TABLE, values, Telephony.Carriers._ID + "=?",
- new String[] { url.getLastPathSegment() });
+ values.put(Telephony.Carriers.USER_EDITED, USER_EDITED_EDITED);
+ // Replace on conflict so that if same APN is present in db with user_edited
+ // as USER_EDITED_UNTOUCHED or USER_EDITED_DELETED, it is replaced with
+ // user_edited USER_EDITED_EDITED
+ count = db.updateWithOnConflict(CARRIERS_TABLE, values,
+ Telephony.Carriers._ID + "=?", new String[] { url.getLastPathSegment() },
+ SQLiteDatabase.CONFLICT_REPLACE);
break;
}
@@ -1194,6 +1642,35 @@
mOpenHelper.initDatabase(db);
}
+ private void updateApnDb() {
+ SQLiteDatabase db = mOpenHelper.getWritableDatabase();
+
+ try {
+ if (VDBG) log("updateApnDb: deleting user_edited=USER_EDITED_UNTOUCHED entries");
+ db.delete(CARRIERS_TABLE, "user_edited=" + USER_EDITED_UNTOUCHED, null);
+ } catch (SQLException e) {
+ loge("got exception when deleting to update: " + e);
+ }
+
+ // Delete preferred APN for all subIds
+ long currentPreferredApnId = getPreferredApnId(SubscriptionManager.getDefaultSubId());
+ Cursor c = db.query(CARRIERS_TABLE, null, "_id=" + currentPreferredApnId, null, null, null,
+ null);
+ if (VDBG) {
+ log("updateApnDb: currentPreferredApnId = " + currentPreferredApnId);
+ if (c != null && c.getCount() != 0) {
+ c.moveToNext();
+ log("updateApnDb: NAME=" + c.getString(c.getColumnIndex(Telephony.Carriers.NAME)));
+ }
+ }
+ deletePreferredApnId();
+
+ mOpenHelper.initDatabase(db);
+
+ // TODO: restore preference APN ID
+ c.close();
+ }
+
/**
* Log with debug
*